From 383d262d32914e775c63d1ed6233557433206c22 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Oct 29 2019 17:14:58 +0000 Subject: import qemu-kvm-ma-2.12.0-37.el7 --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1afb20f --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +SOURCES/bios-256k.bin +SOURCES/kvm-unit-tests.git-4ea7633.tar.bz2 +SOURCES/pxe-e1000e.rom +SOURCES/qemu-2.12.0.tar.xz +SOURCES/rhel6-e1000.rom +SOURCES/rhel6-ne2k_pci.rom +SOURCES/rhel6-pcnet.rom +SOURCES/rhel6-rtl8139.rom +SOURCES/rhel6-virtio.rom diff --git a/.qemu-kvm-ma.metadata b/.qemu-kvm-ma.metadata new file mode 100644 index 0000000..37bd308 --- /dev/null +++ b/.qemu-kvm-ma.metadata @@ -0,0 +1,9 @@ +5678cee702e664634abf28dce0688d01683611dd SOURCES/bios-256k.bin +8d79fca1e904b82272ebf96bbb65f858e1c491a9 SOURCES/kvm-unit-tests.git-4ea7633.tar.bz2 +e304721d2b96cdf9dfa89e07947f19ef3e26107e SOURCES/pxe-e1000e.rom +5a62c911b2cebbd41decd5c77c524395212411cf SOURCES/qemu-2.12.0.tar.xz +957fd6b653b4550c6be727385331d58f1381e082 SOURCES/rhel6-e1000.rom +3f183b9c65e959ab346a013828f1d7530bc4a14e SOURCES/rhel6-ne2k_pci.rom +5bf1eb9f40dc52fa2c9bfecd9330af03a49b35f9 SOURCES/rhel6-pcnet.rom +adffc84ebaf9faf982ecb707423395c1630186a4 SOURCES/rhel6-rtl8139.rom +29e633bcdb4ea9604b7bdaaaeaa0a1223774cb1d SOURCES/rhel6-virtio.rom diff --git a/SOURCES/0001-Revert-qemu-pr-helper-use-new-libmultipath-API.patch b/SOURCES/0001-Revert-qemu-pr-helper-use-new-libmultipath-API.patch new file mode 100644 index 0000000..a02426c --- /dev/null +++ b/SOURCES/0001-Revert-qemu-pr-helper-use-new-libmultipath-API.patch @@ -0,0 +1,75 @@ +From 1bd23e669764623512441f335b3a09b46265a9fa Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Tue, 5 Dec 2017 17:28:56 +0100 +Subject: Revert "qemu-pr-helper: use new libmultipath API" + +This reverts commit b3f1c8c413bc83e4a2cc7a63e4eddf9fe6449052. + +We have to revert this change in RHEL 7 as new libmultipath API is +not available. +--- + configure | 12 ++---------- + scsi/qemu-pr-helper.c | 17 +++-------------- + 2 files changed, 5 insertions(+), 24 deletions(-) + +diff --git a/configure b/configure +index 0a19b03..e9243f5 100755 +--- a/configure ++++ b/configure +@@ -3472,17 +3472,9 @@ if test "$mpath" != "no" ; then + #include + unsigned mpath_mx_alloc_len = 1024; + int logsink; +-static struct config *multipath_conf; +-extern struct udev *udev; +-extern struct config *get_multipath_config(void); +-extern void put_multipath_config(struct config *conf); +-struct udev *udev; +-struct config *get_multipath_config(void) { return multipath_conf; } +-void put_multipath_config(struct config *conf) { } +- + int main(void) { +- udev = udev_new(); +- multipath_conf = mpath_lib_init(); ++ struct udev *udev = udev_new(); ++ mpath_lib_init(udev); + return 0; + } + EOF +diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c +index d0f8317..7a29e64 100644 +--- a/scsi/qemu-pr-helper.c ++++ b/scsi/qemu-pr-helper.c +@@ -276,26 +276,15 @@ static void dm_init(void) + + /* Variables required by libmultipath and libmpathpersist. */ + QEMU_BUILD_BUG_ON(PR_HELPER_DATA_SIZE > MPATH_MAX_PARAM_LEN); +-static struct config *multipath_conf; + unsigned mpath_mx_alloc_len = PR_HELPER_DATA_SIZE; + int logsink; +-struct udev *udev; +- +-extern struct config *get_multipath_config(void); +-struct config *get_multipath_config(void) +-{ +- return multipath_conf; +-} +- +-extern void put_multipath_config(struct config *conf); +-void put_multipath_config(struct config *conf) +-{ +-} + + static void multipath_pr_init(void) + { ++ static struct udev *udev; ++ + udev = udev_new(); +- multipath_conf = mpath_lib_init(); ++ mpath_lib_init(udev); + } + + static int is_mpath(int fd) +-- +1.8.3.1 + diff --git a/SOURCES/0003-Initial-redhat-build.patch b/SOURCES/0003-Initial-redhat-build.patch new file mode 100644 index 0000000..8da937f --- /dev/null +++ b/SOURCES/0003-Initial-redhat-build.patch @@ -0,0 +1,1015 @@ +From ec05305843e6fb1cf3a8bc1dfab7b7e3d9edce4e Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Mon, 11 Sep 2017 07:11:00 +0200 +Subject: Initial redhat build + +This patch introduces redhat build structure in redhat subdirectory. In addition, +several issues are fixed in QEMU tree: + +- Change of app name for sasl_server_init in VNC code from qemu to qemu-kvm + - As we use qemu-kvm as name in all places, this is updated to be consistent +- Man page renamed from qemu to qemu-kvm + - man page is installed using make install so we have to fix it in qemu tree +- Added live-block-migration configuration option support + - Downstream differentiation support +- Use "/share/qemu-kvm" as SHARE_SUFFIX + - We reconfigured our share to qemu-kvm to be consistent with used name +- Added .gitpublish configuration file + - Support for git publish has to be stored in repository root + +-- +Rebase notes (2.12.0): +- Not packaging hppa-firmware.img +- Disable vxhs.o in block/Makefile.obj +- Disable ppc64 builds +- Removed acpi-dsdt.aml (upstream) + +Rebase notes (2.11.0): +- Removed --with-pixman configure option (upstream) +- Disabled multipath for qemu-pr-helper (unsupported API) +- null-co whitelisting moved to this patch +- make check enabled +- conditionaly disable query-block-jobs for qmp-test.c + +Rebase notes (2.10.0): +- live_block_migration option added upstream +- moved qmp-spec.txt file (upstream) +- added s390-netboot.img (added upstream) +- removed qemu_vga.ndrv (added upstream) +- switch to (rhevh-)rhel-7.5-candidate +- switched differentiation defaults +- moved binary files to separate commit + +Rebase notes (2.9.0): +- documentation files handling changes (upstrem) +- removed --enable-colo option and --disable-archipelago (upstream) +- bump BuildRequires versions +- new mandatory argument for tracetool.py (upstream) +- updated RHEL 6 roms +- switch from sha1sum to sha256sum +- Moved adding rhel6-e1000.rom from machine types commit +- Moved adding pxe-e1000e.rom from device disable commit +- Use rdma-core instead of librdmacm +- Add upstream tarballs tar.xz to .gitignore +- Updated git-backport-diff script + +Rebase notes (2.8.0): +- removed vhdx option (upstream) +- qemu-tech.html merged to qemu-doc.html (upstream) +- removed skiboot.lid firmware +- Changed tracetool.py parameters +- Added support for easy z-stream switch + +Rebase notes (2.7.0): +- removed kvm_stat +- added efi-e1000e.rom +- added efi-vmxnet.rom +- added linuxboot_dma.bin +- trace-events renamed to trace-events-all +- reverted dependency to seccomp on aarch64 +- Add ipxe-qemu-roms ad build dependency to pass tests + +Rebase notes (2.6.0): +- removed q35-acpi-dsdt.aml +- add enable-gcrypt option + +Rebase notes (2.5.0): +- New seccomp hadling in configure +- New condition format in migration/migration.c +- libcacard extracted +- vnc fixes +- libsecomp for aarch64 requirements changed downstream + +Rebase notes (2.4.0): +- remove --enable-ws-vnc +- use error_setg instead of error_set in migration/migration.c +- remove target-x86_64.conf +- create /etc/qemu-kvm on copying of bridge.conf +- disabled opengl +- rebased to version 2.3.0-30.el7 + +Merged patches (rebase 2.11.0) +- ce6e8e5b8a redhat/qemu-kvm.spec.template: Enable seccomp on s390x, too +- 8629f208c6 redhat: Remove qemu.binfmt from the downstream repository +- b889ce1c40 Disable build of qemu-kvm-ma for x86_64 +- 4506913c42 redhat: add CONFIG_RHV flag +- 21ecaec46f s390x: vm.allocate_pgste sysctl is no longer needed +- 78a1864d99 Update build_configure for 2.10.0 options +- decf881320 redhat: Provide s390x specific /etc/modprobe.d/kvm.conf +- e0cd3138cc redhat/qemu-kvm.spec: Use the freshly built s390-ccw.img firmware image +- 7af6b9a4fa redhat: install generic kvm.conf except for s390 and x86 architectures +- c4290f50bb redhat: fix rh-srpm target +- 8943f52e8b Package qemu-block-drivers manpage +- 88b41044d6 update spec to build and install qemu-pr-helper +- 34ca391 redhat: Fix permissions of /dev/kvm on a freshly booted s390x system + +Merged patches (rebase 2.10.0) +- feefd46 qemu-kvm.spec: Enable s390x build +- 985051e Removing texi2html from build requirements +- 7c64a2a Update ignore files for redhat usage +- 8f9a95a Disable replication feature +- 1b7bbc5 block/vxhs: modularize VXHS via g_module +- 7511527 Remove the dependencies to seavgabios-bin and ipxe-roms-qemu on s390x +- aa0891c Downstream: Don't disable SMT on POWER9 hosts +- a13a0e9 Update configuration for qemu 2.9 +- bbf46dd disable pulseaudio and alsa +- 9124839 redhat/Makefile: honor BREW_FLAGS like the kernel +- 53c03bd copy SLIT test reference blobs into tests directory +- c4c77e6 Differentiation support +- f1ec0e8 configure: allow to disable VT-d emulation +- b972023 Disable VT-d for rhel builds +- 29a0414 RHEL Diff.: Add option in configure to disable live block ops +- 1f33b29 RHEL Diff.: Unregister live block operations +- c590551 RHEL Diff.: Disable live block operations in HMP monitor +- c7e208f RHEL Diff.: Add rpm spec options for live block ops +- 733af5c pegas: add rpm spec options for vhost-user +- ff16138 Add support for local build +- fb426d4 qemu-kvm.spec: Configure vm.allocate_pgste for s390x + +Merged patches (rebase 2.9.0) +- 9c7ab94 Enable seccomp for ppc64/ppc64le architecture +- f6d7e9d Update qemu-kvm package Summary and Description +- a9e55b6 Disable usbredir and libcacard for unsupported architectures +- 0218220 Update configuration for 2.8.0 release + +Merged patches (rebase 2.7.0) +- 2be6077 Fix SLOF dependency +- dc58590 spec: Remove dependency to ipxe-roms-qemu for aarch64 +- 357ef43 spec: link sgabios.bin only for x86_64 +- 08d82cc spec: Update rules before triggering for kvm device +- 8980a76 spec: Do not package ivshmem-server and ivshmem-client +- 027067c spec: add a sample kvm.conf to enable Nested Virtualization +- ba2ba30 Adjust locked memory limits to allow unprivileged VMs on Power +- e9740b0 Increase locked memory limit for all users, not just kvm group +- 8c301be add vgabios-virtio.bin symlink +- 4d03723 usb: enable streams support +- 2a9363e Add install dependency required for usb streams +- 9a54442 Add dump-guest-memory.py to all archs +- 73fffc9 add e1000e ipxe rom symlink +- aaaa2a9 Add 'luks' to block driver whitelist +- c78c3a8 redhat: switch from gcrypt to nettle for crypto +- bb51a69 redhat: include username and date in RPM N-E-V-R for scratch builds +- 21ecaec s390x: vm.allocate_pgste sysctl is no longer needed +- 78a1864 Update build_configure for 2.10.0 options + +Merged patches (rebase 2.4.0) +- 9201274 spec: Remove obsolete differentiation code +- a938a8c spec: Use external configuration script +- 5ca8d0e spec: Use configure options to prevent default resolution +- 5dca391 spec: Ship complete QMP documentation files +- 7899edd aarch64: allow --enable-seccomp +- a56fb9c aarch64: redhat spec: enable seccomp +- a9571e6 rhel: Update package version for SLOF dependency +- 25c70c4 configure: Add support for tcmalloc +- db72485 Change fsreeze-hook default location +- 14b8a9e redhat: add kvm-unit-tests tarball to environment +- 5ee4238 spec: Build tscdeadline_latency.flat from kvm-unit-tests +- 6ba800b Downstream-only: Start kvm-setup service before libvirtd service +- 59b43d6 Do not stop qemu-guest-agent service on target switch +- 4d851fa provide vhost module config file with max_mem_regions set to 509 +- 0b18027 spec: Require proper version of SLOF +- 3c436c7 Fix rh-brew-aarch64, rh-brew-ppc rh-brew-ga-ppc target + +(cherry picked from commit ba7591ec4a0906121d15ffbf740580bd79ec5814) +(cherry picked from commit c984102495c0cee355158ecc382ad5e44b23b16c) + +Conflicts: + .gitpublish + configure + hw/i386/Makefile.objs + +(cherry picked from commit bea8f0ccd9fb65aefe7f19b3c4cc849c9e4c1bff) +(cherry picked from commit 7bb63d45c5ed63943718d60382c140fbbba270cb) +(cherry picked from commit a451d845da0e2f872b07ae57fd328adb8116f6cc) +(cherry picked from commit db5d48c968290ffc47ef8662d6ee1fece967acac) +--- + .gitignore | 1 + + .gitpublish | 65 +- + Makefile | 2 +- + block/Makefile.objs | 2 +- + block/vxhs.c | 122 +- + configure | 65 +- + hmp-commands-info.hx | 4 + + hmp-commands.hx | 12 + + hmp.c | 12 + + include/block/vxhs_shim.h | 143 + + monitor.c | 16 + + os-posix.c | 2 +- + redhat/.gitignore | 5 + + redhat/80-kvm.rules | 1 + + redhat/85-kvm.preset | 5 + + redhat/95-kvm-memlock.conf | 10 + + redhat/99-qemu-guest-agent.rules | 2 + + redhat/Makefile | 95 + + redhat/Makefile.common | 57 + + redhat/Makefile.local | 76 + + redhat/README.rhel6-gpxe-source | 9 + + redhat/bridge.conf | 1 + + redhat/build_configure.sh | 167 + + redhat/ksm.service | 13 + + redhat/ksm.sysconfig | 4 + + redhat/ksmctl.c | 77 + + redhat/ksmtuned | 138 + + redhat/ksmtuned.conf | 21 + + redhat/ksmtuned.service | 12 + + redhat/kvm-s390x.conf | 7 + + redhat/kvm-setup | 40 + + redhat/kvm-setup.service | 14 + + redhat/kvm-x86.conf | 12 + + redhat/kvm.conf | 3 + + redhat/kvm.modules | 21 + + redhat/qemu-ga.sysconfig | 19 + + redhat/qemu-guest-agent.service | 20 + + redhat/qemu-kvm.spec.template | 6903 ++++++++++++++++++++++++++++++++++++ + redhat/qemu-pr-helper.service | 15 + + redhat/qemu-pr-helper.socket | 9 + + redhat/rpmbuild/BUILD/.gitignore | 2 + + redhat/rpmbuild/RPMS/.gitignore | 2 + + redhat/rpmbuild/SOURCES/.gitignore | 2 + + redhat/rpmbuild/SPECS/.gitignore | 2 + + redhat/rpmbuild/SRPMS/.gitignore | 2 + + redhat/scripts/frh.py | 26 + + redhat/scripts/git-backport-diff | 327 ++ + redhat/scripts/git-compile-check | 215 ++ + redhat/scripts/process-patches.sh | 81 + + redhat/scripts/tarball_checksum.sh | 3 + + redhat/vhost.conf | 3 + + tests/qmp-test.c | 3 + + ui/vnc.c | 2 +- + 53 files changed, 8776 insertions(+), 96 deletions(-) + create mode 100644 include/block/vxhs_shim.h + create mode 100644 redhat/.gitignore + create mode 100644 redhat/80-kvm.rules + create mode 100644 redhat/85-kvm.preset + create mode 100644 redhat/95-kvm-memlock.conf + create mode 100644 redhat/99-qemu-guest-agent.rules + create mode 100644 redhat/Makefile + create mode 100644 redhat/Makefile.common + create mode 100644 redhat/Makefile.local + create mode 100644 redhat/README.rhel6-gpxe-source + create mode 100644 redhat/bridge.conf + create mode 100755 redhat/build_configure.sh + create mode 100644 redhat/ksm.service + create mode 100644 redhat/ksm.sysconfig + create mode 100644 redhat/ksmctl.c + create mode 100644 redhat/ksmtuned + create mode 100644 redhat/ksmtuned.conf + create mode 100644 redhat/ksmtuned.service + create mode 100644 redhat/kvm-s390x.conf + create mode 100644 redhat/kvm-setup + create mode 100644 redhat/kvm-setup.service + create mode 100644 redhat/kvm-x86.conf + create mode 100644 redhat/kvm.conf + create mode 100644 redhat/kvm.modules + create mode 100644 redhat/qemu-ga.sysconfig + create mode 100644 redhat/qemu-guest-agent.service + create mode 100644 redhat/qemu-kvm.spec.template + create mode 100644 redhat/qemu-pr-helper.service + create mode 100644 redhat/qemu-pr-helper.socket + create mode 100644 redhat/rpmbuild/BUILD/.gitignore + create mode 100644 redhat/rpmbuild/RPMS/.gitignore + create mode 100644 redhat/rpmbuild/SOURCES/.gitignore + create mode 100644 redhat/rpmbuild/SPECS/.gitignore + create mode 100644 redhat/rpmbuild/SRPMS/.gitignore + create mode 100755 redhat/scripts/frh.py + create mode 100755 redhat/scripts/git-backport-diff + create mode 100755 redhat/scripts/git-compile-check + create mode 100755 redhat/scripts/process-patches.sh + create mode 100755 redhat/scripts/tarball_checksum.sh + create mode 100644 redhat/vhost.conf + +diff --git a/Makefile b/Makefile +index d71dd5b..89ba4c5 100644 +--- a/Makefile ++++ b/Makefile +@@ -804,7 +804,7 @@ install-doc: $(DOCS) + $(INSTALL_DATA) docs/interop/qemu-qmp-ref.txt "$(DESTDIR)$(qemu_docdir)" + ifdef CONFIG_POSIX + $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1" +- $(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1" ++ $(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1/qemu-kvm.1" + $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7" + $(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7" + $(INSTALL_DATA) docs/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7" +diff --git a/block/Makefile.objs b/block/Makefile.objs +index d644bac..c0693fc 100644 +--- a/block/Makefile.objs ++++ b/block/Makefile.objs +@@ -20,7 +20,7 @@ block-obj-$(CONFIG_LIBNFS) += nfs.o + block-obj-$(CONFIG_CURL) += curl.o + block-obj-$(CONFIG_RBD) += rbd.o + block-obj-$(CONFIG_GLUSTERFS) += gluster.o +-block-obj-$(CONFIG_VXHS) += vxhs.o ++#block-obj-$(CONFIG_VXHS) += vxhs.o + block-obj-$(CONFIG_LIBSSH2) += ssh.o + block-obj-y += accounting.o dirty-bitmap.o + block-obj-y += write-threshold.o +diff --git a/block/vxhs.c b/block/vxhs.c +index 75cc6c8..a18154c 100644 +--- a/block/vxhs.c ++++ b/block/vxhs.c +@@ -9,7 +9,8 @@ + */ + + #include "qemu/osdep.h" +-#include ++#include "block/vxhs_shim.h" ++#include + #include + #include "block/block_int.h" + #include "qapi/qmp/qerror.h" +@@ -58,6 +59,96 @@ typedef struct BDRVVXHSState { + char *tlscredsid; /* tlscredsid */ + } BDRVVXHSState; + ++#define LIBVXHS_FULL_PATHNAME "/usr/lib64/qemu/libvxhs.so.1" ++static bool libvxhs_loaded; ++static GModule *libvxhs_handle; ++ ++static LibVXHSFuncs libvxhs; ++ ++typedef struct LibVXHSSymbols { ++ const char *name; ++ gpointer *addr; ++} LibVXHSSymbols; ++ ++static LibVXHSSymbols libvxhs_symbols[] = { ++ {"iio_init", (gpointer *) &libvxhs.iio_init}, ++ {"iio_fini", (gpointer *) &libvxhs.iio_fini}, ++ {"iio_min_version", (gpointer *) &libvxhs.iio_min_version}, ++ {"iio_max_version", (gpointer *) &libvxhs.iio_max_version}, ++ {"iio_open", (gpointer *) &libvxhs.iio_open}, ++ {"iio_close", (gpointer *) &libvxhs.iio_close}, ++ {"iio_writev", (gpointer *) &libvxhs.iio_writev}, ++ {"iio_readv", (gpointer *) &libvxhs.iio_readv}, ++ {"iio_ioctl", (gpointer *) &libvxhs.iio_ioctl}, ++ {NULL} ++}; ++ ++static void bdrv_vxhs_set_funcs(GModule *handle, Error **errp) ++{ ++ int i = 0; ++ while (libvxhs_symbols[i].name) { ++ const char *name = libvxhs_symbols[i].name; ++ if (!g_module_symbol(handle, name, libvxhs_symbols[i].addr)) { ++ error_setg(errp, "%s could not be loaded from libvxhs: %s", ++ name, g_module_error()); ++ return; ++ } ++ ++i; ++ } ++} ++ ++static void bdrv_vxhs_load_libs(Error **errp) ++{ ++ Error *local_err = NULL; ++ int32_t ver; ++ ++ if (libvxhs_loaded) { ++ return; ++ } ++ ++ if (!g_module_supported()) { ++ error_setg(errp, "modules are not supported on this platform: %s", ++ g_module_error()); ++ return; ++ } ++ ++ libvxhs_handle = g_module_open(LIBVXHS_FULL_PATHNAME, ++ G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); ++ if (!libvxhs_handle) { ++ error_setg(errp, "error loading libvxhs: %s", g_module_error()); ++ return; ++ } ++ ++ g_module_make_resident(libvxhs_handle); ++ ++ bdrv_vxhs_set_funcs(libvxhs_handle, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return; ++ } ++ ++ /* Now check to see if the libvxhs we are using here is supported ++ * by the loaded version */ ++ ++ ver = (*libvxhs.iio_min_version)(); ++ if (ver > QNIO_VERSION) { ++ error_setg(errp, "Trying to use libvxhs version %"PRId32" API, but " ++ "only %"PRId32" or newer is supported by %s", ++ QNIO_VERSION, ver, LIBVXHS_FULL_PATHNAME); ++ return; ++ } ++ ++ ver = (*libvxhs.iio_max_version)(); ++ if (ver < QNIO_VERSION) { ++ error_setg(errp, "Trying to use libvxhs version %"PRId32" API, but " ++ "only %"PRId32" or earlier is supported by %s", ++ QNIO_VERSION, ver, LIBVXHS_FULL_PATHNAME); ++ return; ++ } ++ ++ libvxhs_loaded = true; ++} ++ + static void vxhs_complete_aio_bh(void *opaque) + { + VXHSAIOCB *acb = opaque; +@@ -219,7 +310,7 @@ static void vxhs_parse_filename(const char *filename, QDict *options, + static int vxhs_init_and_ref(void) + { + if (vxhs_ref++ == 0) { +- if (iio_init(QNIO_VERSION, vxhs_iio_callback)) { ++ if ((*libvxhs.iio_init)(QNIO_VERSION, vxhs_iio_callback)) { + return -ENODEV; + } + } +@@ -229,7 +320,7 @@ static int vxhs_init_and_ref(void) + static void vxhs_unref(void) + { + if (--vxhs_ref == 0) { +- iio_fini(); ++ (*libvxhs.iio_fini)(); + } + } + +@@ -299,8 +390,17 @@ static int vxhs_open(BlockDriverState *bs, QDict *options, + char *client_key = NULL; + char *client_cert = NULL; + ++ bdrv_vxhs_load_libs(&local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ /* on error, cannot cleanup because the iio_fini() function ++ * is not loaded */ ++ return -EINVAL; ++ } ++ + ret = vxhs_init_and_ref(); + if (ret < 0) { ++ error_setg(&local_err, "libvxhs iio_init() failed"); + ret = -EINVAL; + goto out; + } +@@ -385,8 +485,8 @@ static int vxhs_open(BlockDriverState *bs, QDict *options, + /* + * Open qnio channel to storage agent if not opened before + */ +- dev_handlep = iio_open(of_vsa_addr, s->vdisk_guid, 0, +- cacert, client_key, client_cert); ++ dev_handlep = (*libvxhs.iio_open)(of_vsa_addr, s->vdisk_guid, 0, ++ cacert, client_key, client_cert); + if (dev_handlep == NULL) { + trace_vxhs_open_iio_open(of_vsa_addr); + ret = -ENODEV; +@@ -450,12 +550,12 @@ static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, int64_t sector_num, + + switch (iodir) { + case VDISK_AIO_WRITE: +- ret = iio_writev(dev_handle, acb, qiov->iov, qiov->niov, +- offset, (uint64_t)size, iio_flags); ++ ret = (*libvxhs.iio_writev)(dev_handle, acb, qiov->iov, qiov->niov, ++ offset, (uint64_t)size, iio_flags); + break; + case VDISK_AIO_READ: +- ret = iio_readv(dev_handle, acb, qiov->iov, qiov->niov, +- offset, (uint64_t)size, iio_flags); ++ ret = (*libvxhs.iio_readv)(dev_handle, acb, qiov->iov, qiov->niov, ++ offset, (uint64_t)size, iio_flags); + break; + default: + trace_vxhs_aio_rw_invalid(iodir); +@@ -505,7 +605,7 @@ static void vxhs_close(BlockDriverState *bs) + * Close vDisk device + */ + if (s->vdisk_hostinfo.dev_handle) { +- iio_close(s->vdisk_hostinfo.dev_handle); ++ (*libvxhs.iio_close)(s->vdisk_hostinfo.dev_handle); + s->vdisk_hostinfo.dev_handle = NULL; + } + +@@ -527,7 +627,7 @@ static int64_t vxhs_get_vdisk_stat(BDRVVXHSState *s) + int ret = 0; + void *dev_handle = s->vdisk_hostinfo.dev_handle; + +- ret = iio_ioctl(dev_handle, IOR_VDISK_STAT, &vdisk_size, 0); ++ ret = (*libvxhs.iio_ioctl)(dev_handle, IOR_VDISK_STAT, &vdisk_size, 0); + if (ret < 0) { + trace_vxhs_get_vdisk_stat_err(s->vdisk_guid, ret, errno); + return -EIO; +diff --git a/configure b/configure +index e9243f5..d2367f1 100755 +--- a/configure ++++ b/configure +@@ -445,12 +445,15 @@ virglrenderer="" + tpm="yes" + libssh2="" + live_block_migration="yes" ++live_block_ops="yes" + numa="" + tcmalloc="no" + jemalloc="no" + replication="yes" + vxhs="" + libxml2="" ++vtd="yes" ++rhel_target="rhv" + + supported_cpu="no" + supported_os="no" +@@ -1348,6 +1351,10 @@ for opt do + ;; + --enable-replication) replication="yes" + ;; ++ --disable-live-block-ops) live_block_ops="no" ++ ;; ++ --enable-live-block-ops) live_block_ops="yes" ++ ;; + --disable-vxhs) vxhs="no" + ;; + --enable-vxhs) vxhs="yes" +@@ -1374,6 +1381,12 @@ for opt do + ;; + --disable-git-update) git_update=no + ;; ++ --disable-vtd) vtd="no" ++ ;; ++ --enable-vtd) vtd="yes" ++ ;; ++ --rhel-target=*) rhel_target="$optarg" ++ ;; + *) + echo "ERROR: unknown option $opt" + echo "Try '$0 --help' for more information" +@@ -1547,6 +1560,7 @@ Advanced options (experts only): + xen pv domain builder + --enable-debug-stack-usage + track the maximum stack usage of stacks created by qemu_alloc_stack ++ --rhel-target set RHEL target (rhv or rhel) + + Optional features, enabled with --enable-FEATURE and + disabled with --disable-FEATURE, default is enabled if available: +@@ -1616,6 +1630,8 @@ disabled with --disable-FEATURE, default is enabled if available: + glusterfs GlusterFS backend + tpm TPM support + libssh2 ssh block device support ++ live-block-migration live block migration support ++ live-block-ops live block operations support + numa libnuma support + libxml2 for Parallels image format + tcmalloc tcmalloc support +@@ -1628,6 +1644,7 @@ disabled with --disable-FEATURE, default is enabled if available: + qom-cast-debug cast debugging support + tools build qemu-io, qemu-nbd and qemu-image tools + vxhs Veritas HyperScale vDisk backend support ++ vtd Emulated VT-d support (only affects x86 targets) + crypto-afalg Linux AF_ALG crypto backend driver + vhost-user vhost-user support + capstone capstone disassembler support +@@ -3369,7 +3386,7 @@ else + glib_req_ver=2.22 + fi + glib_modules=gthread-2.0 +-if test "$modules" = yes; then ++if test "$modules" = yes -o "$vxhs" = yes; then + glib_modules="$glib_modules gmodule-export-2.0" + fi + +@@ -5306,33 +5323,6 @@ if compile_prog "" "" ; then + fi + + ########################################## +-# Veritas HyperScale block driver VxHS +-# Check if libvxhs is installed +- +-if test "$vxhs" != "no" ; then +- cat > $TMPC < +-#include +- +-void *vxhs_callback; +- +-int main(void) { +- iio_init(QNIO_VERSION, vxhs_callback); +- return 0; +-} +-EOF +- vxhs_libs="-lvxhs -lssl" +- if compile_prog "" "$vxhs_libs" ; then +- vxhs=yes +- else +- if test "$vxhs" = "yes" ; then +- feature_not_found "vxhs block device" "Install libvxhs See github" +- fi +- vxhs=no +- fi +-fi +- +-########################################## + # check for _Static_assert() + + have_static_assert=no +@@ -5855,6 +5845,7 @@ echo "TPM passthrough $tpm_passthrough" + echo "TPM emulator $tpm_emulator" + echo "QOM debugging $qom_cast_debug" + echo "Live block migration $live_block_migration" ++echo "Live block ops $live_block_ops" + echo "lzo support $lzo" + echo "snappy support $snappy" + echo "bzip2 support $bzip2" +@@ -5866,6 +5857,8 @@ echo "avx2 optimization $avx2_opt" + echo "replication support $replication" + echo "VxHS block device $vxhs" + echo "capstone $capstone" ++echo "VT-d emulation $vtd" ++echo "RHEL target $rhel_target" + + if test "$sdl_too_old" = "yes"; then + echo "-> Your SDL version is too old - please upgrade to have SDL support" +@@ -6504,6 +6497,10 @@ if test "$live_block_migration" = "yes" ; then + echo "CONFIG_LIVE_BLOCK_MIGRATION=y" >> $config_host_mak + fi + ++if test "$live_block_ops" = "yes" ; then ++ echo "CONFIG_LIVE_BLOCK_OPS=y" >> $config_host_mak ++fi ++ + if test "$tpm" = "yes"; then + echo 'CONFIG_TPM=$(CONFIG_SOFTMMU)' >> $config_host_mak + # TPM passthrough support? +@@ -6606,8 +6603,16 @@ if test "$pthread_setname_np" = "yes" ; then + fi + + if test "$vxhs" = "yes" ; then +- echo "CONFIG_VXHS=y" >> $config_host_mak +- echo "VXHS_LIBS=$vxhs_libs" >> $config_host_mak ++ echo "CONFIG_VXHS=m" >> $config_host_mak ++ echo "VXHS_LIBS= -lssl" >> $config_host_mak ++fi ++ ++if test "$vtd" = "yes" ; then ++ echo "CONFIG_VTD=y" >> $config_host_mak ++fi ++ ++if test "$rhel_target" = "rhv" ; then ++ echo "CONFIG_RHV=y" >> $config_host_mak + fi + + if test "$tcg_interpreter" = "yes"; then +diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx +index ddfcd5a..601fcb6 100644 +--- a/hmp-commands-info.hx ++++ b/hmp-commands-info.hx +@@ -84,6 +84,8 @@ STEXI + Show block device statistics. + ETEXI + ++#ifdef CONFIG_LIVE_BLOCK_OPS ++ + { + .name = "block-jobs", + .args_type = "", +@@ -98,6 +100,8 @@ STEXI + Show progress of ongoing block device operations. + ETEXI + ++#endif /* CONFIG_LIVE_BLOCK_OPS */ ++ + { + .name = "registers", + .args_type = "cpustate_all:-a", +diff --git a/hmp-commands.hx b/hmp-commands.hx +index 35d862a..3918831 100644 +--- a/hmp-commands.hx ++++ b/hmp-commands.hx +@@ -73,6 +73,8 @@ but should be used with extreme caution. Note that this command only + resizes image files, it can not resize block devices like LVM volumes. + ETEXI + ++#ifdef CONFIG_LIVE_BLOCK_OPS ++ + { + .name = "block_stream", + .args_type = "device:B,speed:o?,base:s?", +@@ -160,6 +162,8 @@ STEXI + Resume a paused block streaming operation. + ETEXI + ++#endif /* CONFIG_LIVE_BLOCK_OPS */ ++ + { + .name = "eject", + .args_type = "force:-f,device:B", +@@ -1151,6 +1155,8 @@ STEXI + Enables or disables migration mode. + ETEXI + ++#ifdef CONFIG_LIVE_BLOCK_OPS ++ + { + .name = "snapshot_blkdev", + .args_type = "reuse:-n,device:B,snapshot-file:s?,format:s?", +@@ -1172,6 +1178,8 @@ STEXI + Snapshot device, using snapshot file as target if provided + ETEXI + ++#endif /* CONFIG_LIVE_BLOCK_OPS */ ++ + { + .name = "snapshot_blkdev_internal", + .args_type = "device:B,name:s", +@@ -1206,6 +1214,8 @@ STEXI + Delete an internal snapshot on device if it support + ETEXI + ++#ifdef CONFIG_LIVE_BLOCK_OPS ++ + { + .name = "drive_mirror", + .args_type = "reuse:-n,full:-f,device:B,target:s,format:s?", +@@ -1249,6 +1259,8 @@ STEXI + Start a point-in-time copy of a block device to a specificed target. + ETEXI + ++#endif /* CONFIG_LIVE_BLOCK_OPS */ ++ + { + .name = "drive_add", + .args_type = "node:-n,pci_addr:s,opts:s", +diff --git a/hmp.c b/hmp.c +index a25c7bd..6c92198 100644 +--- a/hmp.c ++++ b/hmp.c +@@ -942,6 +942,8 @@ void hmp_info_pci(Monitor *mon, const QDict *qdict) + qapi_free_PciInfoList(info_list); + } + ++#ifdef CONFIG_LIVE_BLOCK_OPS ++ + void hmp_info_block_jobs(Monitor *mon, const QDict *qdict) + { + BlockJobInfoList *list; +@@ -980,6 +982,8 @@ void hmp_info_block_jobs(Monitor *mon, const QDict *qdict) + qapi_free_BlockJobInfoList(list); + } + ++#endif /* CONFIG_LIVE_BLOCK_OPS */ ++ + void hmp_info_tpm(Monitor *mon, const QDict *qdict) + { + TPMInfoList *info_list, *info; +@@ -1191,6 +1195,8 @@ void hmp_block_resize(Monitor *mon, const QDict *qdict) + hmp_handle_error(mon, &err); + } + ++#ifdef CONFIG_LIVE_BLOCK_OPS ++ + void hmp_drive_mirror(Monitor *mon, const QDict *qdict) + { + const char *filename = qdict_get_str(qdict, "target"); +@@ -1274,6 +1280,8 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict) + hmp_handle_error(mon, &err); + } + ++#endif /* CONFIG_LIVE_BLOCK_OPS */ ++ + void hmp_snapshot_blkdev_internal(Monitor *mon, const QDict *qdict) + { + const char *device = qdict_get_str(qdict, "device"); +@@ -1789,6 +1797,8 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict) + hmp_handle_error(mon, &err); + } + ++#ifdef CONFIG_LIVE_BLOCK_OPS ++ + void hmp_block_stream(Monitor *mon, const QDict *qdict) + { + Error *error = NULL; +@@ -1855,6 +1865,8 @@ void hmp_block_job_complete(Monitor *mon, const QDict *qdict) + hmp_handle_error(mon, &error); + } + ++#endif /* CONFIG_LIVE_BLOCK_OPS */ ++ + typedef struct HMPMigrationStatus + { + QEMUTimer *timer; +diff --git a/include/block/vxhs_shim.h b/include/block/vxhs_shim.h +new file mode 100644 +index 0000000..42519ae +--- /dev/null ++++ b/include/block/vxhs_shim.h +@@ -0,0 +1,143 @@ ++/* ++ * Network IO library for VxHS QEMU block driver (Veritas Technologies) ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ * Contributions after 2014-08-15 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++ ++#ifndef QNIO_API_H ++#define QNIO_API_H ++ ++#include ++ ++/* ++ * Bump up the version everytime this file is modified ++ */ ++#define QNIO_VERSION 34 ++ ++/* ++ * These are the opcodes referenced by callback routine. ++ */ ++#define IRP_READ_REQUEST 0x1FFF ++#define IRP_WRITE_REQUEST 0x2FFF ++#define IRP_VDISK_CHECK_IO_FAILOVER_READY 2020 ++ ++/* ++ * opcodes for iio_ioctl. ++ */ ++#define IOR_VDISK_STAT 1005 ++ ++/* ++ * Error values for iio_cb_t callback function. ++ */ ++#define QNIOERROR_HUP 901 /* Retriable error */ ++#define QNIOERROR_NOCONN 902 /* Non-retriable error */ ++ ++ ++/* Operation Flags */ ++#define IIO_FLAG_ASYNC 0x0001 /* Do an async send */ ++ ++/* ++ * INPUT: ++ * ctx - opaque context ++ * opcode - Operation ++ * error - 0 for sucess, non-zero for failure. ++ * RETURNS: ++ * void ++ * DESCRIPTION: ++ * This callback is called, after Async request completes. ++ * ++ * CONTEXT: ++ * The callback should be wait-free. ++ */ ++typedef void (*iio_cb_t) (void *ctx, uint32_t opcode, uint32_t error); ++ ++typedef struct LibVXHSFuncs { ++/* ++ * RETURNS: ++ * 0 for sucess, non-zero for failure. ++ * DESCRIPTION: ++ * Intilize the library state. This should be called at the ++ * begining before issuing any library call. ++ */ ++ int (*iio_init)(int32_t version, iio_cb_t cb); ++/* ++ * RETURNS: ++ * void ++ * DESCRIPTION: ++ * Relinquish library resources. This should be called on the ++ * close of last open device. ++ */ ++ void (*iio_fini)(void); ++/* ++ * DESCRIPTION: ++ * Returns minimum QNIO API version supported by library. ++ */ ++ int32_t (*iio_min_version)(void); ++/* ++ * DESCRIPTION: ++ * Returns maximum QNIO API version supported by library. ++ */ ++ int32_t (*iio_max_version)(void); ++/* ++ * INPUT: ++ * uri - const string of the format of://:port ++ * devid - Device ID. ++ * flags - currently unused, this must be set to 0 ++ * cacert - CA certificates file in PEM format ++ * client_key - Client private key file in PEM format ++ * client_cert - Client certificate file in PEM format ++ * RETURNS: ++ * opeque device handle on success, NULL on failure. ++ * DESCRIPTION: ++ * This call returns device handle on success. Returns NULL on ++ * failure with errno set ++ * errno can be one of: ++ * ENODEV - remote device not found ++ * EBADF - Unable to open communication channel. ++ * EBUSY - The call cannot be completed right now ++ */ ++ void *(*iio_open)(const char *uri, const char *devid, uint32_t flags, ++ const char *cacert, const char *client_key, ++ const char *client_cert); ++/* ++ * Close the device. ++ * For every matching iio_open() there should be a matching iio_close() ++ * The last close free all data structures associated with the device. ++ */ ++ int32_t (*iio_close)(void *dev_handle); ++/* ++ * INPUT: ++ * dev_handle - device descriptor on which read/write needs to be performed ++ * ctx - an opaque context that is not interpreted This is set for ++ * async calls only. It can be NULL. ++ * iov - an array of iovecs (This is a scatter gather operation) ++ * iovcnt - the number of iovecs ++ * offset - an offset to perform the write ++ * size - I/O size ++ * flags - can be one of ++ * IIO_FLAG_ASYNC - indicating this is a aio call. ++ * RETURNS: ++ * -1 on error, sets errno ++ * EBADF - the remote fd is bad ++ * EBUSY - The call cannot be completed right now ++ * EPIPE - the channel got disconnected, call back would be called in ++ * addition to this. ++ */ ++ int32_t (*iio_writev)(void *dev_handle, void *ctx, struct iovec *iov, ++ int iovcnt, uint64_t offset, uint64_t size, ++ uint32_t flags); ++ ++ int32_t (*iio_readv)(void *dev_handle, void *ctx, struct iovec *iov, ++ int iovcnt, uint64_t offset, uint64_t size, ++ uint32_t flags); ++ ++ int32_t (*iio_ioctl)(void *dev_handle, uint32_t opcode, void *opaque, ++ uint32_t flags); ++ ++} LibVXHSFuncs; ++ ++#endif +diff --git a/monitor.c b/monitor.c +index 39f8ee1..1813d34 100644 +--- a/monitor.c ++++ b/monitor.c +@@ -1168,6 +1168,22 @@ static void qmp_unregister_commands_hack(void) + && !defined(TARGET_S390X) + qmp_unregister_command(&qmp_commands, "query-cpu-definitions"); + #endif ++#ifndef CONFIG_LIVE_BLOCK_OPS ++ qmp_unregister_command(&qmp_commands, "block-stream"); ++ qmp_unregister_command(&qmp_commands, "block-commit"); ++ qmp_unregister_command(&qmp_commands, "drive-mirror"); ++ qmp_unregister_command(&qmp_commands, "blockdev-mirror"); ++ qmp_unregister_command(&qmp_commands, "drive-backup"); ++ qmp_unregister_command(&qmp_commands, "blockdev-backup"); ++ qmp_unregister_command(&qmp_commands, "blockdev-snapshot"); ++ qmp_unregister_command(&qmp_commands, "blockdev-snapshot-sync"); ++ qmp_unregister_command(&qmp_commands, "block-job-set-speed"); ++ qmp_unregister_command(&qmp_commands, "block-job-cancel"); ++ qmp_unregister_command(&qmp_commands, "block-job-pause"); ++ qmp_unregister_command(&qmp_commands, "block-job-resume"); ++ qmp_unregister_command(&qmp_commands, "block-job-complete"); ++ qmp_unregister_command(&qmp_commands, "query-block-jobs"); ++#endif + } + + static void monitor_init_qmp_commands(void) +diff --git a/os-posix.c b/os-posix.c +index b9c2343..05de8ee 100644 +--- a/os-posix.c ++++ b/os-posix.c +@@ -75,7 +75,7 @@ void os_setup_signal_handling(void) + /* Find a likely location for support files using the location of the binary. + For installed binaries this will be "$bindir/../share/qemu". When + running from the build tree this will be "$bindir/../pc-bios". */ +-#define SHARE_SUFFIX "/share/qemu" ++#define SHARE_SUFFIX "/share/qemu-kvm" + #define BUILD_SUFFIX "/pc-bios" + char *os_find_datadir(void) + { +diff --git a/tests/qmp-test.c b/tests/qmp-test.c +index 772058f..f34428e 100644 +--- a/tests/qmp-test.c ++++ b/tests/qmp-test.c +@@ -238,6 +238,9 @@ static int query_error_class(const char *cmd) + { "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE }, + { "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR }, + { "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR }, ++#ifndef CONFIG_RHV ++ { "query-block-jobs", ERROR_CLASS_COMMAND_NOT_FOUND }, ++#endif + { NULL, -1 } + }; + int i; +diff --git a/ui/vnc.c b/ui/vnc.c +index e164eb7..0c3011b 100644 +--- a/ui/vnc.c ++++ b/ui/vnc.c +@@ -4045,7 +4045,7 @@ void vnc_display_open(const char *id, Error **errp) + trace_vnc_auth_init(vd, 1, vd->ws_auth, vd->ws_subauth); + + #ifdef CONFIG_VNC_SASL +- if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) { ++ if ((saslErr = sasl_server_init(NULL, "qemu-kvm")) != SASL_OK) { + error_setg(errp, "Failed to initialize SASL auth: %s", + sasl_errstring(saslErr, NULL, NULL)); + goto fail; +-- +1.8.3.1 + diff --git a/SOURCES/0004-Enable-disable-devices-for-RHEL-7.patch b/SOURCES/0004-Enable-disable-devices-for-RHEL-7.patch new file mode 100644 index 0000000..77ef5fe --- /dev/null +++ b/SOURCES/0004-Enable-disable-devices-for-RHEL-7.patch @@ -0,0 +1,1799 @@ +From 9746c4059558e94c983df315f61203f766ceb500 Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Mon, 11 Jan 2016 11:53:33 +0100 +Subject: Enable/disable devices for RHEL 7 + +This commit adds all changes related to changes in supported devices +up to qemu-kvm-rhev-2.1.2-16.el7. + +Signed-off-by: Miroslav Rezanina + +Rebase notes (2.12.0): +- Disable AMD_IOMMU +- New handling of VHOST_USER_* device disabling +- Use cpu_type instead of cpu_model +- Disabled CONFIG_P4XXX for power64 +- Disabled sam460ex board tests + +Rebase notes (2.11.0): +- Switched order with machine type commit +- Removed CONFIG_STELLARIS from aarch64 +- Blacklist isapc for qom and hmp tests + +Rebase notes (2.10.0): +- replace cannot_instantiate_with_device_add_yet with user_creatable (upstream e90f2a) +- Comment out default configs instead of removing the options +- Reenable test_unaligned_write_same +- Remove default-configs changes for 32bit architectures +- Removed unnecessary usage of user_creatable +- Documented arm cpu changes + +Rebase notes (2.9.0): +- enabled CONFIG_ARM_V7M for aarch64 +- need to fix cpu mask +- disabled some irrelevant ppc64 tests +- Adding pxe-e1000e.rom to Inital redhat commit +- Context changed in vfio_probe_igd_bar4_quirk (rc3) +- Remove fdc test +- Enabled null-co driver +- Disabled megasas and bios-tables test +- Disabled netfilter tests for ppc64 +- Disabled ivshmem test for ppc64 +- Disabled numa test for aarhc64 +- Add CONFIG_USB_XHCI_NEC option for ppc64 +- Enable vhost-user-scsi for ppc64 and aarch64 +- Disable ipmi_bt, ipmi_local, impi_extern for ppc64 + +Rebase notes (2.8.0): +- Removed CONFIG_PIIX_PCI (upstream) +- Disabled POWERNV +- Disabled additional occurencies of prom-env-test +- Disabled additional occurencies of usb-hcd-ohci-test and usb-hcd-uhci-test +- Disabled unsupported machine types in boot-serieal-test +- Disabled 2.7 machine type compatibility test in test-x86-cpuid-compat +- Disabled pnv-xscom-test +- Disabled RHEl 6 machine types for qom-test (failing acpi setting) +- Added diffutils BuildRequires and diff usage for make check + +Rebase notes (2.6.0): +- disabled prom-env-test + +Rebase notes (2.4.0): +- fixed types +- include CONFIG_PLATFORM_BUS for aarch64 +- disable failing virtio-scsi-test + +Rebase notes (2.3.0): +- Added USB=y in 2.3-rc2 (used instead of downstream version fora aarch64) + +Merged patches (2.11.): +- e6c4563d68 hw: Remove the redundant user_creatable = false from SYS_BUS_DEVICEs +- 35474b23b1 hw/pci-host/q35: Remove redundant downstream user_creatable = false +- 374294de46 hw/dma/i8257: Remove redundant downstream user_creatable = false +- 17abc4f5bd RHEL: Disable vfio-ccw and x-terminal270 devices +- b303e792c3 Disable vhost-user-scsi and vhost-user-scsi-pci +- 28f294c023 Disable sm501 and sysbus-sm501 devices +- d5959fcefc s390x/cpumodel: Disable unsupported CPU models + +Merged patches (2.10.0): +- e9b413e Add PCIe bridge devices for AArch64 +- a522114 s390x/virtio-ccw: Disable crypto device in downstream RHEL builds +- 15dbf98 Disable unimplemented device +- 5c0ea49 Disable serial-isa for ppc64 +- 728e7e8 Disable rs6000-mc device +- 2a11896 ppc64le: Remove isabus-bridge device +- 5c4df94 Reenable Educational device +- a936463 aarch64: Enable usb-xhci +- 984f5cd Enable USB_CONFIG for aarch64 +- f13b783 AArch64: Add pci-testdev +- 81867af Disable virtio-pci for s390x builds +- bf5f636 target/ppc: Show POWER9 in -cpu help + +Merged patches (2.9.0): +- 9320fc1 Fix unuseds/Fedora build +- cb16934 config: Remove EHCI from ppc64 builds +- 626fe4d Disable qemu,register device +- 783a0b2 Disable vfio-pci-igd-lpc-bridge device +- bf7c127 Disable new virtio crypto devices +- a4d3c4e Disable amd iommu devices +- 5396ebc Disable loader device +- 1957779 Disable or-irq device +- 25ee621 Hide new floppy device +- 2bd29ed Disable devices for for AArch64 QEMU + +Merged patches (2.7.0): +- e2944a5 RHEL: Disable unsupported PowerPC CPU models + - have to add additional "if 0" sections to hw/ppc/spapr_cpu_core.c +- 81b2836 Remove unsupported VFIO devices from QEMU +- 1248029 Disable spapr-rng +- 351e1fb Disable Windows enlightnementas +- 5760290 Disable mptsas1068 device +- 0b74460 Disable sd-card +- 2eaf71c Disable rocker device +- e0ed699 Disable new ipmi devices + - disable ipmi tests in bios-tables-test +- 30e3bee Disable hyperv-testdev +- 8a9aadf Disable allwiner_ahci device +- a41119d Disable igd-passthrough-i440FX +- e305bb4 Disable vfio-platform device +- a127042 rhel: Revert unwanted inconsequential changes to ivshmem +- ce1419c rhel: Disable ivshmem-plain migration, ivshmem-doorbell, ivshmem +- 3f9349b q35: disable s3/s4 by default +- 2158ca1 i8257: Set no-user flag +- b2a3bb9 e1000e: add boot rom +- have to add pxe-e1000e.rom to source files + +Merged patches (2.6.0): +- ce3206a qemu-iotests: Fix broken test cases + - Reduced to disabling test 071 only +- bb34585 qemu-iotests: Disable 099 (requires blkverify) +- 81be408 build: reenable local builds to pass --enable-debug (downstream only) + +Merged patches (2.4.0): +- fa4fd10 AArch64: Enable ACPI +- 1219d52 ivshmem: RHEL-only: remove unsupported code +- 5f6d954 ivshmem: RHEL-only: explicitly remove dead code +- b88bbf0 Revert "rhel: Drop "ivshmem" device" +- 8f0aadf Split serial-isa into its own config option +- 01bff0f rhel: Disable "info irq" and "info pic" for Power +- b915077 RHEL: Disable remaining unsupported devices for ppc +- 64cbdc5 Mark onboard devices as cannot_instantiate_with_device_add_yet +- 4792566 Disable sdhci device +- bda8169 Disable Educational device +- a17a8fb rhel: Revert unwanted cannot_instantiate_with_device_add_yet changes +- 91c76c5 Remove intel-iommu device +- ec1615d Disable additional e1000 models + +(cherry picked from commit 0e72e616b2d80e47c0eb6c5976276e9f8d920e92) + +Conflicts: + redhat/qemu-kvm.spec.template + +Conflicts: + default-configs/aarch64-softmmu.mak + default-configs/pci.mak + default-configs/ppc64-softmmu.mak + default-configs/s390x-softmmu.mak + hw/arm/Makefile.objs + hw/core/Makefile.objs + hw/i386/Makefile.objs + hw/i386/pc.c + hw/ppc/Makefile.objs + stubs/Makefile.objs + tests/Makefile.include + tests/boot-serial-test.c + tests/qemu-iotests/051 + +Conflicts: + target/i386/cpu.c + tests/boot-serial-test.c + +(cherry picked from commit d62675886ef77b23a774523cb33cc3037b7f7a8b) +--- + default-configs/aarch64-softmmu.mak | 35 ++++++++++++++---- + default-configs/pci.mak | 38 +++++++++---------- + default-configs/ppc64-softmmu.mak | 30 ++++++++++++--- + default-configs/s390x-softmmu.mak | 12 +++--- + default-configs/sound.mak | 8 ++-- + default-configs/usb.mak | 14 +++---- + default-configs/x86_64-softmmu.mak | 30 ++++++++------- + hw/acpi/ich9.c | 4 +- + hw/block/fdc.c | 1 + + hw/char/serial-pci.c | 4 ++ + hw/core/Makefile.objs | 7 ++-- + hw/display/cirrus_vga.c | 2 + + hw/i386/pc.c | 3 +- + hw/ide/piix.c | 5 ++- + hw/ide/via.c | 2 + + hw/input/pckbd.c | 2 + + hw/misc/Makefile.objs | 2 +- + hw/misc/ivshmem.c | 11 ++++++ + hw/net/e1000.c | 2 + + hw/net/e1000e.c | 2 +- + hw/pci-host/piix.c | 4 ++ + hw/ppc/Makefile.objs | 2 +- + hw/ppc/spapr.c | 3 +- + hw/ppc/spapr_cpu_core.c | 2 + + hw/s390x/virtio-ccw.c | 8 ++++ + hw/usb/ccid-card-emulated.c | 2 + + hw/vfio/Makefile.objs | 3 -- + hw/vfio/pci-quirks.c | 5 +++ + hw/virtio/Makefile.objs | 5 ++- + hw/virtio/virtio-pci.c | 8 ++-- + qemu-options.hx | 5 --- + redhat/qemu-kvm.spec.template | 4 +- + stubs/Makefile.objs | 1 + + stubs/ide-isa.c | 13 +++++++ + target/arm/cpu.c | 4 +- + target/i386/cpu.c | 37 +++++++++++++++---- + target/ppc/cpu-models.c | 17 ++++++++- + target/s390x/cpu_models.c | 3 ++ + target/s390x/kvm.c | 8 ++++ + tests/Makefile.include | 73 +++++++++++-------------------------- + tests/bios-tables-test.c | 4 ++ + tests/boot-order-test.c | 7 ++++ + tests/boot-serial-test.c | 10 ++--- + tests/e1000-test.c | 2 + + tests/endianness-test.c | 2 + + tests/ivshmem-test.c | 10 ++++- + tests/qemu-iotests/051 | 12 +++--- + tests/qemu-iotests/group | 4 +- + tests/qom-test.c | 4 +- + tests/test-hmp.c | 2 +- + tests/test-x86-cpuid-compat.c | 2 + + tests/usb-hcd-xhci-test.c | 5 ++- + vl.c | 2 +- + 53 files changed, 318 insertions(+), 169 deletions(-) + create mode 100644 stubs/ide-isa.c + +diff --git a/default-configs/aarch64-softmmu.mak b/default-configs/aarch64-softmmu.mak +index 9ddccf8..001eb8e 100644 +--- a/default-configs/aarch64-softmmu.mak ++++ b/default-configs/aarch64-softmmu.mak +@@ -1,10 +1,29 @@ + # Default configuration for aarch64-softmmu + +-# We support all the 32 bit boards so need all their config +-include arm-softmmu.mak +- +-CONFIG_AUX=y +-CONFIG_DDC=y +-CONFIG_DPCD=y +-CONFIG_XLNX_ZYNQMP=y +-CONFIG_XLNX_ZYNQMP_ARM=y ++# CONFIG_AUX=y ++# CONFIG_DDC=y ++# CONFIG_DPCD=y ++# CONFIG_XLNX_ZYNQMP=y ++# CONFIG_XLNX_ZYNQMP_ARM=y ++CONFIG_PCI=y ++CONFIG_PCI_TESTDEV=y ++CONFIG_VIRTIO_PCI=y ++CONFIG_VIRTIO=y ++CONFIG_ARM_GIC=y ++CONFIG_ARM_GIC_KVM=$(CONFIG_KVM) ++CONFIG_PL011=y ++CONFIG_PL031=y ++CONFIG_PFLASH_CFI01=y ++CONFIG_PCI_GENERIC=y ++CONFIG_ACPI=y ++CONFIG_PLATFORM_BUS=y ++CONFIG_SMBIOS=y ++CONFIG_PL061=y ++CONFIG_GPIO_KEY=y ++CONFIG_ARM_V7M=y ++CONFIG_PCIE_PORT=y ++CONFIG_XIO3130=y ++CONFIG_IOH3420=y ++CONFIG_USB_XHCI=y ++CONFIG_USB=y ++CONFIG_I2C=y +diff --git a/default-configs/pci.mak b/default-configs/pci.mak +index 35e7596..4c8c296 100644 +--- a/default-configs/pci.mak ++++ b/default-configs/pci.mak +@@ -4,21 +4,21 @@ CONFIG_ISA_BUS=y + CONFIG_VIRTIO_PCI=y + CONFIG_VIRTIO=y + CONFIG_USB_UHCI=y +-CONFIG_USB_OHCI=y ++#CONFIG_USB_OHCI=y + CONFIG_USB_EHCI=y + CONFIG_USB_XHCI=y + CONFIG_USB_XHCI_NEC=y +-CONFIG_NE2000_PCI=y +-CONFIG_EEPRO100_PCI=y +-CONFIG_PCNET_PCI=y +-CONFIG_PCNET_COMMON=y ++#CONFIG_NE2000_PCI=y ++#CONFIG_EEPRO100_PCI=y ++#CONFIG_PCNET_PCI=y ++#CONFIG_PCNET_COMMON=y + CONFIG_AC97=y + CONFIG_HDA=y +-CONFIG_ES1370=y +-CONFIG_LSI_SCSI_PCI=y +-CONFIG_VMW_PVSCSI_SCSI_PCI=y +-CONFIG_MEGASAS_SCSI_PCI=y +-CONFIG_MPTSAS_SCSI_PCI=y ++#CONFIG_ES1370=y ++#CONFIG_LSI_SCSI_PCI=y ++#CONFIG_VMW_PVSCSI_SCSI_PCI=y ++#CONFIG_MEGASAS_SCSI_PCI=y ++#CONFIG_MPTSAS_SCSI_PCI=y + CONFIG_RTL8139_PCI=y + CONFIG_E1000_PCI=y + CONFIG_E1000E_PCI=y +@@ -26,24 +26,24 @@ CONFIG_IDE_CORE=y + CONFIG_IDE_QDEV=y + CONFIG_IDE_PCI=y + CONFIG_AHCI=y +-CONFIG_ESP=y +-CONFIG_ESP_PCI=y ++#CONFIG_ESP=y ++#CONFIG_ESP_PCI=y + CONFIG_SERIAL=y + CONFIG_SERIAL_ISA=y + CONFIG_SERIAL_PCI=y + CONFIG_CAN_BUS=y + CONFIG_CAN_SJA1000=y + CONFIG_CAN_PCI=y +-CONFIG_IPACK=y ++#CONFIG_IPACK=y + CONFIG_WDT_IB6300ESB=y + CONFIG_PCI_TESTDEV=y +-CONFIG_NVME_PCI=y +-CONFIG_SD=y +-CONFIG_SDHCI=y ++#CONFIG_NVME_PCI=y ++#CONFIG_SD=y ++#CONFIG_SDHCI=y + CONFIG_EDU=y + CONFIG_VGA=y + CONFIG_VGA_PCI=y + CONFIG_IVSHMEM_DEVICE=$(CONFIG_IVSHMEM) +-CONFIG_ROCKER=y +-CONFIG_VHOST_USER_SCSI=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) +-CONFIG_VHOST_USER_BLK=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) ++#CONFIG_ROCKER=y ++#CONFIG_VHOST_USER_SCSI=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) ++#CONFIG_VHOST_USER_BLK=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) +diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak +index b94af6c..936ac17 100644 +--- a/default-configs/ppc64-softmmu.mak ++++ b/default-configs/ppc64-softmmu.mak +@@ -1,14 +1,31 @@ + # Default configuration for ppc64-softmmu + +-# Include all 32-bit boards +-include ppc-softmmu.mak ++include sound.mak ++include usb.mak ++ ++## PCI configuration - cut down from the defaults in pci.mak ++CONFIG_PCI=y ++CONFIG_VIRTIO_PCI=y ++CONFIG_VIRTIO=y ++CONFIG_USB_XHCI=y ++CONFIG_USB_XHCI_NEC=y ++CONFIG_WDT_IB6300ESB=y ++CONFIG_PCI_TESTDEV=y ++CONFIG_USB_OHCI=y ++CONFIG_VGA=y ++CONFIG_VGA_PCI=y ++CONFIG_SERIAL=y ++CONFIG_I2C=y ++ ++# For embedded PPCs ++#CONFIG_PPC4XX=y + + # For PowerNV +-CONFIG_POWERNV=y ++#CONFIG_POWERNV=y + CONFIG_IPMI=y +-CONFIG_IPMI_LOCAL=y +-CONFIG_IPMI_EXTERN=y +-CONFIG_ISA_IPMI_BT=y ++#CONFIG_IPMI_LOCAL=y ++#CONFIG_IPMI_EXTERN=y ++#CONFIG_ISA_IPMI_BT=y + + # For pSeries + CONFIG_PSERIES=y +@@ -17,3 +34,4 @@ CONFIG_XICS=$(CONFIG_PSERIES) + CONFIG_XICS_SPAPR=$(CONFIG_PSERIES) + CONFIG_XICS_KVM=$(call land,$(CONFIG_PSERIES),$(CONFIG_KVM)) + CONFIG_MEM_HOTPLUG=y ++CONFIG_I2C=y +diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak +index 2f4bfe7..649bf2c 100644 +--- a/default-configs/s390x-softmmu.mak ++++ b/default-configs/s390x-softmmu.mak +@@ -1,11 +1,13 @@ + CONFIG_PCI=y +-CONFIG_VIRTIO_PCI=$(CONFIG_PCI) +-CONFIG_VHOST_USER_SCSI=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) +-CONFIG_VHOST_USER_BLK=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) ++#CONFIG_VIRTIO_PCI=$(CONFIG_PCI) ++#CONFIG_VHOST_USER_SCSI=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) ++#CONFIG_VHOST_USER_BLK=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) + CONFIG_VIRTIO=y + CONFIG_SCLPCONSOLE=y +-CONFIG_TERMINAL3270=y ++# Disabled for Red Hat Enterprise Linux: ++# CONFIG_TERMINAL3270=y + CONFIG_S390_FLIC=y + CONFIG_S390_FLIC_KVM=$(CONFIG_KVM) +-CONFIG_VFIO_CCW=$(CONFIG_LINUX) ++# Disabled for Red Hat Enterprise Linux: ++# CONFIG_VFIO_CCW=$(CONFIG_LINUX) + CONFIG_WDT_DIAG288=y +diff --git a/default-configs/sound.mak b/default-configs/sound.mak +index 4f22c34..1bead9b 100644 +--- a/default-configs/sound.mak ++++ b/default-configs/sound.mak +@@ -1,4 +1,4 @@ +-CONFIG_SB16=y +-CONFIG_ADLIB=y +-CONFIG_GUS=y +-CONFIG_CS4231A=y ++#CONFIG_SB16=y ++#CONFIG_ADLIB=y ++#CONFIG_GUS=y ++#CONFIG_CS4231A=y +diff --git a/default-configs/usb.mak b/default-configs/usb.mak +index f4b8568..a256f84 100644 +--- a/default-configs/usb.mak ++++ b/default-configs/usb.mak +@@ -1,10 +1,10 @@ + CONFIG_USB=y +-CONFIG_USB_TABLET_WACOM=y ++#CONFIG_USB_TABLET_WACOM=y + CONFIG_USB_STORAGE_BOT=y +-CONFIG_USB_STORAGE_UAS=y +-CONFIG_USB_STORAGE_MTP=y ++#CONFIG_USB_STORAGE_UAS=y ++#CONFIG_USB_STORAGE_MTP=y + CONFIG_USB_SMARTCARD=y +-CONFIG_USB_AUDIO=y +-CONFIG_USB_SERIAL=y +-CONFIG_USB_NETWORK=y +-CONFIG_USB_BLUETOOTH=y ++#CONFIG_USB_AUDIO=y ++#CONFIG_USB_SERIAL=y ++#CONFIG_USB_NETWORK=y ++#CONFIG_USB_BLUETOOTH=y +diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak +index 0390b43..2675606 100644 +--- a/default-configs/x86_64-softmmu.mak ++++ b/default-configs/x86_64-softmmu.mak +@@ -4,20 +4,21 @@ include pci.mak + include sound.mak + include usb.mak + CONFIG_QXL=$(CONFIG_SPICE) +-CONFIG_VGA_ISA=y ++#CONFIG_VGA_ISA=y ++CONFIG_VGA_PCI=y + CONFIG_VGA_CIRRUS=y +-CONFIG_VMWARE_VGA=y +-CONFIG_VMXNET3_PCI=y ++#CONFIG_VMWARE_VGA=y ++#CONFIG_VMXNET3_PCI=y + CONFIG_VIRTIO_VGA=y + CONFIG_VMMOUSE=y + CONFIG_IPMI=y +-CONFIG_IPMI_LOCAL=y +-CONFIG_IPMI_EXTERN=y +-CONFIG_ISA_IPMI_KCS=y +-CONFIG_ISA_IPMI_BT=y ++#CONFIG_IPMI_LOCAL=y ++#CONFIG_IPMI_EXTERN=y ++#CONFIG_ISA_IPMI_KCS=y ++#CONFIG_ISA_IPMI_BT=y + CONFIG_SERIAL=y + CONFIG_SERIAL_ISA=y +-CONFIG_PARALLEL=y ++#CONFIG_PARALLEL=y + CONFIG_I8254=y + CONFIG_PCSPK=y + CONFIG_PCKBD=y +@@ -29,11 +30,11 @@ CONFIG_ACPI_MEMORY_HOTPLUG=y + CONFIG_ACPI_CPU_HOTPLUG=y + CONFIG_APM=y + CONFIG_I8257=y +-CONFIG_IDE_ISA=y ++#CONFIG_IDE_ISA=y + CONFIG_IDE_PIIX=y +-CONFIG_NE2000_ISA=y +-CONFIG_HPET=y +-CONFIG_APPLESMC=y ++#CONFIG_NE2000_ISA=y ++#CONFIG_HPET=y ++#CONFIG_APPLESMC=y + CONFIG_I8259=y + CONFIG_PFLASH_CFI01=y + CONFIG_TPM_TIS=$(CONFIG_TPM) +@@ -41,6 +42,7 @@ CONFIG_TPM_CRB=$(CONFIG_TPM) + CONFIG_MC146818RTC=y + CONFIG_PCI_PIIX=y + CONFIG_WDT_IB700=y ++CONFIG_ISA_BUS=y + CONFIG_ISA_DEBUG=y + CONFIG_ISA_TESTDEV=y + CONFIG_VMPORT=y +@@ -58,11 +60,11 @@ CONFIG_XIO3130=y + CONFIG_IOH3420=y + CONFIG_I82801B11=y + CONFIG_SMBIOS=y +-CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM) ++#CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM) + CONFIG_PXB=y + CONFIG_ACPI_VMGENID=y + CONFIG_FW_CFG_DMA=y + CONFIG_I2C=y + CONFIG_SEV=$(CONFIG_KVM) + CONFIG_VTD=y +-CONFIG_AMD_IOMMU=y ++#CONFIG_AMD_IOMMU=y +diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c +index c5d8646..a4e87b8 100644 +--- a/hw/acpi/ich9.c ++++ b/hw/acpi/ich9.c +@@ -446,8 +446,8 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp) + static const uint32_t gpe0_len = ICH9_PMIO_GPE0_LEN; + pm->acpi_memory_hotplug.is_enabled = true; + pm->cpu_hotplug_legacy = true; +- pm->disable_s3 = 0; +- pm->disable_s4 = 0; ++ pm->disable_s3 = 1; ++ pm->disable_s4 = 1; + pm->s4_val = 2; + + object_property_add_uint32_ptr(obj, ACPI_PM_PROP_PM_IO_BASE, +diff --git a/hw/block/fdc.c b/hw/block/fdc.c +index cd29e27..3964096 100644 +--- a/hw/block/fdc.c ++++ b/hw/block/fdc.c +@@ -605,6 +605,7 @@ static void floppy_drive_class_init(ObjectClass *klass, void *data) + k->bus_type = TYPE_FLOPPY_BUS; + k->props = floppy_drive_properties; + k->desc = "virtual floppy drive"; ++ k->user_creatable = false; /* RH state preserve */ + } + + static const TypeInfo floppy_drive_info = { +diff --git a/hw/char/serial-pci.c b/hw/char/serial-pci.c +index cb0d04c..d426982 100644 +--- a/hw/char/serial-pci.c ++++ b/hw/char/serial-pci.c +@@ -228,6 +228,8 @@ static void multi_2x_serial_pci_class_initfn(ObjectClass *klass, void *data) + dc->vmsd = &vmstate_pci_multi_serial; + dc->props = multi_2x_serial_pci_properties; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); ++ /* Disabled for Red Hat Enterprise Linux: */ ++ dc->user_creatable = false; + } + + static void multi_4x_serial_pci_class_initfn(ObjectClass *klass, void *data) +@@ -243,6 +245,8 @@ static void multi_4x_serial_pci_class_initfn(ObjectClass *klass, void *data) + dc->vmsd = &vmstate_pci_multi_serial; + dc->props = multi_4x_serial_pci_properties; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); ++ /* Disabled for Red Hat Enterprise Linux: */ ++ dc->user_creatable = false; + } + + static const TypeInfo serial_pci_info = { +diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs +index eb88ca9..2b4491f 100644 +--- a/hw/core/Makefile.objs ++++ b/hw/core/Makefile.objs +@@ -16,10 +16,11 @@ common-obj-$(CONFIG_SOFTMMU) += machine.o + common-obj-$(CONFIG_SOFTMMU) += loader.o + common-obj-$(CONFIG_FITLOADER) += loader-fit.o + common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o +-common-obj-$(CONFIG_SOFTMMU) += register.o +-common-obj-$(CONFIG_SOFTMMU) += or-irq.o ++# Disabled in Red Hat Enterprise Linux ++# common-obj-$(CONFIG_SOFTMMU) += register.o ++# obj-$(CONFIG_SOFTMMU) += generic-loader.o ++# common-obj-$(CONFIG_SOFTMMU) += or-irq.o + common-obj-$(CONFIG_SOFTMMU) += split-irq.o + common-obj-$(CONFIG_PLATFORM_BUS) += platform-bus.o + +-obj-$(CONFIG_SOFTMMU) += generic-loader.o + obj-$(CONFIG_SOFTMMU) += null-machine.o +diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c +index 138ae96..d116651 100644 +--- a/hw/display/cirrus_vga.c ++++ b/hw/display/cirrus_vga.c +@@ -3074,6 +3074,8 @@ static void isa_cirrus_vga_class_init(ObjectClass *klass, void *data) + dc->realize = isa_cirrus_vga_realizefn; + dc->props = isa_cirrus_vga_properties; + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); ++ /* Disabled for Red Hat Enterprise Linux: */ ++ dc->user_creatable = false; + } + + static const TypeInfo isa_cirrus_vga_info = { +diff --git a/hw/i386/pc.c b/hw/i386/pc.c +index d36bac8..ab4323d 100644 +--- a/hw/i386/pc.c ++++ b/hw/i386/pc.c +@@ -1525,8 +1525,9 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, bool no_vmport) + ISADevice *i8042, *port92, *vmmouse; + + serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS); ++#if 0 /* Disabled for Red Hat Enterprise Linux */ + parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS); +- ++#endif + for (i = 0; i < MAX_FD; i++) { + fd[i] = drive_get(IF_FLOPPY, 0, i); + create_fdctrl |= !!fd[i]; +diff --git a/hw/ide/piix.c b/hw/ide/piix.c +index a3afe1f..6de12ca 100644 +--- a/hw/ide/piix.c ++++ b/hw/ide/piix.c +@@ -253,7 +253,8 @@ static void piix3_ide_class_init(ObjectClass *klass, void *data) + k->device_id = PCI_DEVICE_ID_INTEL_82371SB_1; + k->class_id = PCI_CLASS_STORAGE_IDE; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); +- dc->hotpluggable = false; ++ /* Disabled for Red Hat Enterprise Linux: */ ++ dc->user_creatable = false; + } + + static const TypeInfo piix3_ide_info = { +@@ -280,6 +281,8 @@ static void piix4_ide_class_init(ObjectClass *klass, void *data) + k->class_id = PCI_CLASS_STORAGE_IDE; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + dc->hotpluggable = false; ++ /* Disabled for Red Hat Enterprise Linux: */ ++ dc->user_creatable = false; + } + + static const TypeInfo piix4_ide_info = { +diff --git a/hw/ide/via.c b/hw/ide/via.c +index 117ac4d..b1bafe6 100644 +--- a/hw/ide/via.c ++++ b/hw/ide/via.c +@@ -217,6 +217,8 @@ static void via_ide_class_init(ObjectClass *klass, void *data) + k->revision = 0x06; + k->class_id = PCI_CLASS_STORAGE_IDE; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); ++ /* Disabled for Red Hat Enterprise Linux: */ ++ dc->user_creatable = false; + } + + static const TypeInfo via_ide_info = { +diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c +index f17f18e..66adb83 100644 +--- a/hw/input/pckbd.c ++++ b/hw/input/pckbd.c +@@ -570,6 +570,8 @@ static void i8042_class_initfn(ObjectClass *klass, void *data) + + dc->realize = i8042_realizefn; + dc->vmsd = &vmstate_kbd_isa; ++ /* Disabled for Red Hat Enterprise Linux: */ ++ dc->user_creatable = false; + } + + static const TypeInfo i8042_info = { +diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs +index 00e834d..d7430cf 100644 +--- a/hw/misc/Makefile.objs ++++ b/hw/misc/Makefile.objs +@@ -8,7 +8,7 @@ common-obj-$(CONFIG_ISA_TESTDEV) += pc-testdev.o + common-obj-$(CONFIG_PCI_TESTDEV) += pci-testdev.o + common-obj-$(CONFIG_EDU) += edu.o + +-common-obj-y += unimp.o ++#common-obj-y += unimp.o + common-obj-$(CONFIG_FW_CFG_DMA) += vmcoreinfo.o + + # ARM devices +diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c +index 16f0370..bfbfc0e 100644 +--- a/hw/misc/ivshmem.c ++++ b/hw/misc/ivshmem.c +@@ -892,6 +892,13 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp) + return; + } + ++ /* Migration disabled for Red Hat Enterprise Linux: */ ++ if (s->master == ON_OFF_AUTO_ON) { ++ error_setg(errp, "master=on is not supported"); ++ return; ++ } ++ s->master = ON_OFF_AUTO_OFF; ++ + pci_conf = dev->config; + pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY; + +@@ -1183,6 +1190,8 @@ static void ivshmem_doorbell_class_init(ObjectClass *klass, void *data) + k->realize = ivshmem_doorbell_realize; + dc->props = ivshmem_doorbell_properties; + dc->vmsd = &ivshmem_doorbell_vmsd; ++ /* Disabled for Red Hat Enterprise Linux: */ ++ dc->user_creatable = false; + } + + static const TypeInfo ivshmem_doorbell_info = { +@@ -1352,6 +1361,8 @@ static void ivshmem_class_init(ObjectClass *klass, void *data) + dc->desc = "Inter-VM shared memory (legacy)"; + dc->props = ivshmem_properties; + dc->vmsd = &ivshmem_vmsd; ++ /* Disabled for Red Hat Enterprise Linux: */ ++ dc->user_creatable = false; + } + + static const TypeInfo ivshmem_info = { +diff --git a/hw/net/e1000.c b/hw/net/e1000.c +index 13a9494..742cd0a 100644 +--- a/hw/net/e1000.c ++++ b/hw/net/e1000.c +@@ -1768,6 +1768,7 @@ static const E1000Info e1000_devices[] = { + .revision = 0x03, + .phy_id2 = E1000_PHY_ID2_8254xx_DEFAULT, + }, ++#if 0 /* Disabled for Red Hat Enterprise Linux 7 */ + { + .name = "e1000-82544gc", + .device_id = E1000_DEV_ID_82544GC_COPPER, +@@ -1780,6 +1781,7 @@ static const E1000Info e1000_devices[] = { + .revision = 0x03, + .phy_id2 = E1000_PHY_ID2_8254xx_DEFAULT, + }, ++#endif + }; + + static void e1000_register_types(void) +diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c +index 16a9417..c934c22 100644 +--- a/hw/net/e1000e.c ++++ b/hw/net/e1000e.c +@@ -673,7 +673,7 @@ static void e1000e_class_init(ObjectClass *class, void *data) + c->vendor_id = PCI_VENDOR_ID_INTEL; + c->device_id = E1000_DEV_ID_82574L; + c->revision = 0; +- c->romfile = "efi-e1000e.rom"; ++ c->romfile = "pxe-e1000e.rom"; + c->class_id = PCI_CLASS_NETWORK_ETHERNET; + + dc->desc = "Intel 82574L GbE Controller"; +diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c +index 0e60834..3ce4b14 100644 +--- a/hw/pci-host/piix.c ++++ b/hw/pci-host/piix.c +@@ -787,6 +787,7 @@ static const TypeInfo i440fx_info = { + }, + }; + ++#if 0 /* Disabled in Red Hat Enterprise Linux */ + /* IGD Passthrough Host Bridge. */ + typedef struct { + uint8_t offset; +@@ -870,6 +871,7 @@ static const TypeInfo igd_passthrough_i440fx_info = { + .instance_size = sizeof(PCII440FXState), + .class_init = igd_passthrough_i440fx_class_init, + }; ++#endif + + static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge, + PCIBus *rootbus) +@@ -915,7 +917,9 @@ static const TypeInfo i440fx_pcihost_info = { + static void i440fx_register_types(void) + { + type_register_static(&i440fx_info); ++#if 0 /* Disabled in Red Hat Enterprise Linux */ + type_register_static(&igd_passthrough_i440fx_info); ++#endif + type_register_static(&piix3_pci_type_info); + type_register_static(&piix3_info); + type_register_static(&piix3_xen_info); +diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs +index 86d82a6..a46a989 100644 +--- a/hw/ppc/Makefile.objs ++++ b/hw/ppc/Makefile.objs +@@ -3,7 +3,7 @@ obj-y += ppc.o ppc_booke.o fdt.o + # IBM pSeries (sPAPR) + obj-$(CONFIG_PSERIES) += spapr.o spapr_caps.o spapr_vio.o spapr_events.o + obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o +-obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o ++obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o + obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o + # IBM PowerNV + obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o pnv_occ.o pnv_bmc.o +diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c +index a81570e..6a92b20 100644 +--- a/hw/ppc/spapr.c ++++ b/hw/ppc/spapr.c +@@ -1153,6 +1153,7 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr, + /* /vdevice */ + spapr_dt_vdevice(spapr->vio_bus, fdt); + ++#if 0 /* Disabled in Red Hat Enterprise Linux */ + if (object_resolve_path_type("", TYPE_SPAPR_RNG, NULL)) { + ret = spapr_rng_populate_dt(fdt); + if (ret < 0) { +@@ -1160,7 +1161,7 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr, + exit(1); + } + } +- ++#endif + QLIST_FOREACH(phb, &spapr->phbs, list) { + ret = spapr_populate_pci_dt(phb, PHANDLE_XICP, fdt); + if (ret < 0) { +diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c +index 94afeb3..1eda854 100644 +--- a/hw/ppc/spapr_cpu_core.c ++++ b/hw/ppc/spapr_cpu_core.c +@@ -240,10 +240,12 @@ static const TypeInfo spapr_cpu_core_type_infos[] = { + .instance_size = sizeof(sPAPRCPUCore), + .class_size = sizeof(sPAPRCPUCoreClass), + }, ++#if 0 /* Disabled for Red Hat Enterprise Linux */ + DEFINE_SPAPR_CPU_CORE_TYPE("970_v2.2"), + DEFINE_SPAPR_CPU_CORE_TYPE("970mp_v1.0"), + DEFINE_SPAPR_CPU_CORE_TYPE("970mp_v1.1"), + DEFINE_SPAPR_CPU_CORE_TYPE("power5+_v2.1"), ++#endif + DEFINE_SPAPR_CPU_CORE_TYPE("power7_v2.3"), + DEFINE_SPAPR_CPU_CORE_TYPE("power7+_v2.1"), + DEFINE_SPAPR_CPU_CORE_TYPE("power8_v2.0"), +diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c +index e51fbef..8720e46 100644 +--- a/hw/s390x/virtio-ccw.c ++++ b/hw/s390x/virtio-ccw.c +@@ -927,6 +927,8 @@ static void virtio_ccw_rng_realize(VirtioCcwDevice *ccw_dev, Error **errp) + NULL); + } + ++#if 0 /* Disabled in Red Hat Enterprise Linux */ ++ + static void virtio_ccw_crypto_realize(VirtioCcwDevice *ccw_dev, Error **errp) + { + VirtIOCryptoCcw *dev = VIRTIO_CRYPTO_CCW(ccw_dev); +@@ -944,6 +946,7 @@ static void virtio_ccw_crypto_realize(VirtioCcwDevice *ccw_dev, Error **errp) + OBJECT(dev->vdev.conf.cryptodev), "cryptodev", + NULL); + } ++#endif + + static void virtio_ccw_gpu_realize(VirtioCcwDevice *ccw_dev, Error **errp) + { +@@ -1534,6 +1537,8 @@ static const TypeInfo virtio_ccw_rng = { + .class_init = virtio_ccw_rng_class_init, + }; + ++#if 0 /* Disabled in Red Hat Enterprise Linux */ ++ + static Property virtio_ccw_crypto_properties[] = { + DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, + VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), +@@ -1571,6 +1576,7 @@ static const TypeInfo virtio_ccw_crypto = { + .instance_init = virtio_ccw_crypto_instance_init, + .class_init = virtio_ccw_crypto_class_init, + }; ++#endif + + static Property virtio_ccw_gpu_properties[] = { + DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, +@@ -1895,7 +1901,9 @@ static void virtio_ccw_register(void) + #ifdef CONFIG_VHOST_VSOCK + type_register_static(&vhost_vsock_ccw_info); + #endif ++#if 0 /* Disabled in Red Hat Enterprise Linux */ + type_register_static(&virtio_ccw_crypto); ++#endif + type_register_static(&virtio_ccw_gpu); + type_register_static(&virtio_ccw_input); + type_register_static(&virtio_ccw_input_hid); +diff --git a/hw/usb/ccid-card-emulated.c b/hw/usb/ccid-card-emulated.c +index ea42e46..f2d8b0d 100644 +--- a/hw/usb/ccid-card-emulated.c ++++ b/hw/usb/ccid-card-emulated.c +@@ -588,6 +588,8 @@ static void emulated_class_initfn(ObjectClass *klass, void *data) + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); + dc->desc = "emulated smartcard"; + dc->props = emulated_card_properties; ++ /* Disabled for Red Hat Enterprise Linux: */ ++ dc->user_creatable = false; + } + + static const TypeInfo emulated_card_info = { +diff --git a/hw/vfio/Makefile.objs b/hw/vfio/Makefile.objs +index a2e7a0a..b25ca64 100644 +--- a/hw/vfio/Makefile.objs ++++ b/hw/vfio/Makefile.objs +@@ -2,8 +2,5 @@ ifeq ($(CONFIG_LINUX), y) + obj-$(CONFIG_SOFTMMU) += common.o + obj-$(CONFIG_PCI) += pci.o pci-quirks.o display.o + obj-$(CONFIG_VFIO_CCW) += ccw.o +-obj-$(CONFIG_SOFTMMU) += platform.o +-obj-$(CONFIG_VFIO_XGMAC) += calxeda-xgmac.o +-obj-$(CONFIG_VFIO_AMD_XGBE) += amd-xgbe.o + obj-$(CONFIG_SOFTMMU) += spapr.o + endif +diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c +index e5779a7..cb2f79c 100644 +--- a/hw/vfio/pci-quirks.c ++++ b/hw/vfio/pci-quirks.c +@@ -1193,6 +1193,8 @@ static void vfio_pci_igd_lpc_bridge_class_init(ObjectClass *klass, void *data) + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->desc = "VFIO dummy ISA/LPC bridge for IGD assignment"; + dc->hotpluggable = false; ++ /* Disabled in Red Hat Enterprise Linux */ ++ dc->user_creatable = false; + k->realize = vfio_pci_igd_lpc_bridge_realize; + k->class_id = PCI_CLASS_BRIDGE_ISA; + } +@@ -1386,6 +1388,9 @@ static void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) + 0, PCI_DEVFN(0x2, 0))) { + return; + } ++ ++ /* Disabled in Red Hat Enterprise Linux */ ++ return; + + /* + * We need to create an LPC/ISA bridge at PCI bus address 00:1f.0 that we +diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs +index 765d363..a5a0936 100644 +--- a/hw/virtio/Makefile.objs ++++ b/hw/virtio/Makefile.objs +@@ -7,8 +7,9 @@ common-obj-y += virtio-mmio.o + obj-y += virtio.o virtio-balloon.o + obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o + obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock.o +-obj-y += virtio-crypto.o +-obj-$(CONFIG_VIRTIO_PCI) += virtio-crypto-pci.o ++# Disabled in Red Hat Enterprise Linux ++#obj-y += virtio-crypto.o ++#obj-$(CONFIG_VIRTIO_PCI) += virtio-crypto-pci.o + endif + + common-obj-$(call lnot,$(CONFIG_LINUX)) += vhost-stub.o +diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c +index 1e8ab7b..e3e49d6 100644 +--- a/hw/virtio/virtio-pci.c ++++ b/hw/virtio/virtio-pci.c +@@ -1983,7 +1983,7 @@ static const TypeInfo virtio_blk_pci_info = { + .class_init = virtio_blk_pci_class_init, + }; + +-#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX) ++#if defined(CONFIG_VHOST_USER_BLK) + /* vhost-user-blk */ + + static Property vhost_user_blk_pci_properties[] = { +@@ -2163,7 +2163,7 @@ static const TypeInfo vhost_scsi_pci_info = { + }; + #endif + +-#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX) ++#if defined(CONFIG_VHOST_USER_SCSI) + /* vhost-user-scsi-pci */ + static Property vhost_user_scsi_pci_properties[] = { + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, +@@ -2686,7 +2686,7 @@ static void virtio_pci_register_types(void) + type_register_static(&virtio_9p_pci_info); + #endif + type_register_static(&virtio_blk_pci_info); +-#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX) ++#if defined(CONFIG_VHOST_USER_BLK) + type_register_static(&vhost_user_blk_pci_info); + #endif + type_register_static(&virtio_scsi_pci_info); +@@ -2696,7 +2696,7 @@ static void virtio_pci_register_types(void) + #ifdef CONFIG_VHOST_SCSI + type_register_static(&vhost_scsi_pci_info); + #endif +-#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX) ++#if defined(CONFIG_VHOST_USER_SCSI) + type_register_static(&vhost_user_scsi_pci_info); + #endif + #ifdef CONFIG_VHOST_VSOCK +diff --git a/qemu-options.hx b/qemu-options.hx +index ca4e412..2042dba 100644 +--- a/qemu-options.hx ++++ b/qemu-options.hx +@@ -1811,11 +1811,6 @@ ETEXI + + DEF("no-hpet", 0, QEMU_OPTION_no_hpet, + "-no-hpet disable HPET\n", QEMU_ARCH_I386) +-STEXI +-@item -no-hpet +-@findex -no-hpet +-Disable HPET support. +-ETEXI + + DEF("acpitable", HAS_ARG, QEMU_OPTION_acpitable, + "-acpitable [sig=str][,rev=n][,oem_id=str][,oem_table_id=str][,oem_rev=n][,asl_compiler_id=str][,asl_compiler_rev=n][,{data|file}=file1[:file2]...]\n" +diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs +index 2d59d84..dfdfca7 100644 +--- a/stubs/Makefile.objs ++++ b/stubs/Makefile.objs +@@ -43,3 +43,4 @@ stub-obj-y += xen-common.o + stub-obj-y += xen-hvm.o + stub-obj-y += pci-host-piix.o + stub-obj-y += ram-block.o ++stub-obj-y += ide-isa.o +diff --git a/stubs/ide-isa.c b/stubs/ide-isa.c +new file mode 100644 +index 0000000..9fd50ef +--- /dev/null ++++ b/stubs/ide-isa.c +@@ -0,0 +1,13 @@ ++#include "qemu/osdep.h" ++#include "hw/ide.h" ++#include ++ ++ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int isairq, ++ DriveInfo *hd0, DriveInfo *hd1) ++{ ++ /* ++ * In theory the real isa_ide_init() function can return NULL, but no ++ * caller actually checks for that. Make sure we go out with a clear bang. ++ */ ++ abort(); ++} +diff --git a/target/arm/cpu.c b/target/arm/cpu.c +index 022d8c5..4255e9c 100644 +--- a/target/arm/cpu.c ++++ b/target/arm/cpu.c +@@ -1953,7 +1953,9 @@ static void arm_cpu_register_types(void) + type_register_static(&idau_interface_type_info); + + while (info->name) { +- cpu_register(info); ++ /* RHEL specific: Filter out unsupported cpu models */ ++ if (!strcmp(info->name, "cortex-a15")) ++ cpu_register(info); + info++; + } + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index a20fe26..f483a71 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -825,14 +825,14 @@ static X86CPUDefinition builtin_x86_defs[] = { + .family = 6, + .model = 6, + .stepping = 3, +- .features[FEAT_1_EDX] = +- PPRO_FEATURES | +- CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | +- CPUID_PSE36, +- .features[FEAT_1_ECX] = +- CPUID_EXT_SSE3 | CPUID_EXT_CX16, +- .features[FEAT_8000_0001_EDX] = +- CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, ++ .features[FEAT_1_EDX] = CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | ++ CPUID_MMX | CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | ++ CPUID_MCA | CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | ++ CPUID_CX8 | CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | ++ CPUID_PSE | CPUID_DE | CPUID_FP87, ++ .features[FEAT_1_ECX] = CPUID_EXT_CX16 | CPUID_EXT_SSE3, ++ .features[FEAT_8000_0001_EDX] = CPUID_EXT2_LM | CPUID_EXT2_NX | ++ CPUID_EXT2_SYSCALL, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM, + .xlevel = 0x8000000A, +@@ -1062,6 +1062,25 @@ static X86CPUDefinition builtin_x86_defs[] = { + .model_id = "Intel(R) Atom(TM) CPU N270 @ 1.60GHz", + }, + { ++ .name = "cpu64-rhel6", ++ .level = 4, ++ .vendor = CPUID_VENDOR_AMD, ++ .family = 6, ++ .model = 13, ++ .stepping = 3, ++ .features[FEAT_1_EDX] = CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | ++ CPUID_MMX | CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | ++ CPUID_MCA | CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | ++ CPUID_CX8 | CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | ++ CPUID_PSE | CPUID_DE | CPUID_FP87, ++ .features[FEAT_1_ECX] = CPUID_EXT_CX16 | CPUID_EXT_SSE3, ++ .features[FEAT_8000_0001_EDX] = CPUID_EXT2_LM | CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, ++ .features[FEAT_8000_0001_ECX] = CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | ++ CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM, ++ .xlevel = 0x8000000A, ++ .model_id = "QEMU Virtual CPU version (cpu64-rhel6)", ++ }, ++ { + .name = "Conroe", + .level = 10, + .vendor = CPUID_VENDOR_INTEL, +@@ -4764,11 +4783,13 @@ static Property x86_cpu_properties[] = { + DEFINE_PROP_BOOL("hv-vapic", X86CPU, hyperv_vapic, false), + DEFINE_PROP_BOOL("hv-time", X86CPU, hyperv_time, false), + DEFINE_PROP_BOOL("hv-crash", X86CPU, hyperv_crash, false), ++#if 0 /* Disabled for Red Hat Enterprise Linux */ + DEFINE_PROP_BOOL("hv-reset", X86CPU, hyperv_reset, false), + DEFINE_PROP_BOOL("hv-vpindex", X86CPU, hyperv_vpindex, false), + DEFINE_PROP_BOOL("hv-runtime", X86CPU, hyperv_runtime, false), + DEFINE_PROP_BOOL("hv-synic", X86CPU, hyperv_synic, false), + DEFINE_PROP_BOOL("hv-stimer", X86CPU, hyperv_stimer, false), ++#endif + DEFINE_PROP_BOOL("hv-frequencies", X86CPU, hyperv_frequencies, false), + DEFINE_PROP_BOOL("check", X86CPU, check_cpuid, true), + DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false), +diff --git a/target/ppc/cpu-models.c b/target/ppc/cpu-models.c +index 6c9bfde..77cb298 100644 +--- a/target/ppc/cpu-models.c ++++ b/target/ppc/cpu-models.c +@@ -65,6 +65,7 @@ + #define POWERPC_DEF(_name, _pvr, _type, _desc) \ + POWERPC_DEF_SVR(_name, _desc, _pvr, POWERPC_SVR_NONE, _type) + ++#if 0 /* Embedded and 32-bit CPUs disabled for Red Hat Enterprise Linux */ + /* Embedded PowerPC */ + /* PowerPC 401 family */ + POWERPC_DEF("401", CPU_POWERPC_401, 401, +@@ -739,10 +740,13 @@ + "PowerPC 7447A v1.2 (G4)") + POWERPC_DEF("7457a_v1.2", CPU_POWERPC_74x7A_v12, 7455, + "PowerPC 7457A v1.2 (G4)") ++#endif + /* 64 bits PowerPC */ + #if defined (TARGET_PPC64) ++#if 0 /* Disabled for Red Hat Enterprise Linux */ + POWERPC_DEF("power5+_v2.1", CPU_POWERPC_POWER5P_v21, POWER5P, + "POWER5+ v2.1") ++#endif + POWERPC_DEF("power7_v2.3", CPU_POWERPC_POWER7_v23, POWER7, + "POWER7 v2.3") + POWERPC_DEF("power7+_v2.1", CPU_POWERPC_POWER7P_v21, POWER7, +@@ -753,14 +757,17 @@ + "POWER8 v2.0") + POWERPC_DEF("power8nvl_v1.0", CPU_POWERPC_POWER8NVL_v10, POWER8, + "POWER8NVL v1.0") ++#if 0 /* Disabled for Red Hat Enterprise Linux */ + POWERPC_DEF("970_v2.2", CPU_POWERPC_970_v22, 970, + "PowerPC 970 v2.2") ++#endif + + POWERPC_DEF("power9_v1.0", CPU_POWERPC_POWER9_DD1, POWER9, + "POWER9 v1.0") + POWERPC_DEF("power9_v2.0", CPU_POWERPC_POWER9_DD20, POWER9, + "POWER9 v2.0") + ++#if 0 /* Disabled for Red Hat Enterprise Linux */ + POWERPC_DEF("970fx_v1.0", CPU_POWERPC_970FX_v10, 970, + "PowerPC 970FX v1.0 (G5)") + POWERPC_DEF("970fx_v2.0", CPU_POWERPC_970FX_v20, 970, +@@ -775,12 +782,14 @@ + "PowerPC 970MP v1.0") + POWERPC_DEF("970mp_v1.1", CPU_POWERPC_970MP_v11, 970, + "PowerPC 970MP v1.1") ++#endif + #endif /* defined (TARGET_PPC64) */ + + /***************************************************************************/ + /* PowerPC CPU aliases */ + + PowerPCCPUAlias ppc_cpu_aliases[] = { ++#if 0 /* Embedded and 32-bit CPUs disabled for Red Hat Enterprise Linux */ + { "403", "403gc" }, + { "405", "405d4" }, + { "405cr", "405crc" }, +@@ -939,20 +948,25 @@ PowerPCCPUAlias ppc_cpu_aliases[] = { + { "7447a", "7447a_v1.2" }, + { "7457a", "7457a_v1.2" }, + { "apollo7pm", "7457a_v1.0" }, ++#endif + #if defined(TARGET_PPC64) ++#if 0 /* Disabled for Red Hat Enterprise Linux */ + { "power5+", "power5+_v2.1" }, + { "power5gs", "power5+_v2.1" }, ++#endif + { "power7", "power7_v2.3" }, + { "power7+", "power7+_v2.1" }, + { "power8e", "power8e_v2.1" }, + { "power8", "power8_v2.0" }, + { "power8nvl", "power8nvl_v1.0" }, + { "power9", "power9_v2.0" }, ++#if 0 /* Disabled for Red Hat Enterprise Linux */ + { "970", "970_v2.2" }, + { "970fx", "970fx_v3.1" }, + { "970mp", "970mp_v1.1" }, + #endif +- ++#endif ++#if 0 /* Disabled for Red Hat Enterprise Linux */ + /* Generic PowerPCs */ + #if defined(TARGET_PPC64) + { "ppc64", "970fx_v3.1" }, +@@ -960,5 +974,6 @@ PowerPCCPUAlias ppc_cpu_aliases[] = { + { "ppc32", "604" }, + { "ppc", "604" }, + { "default", "604" }, ++#endif + { NULL, NULL } + }; +diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c +index 2741b68..c4016e0 100644 +--- a/target/s390x/cpu_models.c ++++ b/target/s390x/cpu_models.c +@@ -372,6 +372,9 @@ static void check_unavailable_features(const S390CPUModel *max_model, + (max_model->def->gen == model->def->gen && + max_model->def->ec_ga < model->def->ec_ga)) { + list_add_feat("type", unavailable); ++ } else if (model->def->gen < 11 && kvm_enabled()) { ++ /* Older CPU models are not supported on Red Hat Enterprise Linux */ ++ list_add_feat("type", unavailable); + } + + /* detect missing features if any to properly report them */ +diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c +index fb59d92..fbccceb 100644 +--- a/target/s390x/kvm.c ++++ b/target/s390x/kvm.c +@@ -2285,6 +2285,14 @@ void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp) + error_setg(errp, "KVM doesn't support CPU models"); + return; + } ++ ++ /* Older CPU models are not supported on Red Hat Enterprise Linux */ ++ if (model->def->gen < 11) { ++ error_setg(errp, "KVM: Unsupported CPU type specified: %s", ++ MACHINE(qdev_get_machine())->cpu_type); ++ return; ++ } ++ + prop.cpuid = s390_cpuid_from_cpu_model(model); + prop.ibc = s390_ibc_from_cpu_model(model); + /* configure cpu features indicated via STFL(e) */ +diff --git a/tests/Makefile.include b/tests/Makefile.include +index 3b9a5e3..e4125a6 100644 +--- a/tests/Makefile.include ++++ b/tests/Makefile.include +@@ -181,8 +181,6 @@ check-qtest-generic-y += tests/device-introspect-test$(EXESUF) + gcov-files-generic-y = qdev-monitor.c qmp.c + + gcov-files-ipack-y += hw/ipack/ipack.c +-check-qtest-ipack-y += tests/ipoctal232-test$(EXESUF) +-gcov-files-ipack-y += hw/char/ipoctal232.c + + check-qtest-virtioserial-y += tests/virtio-console-test$(EXESUF) + gcov-files-virtioserial-y += hw/char/virtio-console.c +@@ -214,23 +212,10 @@ check-qtest-pci-y += tests/e1000e-test$(EXESUF) + gcov-files-pci-y += hw/net/e1000e.c hw/net/e1000e_core.c + check-qtest-pci-y += tests/rtl8139-test$(EXESUF) + gcov-files-pci-y += hw/net/rtl8139.c +-check-qtest-pci-y += tests/pcnet-test$(EXESUF) +-gcov-files-pci-y += hw/net/pcnet.c +-gcov-files-pci-y += hw/net/pcnet-pci.c +-check-qtest-pci-y += tests/eepro100-test$(EXESUF) +-gcov-files-pci-y += hw/net/eepro100.c +-check-qtest-pci-y += tests/ne2000-test$(EXESUF) +-gcov-files-pci-y += hw/net/ne2000.c +-check-qtest-pci-y += tests/nvme-test$(EXESUF) +-gcov-files-pci-y += hw/block/nvme.c + check-qtest-pci-y += tests/ac97-test$(EXESUF) + gcov-files-pci-y += hw/audio/ac97.c +-check-qtest-pci-y += tests/es1370-test$(EXESUF) +-gcov-files-pci-y += hw/audio/es1370.c + check-qtest-pci-y += $(check-qtest-virtio-y) + gcov-files-pci-y += $(gcov-files-virtio-y) hw/virtio/virtio-pci.c +-check-qtest-pci-y += tests/tpci200-test$(EXESUF) +-gcov-files-pci-y += hw/ipack/tpci200.c + check-qtest-pci-y += $(check-qtest-ipack-y) + gcov-files-pci-y += $(gcov-files-ipack-y) + check-qtest-pci-y += tests/display-vga-test$(EXESUF) +@@ -244,23 +229,21 @@ check-qtest-pci-y += tests/intel-hda-test$(EXESUF) + gcov-files-pci-y += hw/audio/intel-hda.c hw/audio/hda-codec.c + check-qtest-pci-$(CONFIG_IVSHMEM) += tests/ivshmem-test$(EXESUF) + gcov-files-pci-y += hw/misc/ivshmem.c +-check-qtest-pci-y += tests/megasas-test$(EXESUF) +-gcov-files-pci-y += hw/scsi/megasas.c ++#check-qtest-pci-y += tests/megasas-test$(EXESUF) ++#gcov-files-pci-y += hw/scsi/megasas.c + + check-qtest-i386-y = tests/endianness-test$(EXESUF) +-check-qtest-i386-y += tests/fdc-test$(EXESUF) +-gcov-files-i386-y = hw/block/fdc.c ++#check-qtest-i386-y += tests/fdc-test$(EXESUF) ++#gcov-files-i386-y = hw/block/fdc.c + check-qtest-i386-y += tests/ide-test$(EXESUF) + check-qtest-i386-y += tests/ahci-test$(EXESUF) + check-qtest-i386-y += tests/hd-geo-test$(EXESUF) + gcov-files-i386-y += hw/block/hd-geometry.c + check-qtest-i386-y += tests/boot-order-test$(EXESUF) +-check-qtest-i386-y += tests/bios-tables-test$(EXESUF) ++#check-qtest-i386-y += tests/bios-tables-test$(EXESUF) + check-qtest-i386-y += tests/boot-serial-test$(EXESUF) + check-qtest-i386-$(CONFIG_SLIRP) += tests/pxe-test$(EXESUF) + check-qtest-i386-y += tests/rtc-test$(EXESUF) +-check-qtest-i386-y += tests/ipmi-kcs-test$(EXESUF) +-check-qtest-i386-y += tests/ipmi-bt-test$(EXESUF) + check-qtest-i386-y += tests/i440fx-test$(EXESUF) + check-qtest-i386-y += tests/fw_cfg-test$(EXESUF) + check-qtest-i386-y += tests/drive_del-test$(EXESUF) +@@ -269,8 +252,6 @@ check-qtest-i386-y += tests/tco-test$(EXESUF) + gcov-files-i386-y += hw/watchdog/watchdog.c hw/watchdog/wdt_ib700.c + check-qtest-i386-y += $(check-qtest-pci-y) + gcov-files-i386-y += $(gcov-files-pci-y) +-check-qtest-i386-y += tests/vmxnet3-test$(EXESUF) +-gcov-files-i386-y += hw/net/vmxnet3.c + gcov-files-i386-y += hw/net/net_rx_pkt.c + gcov-files-i386-y += hw/net/net_tx_pkt.c + check-qtest-i386-y += tests/pvpanic-test$(EXESUF) +@@ -279,8 +260,6 @@ check-qtest-i386-y += tests/i82801b11-test$(EXESUF) + gcov-files-i386-y += hw/pci-bridge/i82801b11.c + check-qtest-i386-y += tests/ioh3420-test$(EXESUF) + gcov-files-i386-y += hw/pci-bridge/ioh3420.c +-check-qtest-i386-y += tests/usb-hcd-ohci-test$(EXESUF) +-gcov-files-i386-y += hw/usb/hcd-ohci.c + check-qtest-i386-y += tests/usb-hcd-uhci-test$(EXESUF) + gcov-files-i386-y += hw/usb/hcd-uhci.c + check-qtest-i386-y += tests/usb-hcd-ehci-test$(EXESUF) +@@ -306,7 +285,7 @@ check-qtest-i386-y += tests/migration-test$(EXESUF) + check-qtest-i386-y += tests/test-x86-cpuid-compat$(EXESUF) + check-qtest-i386-y += tests/numa-test$(EXESUF) + check-qtest-x86_64-y += $(check-qtest-i386-y) +-check-qtest-x86_64-y += tests/sdhci-test$(EXESUF) ++#check-qtest-x86_64-y += tests/sdhci-test$(EXESUF) + gcov-files-i386-y += i386-softmmu/hw/timer/mc146818rtc.c + gcov-files-x86_64-y = $(subst i386-softmmu/,x86_64-softmmu/,$(gcov-files-i386-y)) + +@@ -328,7 +307,7 @@ check-qtest-moxie-y = tests/boot-serial-test$(EXESUF) + + check-qtest-ppc-y = tests/endianness-test$(EXESUF) + check-qtest-ppc-y += tests/boot-order-test$(EXESUF) +-check-qtest-ppc-y += tests/prom-env-test$(EXESUF) ++#check-qtest-ppc-y += tests/prom-env-test$(EXESUF) + check-qtest-ppc-y += tests/drive_del-test$(EXESUF) + check-qtest-ppc-y += tests/boot-serial-test$(EXESUF) + check-qtest-ppc-y += tests/m48t59-test$(EXESUF) +@@ -342,19 +321,19 @@ check-qtest-ppc64-y += tests/pnv-xscom-test$(EXESUF) + check-qtest-ppc64-y += tests/migration-test$(EXESUF) + check-qtest-ppc64-y += tests/rtas-test$(EXESUF) + check-qtest-ppc64-$(CONFIG_SLIRP) += tests/pxe-test$(EXESUF) +-check-qtest-ppc64-y += tests/usb-hcd-ohci-test$(EXESUF) ++#check-qtest-ppc64-y += tests/usb-hcd-ohci-test$(EXESUF) + gcov-files-ppc64-y += hw/usb/hcd-ohci.c +-check-qtest-ppc64-y += tests/usb-hcd-uhci-test$(EXESUF) ++#check-qtest-ppc64-y += tests/usb-hcd-uhci-test$(EXESUF) + gcov-files-ppc64-y += hw/usb/hcd-uhci.c + check-qtest-ppc64-y += tests/usb-hcd-xhci-test$(EXESUF) + gcov-files-ppc64-y += hw/usb/hcd-xhci.c + check-qtest-ppc64-y += $(check-qtest-virtio-y) +-check-qtest-ppc64-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF) +-check-qtest-ppc64-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF) +-check-qtest-ppc64-$(CONFIG_POSIX) += tests/test-filter-redirector$(EXESUF) ++#check-qtest-ppc64-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF) ++#check-qtest-ppc64-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF) ++#check-qtest-ppc64-$(CONFIG_POSIX) += tests/test-filter-redirector$(EXESUF) + check-qtest-ppc64-y += tests/display-vga-test$(EXESUF) + check-qtest-ppc64-y += tests/numa-test$(EXESUF) +-check-qtest-ppc64-$(CONFIG_IVSHMEM) += tests/ivshmem-test$(EXESUF) ++#check-qtest-ppc64-$(CONFIG_IVSHMEM) += tests/ivshmem-test$(EXESUF) + check-qtest-ppc64-y += tests/cpu-plug-test$(EXESUF) + + check-qtest-sh4-y = tests/endianness-test$(EXESUF) +@@ -379,10 +358,10 @@ gcov-files-arm-y += arm-softmmu/hw/block/virtio-blk.c + check-qtest-arm-y += tests/test-arm-mptimer$(EXESUF) + gcov-files-arm-y += hw/timer/arm_mptimer.c + check-qtest-arm-y += tests/boot-serial-test$(EXESUF) +-check-qtest-arm-y += tests/sdhci-test$(EXESUF) ++#check-qtest-arm-y += tests/sdhci-test$(EXESUF) + +-check-qtest-aarch64-y = tests/numa-test$(EXESUF) +-check-qtest-aarch64-y += tests/sdhci-test$(EXESUF) ++#check-qtest-aarch64-y = tests/numa-test$(EXESUF) ++#check-qtest-aarch64-y += tests/sdhci-test$(EXESUF) + check-qtest-aarch64-y += tests/boot-serial-test$(EXESUF) + + check-qtest-microblazeel-y = $(check-qtest-microblaze-y) +@@ -757,17 +736,15 @@ tests/rtc-test$(EXESUF): tests/rtc-test.o + tests/m48t59-test$(EXESUF): tests/m48t59-test.o + tests/endianness-test$(EXESUF): tests/endianness-test.o + tests/spapr-phb-test$(EXESUF): tests/spapr-phb-test.o $(libqos-obj-y) +-tests/prom-env-test$(EXESUF): tests/prom-env-test.o $(libqos-obj-y) ++#tests/prom-env-test$(EXESUF): tests/prom-env-test.o $(libqos-obj-y) + tests/rtas-test$(EXESUF): tests/rtas-test.o $(libqos-spapr-obj-y) +-tests/fdc-test$(EXESUF): tests/fdc-test.o ++#tests/fdc-test$(EXESUF): tests/fdc-test.o + tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y) + tests/ahci-test$(EXESUF): tests/ahci-test.o $(libqos-pc-obj-y) +-tests/ipmi-kcs-test$(EXESUF): tests/ipmi-kcs-test.o +-tests/ipmi-bt-test$(EXESUF): tests/ipmi-bt-test.o + tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o + tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y) + tests/boot-serial-test$(EXESUF): tests/boot-serial-test.o $(libqos-obj-y) +-tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o \ ++#tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o \ + tests/boot-sector.o tests/acpi-utils.o $(libqos-obj-y) + tests/pxe-test$(EXESUF): tests/pxe-test.o tests/boot-sector.o $(libqos-obj-y) + tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y) +@@ -779,11 +756,8 @@ tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y) + tests/e1000-test$(EXESUF): tests/e1000-test.o + tests/e1000e-test$(EXESUF): tests/e1000e-test.o $(libqos-pc-obj-y) + tests/rtl8139-test$(EXESUF): tests/rtl8139-test.o $(libqos-pc-obj-y) +-tests/pcnet-test$(EXESUF): tests/pcnet-test.o +-tests/pnv-xscom-test$(EXESUF): tests/pnv-xscom-test.o ++#tests/pnv-xscom-test$(EXESUF): tests/pnv-xscom-test.o + tests/eepro100-test$(EXESUF): tests/eepro100-test.o +-tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o +-tests/ne2000-test$(EXESUF): tests/ne2000-test.o + tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o + tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y) + tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o $(libqos-virtio-obj-y) +@@ -802,14 +776,11 @@ tests/test-hmp$(EXESUF): tests/test-hmp.o + tests/machine-none-test$(EXESUF): tests/machine-none-test.o + tests/drive_del-test$(EXESUF): tests/drive_del-test.o $(libqos-virtio-obj-y) + tests/qdev-monitor-test$(EXESUF): tests/qdev-monitor-test.o $(libqos-pc-obj-y) +-tests/nvme-test$(EXESUF): tests/nvme-test.o + tests/pvpanic-test$(EXESUF): tests/pvpanic-test.o + tests/i82801b11-test$(EXESUF): tests/i82801b11-test.o + tests/ac97-test$(EXESUF): tests/ac97-test.o +-tests/es1370-test$(EXESUF): tests/es1370-test.o + tests/intel-hda-test$(EXESUF): tests/intel-hda-test.o + tests/ioh3420-test$(EXESUF): tests/ioh3420-test.o +-tests/usb-hcd-ohci-test$(EXESUF): tests/usb-hcd-ohci-test.o $(libqos-usb-obj-y) + tests/usb-hcd-uhci-test$(EXESUF): tests/usb-hcd-uhci-test.o $(libqos-usb-obj-y) + tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-usb-obj-y) + tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y) +@@ -827,14 +798,14 @@ tests/test-filter-mirror$(EXESUF): tests/test-filter-mirror.o $(qtest-obj-y) + tests/test-filter-redirector$(EXESUF): tests/test-filter-redirector.o $(qtest-obj-y) + tests/test-x86-cpuid-compat$(EXESUF): tests/test-x86-cpuid-compat.o $(qtest-obj-y) + tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y) $(libqos-spapr-obj-y) +-tests/megasas-test$(EXESUF): tests/megasas-test.o $(libqos-spapr-obj-y) $(libqos-pc-obj-y) ++#tests/megasas-test$(EXESUF): tests/megasas-test.o $(libqos-spapr-obj-y) $(libqos-pc-obj-y) + tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o $(test-util-obj-y) libvhost-user.a + tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y) + tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o + tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y) + tests/numa-test$(EXESUF): tests/numa-test.o + tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o tests/boot-sector.o tests/acpi-utils.o +-tests/sdhci-test$(EXESUF): tests/sdhci-test.o $(libqos-pc-obj-y) ++#tests/sdhci-test$(EXESUF): tests/sdhci-test.o $(libqos-pc-obj-y) + + tests/migration/stress$(EXESUF): tests/migration/stress.o + $(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@") +diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c +index bf3e193..715f90c 100644 +--- a/tests/bios-tables-test.c ++++ b/tests/bios-tables-test.c +@@ -736,6 +736,7 @@ static void test_acpi_q35_tcg_cphp(void) + free_test_data(&data); + } + ++#if 0 /* Disabled in Red Hat Enterprise Linux */ + static uint8_t ipmi_required_struct_types[] = { + 0, 1, 3, 4, 16, 17, 19, 32, 38, 127 + }; +@@ -772,6 +773,7 @@ static void test_acpi_piix4_tcg_ipmi(void) + &data); + free_test_data(&data); + } ++#endif + + static void test_acpi_q35_tcg_memhp(void) + { +@@ -875,8 +877,10 @@ int main(int argc, char *argv[]) + qtest_add_func("acpi/piix4/bridge", test_acpi_piix4_tcg_bridge); + qtest_add_func("acpi/q35", test_acpi_q35_tcg); + qtest_add_func("acpi/q35/bridge", test_acpi_q35_tcg_bridge); ++#if 0 /* Disabled in Red Hat Enterprise Linux */ + qtest_add_func("acpi/piix4/ipmi", test_acpi_piix4_tcg_ipmi); + qtest_add_func("acpi/q35/ipmi", test_acpi_q35_tcg_ipmi); ++#endif + qtest_add_func("acpi/piix4/cpuhp", test_acpi_piix4_tcg_cphp); + qtest_add_func("acpi/q35/cpuhp", test_acpi_q35_tcg_cphp); + qtest_add_func("acpi/piix4/memhp", test_acpi_piix4_tcg_memhp); +diff --git a/tests/boot-order-test.c b/tests/boot-order-test.c +index e70f5de..1be0731 100644 +--- a/tests/boot-order-test.c ++++ b/tests/boot-order-test.c +@@ -106,6 +106,7 @@ static void test_pc_boot_order(void) + test_boot_orders(NULL, read_boot_order_pc, test_cases_pc); + } + ++#if 0 /* Disabled for RHEL, since CONFIG_MAC and CONFIG_PREP are not enabled */ + static uint8_t read_m48t59(uint64_t addr, uint16_t reg) + { + writeb(addr, reg & 0xff); +@@ -136,6 +137,7 @@ static uint64_t read_boot_order_pmac(void) + + return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE); + } ++#endif /* Disabled for RHEL, since CONFIG_MAC and CONFIG_PREP are not enabled */ + + static const boot_order_test test_cases_fw_cfg[] = { + { "", 'c', 'c' }, +@@ -145,6 +147,7 @@ static const boot_order_test test_cases_fw_cfg[] = { + {} + }; + ++#if 0 /* Disabled for RHEL, since CONFIG_MAC and CONFIG_PREP are not enabled */ + static void test_pmac_oldworld_boot_order(void) + { + test_boot_orders("g3beige", read_boot_order_pmac, test_cases_fw_cfg); +@@ -153,7 +156,9 @@ static void test_pmac_oldworld_boot_order(void) + static void test_pmac_newworld_boot_order(void) + { + test_boot_orders("mac99", read_boot_order_pmac, test_cases_fw_cfg); ++ + } ++#endif /* Disabled for RHEL, since CONFIG_MAC and CONFIG_PREP are not enabled */ + + static uint64_t read_boot_order_sun4m(void) + { +@@ -188,11 +193,13 @@ int main(int argc, char *argv[]) + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qtest_add_func("boot-order/pc", test_pc_boot_order); + } else if (strcmp(arch, "ppc") == 0 || strcmp(arch, "ppc64") == 0) { ++#if 0 /* Disabled for RHEL, since CONFIG_MAC and CONFIG_PREP are not enabled */ + qtest_add_func("boot-order/prep", test_prep_boot_order); + qtest_add_func("boot-order/pmac_oldworld", + test_pmac_oldworld_boot_order); + qtest_add_func("boot-order/pmac_newworld", + test_pmac_newworld_boot_order); ++#endif /* Disabled for RHEL, since CONFIG_MAC and CONFIG_PREP are not enabled */ + } else if (strcmp(arch, "sparc") == 0) { + qtest_add_func("boot-order/sun4m", test_sun4m_boot_order); + } else if (strcmp(arch, "sparc64") == 0) { +diff --git a/tests/boot-serial-test.c b/tests/boot-serial-test.c +index 011525d..dc682c1 100644 +--- a/tests/boot-serial-test.c ++++ b/tests/boot-serial-test.c +@@ -73,7 +73,7 @@ typedef struct testdef { + } testdef_t; + + static testdef_t tests[] = { +- { "alpha", "clipper", "", "PCI:" }, ++/* { "alpha", "clipper", "", "PCI:" }, + { "ppc", "ppce500", "", "U-Boot" }, + { "ppc", "prep", "-m 96", "Memory size: 96 MB" }, + { "ppc", "40p", "-boot d", "Booting from device d" }, +@@ -83,14 +83,14 @@ static testdef_t tests[] = { + { "ppc64", "ppce500", "", "U-Boot" }, + { "ppc64", "prep", "-boot e", "Booting from device e" }, + { "ppc64", "40p", "-m 192", "Memory size: 192 MB" }, +- { "ppc64", "mac99", "", "PowerPC,970FX" }, ++ { "ppc64", "mac99", "", "PowerPC,970FX" },*/ + { "ppc64", "pseries", "", "Open Firmware" }, +- { "ppc64", "powernv", "-cpu POWER8", "OPAL" }, +- { "ppc64", "sam460ex", "-device e1000", "8086 100e" }, ++/* { "ppc64", "powernv", "-cpu POWER8", "OPAL" }, ++ { "ppc64", "sam460ex", "-device e1000", "8086 100e" },*/ + { "i386", "isapc", "-cpu qemu32 -device sga", "SGABIOS" }, + { "i386", "pc", "-device sga", "SGABIOS" }, + { "i386", "q35", "-device sga", "SGABIOS" }, +- { "x86_64", "isapc", "-cpu qemu32 -device sga", "SGABIOS" }, ++ { "x86_64", "pc", "-cpu qemu32 -device sga", "SGABIOS" }, + { "x86_64", "q35", "-device sga", "SGABIOS" }, + { "sparc", "LX", "", "TMS390S10" }, + { "sparc", "SS-4", "", "MB86904" }, +diff --git a/tests/e1000-test.c b/tests/e1000-test.c +index 0c5fcdc..0504d33 100644 +--- a/tests/e1000-test.c ++++ b/tests/e1000-test.c +@@ -29,8 +29,10 @@ static void test_device(gconstpointer data) + static const char *models[] = { + "e1000", + "e1000-82540em", ++#if 0 /* Disabled in Red Hat Enterprise Linux 7 */ + "e1000-82544gc", + "e1000-82545em", ++#endif + }; + + int main(int argc, char **argv) +diff --git a/tests/endianness-test.c b/tests/endianness-test.c +index 546e096..1bd87db 100644 +--- a/tests/endianness-test.c ++++ b/tests/endianness-test.c +@@ -34,6 +34,7 @@ static const TestCase test_cases[] = { + { "mips64", "mips", 0x14000000, .bswap = true }, + { "mips64", "malta", 0x10000000, .bswap = true }, + { "mips64el", "fulong2e", 0x1fd00000 }, ++#if 0 /* Disabled for RHEL, since ISA is not enabled */ + { "ppc", "g3beige", 0xfe000000, .bswap = true, .superio = "i82378" }, + { "ppc", "prep", 0x80000000, .bswap = true }, + { "ppc", "bamboo", 0xe8000000, .bswap = true, .superio = "i82378" }, +@@ -41,6 +42,7 @@ static const TestCase test_cases[] = { + { "ppc64", "pseries", (1ULL << 45), .bswap = true, .superio = "i82378" }, + { "ppc64", "pseries-2.7", 0x10080000000ULL, + .bswap = true, .superio = "i82378" }, ++#endif /* Disabled for RHEL, since ISA is not enabled */ + { "sh4", "r2d", 0xfe240000, .superio = "i82378" }, + { "sh4eb", "r2d", 0xfe240000, .bswap = true, .superio = "i82378" }, + { "sparc64", "sun4u", 0x1fe02000000LL, .bswap = true }, +diff --git a/tests/ivshmem-test.c b/tests/ivshmem-test.c +index 8af16ee..df2eb50 100644 +--- a/tests/ivshmem-test.c ++++ b/tests/ivshmem-test.c +@@ -257,6 +257,7 @@ static void test_ivshmem_pair(void) + g_free(data); + } + ++#if 0 /* Disabled for Red Hat Enterprise Linux: */ + typedef struct ServerThread { + GThread *thread; + IvshmemServer *server; +@@ -414,9 +415,11 @@ static void test_ivshmem_server_irq(void) + { + test_ivshmem_server(false); + } ++#endif + + #define PCI_SLOT_HP 0x06 + ++#if 0 /* Test uses legacy ivshmem, which is disabled for Red Hat Enterprise Linux: */ + static void test_ivshmem_hotplug(void) + { + const char *arch = qtest_get_arch(); +@@ -434,6 +437,7 @@ static void test_ivshmem_hotplug(void) + qtest_end(); + g_free(opts); + } ++#endif + + static void test_ivshmem_memdev(void) + { +@@ -501,7 +505,7 @@ static gchar *mktempshm(int size, int *fd) + int main(int argc, char **argv) + { + int ret, fd; +- const char *arch = qtest_get_arch(); ++/* const char *arch = qtest_get_arch(); */ + gchar dir[] = "/tmp/ivshmem-test.XXXXXX"; + + #if !GLIB_CHECK_VERSION(2, 31, 0) +@@ -528,14 +532,18 @@ int main(int argc, char **argv) + tmpserver = g_strconcat(tmpdir, "/server", NULL); + + qtest_add_func("/ivshmem/single", test_ivshmem_single); ++#if 0 /* Test uses legacy ivshmem, which is disabled for Red Hat Enterprise Linux: */ + qtest_add_func("/ivshmem/hotplug", test_ivshmem_hotplug); ++#endif + qtest_add_func("/ivshmem/memdev", test_ivshmem_memdev); + if (g_test_slow()) { + qtest_add_func("/ivshmem/pair", test_ivshmem_pair); ++#if 0 /* Disabled for Red Hat Enterprise Linux: */ + if (strcmp(arch, "ppc64") != 0) { + qtest_add_func("/ivshmem/server-msi", test_ivshmem_server_msi); + qtest_add_func("/ivshmem/server-irq", test_ivshmem_server_irq); + } ++#endif + } + + ret = g_test_run(); +diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 +index f617e25..69d34eb 100755 +--- a/tests/qemu-iotests/051 ++++ b/tests/qemu-iotests/051 +@@ -168,11 +168,11 @@ run_qemu -drive if=virtio + case "$QEMU_DEFAULT_MACHINE" in + pc) + run_qemu -drive if=none,id=disk -device ide-cd,drive=disk +- run_qemu -drive if=none,id=disk -device lsi53c895a -device scsi-cd,drive=disk ++# run_qemu -drive if=none,id=disk -device lsi53c895a -device scsi-cd,drive=disk + run_qemu -drive if=none,id=disk -device ide-drive,drive=disk + run_qemu -drive if=none,id=disk -device ide-hd,drive=disk +- run_qemu -drive if=none,id=disk -device lsi53c895a -device scsi-disk,drive=disk +- run_qemu -drive if=none,id=disk -device lsi53c895a -device scsi-hd,drive=disk ++# run_qemu -drive if=none,id=disk -device lsi53c895a -device scsi-disk,drive=disk ++# run_qemu -drive if=none,id=disk -device lsi53c895a -device scsi-hd,drive=disk + ;; + *) + ;; +@@ -197,11 +197,11 @@ run_qemu -drive file="$TEST_IMG",if=virtio,readonly=on + case "$QEMU_DEFAULT_MACHINE" in + pc) + run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device ide-cd,drive=disk +- run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device lsi53c895a -device scsi-cd,drive=disk ++# run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device lsi53c895a -device scsi-cd,drive=disk + run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device ide-drive,drive=disk + run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device ide-hd,drive=disk +- run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device lsi53c895a -device scsi-disk,drive=disk +- run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device lsi53c895a -device scsi-hd,drive=disk ++# run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device lsi53c895a -device scsi-disk,drive=disk ++# run_qemu -drive file="$TEST_IMG",if=none,id=disk,readonly=on -device lsi53c895a -device scsi-hd,drive=disk + ;; + *) + ;; +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index 52a80f3..99777ec 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -77,7 +77,7 @@ + 068 rw auto quick + 069 rw auto quick + 070 rw auto quick +-071 rw auto quick ++# 071 rw auto quick -- requires whitelisted blkverify + 072 rw auto quick + 073 rw auto quick + 074 rw auto quick +@@ -105,7 +105,7 @@ + 096 rw auto quick + 097 rw auto backing + 098 rw auto backing quick +-099 rw auto quick ++# 099 rw auto quick -- requires whitelisted blkverify + # 100 was removed, do not reuse + 101 rw auto quick + 102 rw auto quick +diff --git a/tests/qom-test.c b/tests/qom-test.c +index a34ff6b..db0d3ab 100644 +--- a/tests/qom-test.c ++++ b/tests/qom-test.c +@@ -16,7 +16,9 @@ + #include "libqtest.h" + + static const char *blacklist_x86[] = { +- "xenfv", "xenpv", NULL ++ "xenfv", "xenpv", "isapc", ++ "rhel6.6.0", "rhel6.5.0", "rhel6.4.0", "rhel6.3.0", ++ "rhel6.2.0", "rhel6.1.0", "rhel6.0.0", NULL + }; + + static const struct { +diff --git a/tests/test-hmp.c b/tests/test-hmp.c +index 5352c9c..674dd64 100644 +--- a/tests/test-hmp.c ++++ b/tests/test-hmp.c +@@ -139,7 +139,7 @@ static void add_machine_test_case(const char *mname) + char *path; + + /* Ignore blacklisted machines that have known problems */ +- if (!strcmp("xenfv", mname) || !strcmp("xenpv", mname)) { ++ if (!strcmp("xenfv", mname) || !strcmp("xenpv", mname) || !strcmp("isapc", mname)) { + return; + } + +diff --git a/tests/test-x86-cpuid-compat.c b/tests/test-x86-cpuid-compat.c +index 02e4184..9e4a508 100644 +--- a/tests/test-x86-cpuid-compat.c ++++ b/tests/test-x86-cpuid-compat.c +@@ -306,6 +306,7 @@ int main(int argc, char **argv) + "-cpu 486,xlevel2=0xC0000002,+xstore", + "xlevel2", 0xC0000002); + ++#if 0 /* Disabled in Red Hat Enterprise Linux */ + /* Check compatibility of old machine-types that didn't + * auto-increase level/xlevel/xlevel2: */ + +@@ -356,6 +357,7 @@ int main(int argc, char **argv) + add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-on", + "-machine pc-i440fx-2.4 -cpu SandyBridge,+npt", + "xlevel", 0x80000008); ++#endif + + /* Test feature parsing */ + add_feature_test("x86/cpuid/features/plus", +diff --git a/tests/usb-hcd-xhci-test.c b/tests/usb-hcd-xhci-test.c +index 9c14e30..192b7f7 100644 +--- a/tests/usb-hcd-xhci-test.c ++++ b/tests/usb-hcd-xhci-test.c +@@ -21,6 +21,7 @@ static void test_xhci_hotplug(void) + usb_test_hotplug("xhci", 1, NULL); + } + ++#if 0 /* Disabled for Red Hat Enterprise Linux 7 */ + static void test_usb_uas_hotplug(void) + { + qtest_qmp_device_add("usb-uas", "uas", NULL); +@@ -34,6 +35,7 @@ static void test_usb_uas_hotplug(void) + qtest_qmp_device_del("scsihd"); + qtest_qmp_device_del("uas"); + } ++#endif + + int main(int argc, char **argv) + { +@@ -43,8 +45,9 @@ int main(int argc, char **argv) + + qtest_add_func("/xhci/pci/init", test_xhci_init); + qtest_add_func("/xhci/pci/hotplug", test_xhci_hotplug); ++#if 0 /* Disabled for Red Hat Enterprise Linux 7 */ + qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug); +- ++#endif + qtest_start("-device nec-usb-xhci,id=xhci" + " -drive id=drive0,if=none,file=null-co://,format=raw"); + ret = g_test_run(); +diff --git a/vl.c b/vl.c +index fce1fd1..03950fc 100644 +--- a/vl.c ++++ b/vl.c +@@ -165,7 +165,7 @@ unsigned int max_cpus; + int smp_cores = 1; + int smp_threads = 1; + int acpi_enabled = 1; +-int no_hpet = 0; ++int no_hpet = 1; /* Always disabled for Red Hat Enterprise Linux */ + int fd_bootchk = 1; + static int no_reboot; + int no_shutdown = 0; +-- +1.8.3.1 + diff --git a/SOURCES/0005-Add-RHEL-7-machine-types.patch b/SOURCES/0005-Add-RHEL-7-machine-types.patch new file mode 100644 index 0000000..f2edcb0 --- /dev/null +++ b/SOURCES/0005-Add-RHEL-7-machine-types.patch @@ -0,0 +1,3695 @@ +From 84733200df4772cf20a4e9f7e5669b87ef5ab4c0 Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Sun, 14 Dec 2014 18:32:18 +0100 +Subject: Add RHEL 7 machine types + +This commit adds all changes related to machine types applied in +qemu-kvm-rhev-2.1.2-16.el7. + +Signed-off-by: Miroslav Rezanina + +Conflicts (on 2.3 rebase): + default-configs/ppc64-softmmu.mak + hw/arm/Makefile.objs + hw/i386/pc_piix.c + hw/i386/pc_q35.c + hw/ppc/spapr.c + savevm.c - has to change shadow_bios tail to rcu list handling + target-i386/machine.c - use xmm instead of ymmh register + +-- + +Rebase notes (2.12.0): +- replace has_dynamic_sysbus with list of dynamic devices (upstream) +- Changed force_rev1_fadt handling +- Added default nic to machine options for pc_piix and pc_q35 +- Disabled m48t59-test for ppc64 +- Disabled unsupported machine types in boot-serial-test +- Disabled pseries-rhel7.2.0 machine type for cpu-plug-test + +Rebase notes (2.11.0): +- Return type for VMStateDescription.pre_save changed (upstream) +- Use default_cpu_type instead of tcg_default_cpu for sPAPRMachineClass +- Use virt_get_default_cpu_node_id in arm rhel machine type +- Set default_cpu_type to cortex-a57 +- Disable options for arm boards instead of object file remove +- Changed order with device disable commit - removed unnecessary changes + +Rebase notes (2.10.0): +- Commented out new unused functions in hw/arm/virt.c +- savevm_skip_section_footers changed to COMPAT variable (upstream) +- global_state_set_optional changed to COMPAT variable (upstream) +- savevm_skip_configuration changed to COMPAT variable (upstream) +- move shadow_bios() to separate module with stub version available +- set possible_cpu_arch_ids and cpu_index_to_instance_props fields for arm machine type +- Re-enable older machine types for ppc64 +- Commented 7.2 rebase error for compat qemu-extended-regs + +Rebase notes (2.9.0): +- new header file for arm (upstream) +- query_hotpluggable_cpus renamed to has_hotpluggable_cpus (upstream) +- replace MAX_CPUMASK_BITS with max_cpus +- Adding rhel6-e1000.rom moved to Initial redhat commit +- Fixed conflict on cirrus_vga.c + +Rebase notes (2.8.0): +- new "m->max_cpus = 288" in pc_q35_machine_options hw/i386/pc_q35.c + +Rebase notes (2.7.0): +- Additional fsl-imx6.o sabrelito.o files in arm hw dir + +Rebase notes (2.6.0): +- Changes in handling of some compat properties +- Fixes in x86_64 copmat models +- Added required devices for aarch64 +- Fixes for ppc machine types + +Rebase notes (2.5.0): +- changed cpu defaults structure +- chnaged cpu compat properties handling +- added fix for arm machine type + +Rebase notes (2.4.0) +- Moved needed attribute (due to 5cd8cadae8db905afcbf877cae568c27d1d55a8a) +- Fixes to machine types changes + +Merged patches (2.12.0): +- 402de2c redhat: Define the pseries-rhel7.5-sxxm machine type +- a232ba5 redhat: Define the pseries-rhel7.4-sxxm machine type +- 54f90c2 redhat: Define the pseries-rhel7.3-sxxm machine type +- 3fe3c44 Disable GeForce quirks in vfio-pci for RHEL machines +- 4780659 target-i386: cpu: add new CPU models for indirect branch predictor restrictions +- d486f00 hw/ppc/spapr_caps: Rework spapr_caps to use uint8 internal representation +- cc06449 spapr: Handle Decimal Floating Point (DFP) as an optional capability +- 64ef8ed spapr: Handle VMX/VSX presence as an spapr capability flag +- acad5a1 spapr: Treat Hardware Transactional Memory (HTM) as an optional capability + +Merged patches (2.11.0): +- 58702e8dbb redhat: fix HW_COMPAT_RHEL7_3 +- ea157600da redhat: define HW_COMPAT_RHEL7_4 +- 86ef5cd189 redhat: define pseries-rhel7.5.0 machine type +- d8bf28e9ec qemu-kvm-ma: define only pseries-rhel7.5.0 machine type for -ma +- 9b07271835 Create x86 7.5.0 machine types +- e63d707db9 acpi: Force rev1 FADT on old q35 machine types +- c091cd7a1b pc: make pc_rom RO only on new machine types +- 082bd3ba3d arm/virt: Add RHEL 7.5 machine typei +- 8663586f2b qemu-kvm-rhev: only allows pseries-rhel7.5.0 machine type with POWER9 guest +- 229441f111 machine compat: pci_bridge/shpc always enable +- 917c9e7df7 pcie_root_port: Fix x-migrate-msix compat +- b0c0614041 q35: Fix mismerge +- 888e98c6d6 hw/pci-host: Fix x86 Host Bridges 64bit PCI hole (partial) +- b2f9f4fcaa i386/cpu/hyperv: support over 64 vcpus for windows guests (partial) +- cc306393d7 migcompat/e1000e: Work around 7.3 msi/intr_state field +- c4753f76a3 migcompat/rtl8139: Work around version bump +- 1ece5bfd00 RHEL: Add RHEL7 machine type for qemu on s390x + +Merged patches (2.10.0) +- 5090ba1 Fix qemu-kvm does not quit when booting guest w/ 241 vcpus and "-no-kvm" +- 3c52050 x86: bump maximum vcpu count of pc-q35-rhel7.4.0 to 288 +- 0ed92ca x86: Work around SMI breakages +- f7b5a7f spapr: update SPAPR_COMPAT_RHEL7_3 +- 04faf4d migration: update HW_COMPAT_RHEL7_3 +- 9982768 x86: bump maximum vcpu count of pc-q35-rhel7.4.0 to 384 +- f5095ba re-enable DMA for 7.3 machine type +- d6ec65d x86 machine compat: 2.9 stragglers +- eda659a disable linuxboot_dma.bin option rom for 7.3 machine types +- 8c122a5 Revert "hw/pci: disable pci-bridge's shpc by default" (partial) +- 627adfa virtio_net: Bypass backends for MTU feature negotiation (partial) +- b430787 pc: Use "min[x]level" on compat_props on RHEL machine-types +- 8c3f45a AArch64: remove mach-virt-7.3 machine type +- d5df1ef Downstream: Update pseries machine types for RHEL-ALT-7.4 + +Merged patches (2.9.0) +- 8475d69 hw/arm/virt: Disable virtio-net-pci option ROM file loading +- 73fe1f6 Workaround rhel6 ctrl_guest_offloads machine type mismatch +- 21d32ca pc_piix: fix compat props typo for RHEL6 machine types +- 55a5002 compat: define HW_COMPAT_RHEL7_3 +- 1b8e927 spapr: define pseries-rhel7.4.0 machine type +- cdb76ec hw/arm/virt: remove aarch64 rhel machine type +- 7dfa88b hw/arm/virt: create virt-rhel7.3.0 machine type +- 6894f91 hw/arm/virt: create virt-rhel7.4.0 machine type +- a9d2d39 x86: Split out options for the head rhel7 machine types +- fdafbdc x86: Create PC_RHEL7_3_COMPAT definition +- 3427c72 x86: Define pc-i440fx-rhel7.4.0 +- aea20ab x86: Define pc-q35-rhel7.4.0 +- 0185c0f x86: Remove downstream opteron rdtscp override +- 6b51073 fix abort in acpi_setup() since 2.8 with rhel6 machine types +- 954fc0d intel-hda: fix rhel6 compat property +- 1b57274 kvmclock: reduce kvmclock difference on migration (rhel only part) + +Merged patches (2.8.0) +- a1da2f0 virtio-pci: reduce modern_mem_bar size (rhel only part) + +Merged patches (2.7.0): +- fe9d1cf pc: Use right HW_COMPAT_* macros at PC_RHEL7* compat macros +- 3938189 compat: Add missing "any_layout" in HW_COMPAT_RHEL7_1 +- 6dffc9d spapr: update RHEL-7.2 machine type +- c5d5910 migration: fix HW_COMPAT_RHEL7_2 +- 2da9bb8 pc: New (default) pc-i440fx-rhel7.3.0 machine-type +- 0520d7e 7.3 mismerge fix: Fix ich9-intel-hda compatibility +- 89528b3 PC migration compat: Section footers/global state +- 2231e35 fw_cfg for 7.2 compatibility +- b8a3ade pc: Create new pc-q35-rhel7.3.0 machine-type +- 340929b q35: Remove 7.0, 7.1, 7.2 machine types +- bb7fc95 machine types: fix pc_machine_*_options chain +- d9fa9aa Fix rhel6 rom file +- dc39363 fix vga type for older machines +- 255a2d1 7.2 machine type compatibility +- 16c3d25 target-i386: Remove SSE4a from qemu64 CPU model (rhel only part) +- 76a1796 target-i386: Remove ABM from qemu64 CPU model (rhel only part) +- a9f8773 pc: Recover PC_RHEL7_1_COMPAT from RHEL-7.2 code +- 7a6ed67 pc: Include missing PC_COMPAT_2_3 entries in PC_RHEL7_2_COMPAT +- 07428f6 Revert "static checker: e1000-82540em got aliased to e1000" +- 446cf1f Revert "e1000: use alias for default model" +- 615096e 7.x compat: e1000-82540em +- 0855905 hw/arm/virt: kill 7.2 machine type +- 18bbea2 usbredir: turn off streams for rhel7.2 & older +- 910cf4a target-i386: Fill high bits of mtrr mask (rhel only part) +- 0e8ab1b target-i386: Enable host-phys-bits on RHEL +- 8c5f8a5 pc: Fix rhel6.3.0 compat_props setting +- 8f869f1 pc: use new CPU hotplug interface since 2.7 machine type (rhel only part) +- d9d646f machine: add properties to compat_props incrementaly (rhel only part) +- acb18fd apic: Use apic_id as apic's migration instance_id (rhel only part) +- c7e37d4 apic: fix broken migration for kvm-apic (rhel only part) +- eca64aee hw/virtio-pci: fix virtio behaviour +- c56b8F6e pc-rhel-7.2: pcie: fix link active status bit migration +- 5522aa3 q35-rhel: allow dynamic sysbus + +Merged patches (2.6.0): +- f915d7f arm: virt: Add an abstract RHEL ARM virt machine type +- deffcc0 arm: virt: Add RHEL 7.3.0 virt machine type +- 04ca07d arm: virt: Consolidate the naming of RHEL virt machine types +- 2856ce2 Define HW_COMPAT_RHEL7_2 +- 1869242 spapr: move pseries-2.5 machine to RHEL disabled machine zone +- cc59ce7 spapr: add RHEL-7.3 machine type +- 98549c5 pc: Fix property names on CPU compat code +- caa47bb Fix ich9-intel-hda compatibility + +Merged patches (2.3.0): +- bb4e53c2 pc: add rhel6.6.0 machine type +- 129a2b3 Downstream-only: Restore "pseries" machine alias + +Merged patches (2.4.0): +- 8e8107c numa: Don't allow memdev= on RHEL-6 machine-types +- 8b220c0 pc_sysfw: prevent pflash and/or mis-sized firmware for rhel6.x.0 machtypes +- 9dba3a5 Add pc-i440fx-rhel7.2.0 machine type +- 1c88ffa Add pc-q35-rhel7.2.0 machine type +- 6f74d0c Downstream-only: Add rhel7.2.0 machine type +- a7d6105 Add flag for pre-2.2 migration compatibility +- 17f9a18 Serial: Migration compatibility pre 2.2/7.2 +- 3799a57 Migration compat for mc146818rtc/irq_reinject_on_ack_count subsection +- 5668cc1 Fix reported machine type +- 2417534 386: drop FDC in pc-q35-rhel7.2.0 if neither it nor fl. drives are anted +- f42eee5 global_state: Make section optional +- 8640f84 migration: Add configuration section +- 48c857b pc: memhotplug: fix incorrectly set reserved-memory-end +- f33f0b6 pc: memhotplug: keep reserved-memory-end broken on rhel71 and earlier machines + +(cherry picked from commit 44f7e7595c416686a00015e317e74183037a8136) + +Conflicts: + redhat/qemu-kvm.spec.template + +Conflicts: + hw/arm/Makefile.objs + hw/i386/acpi-build.c + hw/ppc/spapr.c + hw/timer/mc146818rtc.c + migration/migration.h + target/i386/machine.c + +Conflicts: + tests/boot-serial-test.c + +(cherry picked from commit f3d24bf7acf06b294404ec0c9849df6211b7b4a7) +--- + hw/acpi/ich9.c | 16 + + hw/acpi/piix4.c | 6 +- + hw/arm/virt.c | 121 +++++- + hw/char/serial.c | 28 ++ + hw/display/cirrus_vga.c | 4 +- + hw/display/vga-isa.c | 2 +- + hw/i386/Makefile.objs | 1 + + hw/i386/acpi-build.c | 3 + + hw/i386/pc.c | 7 +- + hw/i386/pc_piix.c | 894 +++++++++++++++++++++++++++++++++++++- + hw/i386/pc_q35.c | 74 +++- + hw/i386/pc_sysfw.c | 16 + + hw/i386/shadow-bios.c | 64 +++ + hw/net/e1000.c | 20 +- + hw/net/e1000e.c | 21 + + hw/net/ne2000.c | 2 +- + hw/net/pcnet-pci.c | 2 +- + hw/net/rtl8139.c | 6 +- + hw/ppc/spapr.c | 213 +++++++++ + hw/ppc/spapr_cpu_core.c | 13 + + hw/s390x/s390-virtio-ccw.c | 17 +- + hw/smbios/smbios.c | 1 + + hw/timer/i8254_common.c | 2 +- + hw/timer/mc146818rtc.c | 6 + + hw/usb/hcd-uhci.c | 15 +- + hw/usb/hcd-xhci.c | 20 + + hw/usb/hcd-xhci.h | 2 + + hw/virtio/virtio-pci.c | 2 +- + hw/virtio/virtio.c | 22 +- + include/hw/acpi/ich9.h | 3 + + include/hw/arm/virt.h | 22 + + include/hw/compat.h | 195 +++++++++ + include/hw/i386/pc.h | 554 +++++++++++++++++++++++ + include/hw/ppc/spapr.h | 1 + + include/hw/usb.h | 7 + + include/hw/virtio/virtio.h | 1 + + include/sysemu/sysemu.h | 2 + + migration/migration.c | 2 + + migration/migration.h | 5 + + migration/savevm.c | 9 + + numa.c | 13 + + qdev-monitor.c | 1 - + redhat/qemu-kvm.spec.template | 15 +- + scripts/vmstate-static-checker.py | 1 - + stubs/Makefile.objs | 1 + + stubs/shadow-bios.c | 7 + + target/i386/cpu.c | 9 +- + target/i386/machine.c | 21 + + target/ppc/compat.c | 11 + + target/ppc/cpu.h | 1 + + tests/Makefile.include | 8 +- + tests/cpu-plug-test.c | 3 +- + 52 files changed, 2450 insertions(+), 42 deletions(-) + create mode 100644 hw/i386/shadow-bios.c + create mode 100644 stubs/shadow-bios.c + +diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c +index a4e87b8..23a7baa 100644 +--- a/hw/acpi/ich9.c ++++ b/hw/acpi/ich9.c +@@ -441,6 +441,18 @@ static void ich9_pm_set_enable_tco(Object *obj, bool value, Error **errp) + s->pm.enable_tco = value; + } + ++static bool ich9_pm_get_force_rev1_fadt(Object *obj, Error **errp) ++{ ++ ICH9LPCState *s = ICH9_LPC_DEVICE(obj); ++ return s->pm.force_rev1_fadt; ++} ++ ++static void ich9_pm_set_force_rev1_fadt(Object *obj, bool value, Error **errp) ++{ ++ ICH9LPCState *s = ICH9_LPC_DEVICE(obj); ++ s->pm.force_rev1_fadt = value; ++} ++ + void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp) + { + static const uint32_t gpe0_len = ICH9_PMIO_GPE0_LEN; +@@ -465,6 +477,10 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp) + ich9_pm_get_cpu_hotplug_legacy, + ich9_pm_set_cpu_hotplug_legacy, + NULL); ++ object_property_add_bool(obj, "__com.redhat_force-rev1-fadt", ++ ich9_pm_get_force_rev1_fadt, ++ ich9_pm_set_force_rev1_fadt, ++ NULL); + object_property_add(obj, ACPI_PM_PROP_S3_DISABLED, "uint8", + ich9_pm_get_disable_s3, + ich9_pm_set_disable_s3, +diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c +index 8b70345..d706360 100644 +--- a/hw/acpi/piix4.c ++++ b/hw/acpi/piix4.c +@@ -311,7 +311,7 @@ static const VMStateDescription vmstate_cpuhp_state = { + static const VMStateDescription vmstate_acpi = { + .name = "piix4_pm", + .version_id = 3, +- .minimum_version_id = 3, ++ .minimum_version_id = 2, + .minimum_version_id_old = 1, + .load_state_old = acpi_load_old, + .post_load = vmstate_acpi_post_load, +@@ -671,8 +671,8 @@ static void piix4_send_gpe(AcpiDeviceIf *adev, AcpiEventStatusBits ev) + + static Property piix4_pm_properties[] = { + DEFINE_PROP_UINT32("smb_io_base", PIIX4PMState, smb_io_base, 0), +- DEFINE_PROP_UINT8(ACPI_PM_PROP_S3_DISABLED, PIIX4PMState, disable_s3, 0), +- DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_DISABLED, PIIX4PMState, disable_s4, 0), ++ DEFINE_PROP_UINT8(ACPI_PM_PROP_S3_DISABLED, PIIX4PMState, disable_s3, 1), ++ DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_DISABLED, PIIX4PMState, disable_s4, 1), + DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_VAL, PIIX4PMState, s4_val, 2), + DEFINE_PROP_BOOL("acpi-pci-hotplug-with-bridge-support", PIIX4PMState, + use_acpi_pci_hotplug, true), +diff --git a/hw/arm/virt.c b/hw/arm/virt.c +index 94dcb12..806eb1e 100644 +--- a/hw/arm/virt.c ++++ b/hw/arm/virt.c +@@ -59,6 +59,7 @@ + #include "qapi/visitor.h" + #include "standard-headers/linux/input.h" + ++#if 0 /* disabled Red Hat Enterprise Linux */ + #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \ + static void virt_##major##_##minor##_class_init(ObjectClass *oc, \ + void *data) \ +@@ -86,7 +87,36 @@ + DEFINE_VIRT_MACHINE_LATEST(major, minor, true) + #define DEFINE_VIRT_MACHINE(major, minor) \ + DEFINE_VIRT_MACHINE_LATEST(major, minor, false) +- ++#endif /* disabled for RHEL */ ++ ++#define DEFINE_RHEL_MACHINE_LATEST(m, n, s, latest) \ ++ static void rhel##m##n##s##_virt_class_init(ObjectClass *oc, \ ++ void *data) \ ++ { \ ++ MachineClass *mc = MACHINE_CLASS(oc); \ ++ rhel##m##n##s##_virt_options(mc); \ ++ mc->desc = "RHEL " # m "." # n "." # s " ARM Virtual Machine"; \ ++ if (latest) { \ ++ mc->alias = "virt"; \ ++ mc->is_default = 1; \ ++ } \ ++ } \ ++ static const TypeInfo rhel##m##n##s##_machvirt_info = { \ ++ .name = MACHINE_TYPE_NAME("virt-rhel" # m "." # n "." # s), \ ++ .parent = TYPE_RHEL_MACHINE, \ ++ .instance_init = rhel##m##n##s##_virt_instance_init, \ ++ .class_init = rhel##m##n##s##_virt_class_init, \ ++ }; \ ++ static void rhel##m##n##s##_machvirt_init(void) \ ++ { \ ++ type_register_static(&rhel##m##n##s##_machvirt_info); \ ++ } \ ++ type_init(rhel##m##n##s##_machvirt_init); ++ ++#define DEFINE_RHEL_MACHINE_AS_LATEST(major, minor, subminor) \ ++ DEFINE_RHEL_MACHINE_LATEST(major, minor, subminor, true) ++#define DEFINE_RHEL_MACHINE(major, minor, subminor) \ ++ DEFINE_RHEL_MACHINE_LATEST(major, minor, subminor, false) + + /* Number of external interrupt lines to configure the GIC with */ + #define NUM_IRQS 256 +@@ -1416,6 +1446,7 @@ static void machvirt_init(MachineState *machine) + create_platform_bus(vms, pic); + } + ++#if 0 /* disabled for RHEL */ + static bool virt_get_secure(Object *obj, Error **errp) + { + VirtMachineState *vms = VIRT_MACHINE(obj); +@@ -1444,6 +1475,7 @@ static void virt_set_virt(Object *obj, bool value, Error **errp) + vms->virt = value; + } + ++#endif /* disabled for RHEL */ + static bool virt_get_highmem(Object *obj, Error **errp) + { + VirtMachineState *vms = VIRT_MACHINE(obj); +@@ -1536,6 +1568,7 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) + return ms->possible_cpus; + } + ++#if 0 /* disabled for RHEL */ + static void virt_machine_class_init(ObjectClass *oc, void *data) + { + MachineClass *mc = MACHINE_CLASS(oc); +@@ -1748,3 +1781,89 @@ static void virt_machine_2_6_options(MachineClass *mc) + vmc->no_pmu = true; + } + DEFINE_VIRT_MACHINE(2, 6) ++#endif /* disabled for RHEL */ ++ ++static void rhel_machine_class_init(ObjectClass *oc, void *data) ++{ ++ MachineClass *mc = MACHINE_CLASS(oc); ++ ++ mc->family = "virt-rhel-Z"; ++ mc->init = machvirt_init; ++ /* Start max_cpus at the maximum QEMU supports. We'll further restrict ++ * it later in machvirt_init, where we have more information about the ++ * configuration of the particular instance. ++ */ ++ mc->max_cpus = 255; ++ mc->block_default_type = IF_VIRTIO; ++ mc->no_cdrom = 1; ++ mc->pci_allow_0_address = true; ++ /* We know we will never create a pre-ARMv7 CPU which needs 1K pages */ ++ mc->minimum_page_bits = 12; ++ mc->possible_cpu_arch_ids = virt_possible_cpu_arch_ids; ++ mc->cpu_index_to_instance_props = virt_cpu_index_to_props; ++ mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a57"); ++ mc->get_default_cpu_node_id = virt_get_default_cpu_node_id; ++} ++ ++static const TypeInfo rhel_machine_info = { ++ .name = TYPE_RHEL_MACHINE, ++ .parent = TYPE_MACHINE, ++ .abstract = true, ++ .instance_size = sizeof(VirtMachineState), ++ .class_size = sizeof(VirtMachineClass), ++ .class_init = rhel_machine_class_init, ++}; ++ ++static void rhel_machine_init(void) ++{ ++ type_register_static(&rhel_machine_info); ++} ++type_init(rhel_machine_init); ++ ++static void rhel750_virt_instance_init(Object *obj) ++{ ++ VirtMachineState *vms = VIRT_MACHINE(obj); ++ VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); ++ ++ /* EL3 is disabled by default and non-configurable for RHEL */ ++ vms->secure = false; ++ /* EL2 is disabled by default and non-configurable for RHEL */ ++ vms->virt = false; ++ /* High memory is enabled by default for RHEL */ ++ vms->highmem = true; ++ object_property_add_bool(obj, "highmem", virt_get_highmem, ++ virt_set_highmem, NULL); ++ object_property_set_description(obj, "highmem", ++ "Set on/off to enable/disable using " ++ "physical address space above 32 bits", ++ NULL); ++ /* Default GIC type is still v2, but became configurable for RHEL */ ++ vms->gic_version = 2; ++ object_property_add_str(obj, "gic-version", virt_get_gic_version, ++ virt_set_gic_version, NULL); ++ object_property_set_description(obj, "gic-version", ++ "Set GIC version. " ++ "Valid values are 2, 3 and host", NULL); ++ ++ if (vmc->no_its) { ++ vms->its = false; ++ } else { ++ /* Default allows ITS instantiation */ ++ vms->its = true; ++ object_property_add_bool(obj, "its", virt_get_its, ++ virt_set_its, NULL); ++ object_property_set_description(obj, "its", ++ "Set on/off to enable/disable " ++ "ITS instantiation", ++ NULL); ++ } ++ ++ vms->memmap=a15memmap; ++ vms->irqmap=a15irqmap; ++} ++ ++static void rhel750_virt_options(MachineClass *mc) ++{ ++ SET_MACHINE_COMPAT(mc, ARM_RHEL_COMPAT); ++} ++DEFINE_RHEL_MACHINE_AS_LATEST(7, 5, 0) +diff --git a/hw/char/serial.c b/hw/char/serial.c +index eb72191..7647fac 100644 +--- a/hw/char/serial.c ++++ b/hw/char/serial.c +@@ -30,6 +30,7 @@ + #include "qemu/timer.h" + #include "exec/address-spaces.h" + #include "qemu/error-report.h" ++#include "migration/migration.h" + + //#define DEBUG_SERIAL + +@@ -691,6 +692,9 @@ static int serial_post_load(void *opaque, int version_id) + static bool serial_thr_ipending_needed(void *opaque) + { + SerialState *s = opaque; ++ if (migrate_pre_2_2) { ++ return false; ++ } + + if (s->ier & UART_IER_THRI) { + bool expected_value = ((s->iir & UART_IIR_ID) == UART_IIR_THRI); +@@ -718,6 +722,10 @@ static const VMStateDescription vmstate_serial_thr_ipending = { + static bool serial_tsr_needed(void *opaque) + { + SerialState *s = (SerialState *)opaque; ++ if (migrate_pre_2_2) { ++ return false; ++ } ++ + return s->tsr_retry != 0; + } + +@@ -737,6 +745,10 @@ static const VMStateDescription vmstate_serial_tsr = { + static bool serial_recv_fifo_needed(void *opaque) + { + SerialState *s = (SerialState *)opaque; ++ if (migrate_pre_2_2) { ++ return false; ++ } ++ + return !fifo8_is_empty(&s->recv_fifo); + + } +@@ -755,6 +767,10 @@ static const VMStateDescription vmstate_serial_recv_fifo = { + static bool serial_xmit_fifo_needed(void *opaque) + { + SerialState *s = (SerialState *)opaque; ++ if (migrate_pre_2_2) { ++ return false; ++ } ++ + return !fifo8_is_empty(&s->xmit_fifo); + } + +@@ -772,6 +788,10 @@ static const VMStateDescription vmstate_serial_xmit_fifo = { + static bool serial_fifo_timeout_timer_needed(void *opaque) + { + SerialState *s = (SerialState *)opaque; ++ if (migrate_pre_2_2) { ++ return false; ++ } ++ + return timer_pending(s->fifo_timeout_timer); + } + +@@ -789,6 +809,10 @@ static const VMStateDescription vmstate_serial_fifo_timeout_timer = { + static bool serial_timeout_ipending_needed(void *opaque) + { + SerialState *s = (SerialState *)opaque; ++ if (migrate_pre_2_2) { ++ return false; ++ } ++ + return s->timeout_ipending != 0; + } + +@@ -806,6 +830,10 @@ static const VMStateDescription vmstate_serial_timeout_ipending = { + static bool serial_poll_needed(void *opaque) + { + SerialState *s = (SerialState *)opaque; ++ if (migrate_pre_2_2) { ++ return false; ++ } ++ + return s->poll_msl >= 0; + } + +diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c +index d116651..feacb45 100644 +--- a/hw/display/cirrus_vga.c ++++ b/hw/display/cirrus_vga.c +@@ -3060,7 +3060,7 @@ static void isa_cirrus_vga_realizefn(DeviceState *dev, Error **errp) + + static Property isa_cirrus_vga_properties[] = { + DEFINE_PROP_UINT32("vgamem_mb", struct ISACirrusVGAState, +- cirrus_vga.vga.vram_size_mb, 4), ++ cirrus_vga.vga.vram_size_mb, 16), + DEFINE_PROP_BOOL("blitter", struct ISACirrusVGAState, + cirrus_vga.enable_blitter, true), + DEFINE_PROP_END_OF_LIST(), +@@ -3133,7 +3133,7 @@ static void pci_cirrus_vga_realize(PCIDevice *dev, Error **errp) + + static Property pci_vga_cirrus_properties[] = { + DEFINE_PROP_UINT32("vgamem_mb", struct PCICirrusVGAState, +- cirrus_vga.vga.vram_size_mb, 4), ++ cirrus_vga.vga.vram_size_mb, 16), + DEFINE_PROP_BOOL("blitter", struct PCICirrusVGAState, + cirrus_vga.enable_blitter, true), + DEFINE_PROP_END_OF_LIST(), +diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c +index 469834a..eb44d21 100644 +--- a/hw/display/vga-isa.c ++++ b/hw/display/vga-isa.c +@@ -79,7 +79,7 @@ static void vga_isa_realizefn(DeviceState *dev, Error **errp) + } + + static Property vga_isa_properties[] = { +- DEFINE_PROP_UINT32("vgamem_mb", ISAVGAState, state.vram_size_mb, 8), ++ DEFINE_PROP_UINT32("vgamem_mb", ISAVGAState, state.vram_size_mb, 16), + DEFINE_PROP_END_OF_LIST(), + }; + +diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs +index fa87a14..8c25538 100644 +--- a/hw/i386/Makefile.objs ++++ b/hw/i386/Makefile.objs +@@ -10,3 +10,4 @@ obj-$(CONFIG_VMMOUSE) += vmmouse.o + + obj-y += kvmvapic.o + obj-y += acpi-build.o ++obj-y += shadow-bios.o +diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c +index 3cf2a16..976d151 100644 +--- a/hw/i386/acpi-build.c ++++ b/hw/i386/acpi-build.c +@@ -183,6 +183,9 @@ static void acpi_get_pm_info(AcpiPmInfo *pm) + pm->fadt.reset_reg = r; + pm->fadt.reset_val = 0xf; + pm->fadt.flags |= 1 << ACPI_FADT_F_RESET_REG_SUP; ++ if (object_property_get_bool(lpc, ++ "__com.redhat_force-rev1-fadt", NULL)) ++ pm->fadt.rev = 1; + pm->cpu_hp_io_base = ICH9_CPU_HOTPLUG_IO_BASE; + } + assert(obj); +diff --git a/hw/i386/pc.c b/hw/i386/pc.c +index ab4323d..d38a328 100644 +--- a/hw/i386/pc.c ++++ b/hw/i386/pc.c +@@ -1417,7 +1417,8 @@ void pc_memory_init(PCMachineState *pcms, + option_rom_mr = g_malloc(sizeof(*option_rom_mr)); + memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE, + &error_fatal); +- if (pcmc->pci_enabled) { ++ /* RH difference: See bz 1489800, explicitly make ROM ro */ ++ if (pcmc->pc_rom_ro) { + memory_region_set_readonly(option_rom_mr, true); + } + memory_region_add_subregion_overlap(rom_memory, +@@ -2360,6 +2361,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) + pcmc->acpi_data_size = 0x20000 + 0x8000; + pcmc->save_tsc_khz = true; + pcmc->linuxboot_dma_enabled = true; ++ pcmc->pc_rom_ro = true; + mc->get_hotplug_handler = pc_get_hotpug_handler; + mc->cpu_index_to_instance_props = pc_cpu_index_to_props; + mc->get_default_cpu_node_id = pc_get_default_cpu_node_id; +@@ -2369,7 +2371,8 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) + mc->default_boot_order = "cad"; + mc->hot_add_cpu = pc_hot_add_cpu; + mc->block_default_type = IF_IDE; +- mc->max_cpus = 255; ++ /* 240: max CPU count for RHEL */ ++ mc->max_cpus = 240; + mc->reset = pc_machine_reset; + hc->pre_plug = pc_machine_device_pre_plug_cb; + hc->plug = pc_machine_device_plug_cb; +diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c +index 729a050..6794bb7 100644 +--- a/hw/i386/pc_piix.c ++++ b/hw/i386/pc_piix.c +@@ -48,6 +48,7 @@ + #include "cpu.h" + #include "qapi/error.h" + #include "qemu/error-report.h" ++#include "migration/migration.h" + #ifdef CONFIG_XEN + #include + #include "hw/xen/xen_pt.h" +@@ -168,8 +169,8 @@ static void pc_init1(MachineState *machine, + if (pcmc->smbios_defaults) { + MachineClass *mc = MACHINE_GET_CLASS(machine); + /* These values are guest ABI, do not change */ +- smbios_set_defaults("QEMU", "Standard PC (i440FX + PIIX, 1996)", +- mc->name, pcmc->smbios_legacy_mode, ++ smbios_set_defaults("Red Hat", "KVM", ++ mc->desc, pcmc->smbios_legacy_mode, + pcmc->smbios_uuid_encoded, + SMBIOS_ENTRY_POINT_21); + } +@@ -307,6 +308,7 @@ static void pc_init1(MachineState *machine, + * HW_COMPAT_*, PC_COMPAT_*, or * pc_*_machine_options(). + */ + ++#if 0 /* Disabled for Red Hat Enterprise Linux */ + static void pc_compat_2_3(MachineState *machine) + { + PCMachineState *pcms = PC_MACHINE(machine); +@@ -1135,3 +1137,891 @@ static void xenfv_machine_options(MachineClass *m) + DEFINE_PC_MACHINE(xenfv, "xenfv", pc_xen_hvm_init, + xenfv_machine_options); + #endif ++machine_init(pc_machine_init); ++ ++#endif /* Disabled for Red Hat Enterprise Linux */ ++ ++/* Red Hat Enterprise Linux machine types */ ++ ++/* Options for the latest rhel7 machine type */ ++static void pc_machine_rhel7_options(MachineClass *m) ++{ ++ PCMachineClass *pcmc = PC_MACHINE_CLASS(m); ++ m->family = "pc_piix_Y"; ++ m->default_machine_opts = "firmware=bios-256k.bin"; ++ pcmc->default_nic_model = "e1000"; ++ m->default_display = "std"; ++ SET_MACHINE_COMPAT(m, PC_RHEL_COMPAT); ++ m->alias = "pc"; ++ m->is_default = 1; ++} ++ ++static void pc_init_rhel750(MachineState *machine) ++{ ++ pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \ ++ TYPE_I440FX_PCI_DEVICE); ++} ++ ++static void pc_machine_rhel750_options(MachineClass *m) ++{ ++ pc_machine_rhel7_options(m); ++ m->desc = "RHEL 7.5.0 PC (i440FX + PIIX, 1996)"; ++} ++ ++DEFINE_PC_MACHINE(rhel750, "pc-i440fx-rhel7.5.0", pc_init_rhel750, ++ pc_machine_rhel750_options); ++ ++static void pc_init_rhel740(MachineState *machine) ++{ ++ pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \ ++ TYPE_I440FX_PCI_DEVICE); ++} ++ ++static void pc_machine_rhel740_options(MachineClass *m) ++{ ++ PCMachineClass *pcmc = PC_MACHINE_CLASS(m); ++ pc_machine_rhel750_options(m); ++ m->alias = NULL; ++ m->is_default = 0; ++ m->desc = "RHEL 7.4.0 PC (i440FX + PIIX, 1996)"; ++ m->numa_auto_assign_ram = numa_legacy_auto_assign_ram; ++ pcmc->pc_rom_ro = false; ++ SET_MACHINE_COMPAT(m, PC_RHEL7_4_COMPAT); ++} ++ ++DEFINE_PC_MACHINE(rhel740, "pc-i440fx-rhel7.4.0", pc_init_rhel740, ++ pc_machine_rhel740_options); ++ ++static void pc_init_rhel730(MachineState *machine) ++{ ++ pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \ ++ TYPE_I440FX_PCI_DEVICE); ++} ++ ++static void pc_machine_rhel730_options(MachineClass *m) ++{ ++ PCMachineClass *pcmc = PC_MACHINE_CLASS(m); ++ pc_machine_rhel740_options(m); ++ m->alias = NULL; ++ m->is_default = 0; ++ m->desc = "RHEL 7.3.0 PC (i440FX + PIIX, 1996)"; ++ pcmc->linuxboot_dma_enabled = false; ++ SET_MACHINE_COMPAT(m, PC_RHEL7_3_COMPAT); ++} ++ ++DEFINE_PC_MACHINE(rhel730, "pc-i440fx-rhel7.3.0", pc_init_rhel730, ++ pc_machine_rhel730_options); ++ ++ ++static void pc_init_rhel720(MachineState *machine) ++{ ++ pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \ ++ TYPE_I440FX_PCI_DEVICE); ++} ++ ++static void pc_machine_rhel720_options(MachineClass *m) ++{ ++ PCMachineClass *pcmc = PC_MACHINE_CLASS(m); ++ pc_machine_rhel730_options(m); ++ m->desc = "RHEL 7.2.0 PC (i440FX + PIIX, 1996)"; ++ /* From pc_i440fx_2_5_machine_options */ ++ pcmc->save_tsc_khz = false; ++ m->legacy_fw_cfg_order = 1; ++ /* Note: broken_reserved_end was already in 7.2 */ ++ /* From pc_i440fx_2_6_machine_options */ ++ pcmc->legacy_cpu_hotplug = true; ++ SET_MACHINE_COMPAT(m, PC_RHEL7_2_COMPAT); ++} ++ ++DEFINE_PC_MACHINE(rhel720, "pc-i440fx-rhel7.2.0", pc_init_rhel720, ++ pc_machine_rhel720_options); ++ ++static void pc_compat_rhel710(MachineState *machine) ++{ ++ PCMachineState *pcms = PC_MACHINE(machine); ++ PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); ++ ++ /* From pc_compat_2_2 */ ++ pcmc->rsdp_in_ram = false; ++ machine->suppress_vmdesc = true; ++ ++ /* From pc_compat_2_1 */ ++ pcmc->smbios_uuid_encoded = false; ++ x86_cpu_change_kvm_default("svm", NULL); ++ pcmc->enforce_aligned_dimm = false; ++ ++ /* Disable all the extra subsections that were added in 2.2 */ ++ migrate_pre_2_2 = true; ++ ++ /* From pc_i440fx_2_4_machine_options */ ++ pcmc->broken_reserved_end = true; ++} ++ ++static void pc_init_rhel710(MachineState *machine) ++{ ++ pc_compat_rhel710(machine); ++ pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \ ++ TYPE_I440FX_PCI_DEVICE); ++} ++ ++static void pc_machine_rhel710_options(MachineClass *m) ++{ ++ pc_machine_rhel720_options(m); ++ m->family = "pc_piix_Y"; ++ m->desc = "RHEL 7.1.0 PC (i440FX + PIIX, 1996)"; ++ m->default_display = "cirrus"; ++ SET_MACHINE_COMPAT(m, PC_RHEL7_1_COMPAT); ++} ++ ++DEFINE_PC_MACHINE(rhel710, "pc-i440fx-rhel7.1.0", pc_init_rhel710, ++ pc_machine_rhel710_options); ++ ++static void pc_compat_rhel700(MachineState *machine) ++{ ++ PCMachineState *pcms = PC_MACHINE(machine); ++ PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); ++ ++ pc_compat_rhel710(machine); ++ ++ /* Upstream enables it for everyone, we're a little more selective */ ++ x86_cpu_change_kvm_default("x2apic", NULL); ++ x86_cpu_change_kvm_default("svm", NULL); ++ pcmc->legacy_acpi_table_size = 6418; /* see pc_compat_2_0() */ ++ pcmc->smbios_legacy_mode = true; ++ pcmc->has_reserved_memory = false; ++ migrate_cve_2014_5263_xhci_fields = true; ++} ++ ++static void pc_init_rhel700(MachineState *machine) ++{ ++ pc_compat_rhel700(machine); ++ pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \ ++ TYPE_I440FX_PCI_DEVICE); ++} ++ ++static void pc_machine_rhel700_options(MachineClass *m) ++{ ++ pc_machine_rhel710_options(m); ++ m->family = "pc_piix_Y"; ++ m->desc = "RHEL 7.0.0 PC (i440FX + PIIX, 1996)"; ++ SET_MACHINE_COMPAT(m, PC_RHEL7_0_COMPAT); ++} ++ ++DEFINE_PC_MACHINE(rhel700, "pc-i440fx-rhel7.0.0", pc_init_rhel700, ++ pc_machine_rhel700_options); ++ ++#define PC_RHEL6_6_COMPAT \ ++ {\ ++ .driver = "scsi-hd",\ ++ .property = "discard_granularity",\ ++ .value = stringify(0),\ ++ },{\ ++ .driver = "scsi-cd",\ ++ .property = "discard_granularity",\ ++ .value = stringify(0),\ ++ },{\ ++ .driver = "scsi-disk",\ ++ .property = "discard_granularity",\ ++ .value = stringify(0),\ ++ },{\ ++ .driver = "ide-hd",\ ++ .property = "discard_granularity",\ ++ .value = stringify(0),\ ++ },{\ ++ .driver = "ide-cd",\ ++ .property = "discard_granularity",\ ++ .value = stringify(0),\ ++ },{\ ++ .driver = "ide-drive",\ ++ .property = "discard_granularity",\ ++ .value = stringify(0),\ ++ },{\ ++ .driver = "virtio-blk-pci",\ ++ .property = "discard_granularity",\ ++ .value = stringify(0),\ ++ },{\ ++ .driver = "virtio-serial-pci",\ ++ .property = "vectors",\ ++ /* DEV_NVECTORS_UNSPECIFIED as a uint32_t string */\ ++ .value = stringify(0xFFFFFFFF),\ ++ },{\ ++ .driver = "486-" TYPE_X86_CPU,\ ++ .property = "model",\ ++ .value = stringify(0),\ ++ },{\ ++ .driver = "usb-tablet",\ ++ .property = "usb_version",\ ++ .value = stringify(1),\ ++ },{\ ++ .driver = "virtio-net-pci",\ ++ .property = "mq",\ ++ .value = "off",\ ++ },{\ ++ .driver = "VGA",\ ++ .property = "mmio",\ ++ .value = "off",\ ++ },{\ ++ .driver = "virtio-blk-pci",\ ++ .property = "config-wce",\ ++ .value = "off",\ ++ },{\ ++ .driver = TYPE_ISA_FDC,\ ++ .property = "check_media_rate",\ ++ .value = "off",\ ++ },{\ ++ .driver = "virtio-balloon-pci",\ ++ .property = "class",\ ++ .value = stringify(PCI_CLASS_MEMORY_RAM),\ ++ },{\ ++ .driver = TYPE_PCI_DEVICE,\ ++ .property = "command_serr_enable",\ ++ .value = "off",\ ++ },{\ ++ .driver = "AC97",\ ++ .property = "use_broken_id",\ ++ .value = stringify(1),\ ++ },{\ ++ .driver = "intel-hda",\ ++ .property = "msi",\ ++ .value = "off",\ ++ },{\ ++ .driver = "qemu32-" TYPE_X86_CPU,\ ++ .property = "min-xlevel",\ ++ .value = stringify(0),\ ++ },{\ ++ .driver = "486-" TYPE_X86_CPU,\ ++ .property = "min-level",\ ++ .value = stringify(0),\ ++ },{\ ++ .driver = "qemu32-" TYPE_X86_CPU,\ ++ .property = "model",\ ++ .value = stringify(3),\ ++ },{\ ++ .driver = "usb-ccid",\ ++ .property = "serial",\ ++ .value = "1",\ ++ },{\ ++ .driver = "ne2k_pci",\ ++ .property = "romfile",\ ++ .value = "rhel6-ne2k_pci.rom",\ ++ },{\ ++ .driver = "pcnet",\ ++ .property = "romfile",\ ++ .value = "rhel6-pcnet.rom",\ ++ },{\ ++ .driver = "rtl8139",\ ++ .property = "romfile",\ ++ .value = "rhel6-rtl8139.rom",\ ++ },{\ ++ .driver = "e1000",\ ++ .property = "romfile",\ ++ .value = "rhel6-e1000.rom",\ ++ },{\ ++ .driver = "virtio-net-pci",\ ++ .property = "romfile",\ ++ .value = "rhel6-virtio.rom",\ ++ },{\ ++ .driver = "virtio-net-pci",\ ++ .property = "any_layout",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "pentium" "-" TYPE_X86_CPU,\ ++ .property = "apic",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "pentium2" "-" TYPE_X86_CPU,\ ++ .property = "apic",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "pentium3" "-" TYPE_X86_CPU,\ ++ .property = "apic",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "Conroe" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Penryn" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Nehalem" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL6_6_COMPAT (copied from the entry above) */ \ ++ .driver = "Nehalem-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Westmere" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL6_6_COMPAT (copied from the entry above) */ \ ++ .driver = "Westmere-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Westmere" "-" TYPE_X86_CPU,\ ++ .property = "pclmulqdq",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL6_6_COMPAT (copied from the entry above) */ \ ++ .driver = "Westmere-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "pclmulqdq",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "Westmere" "-" TYPE_X86_CPU,\ ++ .property = "fxsr",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL6_6_COMPAT (copied from the entry above) */ \ ++ .driver = "Westmere-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "fxsr",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Westmere" "-" TYPE_X86_CPU,\ ++ .property = "mmx",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL6_6_COMPAT (copied from the entry above) */ \ ++ .driver = "Westmere-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "mmx",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Westmere" "-" TYPE_X86_CPU,\ ++ .property = "pat",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL6_6_COMPAT (copied from the entry above) */ \ ++ .driver = "Westmere-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "pat",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Westmere" "-" TYPE_X86_CPU,\ ++ .property = "cmov",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL6_6_COMPAT (copied from the entry above) */ \ ++ .driver = "Westmere-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "cmov",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Westmere" "-" TYPE_X86_CPU,\ ++ .property = "pge",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL6_6_COMPAT (copied from the entry above) */ \ ++ .driver = "Westmere-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "pge",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Westmere" "-" TYPE_X86_CPU,\ ++ .property = "apic",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL6_6_COMPAT (copied from the entry above) */ \ ++ .driver = "Westmere-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "apic",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Westmere" "-" TYPE_X86_CPU,\ ++ .property = "cx8",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL6_6_COMPAT (copied from the entry above) */ \ ++ .driver = "Westmere-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "cx8",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Westmere" "-" TYPE_X86_CPU,\ ++ .property = "mce",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL6_6_COMPAT (copied from the entry above) */ \ ++ .driver = "Westmere-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "mce",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Westmere" "-" TYPE_X86_CPU,\ ++ .property = "pae",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL6_6_COMPAT (copied from the entry above) */ \ ++ .driver = "Westmere-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "pae",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Westmere" "-" TYPE_X86_CPU,\ ++ .property = "msr",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL6_6_COMPAT (copied from the entry above) */ \ ++ .driver = "Westmere-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "msr",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Westmere" "-" TYPE_X86_CPU,\ ++ .property = "tsc",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL6_6_COMPAT (copied from the entry above) */ \ ++ .driver = "Westmere-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "tsc",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Westmere" "-" TYPE_X86_CPU,\ ++ .property = "pse",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL6_6_COMPAT (copied from the entry above) */ \ ++ .driver = "Westmere-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "pse",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Westmere" "-" TYPE_X86_CPU,\ ++ .property = "de",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL6_6_COMPAT (copied from the entry above) */ \ ++ .driver = "Westmere-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "de",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Westmere" "-" TYPE_X86_CPU,\ ++ .property = "fpu",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL6_6_COMPAT (copied from the entry above) */ \ ++ .driver = "Westmere-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "fpu",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Broadwell" "-" TYPE_X86_CPU,\ ++ .property = "rdtscp",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL6_6_COMPAT (copied from the entry above) */ \ ++ .driver = "Broadwell-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "rdtscp",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "Broadwell" "-" TYPE_X86_CPU,\ ++ .property = "smap",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL6_6_COMPAT (copied from the entry above) */ \ ++ .driver = "Broadwell-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "smap",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = TYPE_X86_CPU,\ ++ .property = "rdtscp",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "Opteron_G1" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Opteron_G2" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Opteron_G3" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Opteron_G4" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "Opteron_G5" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = TYPE_X86_CPU,\ ++ .property = "3dnow",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = TYPE_X86_CPU,\ ++ .property = "3dnowext",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "virtio-net-pci",\ ++ .property = "__com.redhat_rhel6_ctrl_guest_workaround", \ ++ .value = "on",\ ++ }, ++ ++static void pc_compat_rhel660(MachineState *machine) ++{ ++ PCMachineState *pcms = PC_MACHINE(machine); ++ PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); ++ ++ pc_compat_rhel700(machine); ++ if (!machine->cpu_type) { ++ machine->cpu_type = "cpu64-rhel6"; ++ } ++ ++ x86_cpu_change_kvm_default("kvm-pv-unhalt", NULL); ++ ++ pcmc->gigabyte_align = false; ++ shadow_bios_after_incoming = true; ++ ich9_uhci123_irqpin_override = true; ++} ++ ++static void pc_init_rhel660(MachineState *machine) ++{ ++ pc_compat_rhel660(machine); ++ pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \ ++ TYPE_I440FX_PCI_DEVICE);} ++ ++static void pc_machine_rhel660_options(MachineClass *m) ++{ ++ PCMachineClass *pcmc = PC_MACHINE_CLASS(m); ++ pc_machine_rhel700_options(m); ++ m->family = "pc_piix_Z"; ++ m->desc = "RHEL 6.6.0 PC"; ++ m->rom_file_has_mr = false; ++ m->default_machine_opts = "firmware=bios.bin"; ++ pcmc->has_acpi_build = false; ++ SET_MACHINE_COMPAT(m, PC_RHEL6_6_COMPAT); ++} ++ ++DEFINE_PC_MACHINE(rhel660, "rhel6.6.0", pc_init_rhel660, ++ pc_machine_rhel660_options); ++ ++#define PC_RHEL6_5_COMPAT \ ++ {\ ++ .driver = TYPE_USB_DEVICE,\ ++ .property = "msos-desc",\ ++ .value = "no",\ ++ }, ++ ++static void pc_compat_rhel650(MachineState *machine) ++{ ++ pc_compat_rhel660(machine); ++} ++ ++static void pc_init_rhel650(MachineState *machine) ++{ ++ pc_compat_rhel650(machine); ++ pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \ ++ TYPE_I440FX_PCI_DEVICE);} ++ ++static void pc_machine_rhel650_options(MachineClass *m) ++{ ++ pc_machine_rhel660_options(m); ++ m->family = "pc_piix_Z"; ++ m->desc = "RHEL 6.5.0 PC"; ++ SET_MACHINE_COMPAT(m, PC_RHEL6_5_COMPAT); ++} ++ ++DEFINE_PC_MACHINE(rhel650, "rhel6.5.0", pc_init_rhel650, ++ pc_machine_rhel650_options); ++ ++#define PC_RHEL6_4_COMPAT \ ++ {\ ++ .driver = "virtio-scsi-pci",\ ++ .property = "vectors",\ ++ .value = stringify(2),\ ++ },{\ ++ .driver = "hda-micro",\ ++ .property = "mixer",\ ++ .value = "off",\ ++ },{\ ++ .driver = "hda-duplex",\ ++ .property = "mixer",\ ++ .value = "off",\ ++ },{\ ++ .driver = "hda-output",\ ++ .property = "mixer",\ ++ .value = "off",\ ++ },{\ ++ .driver = "virtio-net-pci",\ ++ .property = "ctrl_mac_addr",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = TYPE_X86_CPU,\ ++ .property = "sep",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "virtio-net-pci",\ ++ .property = "__com.redhat_rhel6_ctrl_guest_workaround", \ ++ .value = "off",\ ++ }, ++ ++static void pc_compat_rhel640(MachineState *machine) ++{ ++ pc_compat_rhel650(machine); ++} ++ ++static void pc_init_rhel640(MachineState *machine) ++{ ++ pc_compat_rhel640(machine); ++ pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \ ++ TYPE_I440FX_PCI_DEVICE);} ++ ++static void pc_machine_rhel640_options(MachineClass *m) ++{ ++ pc_machine_rhel650_options(m); ++ m->family = "pc_piix_Z"; ++ m->desc = "RHEL 6.4.0 PC"; ++ SET_MACHINE_COMPAT(m, PC_RHEL6_4_COMPAT); ++} ++ ++DEFINE_PC_MACHINE(rhel640, "rhel6.4.0", pc_init_rhel640, ++ pc_machine_rhel640_options); ++ ++#define PC_RHEL6_3_COMPAT \ ++ {\ ++ .driver = "Conroe-" TYPE_X86_CPU,\ ++ .property = "min-level",\ ++ .value = stringify(2),\ ++ },{\ ++ .driver = "Penryn-" TYPE_X86_CPU,\ ++ .property = "min-level",\ ++ .value = stringify(2),\ ++ },{\ ++ .driver = "Nehalem-" TYPE_X86_CPU,\ ++ .property = "min-level",\ ++ .value = stringify(2),\ ++ },{\ ++ .driver = "e1000",\ ++ .property = "autonegotiation",\ ++ .value = "off",\ ++ },{\ ++ .driver = "qxl",\ ++ .property = "revision",\ ++ .value = stringify(3),\ ++ },{\ ++ .driver = "qxl-vga",\ ++ .property = "revision",\ ++ .value = stringify(3),\ ++ },{\ ++ .driver = "virtio-scsi-pci",\ ++ .property = "hotplug",\ ++ .value = "off",\ ++ },{\ ++ .driver = "virtio-scsi-pci",\ ++ .property = "param_change",\ ++ .value = "off",\ ++ },{\ ++ .driver = TYPE_X86_CPU,\ ++ .property = "pmu",\ ++ .value = "on",\ ++ },{\ ++ .driver = "usb-hub",\ ++ .property = "serial",\ ++ .value = "314159",\ ++ },{\ ++ .driver = "usb-storage",\ ++ .property = "serial",\ ++ .value = "1",\ ++ },\ ++ {\ ++ .driver = "SandyBridge" "-" TYPE_X86_CPU,\ ++ .property = "tsc-deadline",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL6_3_COMPAT (copied from the entry above) */ \ ++ .driver = "SandyBridge-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "tsc-deadline",\ ++ .value = "off",\ ++ }, ++ ++static void pc_compat_rhel630(MachineState *machine) ++{ ++ pc_compat_rhel640(machine); ++ x86_cpu_change_kvm_default("kvm-pv-eoi",NULL); ++ enable_compat_apic_id_mode(); ++} ++ ++static void pc_init_rhel630(MachineState *machine) ++{ ++ pc_compat_rhel630(machine); ++ pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \ ++ TYPE_I440FX_PCI_DEVICE);} ++ ++static void pc_machine_rhel630_options(MachineClass *m) ++{ ++ pc_machine_rhel640_options(m); ++ m->family = "pc_piix_Z"; ++ m->desc = "RHEL 6.3.0 PC"; ++ SET_MACHINE_COMPAT(m, PC_RHEL6_3_COMPAT); ++} ++ ++DEFINE_PC_MACHINE(rhel630, "rhel6.3.0", pc_init_rhel630, ++ pc_machine_rhel630_options); ++ ++ ++#define PC_RHEL6_2_COMPAT \ ++ {\ ++ .driver = TYPE_X86_CPU,\ ++ .property = "pmu",\ ++ .value = "off",\ ++ }, ++ ++static void pc_compat_rhel620(MachineState *machine) ++{ ++ pc_compat_rhel630(machine); ++} ++ ++static void pc_init_rhel620(MachineState *machine) ++{ ++ pc_compat_rhel620(machine); ++ pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \ ++ TYPE_I440FX_PCI_DEVICE);} ++ ++static void pc_machine_rhel620_options(MachineClass *m) ++{ ++ pc_machine_rhel630_options(m); ++ m->family = "pc_piix_Z"; ++ m->desc = "RHEL 6.2.0 PC"; ++ SET_MACHINE_COMPAT(m, PC_RHEL6_2_COMPAT); ++} ++ ++DEFINE_PC_MACHINE(rhel620, "rhel6.2.0", pc_init_rhel620, ++ pc_machine_rhel620_options); ++ ++/* ++ * NOTE: We don't have the event_idx compat entry for the ++ * virtio-balloon-pci driver because RHEL6 doesn't disable ++ * it either due to a bug (see RHBZ 1029539 fo more info) ++ */ ++#define PC_RHEL6_1_COMPAT \ ++ {\ ++ .driver = "PIIX4_PM",\ ++ .property = "disable_s3",\ ++ .value = "0",\ ++ },{\ ++ .driver = "PIIX4_PM",\ ++ .property = "disable_s4",\ ++ .value = "0",\ ++ },{\ ++ .driver = "qxl",\ ++ .property = "revision",\ ++ .value = stringify(2),\ ++ },{\ ++ .driver = "qxl-vga",\ ++ .property = "revision",\ ++ .value = stringify(2),\ ++ },{\ ++ .driver = "virtio-blk-pci",\ ++ .property = "event_idx",\ ++ .value = "off",\ ++ },{\ ++ .driver = "virtio-serial-pci",\ ++ .property = "event_idx",\ ++ .value = "off",\ ++ },{\ ++ .driver = "virtio-net-pci",\ ++ .property = "event_idx",\ ++ .value = "off",\ ++ },{\ ++ .driver = "usb-kbd",\ ++ .property = "serial",\ ++ .value = "1",\ ++ },{\ ++ .driver = "usb-mouse",\ ++ .property = "serial",\ ++ .value = "1",\ ++ },{\ ++ .driver = "usb-tablet",\ ++ .property = "serial",\ ++ .value = "1",\ ++ }, ++ ++static void pc_compat_rhel610(MachineState *machine) ++{ ++ pc_compat_rhel620(machine); ++} ++ ++static void pc_init_rhel610(MachineState *machine) ++{ ++ pc_compat_rhel610(machine); ++ pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \ ++ TYPE_I440FX_PCI_DEVICE);} ++ ++static void pc_machine_rhel610_options(MachineClass *m) ++{ ++ pc_machine_rhel620_options(m); ++ m->family = "pc_piix_Z"; ++ m->desc = "RHEL 6.1.0 PC"; ++ SET_MACHINE_COMPAT(m, PC_RHEL6_1_COMPAT); ++} ++ ++DEFINE_PC_MACHINE(rhel610, "rhel6.1.0", pc_init_rhel610, ++ pc_machine_rhel610_options); ++ ++#define PC_RHEL6_0_COMPAT \ ++ {\ ++ .driver = "qxl",\ ++ .property = "revision",\ ++ .value = stringify(1),\ ++ },{\ ++ .driver = "qxl-vga",\ ++ .property = "revision",\ ++ .value = stringify(1),\ ++ },{\ ++ .driver = "VGA",\ ++ .property = "rombar",\ ++ .value = stringify(0),\ ++ }, ++ ++static void pc_compat_rhel600(MachineState *machine) ++{ ++ pc_compat_rhel610(machine); ++} ++ ++static void pc_init_rhel600(MachineState *machine) ++{ ++ pc_compat_rhel600(machine); ++ pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \ ++ TYPE_I440FX_PCI_DEVICE);} ++ ++static void pc_machine_rhel600_options(MachineClass *m) ++{ ++ pc_machine_rhel610_options(m); ++ m->family = "pc_piix_Z"; ++ m->desc = "RHEL 6.0.0 PC"; ++ SET_MACHINE_COMPAT(m, PC_RHEL6_0_COMPAT); ++} ++ ++DEFINE_PC_MACHINE(rhel600, "rhel6.0.0", pc_init_rhel600, ++ pc_machine_rhel600_options); +diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c +index 9ae9163..ecd6255 100644 +--- a/hw/i386/pc_q35.c ++++ b/hw/i386/pc_q35.c +@@ -143,8 +143,8 @@ static void pc_q35_init(MachineState *machine) + + if (pcmc->smbios_defaults) { + /* These values are guest ABI, do not change */ +- smbios_set_defaults("QEMU", "Standard PC (Q35 + ICH9, 2009)", +- mc->name, pcmc->smbios_legacy_mode, ++ smbios_set_defaults("Red Hat", "KVM", ++ mc->desc, pcmc->smbios_legacy_mode, + pcmc->smbios_uuid_encoded, + SMBIOS_ENTRY_POINT_21); + } +@@ -292,6 +292,7 @@ static void pc_q35_init(MachineState *machine) + DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn) + + ++#if 0 /* Disabled for Red Hat Enterprise Linux */ + static void pc_q35_machine_options(MachineClass *m) + { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); +@@ -404,3 +405,72 @@ static void pc_q35_2_4_machine_options(MachineClass *m) + + DEFINE_Q35_MACHINE(v2_4, "pc-q35-2.4", NULL, + pc_q35_2_4_machine_options); ++#endif /* Disabled for Red Hat Enterprise Linux */ ++ ++/* Red Hat Enterprise Linux machine types */ ++ ++/* Options for the latest rhel7 q35 machine type */ ++static void pc_q35_machine_rhel7_options(MachineClass *m) ++{ ++ PCMachineClass *pcmc = PC_MACHINE_CLASS(m); ++ pcmc->default_nic_model = "e1000e"; ++ m->family = "pc_q35_Z"; ++ m->default_machine_opts = "firmware=bios-256k.bin"; ++ m->default_display = "std"; ++ m->no_floppy = 1; ++ machine_class_allow_dynamic_sysbus_dev(m, TYPE_SYS_BUS_DEVICE); m->alias = "q35"; ++ m->max_cpus = 384; ++ SET_MACHINE_COMPAT(m, PC_RHEL_COMPAT); ++} ++ ++static void pc_q35_init_rhel750(MachineState *machine) ++{ ++ pc_q35_init(machine); ++} ++ ++static void pc_q35_machine_rhel750_options(MachineClass *m) ++{ ++ pc_q35_machine_rhel7_options(m); ++ m->desc = "RHEL-7.5.0 PC (Q35 + ICH9, 2009)"; ++} ++ ++DEFINE_PC_MACHINE(q35_rhel750, "pc-q35-rhel7.5.0", pc_q35_init_rhel750, ++ pc_q35_machine_rhel750_options); ++ ++static void pc_q35_init_rhel740(MachineState *machine) ++{ ++ pc_q35_init(machine); ++} ++ ++static void pc_q35_machine_rhel740_options(MachineClass *m) ++{ ++ PCMachineClass *pcmc = PC_MACHINE_CLASS(m); ++ pc_q35_machine_rhel750_options(m); ++ m->alias = NULL; ++ m->desc = "RHEL-7.4.0 PC (Q35 + ICH9, 2009)"; ++ m->numa_auto_assign_ram = numa_legacy_auto_assign_ram; ++ pcmc->pc_rom_ro = false; ++ SET_MACHINE_COMPAT(m, PC_RHEL7_4_COMPAT); ++} ++ ++DEFINE_PC_MACHINE(q35_rhel740, "pc-q35-rhel7.4.0", pc_q35_init_rhel740, ++ pc_q35_machine_rhel740_options); ++ ++static void pc_q35_init_rhel730(MachineState *machine) ++{ ++ pc_q35_init(machine); ++} ++ ++static void pc_q35_machine_rhel730_options(MachineClass *m) ++{ ++ PCMachineClass *pcmc = PC_MACHINE_CLASS(m); ++ pc_q35_machine_rhel740_options(m); ++ m->alias = NULL; ++ m->desc = "RHEL-7.3.0 PC (Q35 + ICH9, 2009)"; ++ m->max_cpus = 255; ++ pcmc->linuxboot_dma_enabled = false; ++ SET_MACHINE_COMPAT(m, PC_RHEL7_3_COMPAT); ++} ++ ++DEFINE_PC_MACHINE(q35_rhel730, "pc-q35-rhel7.3.0", pc_q35_init_rhel730, ++ pc_q35_machine_rhel730_options); +diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c +index 73ac783..2a6de35 100644 +--- a/hw/i386/pc_sysfw.c ++++ b/hw/i386/pc_sysfw.c +@@ -207,6 +207,13 @@ static void old_pc_system_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw) + (bios_size % 65536) != 0) { + goto bios_error; + } ++ if (shadow_bios_after_incoming && bios_size != 128 * 1024) { ++ MachineClass *mc; ++ ++ mc = MACHINE_GET_CLASS(current_machine); ++ error_report("machine %s only supports a 128KB BIOS image", mc->name); ++ exit(1); ++ } + bios = g_malloc(sizeof(*bios)); + memory_region_init_ram(bios, NULL, "pc.bios", bios_size, &error_fatal); + if (!isapc_ram_fw) { +@@ -254,6 +261,15 @@ void pc_system_firmware_init(MemoryRegion *rom_memory, bool isapc_ram_fw) + return; + } + ++ if (shadow_bios_after_incoming) { ++ MachineClass *mc; ++ ++ mc = MACHINE_GET_CLASS(current_machine); ++ error_report("flash-based firmware is not supported by machine %s", ++ mc->name); ++ exit(1); ++ } ++ + if (kvm_enabled() && !kvm_readonly_mem_enabled()) { + /* Older KVM cannot execute from device memory. So, flash memory + * cannot be used unless the readonly memory kvm capability is present. */ +diff --git a/hw/i386/shadow-bios.c b/hw/i386/shadow-bios.c +new file mode 100644 +index 0000000..65a4cb8 +--- /dev/null ++++ b/hw/i386/shadow-bios.c +@@ -0,0 +1,64 @@ ++#include "qemu/osdep.h" ++#include "sysemu/sysemu.h" ++#include "target/i386/cpu.h" ++#include "exec/ram_addr.h" ++#include "qemu/cutils.h" ++ ++void shadow_bios(void) ++{ ++ RAMBlock *block, *ram, *oprom, *bios; ++ size_t one_meg, oprom_size, bios_size; ++ uint8_t *cd_seg_host, *ef_seg_host; ++ ++ ram = NULL; ++ oprom = NULL; ++ bios = NULL; ++ rcu_read_lock(); ++ QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { ++ if (strcmp("pc.ram", block->idstr) == 0) { ++ assert(ram == NULL); ++ ram = block; ++ } else if (strcmp("pc.rom", block->idstr) == 0) { ++ assert(oprom == NULL); ++ oprom = block; ++ } else if (strcmp("pc.bios", block->idstr) == 0) { ++ assert(bios == NULL); ++ bios = block; ++ } ++ } ++ assert(ram != NULL); ++ assert(oprom != NULL); ++ assert(bios != NULL); ++ assert(memory_region_is_ram(ram->mr)); ++ assert(memory_region_is_ram(oprom->mr)); ++ assert(memory_region_is_ram(bios->mr)); ++ assert(int128_eq(ram->mr->size, int128_make64(ram->used_length))); ++ assert(int128_eq(oprom->mr->size, int128_make64(oprom->used_length))); ++ assert(int128_eq(bios->mr->size, int128_make64(bios->used_length))); ++ ++ one_meg = 1024 * 1024; ++ oprom_size = 128 * 1024; ++ bios_size = 128 * 1024; ++ assert(ram->used_length >= one_meg); ++ assert(oprom->used_length == oprom_size); ++ assert(bios->used_length == bios_size); ++ ++ ef_seg_host = memory_region_get_ram_ptr(ram->mr) + (one_meg - bios_size); ++ cd_seg_host = ef_seg_host - oprom_size; ++ ++ /* This is a crude hack, but we must distinguish a rhel6.x.0 machtype guest ++ * coming in from a RHEL-6 emulator (where shadowing has had no effect on ++ * "pc.ram") from a similar guest coming in from a RHEL-7 emulator (where ++ * shadowing has worked). In the latter case we must not trample the live ++ * SeaBIOS variables in "pc.ram". ++ */ ++ if (buffer_is_zero(ef_seg_host, bios_size)) { ++ fprintf(stderr, "copying E and F segments from pc.bios to pc.ram\n"); ++ memcpy(ef_seg_host, memory_region_get_ram_ptr(bios->mr), bios_size); ++ } ++ if (buffer_is_zero(cd_seg_host, oprom_size)) { ++ fprintf(stderr, "copying C and D segments from pc.rom to pc.ram\n"); ++ memcpy(cd_seg_host, memory_region_get_ram_ptr(oprom->mr), oprom_size); ++ } ++ rcu_read_unlock(); ++} +diff --git a/hw/net/e1000.c b/hw/net/e1000.c +index 742cd0a..479ac77 100644 +--- a/hw/net/e1000.c ++++ b/hw/net/e1000.c +@@ -1663,6 +1663,16 @@ static void pci_e1000_realize(PCIDevice *pci_dev, Error **errp) + + pci_conf = pci_dev->config; + ++ if (!(d->compat_flags & E1000_FLAG_AUTONEG)) { ++ /* ++ * We have no capabilities, so capability list bit should normally be 0. ++ * Keep it on for compat machine types to avoid breaking migration. ++ * HACK: abuse E1000_FLAG_AUTONEG, which is off exactly for ++ * the machine types that need this. ++ */ ++ pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_CAP_LIST); ++ } ++ + /* TODO: RST# value should be 0, PCI spec 6.2.4 */ + pci_conf[PCI_CACHE_LINE_SIZE] = 0x10; + +@@ -1727,7 +1737,7 @@ static void e1000_class_init(ObjectClass *klass, void *data) + + k->realize = pci_e1000_realize; + k->exit = pci_e1000_uninit; +- k->romfile = "efi-e1000.rom"; ++ k->romfile = "pxe-e1000.rom"; + k->vendor_id = PCI_VENDOR_ID_INTEL; + k->device_id = info->device_id; + k->revision = info->revision; +@@ -1763,7 +1773,7 @@ static const TypeInfo e1000_base_info = { + + static const E1000Info e1000_devices[] = { + { +- .name = "e1000", ++ .name = "e1000-82540em", + .device_id = E1000_DEV_ID_82540EM, + .revision = 0x03, + .phy_id2 = E1000_PHY_ID2_8254xx_DEFAULT, +@@ -1784,6 +1794,11 @@ static const E1000Info e1000_devices[] = { + #endif + }; + ++static const TypeInfo e1000_default_info = { ++ .name = "e1000", ++ .parent = "e1000-82540em", ++}; ++ + static void e1000_register_types(void) + { + int i; +@@ -1801,6 +1816,7 @@ static void e1000_register_types(void) + + type_register(&type_info); + } ++ type_register_static(&e1000_default_info); + } + + type_init(e1000_register_types) +diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c +index c934c22..7dd8744 100644 +--- a/hw/net/e1000e.c ++++ b/hw/net/e1000e.c +@@ -74,6 +74,11 @@ typedef struct E1000EState { + + E1000ECore core; + ++ /* 7.3 had the intr_state field that was in the original e1000e code ++ * but that was removed prior to 2.7's release ++ */ ++ bool redhat_7_3_intr_state_enable; ++ uint32_t redhat_7_3_intr_state; + } E1000EState; + + #define E1000E_MMIO_IDX 0 +@@ -89,6 +94,10 @@ typedef struct E1000EState { + #define E1000E_MSIX_TABLE (0x0000) + #define E1000E_MSIX_PBA (0x2000) + ++/* Values as in RHEL 7.3 build and original upstream */ ++#define RH_E1000E_USE_MSI BIT(0) ++#define RH_E1000E_USE_MSIX BIT(1) ++ + static uint64_t + e1000e_mmio_read(void *opaque, hwaddr addr, unsigned size) + { +@@ -300,6 +309,8 @@ e1000e_init_msix(E1000EState *s) + } else { + if (!e1000e_use_msix_vectors(s, E1000E_MSIX_VEC_NUM)) { + msix_uninit(d, &s->msix, &s->msix); ++ } else { ++ s->redhat_7_3_intr_state |= RH_E1000E_USE_MSIX; + } + } + } +@@ -471,6 +482,8 @@ static void e1000e_pci_realize(PCIDevice *pci_dev, Error **errp) + ret = msi_init(PCI_DEVICE(s), 0xD0, 1, true, false, NULL); + if (ret) { + trace_e1000e_msi_init_fail(ret); ++ } else { ++ s->redhat_7_3_intr_state |= RH_E1000E_USE_MSI; + } + + if (e1000e_add_pm_capability(pci_dev, e1000e_pmrb_offset, +@@ -594,6 +607,11 @@ static const VMStateDescription e1000e_vmstate_intr_timer = { + VMSTATE_STRUCT_ARRAY(_f, _s, _num, 0, \ + e1000e_vmstate_intr_timer, E1000IntrDelayTimer) + ++static bool rhel_7_3_check(void *opaque, int version_id) ++{ ++ return ((E1000EState *)opaque)->redhat_7_3_intr_state_enable; ++} ++ + static const VMStateDescription e1000e_vmstate = { + .name = "e1000e", + .version_id = 1, +@@ -605,6 +623,7 @@ static const VMStateDescription e1000e_vmstate = { + VMSTATE_MSIX(parent_obj, E1000EState), + + VMSTATE_UINT32(ioaddr, E1000EState), ++ VMSTATE_UINT32_TEST(redhat_7_3_intr_state, E1000EState, rhel_7_3_check), + VMSTATE_UINT32(core.rxbuf_min_shift, E1000EState), + VMSTATE_UINT8(core.rx_desc_len, E1000EState), + VMSTATE_UINT32_ARRAY(core.rxbuf_sizes, E1000EState, +@@ -653,6 +672,8 @@ static PropertyInfo e1000e_prop_disable_vnet, + + static Property e1000e_properties[] = { + DEFINE_NIC_PROPERTIES(E1000EState, conf), ++ DEFINE_PROP_BOOL("__redhat_e1000e_7_3_intr_state", E1000EState, ++ redhat_7_3_intr_state_enable, false), + DEFINE_PROP_SIGNED("disable_vnet_hdr", E1000EState, disable_vnet, false, + e1000e_prop_disable_vnet, bool), + DEFINE_PROP_SIGNED("subsys_ven", E1000EState, subsys_ven, +diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c +index 3a9fc89..c629530 100644 +--- a/hw/net/ne2000.c ++++ b/hw/net/ne2000.c +@@ -769,7 +769,7 @@ static void ne2000_class_init(ObjectClass *klass, void *data) + + k->realize = pci_ne2000_realize; + k->exit = pci_ne2000_exit; +- k->romfile = "efi-ne2k_pci.rom", ++ k->romfile = "pxe-ne2k_pci.rom", + k->vendor_id = PCI_VENDOR_ID_REALTEK; + k->device_id = PCI_DEVICE_ID_REALTEK_8029; + k->class_id = PCI_CLASS_NETWORK_ETHERNET; +diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c +index 70dc8b3..35b6d8a 100644 +--- a/hw/net/pcnet-pci.c ++++ b/hw/net/pcnet-pci.c +@@ -347,7 +347,7 @@ static void pcnet_class_init(ObjectClass *klass, void *data) + + k->realize = pci_pcnet_realize; + k->exit = pci_pcnet_uninit; +- k->romfile = "efi-pcnet.rom", ++ k->romfile = "pxe-pcnet.rom", + k->vendor_id = PCI_VENDOR_ID_AMD; + k->device_id = PCI_DEVICE_ID_AMD_LANCE; + k->revision = 0x10; +diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c +index 46daa16..bc37326 100644 +--- a/hw/net/rtl8139.c ++++ b/hw/net/rtl8139.c +@@ -3174,7 +3174,7 @@ static int rtl8139_pre_save(void *opaque) + + static const VMStateDescription vmstate_rtl8139 = { + .name = "rtl8139", +- .version_id = 5, ++ .version_id = 4, + .minimum_version_id = 3, + .post_load = rtl8139_post_load, + .pre_save = rtl8139_pre_save, +@@ -3255,7 +3255,9 @@ static const VMStateDescription vmstate_rtl8139 = { + VMSTATE_UINT32(tally_counters.TxMCol, RTL8139State), + VMSTATE_UINT64(tally_counters.RxOkPhy, RTL8139State), + VMSTATE_UINT64(tally_counters.RxOkBrd, RTL8139State), ++#if 0 /* Disabled for Red Hat Enterprise Linux bz 1420195 */ + VMSTATE_UINT32_V(tally_counters.RxOkMul, RTL8139State, 5), ++#endif + VMSTATE_UINT16(tally_counters.TxAbt, RTL8139State), + VMSTATE_UINT16(tally_counters.TxUndrn, RTL8139State), + +@@ -3425,7 +3427,7 @@ static void rtl8139_class_init(ObjectClass *klass, void *data) + + k->realize = pci_rtl8139_realize; + k->exit = pci_rtl8139_uninit; +- k->romfile = "efi-rtl8139.rom"; ++ k->romfile = "pxe-rtl8139.rom"; + k->vendor_id = PCI_VENDOR_ID_REALTEK; + k->device_id = PCI_DEVICE_ID_REALTEK_8139; + k->revision = RTL8139_PCI_REVID; /* >=0x20 is for 8139C+ */ +diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c +index 6a92b20..3ea94de 100644 +--- a/hw/ppc/spapr.c ++++ b/hw/ppc/spapr.c +@@ -3935,6 +3935,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) + smc->default_caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_BROKEN; + smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN; + spapr_caps_add_properties(smc, &error_abort); ++ smc->has_power9_support = true; + } + + static const TypeInfo spapr_machine_info = { +@@ -3985,6 +3986,7 @@ static const TypeInfo spapr_machine_info = { + } \ + type_init(spapr_machine_register_##suffix) + ++#if 0 /* Disabled for Red Hat Enterprise Linux */ + /* + * pseries-2.12 + */ +@@ -4136,7 +4138,9 @@ DEFINE_SPAPR_MACHINE(2_8, "2.8", false); + .property = "pre-2.8-migration", \ + .value = "on", \ + }, ++#endif + ++#if defined(CONFIG_RHV) + static void phb_placement_2_7(sPAPRMachineState *spapr, uint32_t index, + uint64_t *buid, hwaddr *pio, + hwaddr *mmio32, hwaddr *mmio64, +@@ -4185,7 +4189,9 @@ static void phb_placement_2_7(sPAPRMachineState *spapr, uint32_t index, + * window into contiguous 32-bit and 64-bit windows + */ + } ++#endif /* CONFIG_RHV */ + ++#if 0 /* Disabled for Red Hat Enterprise Linux */ + static void spapr_machine_2_7_instance_options(MachineState *machine) + { + sPAPRMachineState *spapr = SPAPR_MACHINE(machine); +@@ -4345,6 +4351,213 @@ static void spapr_machine_2_1_class_options(MachineClass *mc) + SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_1); + } + DEFINE_SPAPR_MACHINE(2_1, "2.1", false); ++#endif ++ ++/* ++ * pseries-rhel7.5.0 ++ */ ++ ++static void spapr_machine_rhel750_instance_options(MachineState *machine) ++{ ++} ++ ++static void spapr_machine_rhel750_class_options(MachineClass *mc) ++{ ++ /* Defaults for the latest behaviour inherited from the base class */ ++} ++ ++DEFINE_SPAPR_MACHINE(rhel750, "rhel7.5.0", true); ++ ++/* ++ * pseries-rhel7.5.0-sxxm ++ * ++ * pseries-rhel7.5.0 with speculative execution exploit mitigations enabled by default ++ */ ++static void spapr_machine_rhel750sxxm_instance_options(MachineState *machine) ++{ ++ spapr_machine_rhel750_instance_options(machine); ++} ++ ++static void spapr_machine_rhel750sxxm_class_options(MachineClass *mc) ++{ ++ sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); ++ ++ spapr_machine_rhel750_class_options(mc); ++ smc->default_caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_WORKAROUND; ++ smc->default_caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_WORKAROUND; ++ smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_FIXED_CCD; ++} ++ ++DEFINE_SPAPR_MACHINE(rhel750sxxm, "rhel7.5.0-sxxm", false); ++ ++#if defined(CONFIG_RHV) ++ ++/* ++ * pseries-rhel7.4.0 ++ * like SPAPR_COMPAT_2_9 ++ */ ++ ++#define SPAPR_COMPAT_RHEL7_4 \ ++ HW_COMPAT_RHEL7_4 \ ++ { \ ++ .driver = TYPE_POWERPC_CPU, \ ++ .property = "pre-2.10-migration", \ ++ .value = "on", \ ++ }, \ ++ ++static void spapr_machine_rhel740_instance_options(MachineState *machine) ++{ ++ spapr_machine_rhel750_instance_options(machine); ++} ++ ++static void spapr_machine_rhel740_class_options(MachineClass *mc) ++{ ++ sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); ++ ++ spapr_machine_rhel750_class_options(mc); ++ SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_RHEL7_4); ++ mc->numa_auto_assign_ram = numa_legacy_auto_assign_ram; ++ smc->has_power9_support = false; ++ smc->pre_2_10_has_unused_icps = true; ++ smc->resize_hpt_default = SPAPR_RESIZE_HPT_DISABLED; ++ smc->default_caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_ON; ++} ++ ++DEFINE_SPAPR_MACHINE(rhel740, "rhel7.4.0", false); ++ ++/* ++ * pseries-rhel7.4.0-sxxm ++ * ++ * pseries-rhel7.4.0 with speculative execution exploit mitigations enabled by default ++ */ ++static void spapr_machine_rhel740sxxm_instance_options(MachineState *machine) ++{ ++ spapr_machine_rhel740_instance_options(machine); ++} ++ ++static void spapr_machine_rhel740sxxm_class_options(MachineClass *mc) ++{ ++ sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); ++ ++ spapr_machine_rhel740_class_options(mc); ++ smc->default_caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_WORKAROUND; ++ smc->default_caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_WORKAROUND; ++ smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_FIXED_CCD; ++} ++ ++DEFINE_SPAPR_MACHINE(rhel740sxxm, "rhel7.4.0-sxxm", false); ++ ++/* ++ * pseries-rhel7.3.0 ++ * like SPAPR_COMPAT_2_6/_2_7/_2_8 but "ddw" has been backported to RHEL7_3 ++ */ ++#define SPAPR_COMPAT_RHEL7_3 \ ++ HW_COMPAT_RHEL7_3 \ ++ { \ ++ .driver = TYPE_SPAPR_PCI_HOST_BRIDGE, \ ++ .property = "mem_win_size", \ ++ .value = stringify(SPAPR_PCI_2_7_MMIO_WIN_SIZE),\ ++ }, \ ++ { \ ++ .driver = TYPE_SPAPR_PCI_HOST_BRIDGE, \ ++ .property = "mem64_win_size", \ ++ .value = "0", \ ++ }, \ ++ { \ ++ .driver = TYPE_POWERPC_CPU, \ ++ .property = "pre-2.8-migration", \ ++ .value = "on", \ ++ }, \ ++ { \ ++ .driver = TYPE_SPAPR_PCI_HOST_BRIDGE, \ ++ .property = "pre-2.8-migration", \ ++ .value = "on", \ ++ }, \ ++ { \ ++ .driver = TYPE_SPAPR_PCI_HOST_BRIDGE, \ ++ .property = "pcie-extended-configuration-space",\ ++ .value = "off", \ ++ }, ++ ++static void spapr_machine_rhel730_instance_options(MachineState *machine) ++{ ++ sPAPRMachineState *spapr = SPAPR_MACHINE(machine); ++ ++ spapr_machine_rhel740_instance_options(machine); ++ spapr->use_hotplug_event_source = false; ++} ++ ++static void spapr_machine_rhel730_class_options(MachineClass *mc) ++{ ++ sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); ++ ++ spapr_machine_rhel740_class_options(mc); ++ mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power7_v2.3"); ++ SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_RHEL7_3); ++ smc->phb_placement = phb_placement_2_7; ++} ++ ++DEFINE_SPAPR_MACHINE(rhel730, "rhel7.3.0", false); ++ ++/* ++ * pseries-rhel7.3.0-sxxm ++ * ++ * pseries-rhel7.3.0 with speculative execution exploit mitigations enabled by default ++ */ ++static void spapr_machine_rhel730sxxm_instance_options(MachineState *machine) ++{ ++ spapr_machine_rhel730_instance_options(machine); ++} ++ ++static void spapr_machine_rhel730sxxm_class_options(MachineClass *mc) ++{ ++ sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); ++ ++ spapr_machine_rhel730_class_options(mc); ++ smc->default_caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_WORKAROUND; ++ smc->default_caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_WORKAROUND; ++ smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_FIXED_CCD; ++} ++ ++DEFINE_SPAPR_MACHINE(rhel730sxxm, "rhel7.3.0-sxxm", false); ++ ++/* ++ * pseries-rhel7.2.0 ++ */ ++/* Should be like SPAPR_COMPAT_2_5 + 2_4 + 2_3, but "dynamic-reconfiguration" ++ * has been backported to RHEL7_2 so we don't need it here. ++ */ ++ ++#define SPAPR_COMPAT_RHEL7_2 \ ++ HW_COMPAT_RHEL7_2 \ ++ { \ ++ .driver = "spapr-vlan", \ ++ .property = "use-rx-buffer-pools", \ ++ .value = "off", \ ++ },{ \ ++ .driver = TYPE_SPAPR_PCI_HOST_BRIDGE,\ ++ .property = "ddw",\ ++ .value = stringify(off),\ ++ }, ++ ++ ++static void spapr_machine_rhel720_instance_options(MachineState *machine) ++{ ++ spapr_machine_rhel730_instance_options(machine); ++} ++ ++static void spapr_machine_rhel720_class_options(MachineClass *mc) ++{ ++ sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); ++ ++ spapr_machine_rhel730_class_options(mc); ++ smc->use_ohci_by_default = true; ++ mc->has_hotpluggable_cpus = NULL; ++ SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_RHEL7_2); ++} ++ ++DEFINE_SPAPR_MACHINE(rhel720, "rhel7.2.0", false); ++#endif /* CONFIG_RHV */ + + static void spapr_machine_register_types(void) + { +diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c +index 1eda854..c5f0c72 100644 +--- a/hw/ppc/spapr_cpu_core.c ++++ b/hw/ppc/spapr_cpu_core.c +@@ -21,6 +21,7 @@ + #include "sysemu/numa.h" + #include "sysemu/hw_accel.h" + #include "qemu/error-report.h" ++#include "cpu-models.h" + + static void spapr_cpu_reset(void *opaque) + { +@@ -62,6 +63,7 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, + Error **errp) + { + CPUPPCState *env = &cpu->env; ++ sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); + + /* Set time-base frequency to 512 MHz */ + cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ); +@@ -69,6 +71,17 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, + /* Enable PAPR mode in TCG or KVM */ + cpu_ppc_set_papr(cpu, PPC_VIRTUAL_HYPERVISOR(spapr)); + ++ if (!smc->has_power9_support && ++ (((spapr->max_compat_pvr && ++ ppc_compat_cmp(spapr->max_compat_pvr, ++ CPU_POWERPC_LOGICAL_3_00) >= 0)) || ++ (!spapr->max_compat_pvr && ++ ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_00, 0, 0)))) { ++ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, ++ "POWER9 CPU is not supported by this machine class"); ++ return; ++ } ++ + qemu_register_reset(spapr_cpu_reset, cpu); + spapr_cpu_reset(cpu); + } +diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c +index 435f7c9..3956ac3 100644 +--- a/hw/s390x/s390-virtio-ccw.c ++++ b/hw/s390x/s390-virtio-ccw.c +@@ -647,7 +647,7 @@ bool css_migration_enabled(void) + { \ + MachineClass *mc = MACHINE_CLASS(oc); \ + ccw_machine_##suffix##_class_options(mc); \ +- mc->desc = "VirtIO-ccw based S390 machine v" verstr; \ ++ mc->desc = "VirtIO-ccw based S390 machine " verstr; \ + if (latest) { \ + mc->alias = "s390-ccw-virtio"; \ + mc->is_default = 1; \ +@@ -682,6 +682,8 @@ bool css_migration_enabled(void) + #define CCW_COMPAT_2_10 \ + HW_COMPAT_2_10 + ++#if 0 /* Disabled for Red Hat Enterprise Linux */ ++ + #define CCW_COMPAT_2_9 \ + HW_COMPAT_2_9 \ + {\ +@@ -879,6 +881,19 @@ static void ccw_machine_2_4_class_options(MachineClass *mc) + } + DEFINE_CCW_MACHINE(2_4, "2.4", false); + ++#else ++ ++static void ccw_machine_rhel750_instance_options(MachineState *machine) ++{ ++} ++ ++static void ccw_machine_rhel750_class_options(MachineClass *mc) ++{ ++} ++DEFINE_CCW_MACHINE(rhel750, "rhel7.5.0", true); ++ ++#endif ++ + static void ccw_machine_register_types(void) + { + type_register_static(&ccw_machine_info); +diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c +index 27a07e9..df8f0ae 100644 +--- a/hw/smbios/smbios.c ++++ b/hw/smbios/smbios.c +@@ -778,6 +778,7 @@ void smbios_set_defaults(const char *manufacturer, const char *product, + SMBIOS_SET_DEFAULT(type1.manufacturer, manufacturer); + SMBIOS_SET_DEFAULT(type1.product, product); + SMBIOS_SET_DEFAULT(type1.version, version); ++ SMBIOS_SET_DEFAULT(type1.family, "Red Hat Enterprise Linux"); + SMBIOS_SET_DEFAULT(type2.manufacturer, manufacturer); + SMBIOS_SET_DEFAULT(type2.product, product); + SMBIOS_SET_DEFAULT(type2.version, version); +diff --git a/hw/timer/i8254_common.c b/hw/timer/i8254_common.c +index 6190b6f..ad2ad2d 100644 +--- a/hw/timer/i8254_common.c ++++ b/hw/timer/i8254_common.c +@@ -268,7 +268,7 @@ static const VMStateDescription vmstate_pit_common = { + .pre_save = pit_dispatch_pre_save, + .post_load = pit_dispatch_post_load, + .fields = (VMStateField[]) { +- VMSTATE_UINT32_V(channels[0].irq_disabled, PITCommonState, 3), ++ VMSTATE_UINT32(channels[0].irq_disabled, PITCommonState), /* qemu-kvm's v2 had 'flags' here */ + VMSTATE_STRUCT_ARRAY(channels, PITCommonState, 3, 2, + vmstate_pit_channel, PITChannelState), + VMSTATE_INT64(channels[0].next_transition_time, +diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c +index 6f1f723..68c353f 100644 +--- a/hw/timer/mc146818rtc.c ++++ b/hw/timer/mc146818rtc.c +@@ -34,6 +34,7 @@ + #include "qapi/qapi-commands-misc.h" + #include "qapi/qapi-events-misc.h" + #include "qapi/visitor.h" ++#include "migration/migration.h" + + #ifdef TARGET_I386 + #include "hw/i386/apic.h" +@@ -839,6 +840,11 @@ static int rtc_post_load(void *opaque, int version_id) + static bool rtc_irq_reinject_on_ack_count_needed(void *opaque) + { + RTCState *s = (RTCState *)opaque; ++ ++ if (migrate_pre_2_2) { ++ return false; ++ } ++ + return s->irq_reinject_on_ack_count != 0; + } + +diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c +index 836b11f..86d6ab8 100644 +--- a/hw/usb/hcd-uhci.c ++++ b/hw/usb/hcd-uhci.c +@@ -152,6 +152,8 @@ typedef struct UHCI_QH { + uint32_t el_link; + } UHCI_QH; + ++bool ich9_uhci123_irqpin_override; ++ + static void uhci_async_cancel(UHCIAsync *async); + static void uhci_queue_fill(UHCIQueue *q, UHCI_TD *td); + static void uhci_resume(void *opaque); +@@ -1214,12 +1216,23 @@ static void usb_uhci_common_realize(PCIDevice *dev, Error **errp) + UHCIState *s = UHCI(dev); + uint8_t *pci_conf = s->dev.config; + int i; ++ int irq_pin; + + pci_conf[PCI_CLASS_PROG] = 0x00; + /* TODO: reset value should be 0. */ + pci_conf[USB_SBRN] = USB_RELEASE_1; // release number + +- pci_config_set_interrupt_pin(pci_conf, u->info.irq_pin + 1); ++ if (ich9_uhci123_irqpin_override && ++ u->info.vendor_id == PCI_VENDOR_ID_INTEL && ++ (u->info.device_id == PCI_DEVICE_ID_INTEL_82801I_UHCI1 || ++ u->info.device_id == PCI_DEVICE_ID_INTEL_82801I_UHCI2 || ++ u->info.device_id == PCI_DEVICE_ID_INTEL_82801I_UHCI3)) { ++ fprintf(stderr, "RHEL-6 compat: %s: irq_pin = 3\n", u->info.name); ++ irq_pin = 3; ++ } else { ++ irq_pin = u->info.irq_pin; ++ } ++ pci_config_set_interrupt_pin(pci_conf, irq_pin + 1); + + if (s->masterbus) { + USBPort *ports[NB_PORTS]; +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index 721beb5..883141f 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -3555,9 +3555,27 @@ static const VMStateDescription vmstate_xhci_slot = { + } + }; + ++static int xhci_event_pre_save(void *opaque) ++{ ++ XHCIEvent *s = opaque; ++ ++ s->cve_2014_5263_a = ((uint8_t *)&s->type)[0]; ++ s->cve_2014_5263_b = ((uint8_t *)&s->type)[1]; ++ ++ return 0; ++} ++ ++bool migrate_cve_2014_5263_xhci_fields; ++ ++static bool xhci_event_cve_2014_5263(void *opaque, int version_id) ++{ ++ return migrate_cve_2014_5263_xhci_fields; ++} ++ + static const VMStateDescription vmstate_xhci_event = { + .name = "xhci-event", + .version_id = 1, ++ .pre_save = xhci_event_pre_save, + .fields = (VMStateField[]) { + VMSTATE_UINT32(type, XHCIEvent), + VMSTATE_UINT32(ccode, XHCIEvent), +@@ -3566,6 +3584,8 @@ static const VMStateDescription vmstate_xhci_event = { + VMSTATE_UINT32(flags, XHCIEvent), + VMSTATE_UINT8(slotid, XHCIEvent), + VMSTATE_UINT8(epid, XHCIEvent), ++ VMSTATE_UINT8_TEST(cve_2014_5263_a, XHCIEvent, xhci_event_cve_2014_5263), ++ VMSTATE_UINT8_TEST(cve_2014_5263_b, XHCIEvent, xhci_event_cve_2014_5263), + VMSTATE_END_OF_LIST() + } + }; +diff --git a/hw/usb/hcd-xhci.h b/hw/usb/hcd-xhci.h +index fc36a4c..89d4cf7 100644 +--- a/hw/usb/hcd-xhci.h ++++ b/hw/usb/hcd-xhci.h +@@ -153,6 +153,8 @@ typedef struct XHCIEvent { + uint32_t flags; + uint8_t slotid; + uint8_t epid; ++ uint8_t cve_2014_5263_a; ++ uint8_t cve_2014_5263_b; + } XHCIEvent; + + typedef struct XHCIInterrupter { +diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c +index e3e49d6..18f7c2c 100644 +--- a/hw/virtio/virtio-pci.c ++++ b/hw/virtio/virtio-pci.c +@@ -2422,7 +2422,7 @@ static void virtio_net_pci_class_init(ObjectClass *klass, void *data) + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass); + +- k->romfile = "efi-virtio.rom"; ++ k->romfile = "pxe-virtio.rom"; + k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + k->device_id = PCI_DEVICE_ID_VIRTIO_NET; + k->revision = VIRTIO_PCI_ABI_VERSION; +diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c +index 006d3d1..4bcb4f4 100644 +--- a/hw/virtio/virtio.c ++++ b/hw/virtio/virtio.c +@@ -24,6 +24,7 @@ + #include "hw/virtio/virtio-access.h" + #include "sysemu/dma.h" + ++#include "standard-headers/linux/virtio_net.h" + /* + * The alignment to use between consumer and producer parts of vring. + * x86 pagesize again. This is the default, used by transports like PCI +@@ -1991,7 +1992,24 @@ const VMStateInfo virtio_vmstate_info = { + static int virtio_set_features_nocheck(VirtIODevice *vdev, uint64_t val) + { + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); +- bool bad = (val & ~(vdev->host_features)) != 0; ++ bool bad; ++ uint64_t ctrl_guest_mask = 1ull << VIRTIO_NET_F_CTRL_GUEST_OFFLOADS; ++ ++ if (vdev->rhel6_ctrl_guest_workaround && (val & ctrl_guest_mask) && ++ !(vdev->host_features & ctrl_guest_mask)) { ++ /* ++ * This works around a mistake in the definition of the rhel6.[56].0 ++ * machinetypes, ctrl-guest-offload was not set in qemu-kvm-rhev for ++ * those machine types, but is set on the rhel6 qemu-kvm-rhev build. ++ * If an incoming rhel6 guest uses it then we need to allow it. ++ * Note: There's a small race where a guest read the flag but didn't ++ * declare it's useage yet. ++ */ ++ fprintf(stderr, "RHEL6 ctrl_guest_offload workaround\n"); ++ vdev->host_features |= ctrl_guest_mask; ++ } ++ ++ bad = (val & ~(vdev->host_features)) != 0; + + val &= vdev->host_features; + if (k->set_features) { +@@ -2566,6 +2584,8 @@ static void virtio_device_instance_finalize(Object *obj) + + static Property virtio_properties[] = { + DEFINE_VIRTIO_COMMON_FEATURES(VirtIODevice, host_features), ++ DEFINE_PROP_BOOL("__com.redhat_rhel6_ctrl_guest_workaround", VirtIODevice, ++ rhel6_ctrl_guest_workaround, false), + DEFINE_PROP_END_OF_LIST(), + }; + +diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h +index 59aeb06..7b5cc25 100644 +--- a/include/hw/acpi/ich9.h ++++ b/include/hw/acpi/ich9.h +@@ -61,6 +61,9 @@ typedef struct ICH9LPCPMRegs { + uint8_t smm_enabled; + bool enable_tco; + TCOIORegs tco_regs; ++ ++ /* RH addition, see bz 1489800 */ ++ bool force_rev1_fadt; + } ICH9LPCPMRegs; + + #define ACPI_PM_PROP_TCO_ENABLED "enable_tco" +diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h +index ba0c1a4..2e8e78a 100644 +--- a/include/hw/arm/virt.h ++++ b/include/hw/arm/virt.h +@@ -109,6 +109,7 @@ typedef struct { + int psci_conduit; + } VirtMachineState; + ++#if 0 /* disabled for Red Hat Enterprise Linux */ + #define TYPE_VIRT_MACHINE MACHINE_TYPE_NAME("virt") + #define VIRT_MACHINE(obj) \ + OBJECT_CHECK(VirtMachineState, (obj), TYPE_VIRT_MACHINE) +@@ -117,6 +118,27 @@ typedef struct { + #define VIRT_MACHINE_CLASS(klass) \ + OBJECT_CLASS_CHECK(VirtMachineClass, klass, TYPE_VIRT_MACHINE) + ++#else ++#define TYPE_RHEL_MACHINE MACHINE_TYPE_NAME("virt-rhel") ++#define VIRT_MACHINE(obj) \ ++ OBJECT_CHECK(VirtMachineState, (obj), TYPE_RHEL_MACHINE) ++#define VIRT_MACHINE_GET_CLASS(obj) \ ++ OBJECT_GET_CLASS(VirtMachineClass, obj, TYPE_RHEL_MACHINE) ++#define VIRT_MACHINE_CLASS(klass) \ ++ OBJECT_CLASS_CHECK(VirtMachineClass, klass, TYPE_RHEL_MACHINE) ++#endif ++ ++/* This macro is for changes to properties that are RHEL specific, ++ * different to the current upstream and to be applied to the latest ++ * machine type. ++ */ ++#define ARM_RHEL_COMPAT \ ++ {\ ++ .driver = "virtio-net-pci",\ ++ .property = "romfile",\ ++ .value = "",\ ++ }, ++ + void virt_acpi_setup(VirtMachineState *vms); + + #endif /* QEMU_ARM_VIRT_H */ +diff --git a/include/hw/compat.h b/include/hw/compat.h +index 13242b8..503b5c8 100644 +--- a/include/hw/compat.h ++++ b/include/hw/compat.h +@@ -255,4 +255,199 @@ + .value = "on",\ + }, + ++/* Mostly like HW_COMPAT_2_1 but: ++ * we don't need virtio-scsi-pci since 7.0 already had that on ++ * ++ * RH: Note, qemu-extended-regs should have been enabled in the 7.1 ++ * machine type, but was accidentally turned off in 7.2 onwards. ++ * ++ */ ++#define HW_COMPAT_RHEL7_1 \ ++ { /* COMPAT_RHEL7.1 */ \ ++ .driver = "intel-hda-generic",\ ++ .property = "old_msi_addr",\ ++ .value = "on",\ ++ },{\ ++ .driver = "VGA",\ ++ .property = "qemu-extended-regs",\ ++ .value = "off",\ ++ },{\ ++ .driver = "secondary-vga",\ ++ .property = "qemu-extended-regs",\ ++ .value = "off",\ ++ },{\ ++ .driver = "usb-mouse",\ ++ .property = "usb_version",\ ++ .value = stringify(1),\ ++ },{\ ++ .driver = "usb-kbd",\ ++ .property = "usb_version",\ ++ .value = stringify(1),\ ++ },{\ ++ .driver = "virtio-pci",\ ++ .property = "virtio-pci-bus-master-bug-migration",\ ++ .value = "on",\ ++ },{\ ++ .driver = "virtio-blk-pci",\ ++ .property = "any_layout",\ ++ .value = "off",\ ++ },{\ ++ .driver = "virtio-balloon-pci",\ ++ .property = "any_layout",\ ++ .value = "off",\ ++ },{\ ++ .driver = "virtio-serial-pci",\ ++ .property = "any_layout",\ ++ .value = "off",\ ++ },{\ ++ .driver = "virtio-9p-pci",\ ++ .property = "any_layout",\ ++ .value = "off",\ ++ },{\ ++ .driver = "virtio-rng-pci",\ ++ .property = "any_layout",\ ++ .value = "off",\ ++ },{ /* HW_COMPAT_RHEL7_1 - introduced with 2.10.0 */ \ ++ .driver = "migration",\ ++ .property = "send-configuration",\ ++ .value = "off",\ ++ }, ++ ++/* Mostly like HW_COMPAT_2_4 + 2_3 but: ++ * we don't need "any_layout" as it has been backported to 7.2 ++ */ ++ ++#define HW_COMPAT_RHEL7_2 \ ++ {\ ++ .driver = "virtio-blk-device",\ ++ .property = "scsi",\ ++ .value = "true",\ ++ },{\ ++ .driver = "e1000-82540em",\ ++ .property = "extra_mac_registers",\ ++ .value = "off",\ ++ },{\ ++ .driver = "virtio-pci",\ ++ .property = "x-disable-pcie",\ ++ .value = "on",\ ++ },{\ ++ .driver = "virtio-pci",\ ++ .property = "migrate-extra",\ ++ .value = "off",\ ++ },{ /* HW_COMPAT_RHEL7_2 */ \ ++ .driver = "fw_cfg_mem",\ ++ .property = "dma_enabled",\ ++ .value = "off",\ ++ },{ /* HW_COMPAT_RHEL7_2 */ \ ++ .driver = "fw_cfg_io",\ ++ .property = "dma_enabled",\ ++ .value = "off",\ ++ },{ /* HW_COMPAT_RHEL7_2 */ \ ++ .driver = "isa-fdc",\ ++ .property = "fallback",\ ++ .value = "144",\ ++ },{ /* HW_COMPAT_RHEL7_2 */ \ ++ .driver = "virtio-pci",\ ++ .property = "disable-modern",\ ++ .value = "on",\ ++ },{ /* HW_COMPAT_RHEL7_2 */ \ ++ .driver = "virtio-pci",\ ++ .property = "disable-legacy",\ ++ .value = "off",\ ++ },{ /* HW_COMPAT_RHEL7_2 */ \ ++ .driver = TYPE_PCI_DEVICE,\ ++ .property = "x-pcie-lnksta-dllla",\ ++ .value = "off",\ ++ },{ /* HW_COMPAT_RHEL7_2 */ \ ++ .driver = "virtio-pci",\ ++ .property = "page-per-vq",\ ++ .value = "on",\ ++ },{ /* HW_COMPAT_RHEL7_2 from HW_COMPAT_2_4 added in 2.9 */ \ ++ .driver = "vmgenid",\ ++ .property = "x-write-pointer-available",\ ++ .value = "off",\ ++ },{ /* HW_COMPAT_RHEL7_2 - introduced with 2.10.0 */ \ ++ .driver = "migration",\ ++ .property = "send-section-footer",\ ++ .value = "off",\ ++ },{ /* HW_COMPAT_RHEL7_2 - introduced with 2.10.0 */ \ ++ .driver = "migration",\ ++ .property = "store-global-state",\ ++ .value = "off",\ ++ }, ++ ++/* Mostly like HW_COMPAT_2_6 + HW_COMPAT_2_7 + HW_COMPAT_2_8 except ++ * disable-modern, disable-legacy, page-per-vq have already been ++ * backported to RHEL7.3 ++ */ ++#define HW_COMPAT_RHEL7_3 \ ++ { /* HW_COMPAT_RHEL7_3 */ \ ++ .driver = "virtio-mmio",\ ++ .property = "format_transport_address",\ ++ .value = "off",\ ++ },{ /* HW_COMPAT_RHEL7_3 */ \ ++ .driver = "virtio-serial-device",\ ++ .property = "emergency-write",\ ++ .value = "off",\ ++ },{ /* HW_COMPAT_RHEL7_3 */ \ ++ .driver = "ioapic",\ ++ .property = "version",\ ++ .value = "0x11",\ ++ },{ /* HW_COMPAT_RHEL7_3 */ \ ++ .driver = "intel-iommu",\ ++ .property = "x-buggy-eim",\ ++ .value = "true",\ ++ },{ /* HW_COMPAT_RHEL7_3 */ \ ++ .driver = "virtio-pci",\ ++ .property = "x-ignore-backend-features",\ ++ .value = "on",\ ++ },{ /* HW_COMPAT_RHEL7_3 */ \ ++ .driver = "fw_cfg_mem",\ ++ .property = "x-file-slots",\ ++ .value = stringify(0x10),\ ++ },{ /* HW_COMPAT_RHEL7_3 */ \ ++ .driver = "fw_cfg_io",\ ++ .property = "x-file-slots",\ ++ .value = stringify(0x10),\ ++ },{ /* HW_COMPAT_RHEL7_3 */ \ ++ .driver = "pflash_cfi01",\ ++ .property = "old-multiple-chip-handling",\ ++ .value = "on",\ ++ },{ /* HW_COMPAT_RHEL7_3 */ \ ++ .driver = TYPE_PCI_DEVICE,\ ++ .property = "x-pcie-extcap-init",\ ++ .value = "off",\ ++ },{ /* HW_COMPAT_RHEL7_3 */ \ ++ .driver = "virtio-pci",\ ++ .property = "x-pcie-deverr-init",\ ++ .value = "off",\ ++ },{ /* HW_COMPAT_RHEL7_3 */ \ ++ .driver = "virtio-pci",\ ++ .property = "x-pcie-lnkctl-init",\ ++ .value = "off",\ ++ },{ /* HW_COMPAT_RHEL7_3 */ \ ++ .driver = "virtio-pci",\ ++ .property = "x-pcie-pm-init",\ ++ .value = "off",\ ++ },{ /* HW_COMPAT_RHEL7_3 */ \ ++ .driver = "virtio-net-device",\ ++ .property = "x-mtu-bypass-backend",\ ++ .value = "off",\ ++ },{ /* HW_COMPAT_RHEL7_3 */ \ ++ .driver = "e1000e",\ ++ .property = "__redhat_e1000e_7_3_intr_state",\ ++ .value = "on",\ ++ }, ++ ++/* Mostly like HW_COMPAT_2_9 except ++ * x-mtu-bypass-backend, x-migrate-msix has already been ++ * backported to RHEL7.4. shpc was already on in 7.4. ++ */ ++#define HW_COMPAT_RHEL7_4 \ ++ { /* HW_COMPAT_RHEL7_4 */ \ ++ .driver = "intel-iommu",\ ++ .property = "pt",\ ++ .value = "off",\ ++ }, ++ + #endif /* HW_COMPAT_H */ +diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h +index ffee841..faddeba 100644 +--- a/include/hw/i386/pc.h ++++ b/include/hw/i386/pc.h +@@ -142,6 +142,9 @@ struct PCMachineClass { + + /* use DMA capable linuxboot option rom */ + bool linuxboot_dma_enabled; ++ ++ /* RH only, see bz 1489800 */ ++ bool pc_rom_ro; + }; + + #define TYPE_PC_MACHINE "generic-pc-machine" +@@ -947,4 +950,555 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); + type_init(pc_machine_init_##suffix) + + extern void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id); ++ ++/* See include/hw/compat.h for shared compatibility lists */ ++ ++/* This macro is for changes to properties that are RHEL specific, ++ * different to the current upstream and to be applied to the latest ++ * machine type. ++ */ ++#define PC_RHEL_COMPAT \ ++ { /* PC_RHEL_COMPAT */ \ ++ .driver = TYPE_X86_CPU,\ ++ .property = "host-phys-bits",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL_COMPAT bz 1508330 */ \ ++ .driver = "vfio-pci",\ ++ .property = "x-no-geforce-quirks",\ ++ .value = "on",\ ++ }, ++ ++#define PC_RHEL7_4_COMPAT \ ++ HW_COMPAT_RHEL7_4 \ ++ { /* PC_RHEL7_4_COMPAT from PC_COMPAT_2_9 */ \ ++ .driver = "mch",\ ++ .property = "extended-tseg-mbytes",\ ++ .value = stringify(0),\ ++ },\ ++ { /* PC_RHEL7_4_COMPAT bz 1489800 */ \ ++ .driver = "ICH9-LPC",\ ++ .property = "__com.redhat_force-rev1-fadt",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL7_4_COMPAT from PC_COMPAT_2_10 */ \ ++ .driver = "i440FX-pcihost",\ ++ .property = "x-pci-hole64-fix",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_4_COMPAT from PC_COMPAT_2_10 */ \ ++ .driver = "q35-pcihost",\ ++ .property = "x-pci-hole64-fix",\ ++ .value = "off",\ ++ }, ++ ++ ++#define PC_RHEL7_3_COMPAT \ ++ HW_COMPAT_RHEL7_3 \ ++ { /* PC_RHEL7_3_COMPAT from PC_COMPAT_2_8 */ \ ++ .driver = "kvmclock",\ ++ .property = "x-mach-use-reliable-get-clock",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_3_COMPAT from PC_COMPAT_2_7 */ \ ++ .driver = TYPE_X86_CPU,\ ++ .property = "l3-cache",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_3_COMPAT from PC_COMPAT_2_7 */ \ ++ .driver = TYPE_X86_CPU,\ ++ .property = "full-cpuid-auto-level",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_3_COMPAT from PC_COMPAT_2_7 */ \ ++ .driver = "Opteron_G3" "-" TYPE_X86_CPU,\ ++ .property = "family",\ ++ .value = "15",\ ++ },\ ++ { /* PC_RHEL7_3_COMPAT from PC_COMPAT_2_7 */ \ ++ .driver = "Opteron_G3" "-" TYPE_X86_CPU,\ ++ .property = "model",\ ++ .value = "6",\ ++ },\ ++ { /* PC_RHEL7_3_COMPAT from PC_COMPAT_2_7 */ \ ++ .driver = "Opteron_G3" "-" TYPE_X86_CPU,\ ++ .property = "stepping",\ ++ .value = "1",\ ++ },\ ++ { /* PC_RHEL7_3_COMPAT from PC_COMPAT_2_7 */ \ ++ .driver = "isa-pcspk",\ ++ .property = "migrate",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_3_COMPAT from PC_COMPAT_2_6 */ \ ++ .driver = TYPE_X86_CPU,\ ++ .property = "cpuid-0xb",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_3_COMPAT from PC_COMPAT_2_8 */ \ ++ .driver = "ICH9-LPC",\ ++ .property = "x-smi-broadcast",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_3_COMPAT from PC_COMPAT_2_8 */ \ ++ .driver = TYPE_X86_CPU,\ ++ .property = "vmware-cpuid-freq",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_3_COMPAT from PC_COMPAT_2_8 */ \ ++ .driver = "Haswell-" TYPE_X86_CPU,\ ++ .property = "stepping",\ ++ .value = "1",\ ++ },\ ++ { /* PC_RHEL7_3_COMPAT from PC_COMPAT_2_3 added in 2.9 */ \ ++ .driver = TYPE_X86_CPU,\ ++ .property = "kvm-no-smi-migration",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL7_4_COMPAT from PC_COMPAT_2_10 */ \ ++ .driver = TYPE_X86_CPU,\ ++ .property = "x-hv-max-vps",\ ++ .value = "0x40",\ ++ }, ++ ++#define PC_RHEL7_2_COMPAT \ ++ HW_COMPAT_RHEL7_2 \ ++ {\ ++ .driver = "phenom" "-" TYPE_X86_CPU,\ ++ .property = "rdtscp",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_2_COMPAT */ \ ++ .driver = "qemu64" "-" TYPE_X86_CPU,\ ++ .property = "sse4a",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL7_2_COMPAT */ \ ++ .driver = "qemu64" "-" TYPE_X86_CPU,\ ++ .property = "abm",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL7_2_COMPAT */ \ ++ .driver = "Haswell-" TYPE_X86_CPU,\ ++ .property = "abm",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_2_COMPAT (copied from the entry above) */ \ ++ .driver = "Haswell-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "abm",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_2_COMPAT */ \ ++ .driver = "Haswell-noTSX-" TYPE_X86_CPU,\ ++ .property = "abm",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_2_COMPAT (copied from the entry above) */ \ ++ .driver = "Haswell-noTSX-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "abm",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_2_COMPAT */ \ ++ .driver = "Broadwell-" TYPE_X86_CPU,\ ++ .property = "abm",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_2_COMPAT (copied from the entry above) */ \ ++ .driver = "Broadwell-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "abm",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_2_COMPAT */ \ ++ .driver = "Broadwell-noTSX-" TYPE_X86_CPU,\ ++ .property = "abm",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_2_COMPAT (copied from the entry above) */ \ ++ .driver = "Broadwell-noTSX-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "abm",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_2_COMPAT */ \ ++ .driver = "host" "-" TYPE_X86_CPU,\ ++ .property = "host-cache-info",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL7_2_COMPAT */ \ ++ .driver = TYPE_X86_CPU,\ ++ .property = "check",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_2_COMPAT */ \ ++ .driver = "qemu32" "-" TYPE_X86_CPU,\ ++ .property = "popcnt",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL7_2_COMPAT */ \ ++ .driver = TYPE_X86_CPU,\ ++ .property = "arat",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_2_COMPAT */ \ ++ .driver = "usb-redir",\ ++ .property = "streams",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_2_COMPAT */ \ ++ .driver = TYPE_X86_CPU,\ ++ .property = "fill-mtrr-mask",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_2_COMPAT */ \ ++ .driver = "apic-common",\ ++ .property = "legacy-instance-id",\ ++ .value = "on",\ ++ }, ++ ++ ++ ++#define PC_RHEL7_1_COMPAT \ ++ HW_COMPAT_RHEL7_1 \ ++ {\ ++ .driver = "kvm64" "-" TYPE_X86_CPU,\ ++ .property = "vme",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "kvm32" "-" TYPE_X86_CPU,\ ++ .property = "vme",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "Conroe" "-" TYPE_X86_CPU,\ ++ .property = "vme",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "Penryn" "-" TYPE_X86_CPU,\ ++ .property = "vme",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "Nehalem" "-" TYPE_X86_CPU,\ ++ .property = "vme",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_1_COMPAT (copied from the entry above) */ \ ++ .driver = "Nehalem-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "vme",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "Westmere" "-" TYPE_X86_CPU,\ ++ .property = "vme",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_1_COMPAT (copied from the entry above) */ \ ++ .driver = "Westmere-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "vme",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "SandyBridge" "-" TYPE_X86_CPU,\ ++ .property = "vme",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_1_COMPAT (copied from the entry above) */ \ ++ .driver = "SandyBridge-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "vme",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "Haswell" "-" TYPE_X86_CPU,\ ++ .property = "vme",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_1_COMPAT (copied from the entry above) */ \ ++ .driver = "Haswell-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "vme",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "Broadwell" "-" TYPE_X86_CPU,\ ++ .property = "vme",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_1_COMPAT (copied from the entry above) */ \ ++ .driver = "Broadwell-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "vme",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "Opteron_G1" "-" TYPE_X86_CPU,\ ++ .property = "vme",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "Opteron_G2" "-" TYPE_X86_CPU,\ ++ .property = "vme",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "Opteron_G3" "-" TYPE_X86_CPU,\ ++ .property = "vme",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "Opteron_G4" "-" TYPE_X86_CPU,\ ++ .property = "vme",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "Opteron_G5" "-" TYPE_X86_CPU,\ ++ .property = "vme",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "Haswell" "-" TYPE_X86_CPU,\ ++ .property = "f16c",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_1_COMPAT (copied from the entry above) */ \ ++ .driver = "Haswell-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "f16c",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "Haswell" "-" TYPE_X86_CPU,\ ++ .property = "rdrand",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_1_COMPAT (copied from the entry above) */ \ ++ .driver = "Haswell-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "rdrand",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "Broadwell" "-" TYPE_X86_CPU,\ ++ .property = "f16c",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_1_COMPAT (copied from the entry above) */ \ ++ .driver = "Broadwell-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "f16c",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "Broadwell" "-" TYPE_X86_CPU,\ ++ .property = "rdrand",\ ++ .value = "off",\ ++ },\ ++ { /* PC_RHEL7_1_COMPAT (copied from the entry above) */ \ ++ .driver = "Broadwell-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "rdrand",\ ++ .value = "off",\ ++ },\ ++ {\ ++ .driver = "coreduo" "-" TYPE_X86_CPU,\ ++ .property = "vmx",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "core2duo" "-" TYPE_X86_CPU,\ ++ .property = "vmx",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL7_1_COMPAT */ \ ++ .driver = "qemu64" "-" TYPE_X86_CPU,\ ++ .property = "min-level",\ ++ .value = stringify(4),\ ++ },{ /* PC_RHEL7_1_COMPAT */ \ ++ .driver = "kvm64" "-" TYPE_X86_CPU,\ ++ .property = "min-level",\ ++ .value = stringify(5),\ ++ },{ /* PC_RHEL7_1_COMPAT */ \ ++ .driver = "pentium3" "-" TYPE_X86_CPU,\ ++ .property = "min-level",\ ++ .value = stringify(2),\ ++ },{ /* PC_RHEL7_1_COMPAT */ \ ++ .driver = "n270" "-" TYPE_X86_CPU,\ ++ .property = "min-level",\ ++ .value = stringify(5),\ ++ },{ /* PC_RHEL7_1_COMPAT */ \ ++ .driver = "Conroe" "-" TYPE_X86_CPU,\ ++ .property = "min-level",\ ++ .value = stringify(4),\ ++ },{ /* PC_RHEL7_1_COMPAT */ \ ++ .driver = "Penryn" "-" TYPE_X86_CPU,\ ++ .property = "min-level",\ ++ .value = stringify(4),\ ++ },{ /* PC_RHEL7_1_COMPAT */ \ ++ .driver = "Nehalem" "-" TYPE_X86_CPU,\ ++ .property = "min-level",\ ++ .value = stringify(4),\ ++ },{ /* PC_RHEL7_1_COMPAT */ \ ++ .driver = "n270" "-" TYPE_X86_CPU,\ ++ .property = "min-xlevel",\ ++ .value = stringify(0x8000000a),\ ++ },{ /* PC_RHEL7_1_COMPAT */ \ ++ .driver = "Penryn" "-" TYPE_X86_CPU,\ ++ .property = "min-xlevel",\ ++ .value = stringify(0x8000000a),\ ++ },{ /* PC_RHEL7_1_COMPAT */ \ ++ .driver = "Conroe" "-" TYPE_X86_CPU,\ ++ .property = "min-xlevel",\ ++ .value = stringify(0x8000000a),\ ++ },{ /* PC_RHEL7_1_COMPAT */ \ ++ .driver = "Nehalem" "-" TYPE_X86_CPU,\ ++ .property = "min-xlevel",\ ++ .value = stringify(0x8000000a),\ ++ },{ /* PC_RHEL7_1_COMPAT */ \ ++ .driver = "Westmere" "-" TYPE_X86_CPU,\ ++ .property = "min-xlevel",\ ++ .value = stringify(0x8000000a),\ ++ },{ /* PC_RHEL7_1_COMPAT */ \ ++ .driver = "SandyBridge" "-" TYPE_X86_CPU,\ ++ .property = "min-xlevel",\ ++ .value = stringify(0x8000000a),\ ++ },{ /* PC_RHEL7_1_COMPAT */ \ ++ .driver = "IvyBridge" "-" TYPE_X86_CPU,\ ++ .property = "min-xlevel",\ ++ .value = stringify(0x8000000a),\ ++ },{ /* PC_RHEL7_1_COMPAT */ \ ++ .driver = "Haswell" "-" TYPE_X86_CPU,\ ++ .property = "min-xlevel",\ ++ .value = stringify(0x8000000a),\ ++ },{ /* PC_RHEL7_1_COMPAT */ \ ++ .driver = "Haswell-noTSX" "-" TYPE_X86_CPU,\ ++ .property = "min-xlevel",\ ++ .value = stringify(0x8000000a),\ ++ },{ /* PC_RHEL7_1_COMPAT */ \ ++ .driver = "Broadwell" "-" TYPE_X86_CPU,\ ++ .property = "min-xlevel",\ ++ .value = stringify(0x8000000a),\ ++ },{ /* PC_RHEL7_1_COMPAT */ \ ++ .driver = "Broadwell-noTSX" "-" TYPE_X86_CPU,\ ++ .property = "min-xlevel",\ ++ .value = stringify(0x8000000a),\ ++ }, ++ ++/* ++ * The PC_RHEL_*_COMPAT serve the same purpose for RHEL-7 machine ++ * types as the PC_COMPAT_* do for upstream types. ++ * PC_RHEL_7_*_COMPAT apply both to i440fx and q35 types. ++ * PC_RHEL6_*_COMPAT apply to i440fx types only, and therefore live ++ * in pc_piix.c. ++ */ ++ ++/* ++ * RHEL-7 is based on QEMU 1.5.3, so this needs the PC_COMPAT_* ++ * between our base and 1.5, less stuff backported to RHEL-7.0 ++ * (usb-device.msos-desc), less stuff for devices we changed ++ * (qemu64-x86_64-cpu) or don't support (hpet, pci-serial-2x, ++ * pci-serial-4x) in 7.0. ++ */ ++#define PC_RHEL7_0_COMPAT \ ++ {\ ++ .driver = "virtio-scsi-pci",\ ++ .property = "any_layout",\ ++ .value = "off",\ ++ },{\ ++ .driver = "PIIX4_PM",\ ++ .property = "memory-hotplug-support",\ ++ .value = "off",\ ++ },{\ ++ .driver = "apic",\ ++ .property = "version",\ ++ .value = stringify(0x11),\ ++ },{\ ++ .driver = "nec-usb-xhci",\ ++ .property = "superspeed-ports-first",\ ++ .value = "off",\ ++ },{\ ++ .driver = "nec-usb-xhci",\ ++ .property = "force-pcie-endcap",\ ++ .value = "on",\ ++ },{\ ++ .driver = "pci-serial",\ ++ .property = "prog_if",\ ++ .value = stringify(0),\ ++ },{\ ++ .driver = "virtio-net-pci",\ ++ .property = "guest_announce",\ ++ .value = "off",\ ++ },{\ ++ .driver = "ICH9-LPC",\ ++ .property = "memory-hotplug-support",\ ++ .value = "off",\ ++ },{\ ++ .driver = "xio3130-downstream",\ ++ .property = COMPAT_PROP_PCP,\ ++ .value = "off",\ ++ },{\ ++ .driver = "ioh3420",\ ++ .property = COMPAT_PROP_PCP,\ ++ .value = "off",\ ++ },{\ ++ .driver = "PIIX4_PM",\ ++ .property = "acpi-pci-hotplug-with-bridge-support",\ ++ .value = "off",\ ++ },{\ ++ .driver = "e1000",\ ++ .property = "mitigation",\ ++ .value = "off",\ ++ },{ \ ++ .driver = "virtio-net-pci", \ ++ .property = "ctrl_guest_offloads", \ ++ .value = "off", \ ++ },\ ++ {\ ++ .driver = "Conroe" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Penryn" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Nehalem" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL7_0_COMPAT (copied from the entry above) */ \ ++ .driver = "Nehalem-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Westmere" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "on",\ ++ },\ ++ { /* PC_RHEL7_0_COMPAT (copied from the entry above) */ \ ++ .driver = "Westmere-IBRS" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Opteron_G1" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Opteron_G2" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Opteron_G3" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Opteron_G4" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "on",\ ++ },\ ++ {\ ++ .driver = "Opteron_G5" "-" TYPE_X86_CPU,\ ++ .property = "x2apic",\ ++ .value = "on",\ ++ }, + #endif +diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h +index d60b7c6..5118af6 100644 +--- a/include/hw/ppc/spapr.h ++++ b/include/hw/ppc/spapr.h +@@ -98,6 +98,7 @@ struct sPAPRMachineClass { + bool dr_lmb_enabled; /* enable dynamic-reconfig/hotplug of LMBs */ + bool use_ohci_by_default; /* use USB-OHCI instead of XHCI */ + bool pre_2_10_has_unused_icps; ++ bool has_power9_support; + void (*phb_placement)(sPAPRMachineState *spapr, uint32_t index, + uint64_t *buid, hwaddr *pio, + hwaddr *mmio32, hwaddr *mmio64, +diff --git a/include/hw/usb.h b/include/hw/usb.h +index a5080ad..5b3fb1f 100644 +--- a/include/hw/usb.h ++++ b/include/hw/usb.h +@@ -606,4 +606,11 @@ int usb_get_quirks(uint16_t vendor_id, uint16_t product_id, + uint8_t interface_class, uint8_t interface_subclass, + uint8_t interface_protocol); + ++ ++/* hcd-uhci.c -- RHEL-6 machine type compatibility */ ++extern bool ich9_uhci123_irqpin_override; ++ ++/* hcd-xhci.c -- rhel7.0.0 machine type compatibility */ ++extern bool migrate_cve_2014_5263_xhci_fields; ++ + #endif +diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h +index 098bdaa..41e13d2 100644 +--- a/include/hw/virtio/virtio.h ++++ b/include/hw/virtio/virtio.h +@@ -95,6 +95,7 @@ struct VirtIODevice + uint8_t device_endian; + bool use_guest_notifier_mask; + AddressSpace *dma_as; ++ bool rhel6_ctrl_guest_workaround; + QLIST_HEAD(, VirtQueue) *vector_queues; + }; + +diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h +index 2b42151..5832c38 100644 +--- a/include/sysemu/sysemu.h ++++ b/include/sysemu/sysemu.h +@@ -94,6 +94,8 @@ void qemu_add_machine_init_done_notifier(Notifier *notify); + void qemu_remove_machine_init_done_notifier(Notifier *notify); + + void qemu_announce_self(void); ++extern bool shadow_bios_after_incoming; ++void shadow_bios(void); + + extern int autostart; + +diff --git a/migration/migration.c b/migration/migration.c +index 52a5092..ceb1697 100644 +--- a/migration/migration.c ++++ b/migration/migration.c +@@ -99,6 +99,8 @@ enum mig_rp_message_type { + MIG_RP_MSG_MAX + }; + ++bool migrate_pre_2_2; ++ + /* When we add fault tolerance, we could have several + migrations at once. For now we don't need to add + dynamic creation of migration */ +diff --git a/migration/migration.h b/migration/migration.h +index 8d2f320..06833d7 100644 +--- a/migration/migration.h ++++ b/migration/migration.h +@@ -241,5 +241,10 @@ int migrate_send_rp_req_pages(MigrationIncomingState *mis, const char* rbname, + + void dirty_bitmap_mig_before_vm_start(void); + void init_dirty_bitmap_incoming_migration(void); ++/* ++ * Disables a load of subsections that were added in 2.2/rh7.2 for backwards ++ * migration compatibility. ++ */ ++extern bool migrate_pre_2_2; + + #endif +diff --git a/migration/savevm.c b/migration/savevm.c +index e2be02a..56c9feb 100644 +--- a/migration/savevm.c ++++ b/migration/savevm.c +@@ -45,6 +45,7 @@ + #include "qapi/qapi-commands-misc.h" + #include "qapi/qmp/qerror.h" + #include "qemu/error-report.h" ++#include "qemu/rcu_queue.h" + #include "sysemu/cpus.h" + #include "exec/memory.h" + #include "exec/target_page.h" +@@ -83,6 +84,7 @@ enum qemu_vm_cmd { + MIG_CMD_PACKAGED, /* Send a wrapped stream within this stream */ + MIG_CMD_MAX + }; ++bool shadow_bios_after_incoming; + + #define MAX_VM_CMD_PACKAGED_SIZE UINT32_MAX + static struct mig_cmd_args { +@@ -2204,6 +2206,13 @@ int qemu_loadvm_state(QEMUFile *f) + } + + qemu_loadvm_state_cleanup(); ++ /* Supplement SeaBIOS's shadowing now, because it was useless when the ++ * incoming VM started on the RHEL-6 emulator. ++ */ ++ if (shadow_bios_after_incoming) { ++ shadow_bios(); ++ } ++ + cpu_synchronize_all_post_init(); + + return ret; +diff --git a/numa.c b/numa.c +index 1116c90..daf10d8 100644 +--- a/numa.c ++++ b/numa.c +@@ -493,6 +493,19 @@ void memory_region_allocate_system_memory(MemoryRegion *mr, Object *owner, + return; + } + ++ /* The shadow_bios_after_incoming hack at savevm.c:shadow_bios() is not ++ * able to handle the multiple memory blocks added when using NUMA ++ * memdevs. We can disallow -numa memdev= when using rhel6.* machine-types ++ * because RHEL-6 didn't support the NUMA memdev option. ++ */ ++ if (shadow_bios_after_incoming) { ++ MachineClass *mc; ++ mc = MACHINE_GET_CLASS(current_machine); ++ error_report("-numa memdev is not supported by machine %s", ++ mc->name); ++ exit(1); ++ } ++ + memory_region_init(mr, owner, name, ram_size); + for (i = 0; i < nb_numa_nodes; i++) { + uint64_t size = numa_info[i].node_mem; +diff --git a/qdev-monitor.c b/qdev-monitor.c +index 61e0300..f439b83 100644 +--- a/qdev-monitor.c ++++ b/qdev-monitor.c +@@ -47,7 +47,6 @@ typedef struct QDevAlias + + /* Please keep this table sorted by typename. */ + static const QDevAlias qdev_alias_table[] = { +- { "e1000", "e1000-82540em" }, + { "ich9-ahci", "ahci" }, + { "lsi53c895a", "lsi" }, + { "virtio-9p-ccw", "virtio-9p", QEMU_ARCH_S390X }, +diff --git a/scripts/vmstate-static-checker.py b/scripts/vmstate-static-checker.py +index bcef7ee..ffb13d1 100755 +--- a/scripts/vmstate-static-checker.py ++++ b/scripts/vmstate-static-checker.py +@@ -104,7 +104,6 @@ def get_changed_sec_name(sec): + # Section names can change -- see commit 292b1634 for an example. + changes = { + "ICH9 LPC": "ICH9-LPC", +- "e1000-82540em": "e1000", + } + + for item in changes: +diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs +index dfdfca7..8f111c5 100644 +--- a/stubs/Makefile.objs ++++ b/stubs/Makefile.objs +@@ -44,3 +44,4 @@ stub-obj-y += xen-hvm.o + stub-obj-y += pci-host-piix.o + stub-obj-y += ram-block.o + stub-obj-y += ide-isa.o ++stub-obj-y += shadow-bios.o +diff --git a/stubs/shadow-bios.c b/stubs/shadow-bios.c +new file mode 100644 +index 0000000..c77cd7a +--- /dev/null ++++ b/stubs/shadow-bios.c +@@ -0,0 +1,7 @@ ++#include "qemu/osdep.h" ++#include "sysemu/sysemu.h" ++ ++void shadow_bios(void) ++{ ++ abort(); ++} +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index f483a71..a9db495 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -819,11 +819,17 @@ struct X86CPUDefinition { + + static X86CPUDefinition builtin_x86_defs[] = { + { ++ /* qemu64 is the default CPU model for all *-rhel7.* machine-types. ++ * The default on RHEL-6 was cpu64-rhel6. ++ * libvirt assumes that qemu64 is the default for _all_ machine-types, ++ * so we should try to keep qemu64 and cpu64-rhel6 as similar as ++ * possible. ++ */ + .name = "qemu64", + .level = 0xd, + .vendor = CPUID_VENDOR_AMD, + .family = 6, +- .model = 6, ++ .model = 13, + .stepping = 3, + .features[FEAT_1_EDX] = CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | + CPUID_MMX | CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | +@@ -2097,6 +2103,7 @@ static PropValue kvm_default_props[] = { + { "acpi", "off" }, + { "monitor", "off" }, + { "svm", "off" }, ++ { "kvm-pv-unhalt", "on" }, + { NULL, NULL }, + }; + +diff --git a/target/i386/machine.c b/target/i386/machine.c +index bd2d82e..c9a3b5c 100644 +--- a/target/i386/machine.c ++++ b/target/i386/machine.c +@@ -893,6 +893,26 @@ static const VMStateDescription vmstate_msr_intel_pt = { + } + }; + ++static bool vmstate_xsave_needed(void *opaque) ++{ ++ /* The xsave state is already on the main "cpu" section */ ++ return false; ++} ++ ++static const VMStateDescription vmstate_xsave ={ ++ .name = "cpu/xsave", ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .minimum_version_id_old = 1, ++ .needed = vmstate_xsave_needed, ++ .fields = (VMStateField []) { ++ VMSTATE_UINT64_V(env.xcr0, X86CPU, 1), ++ VMSTATE_UINT64_V(env.xstate_bv, X86CPU, 1), ++ VMSTATE_YMMH_REGS_VARS(env.xmm_regs, X86CPU, CPU_NB_REGS, 1), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ + VMStateDescription vmstate_x86_cpu = { + .name = "cpu", + .version_id = 12, +@@ -1015,6 +1035,7 @@ VMStateDescription vmstate_x86_cpu = { + &vmstate_spec_ctrl, + &vmstate_mcg_ext_ctl, + &vmstate_msr_intel_pt, ++ &vmstate_xsave, + NULL + } + }; +diff --git a/target/ppc/compat.c b/target/ppc/compat.c +index 807c906..33658fb 100644 +--- a/target/ppc/compat.c ++++ b/target/ppc/compat.c +@@ -105,6 +105,17 @@ static const CompatInfo *compat_by_pvr(uint32_t pvr) + return NULL; + } + ++long ppc_compat_cmp(uint32_t pvr1, uint32_t pvr2) ++{ ++ const CompatInfo *compat1 = compat_by_pvr(pvr1); ++ const CompatInfo *compat2 = compat_by_pvr(pvr2); ++ ++ g_assert(compat1); ++ g_assert(compat2); ++ ++ return compat1 - compat2; ++} ++ + bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr, + uint32_t min_compat_pvr, uint32_t max_compat_pvr) + { +diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h +index c621a6b..1932c2e 100644 +--- a/target/ppc/cpu.h ++++ b/target/ppc/cpu.h +@@ -1391,6 +1391,7 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch) + + /* Compatibility modes */ + #if defined(TARGET_PPC64) ++long ppc_compat_cmp(uint32_t pvr1, uint32_t pvr2); + bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr, + uint32_t min_compat_pvr, uint32_t max_compat_pvr); + void ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp); +diff --git a/tests/Makefile.include b/tests/Makefile.include +index e4125a6..5cb1902 100644 +--- a/tests/Makefile.include ++++ b/tests/Makefile.include +@@ -310,14 +310,14 @@ check-qtest-ppc-y += tests/boot-order-test$(EXESUF) + #check-qtest-ppc-y += tests/prom-env-test$(EXESUF) + check-qtest-ppc-y += tests/drive_del-test$(EXESUF) + check-qtest-ppc-y += tests/boot-serial-test$(EXESUF) +-check-qtest-ppc-y += tests/m48t59-test$(EXESUF) +-gcov-files-ppc-y += hw/timer/m48t59.c ++#check-qtest-ppc-y += tests/m48t59-test$(EXESUF) ++#gcov-files-ppc-y += hw/timer/m48t59.c + + check-qtest-ppc64-y = $(check-qtest-ppc-y) + gcov-files-ppc64-y = $(subst ppc-softmmu/,ppc64-softmmu/,$(gcov-files-ppc-y)) + check-qtest-ppc64-y += tests/spapr-phb-test$(EXESUF) + gcov-files-ppc64-y += ppc64-softmmu/hw/ppc/spapr_pci.c +-check-qtest-ppc64-y += tests/pnv-xscom-test$(EXESUF) ++#check-qtest-ppc64-y += tests/pnv-xscom-test$(EXESUF) + check-qtest-ppc64-y += tests/migration-test$(EXESUF) + check-qtest-ppc64-y += tests/rtas-test$(EXESUF) + check-qtest-ppc64-$(CONFIG_SLIRP) += tests/pxe-test$(EXESUF) +@@ -733,7 +733,7 @@ libqos-virtio-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/virt + tests/qmp-test$(EXESUF): tests/qmp-test.o + tests/device-introspect-test$(EXESUF): tests/device-introspect-test.o + tests/rtc-test$(EXESUF): tests/rtc-test.o +-tests/m48t59-test$(EXESUF): tests/m48t59-test.o ++#tests/m48t59-test$(EXESUF): tests/m48t59-test.o + tests/endianness-test$(EXESUF): tests/endianness-test.o + tests/spapr-phb-test$(EXESUF): tests/spapr-phb-test.o $(libqos-obj-y) + #tests/prom-env-test$(EXESUF): tests/prom-env-test.o $(libqos-obj-y) +diff --git a/tests/cpu-plug-test.c b/tests/cpu-plug-test.c +index 8b5ab1f..112869b 100644 +--- a/tests/cpu-plug-test.c ++++ b/tests/cpu-plug-test.c +@@ -192,7 +192,8 @@ static void add_pseries_test_case(const char *mname) + PlugTestData *data; + + if (!g_str_has_prefix(mname, "pseries-") || +- (g_str_has_prefix(mname, "pseries-2.") && atoi(&mname[10]) < 7)) { ++ (g_str_has_prefix(mname, "pseries-2.") && atoi(&mname[10]) < 7) || ++ strcmp(mname,"pseries-rhel7.2.0") == 0) { + return; + } + data = g_new(PlugTestData, 1); +-- +1.8.3.1 + diff --git a/SOURCES/0006-Revert-kvm_stat-Remove.patch b/SOURCES/0006-Revert-kvm_stat-Remove.patch new file mode 100644 index 0000000..fe519e9 --- /dev/null +++ b/SOURCES/0006-Revert-kvm_stat-Remove.patch @@ -0,0 +1,1815 @@ +From 49ebd26734d492cc4189b97704052780b3641854 Mon Sep 17 00:00:00 2001 +From: "Danilo C. L. de Paula" +Date: Mon, 16 Jan 2017 11:52:49 +0100 +Subject: Revert "kvm_stat: Remove" + +RH-Author: ddepaula +Message-id: <1479302806-10135-2-git-send-email-ddepaula@redhat.com> +Patchwork-id: 72851 +O-Subject: [RHEV-7.4 qemu-kvm-rhev PATCH v3 1/3] Revert "kvm_stat: Remove" +Bugzilla: 1389238 +RH-Acked-by: John Snow +RH-Acked-by: David Hildenbrand +RH-Acked-by: Miroslav Rezanina + +kvm_stat script was removed in QEMU 2.7.0 as it become part of kernel +tree. However kvm_stat is shipped in qemu-kvm-tools package in RHEL. + +This reverts commit 60b412dd18362bd4ddc44ba7022aacb6af074b5d. + +Signed-off-by: Danilo Cesar Lemes de Paula +Signed-off-by: Miroslav Rezanina + +Merged patches (2.11.0): +- beffa30342 tools/kvm_stat: hide cursor +- 98cf56caaa tools/kvm_stat: catch curses exceptions only +- dada85e20a tools/kvm_stat: handle SIGINT in log and batch modes +- 8654f68785 tools/kvm_stat: fix misc glitches +- d4525d7460 tools/kvm_stat: fix trace setup glitch on field updates in TracepointProvider +- 5f41d9716a tools/kvm_stat: full PEP8 compliance +- 5bf653923c tools/kvm_stat: reduce perceived idle time on filter updates +- 2ef292f12d tools/kvm_stat: document list of interactive commands +- 3e1d2fc34c tools/kvm_stat: display guest name when using pid filter +- 66f2e8203e tools/kvm_stat: remove pid filter on empty input +- 05cc1c1aab tools/kvm_stat: print error messages on faulty pid filter input +- a0c6451760 tools/kvm_stat: display regex when set to non-default +- 3c7dae7507 tools/kvm_stat: remove regex filter on empty input +- 8b7b31fc24 tools/kvm_stat: add option '--guest' +- 38362f9d75 tools/kvm_stat: add interactive command 'c' +- 243d90a041 tools/kvm_stat: add interactive command 'r' +- 4f80c9081b tools/kvm_stat: add '%Total' column +- e21fb4a96e tools/kvm_stat: fix typo +- 7bc419ee18 tools/kvm_stat: fix event counts display for interrupted intervals +- d54183dd4b tools/kvm_stat: fix undue use of initial sleeptime +- 6514729674 tools/kvm_stat: remove unnecessary header redraws +- c4f941df75 tools/kvm_stat: simplify line print logic +- 35c050e4fd tools/kvm_stat: removed unused function +- 92d06e843c tools/kvm_stat: remove extra statement +- d0ced131bf tools/kvm_stat: simplify initializers +- 1dbc0659ac tools/kvm_stat: move functions to corresponding classes +- bf53d77372 tools/kvm_stat: show cursor in selection screens +- 6b557dc235 tools/kvm_stat: display message indicating lack of events +- 7f50a8be62 tools/kvm_stat: make heading look a bit more like 'top' +- 8ba2fd89d1 tools/kvm_stat: rename 'Current' column to 'CurAvg/s' +- 95b5c46145 tools/kvm_stat: add new interactive command 'h' +- 25c56e3212 tools/kvm_stat: add new interactive command 's' +- 55f7ed9b2c tools/kvm_stat: add new interactive command 'o' +- 53a1267b00 tools/kvm_stat: display guest list in pid/guest selection screens +- 554fa10df5 tools/kvm_stat: display guest list in pid/guest selection screens +- 72f660ddfc tools/kvm_stat: add new command line switch '-i' +- 17165a0bc8 tools/kvm_stat: add new interactive command 'b' +- ac6ba9a14b tools/kvm_stat: use variables instead of hard paths in help output +- 592b801e5c tools/kvm_stat: add '-f help' to get the available event list +- 283aa25f75 tools/kvm_stat: fix command line option '-g' + +Merged patches (2.9.0): +- 1e69b1b Include kvm_stat in qemu-kvm.spec +- 7fcfc94 tools: kvm_stat: Powerpc related fixes +- 7f89136 tools: kvm_stat: Introduce pid monitoring +- c728a6b tools: kvm_stat: Add comments +- 27fb856 Package man page of "kvm_stat" tool + +(cherry picked from commit d4a8e35b84072816c79e23f5d0a69a2145217004) +(cherry picked from commit e0425f69f136a05a59ee5cb7022409ed2be94a0b) +(cherry picked from commit 57e760aee8b28f3b2c6b9f3aad0d2b916dd13595) +(cherry picked from commit d711afa1b8ef8aba1397158626816008936ed0d6) +(cherry picked from commit 8ceca7bebce67440898c22d058047daa8ec37031) +--- + Makefile | 8 + + redhat/qemu-kvm.spec.template | 7 +- + scripts/kvm/kvm_stat | 1585 +++++++++++++++++++++++++++++++++++++++++ + scripts/kvm/kvm_stat.texi | 104 +++ + 4 files changed, 1703 insertions(+), 1 deletion(-) + create mode 100755 scripts/kvm/kvm_stat + create mode 100644 scripts/kvm/kvm_stat.texi + +diff --git a/Makefile b/Makefile +index 89ba4c5..2ffac2b 100644 +--- a/Makefile ++++ b/Makefile +@@ -354,6 +354,9 @@ DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8 + DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7 + DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7 + DOCS+=docs/qemu-block-drivers.7 ++ifdef CONFIG_LINUX ++DOCS+=kvm_stat.1 ++endif + ifdef CONFIG_VIRTFS + DOCS+=fsdev/virtfs-proxy-helper.1 + endif +@@ -955,6 +958,11 @@ html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html + info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info + pdf: qemu-doc.pdf docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf + txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt ++kvm_stat.1: scripts/kvm/kvm_stat.texi ++ $(call quiet-command, \ ++ perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< kvm_stat.pod && \ ++ $(POD2MAN) --section=1 --center=" " --release=" " kvm_stat.pod > $@, \ ++ " GEN $@") + + qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \ + qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \ +diff --git a/scripts/kvm/kvm_stat b/scripts/kvm/kvm_stat +new file mode 100755 +index 0000000..c74a9a0 +--- /dev/null ++++ b/scripts/kvm/kvm_stat +@@ -0,0 +1,1585 @@ ++#!/usr/bin/python ++# ++# top-like utility for displaying kvm statistics ++# ++# Copyright 2006-2008 Qumranet Technologies ++# Copyright 2008-2011 Red Hat, Inc. ++# ++# Authors: ++# Avi Kivity ++# ++# This work is licensed under the terms of the GNU GPL, version 2. See ++# the COPYING file in the top-level directory. ++"""The kvm_stat module outputs statistics about running KVM VMs ++ ++Three different ways of output formatting are available: ++- as a top-like text ui ++- in a key -> value format ++- in an all keys, all values format ++ ++The data is sampled from the KVM's debugfs entries and its perf events. ++""" ++ ++import curses ++import sys ++import os ++import time ++import optparse ++import ctypes ++import fcntl ++import resource ++import struct ++import re ++import subprocess ++from collections import defaultdict ++ ++VMX_EXIT_REASONS = { ++ 'EXCEPTION_NMI': 0, ++ 'EXTERNAL_INTERRUPT': 1, ++ 'TRIPLE_FAULT': 2, ++ 'PENDING_INTERRUPT': 7, ++ 'NMI_WINDOW': 8, ++ 'TASK_SWITCH': 9, ++ 'CPUID': 10, ++ 'HLT': 12, ++ 'INVLPG': 14, ++ 'RDPMC': 15, ++ 'RDTSC': 16, ++ 'VMCALL': 18, ++ 'VMCLEAR': 19, ++ 'VMLAUNCH': 20, ++ 'VMPTRLD': 21, ++ 'VMPTRST': 22, ++ 'VMREAD': 23, ++ 'VMRESUME': 24, ++ 'VMWRITE': 25, ++ 'VMOFF': 26, ++ 'VMON': 27, ++ 'CR_ACCESS': 28, ++ 'DR_ACCESS': 29, ++ 'IO_INSTRUCTION': 30, ++ 'MSR_READ': 31, ++ 'MSR_WRITE': 32, ++ 'INVALID_STATE': 33, ++ 'MWAIT_INSTRUCTION': 36, ++ 'MONITOR_INSTRUCTION': 39, ++ 'PAUSE_INSTRUCTION': 40, ++ 'MCE_DURING_VMENTRY': 41, ++ 'TPR_BELOW_THRESHOLD': 43, ++ 'APIC_ACCESS': 44, ++ 'EPT_VIOLATION': 48, ++ 'EPT_MISCONFIG': 49, ++ 'WBINVD': 54, ++ 'XSETBV': 55, ++ 'APIC_WRITE': 56, ++ 'INVPCID': 58, ++} ++ ++SVM_EXIT_REASONS = { ++ 'READ_CR0': 0x000, ++ 'READ_CR3': 0x003, ++ 'READ_CR4': 0x004, ++ 'READ_CR8': 0x008, ++ 'WRITE_CR0': 0x010, ++ 'WRITE_CR3': 0x013, ++ 'WRITE_CR4': 0x014, ++ 'WRITE_CR8': 0x018, ++ 'READ_DR0': 0x020, ++ 'READ_DR1': 0x021, ++ 'READ_DR2': 0x022, ++ 'READ_DR3': 0x023, ++ 'READ_DR4': 0x024, ++ 'READ_DR5': 0x025, ++ 'READ_DR6': 0x026, ++ 'READ_DR7': 0x027, ++ 'WRITE_DR0': 0x030, ++ 'WRITE_DR1': 0x031, ++ 'WRITE_DR2': 0x032, ++ 'WRITE_DR3': 0x033, ++ 'WRITE_DR4': 0x034, ++ 'WRITE_DR5': 0x035, ++ 'WRITE_DR6': 0x036, ++ 'WRITE_DR7': 0x037, ++ 'EXCP_BASE': 0x040, ++ 'INTR': 0x060, ++ 'NMI': 0x061, ++ 'SMI': 0x062, ++ 'INIT': 0x063, ++ 'VINTR': 0x064, ++ 'CR0_SEL_WRITE': 0x065, ++ 'IDTR_READ': 0x066, ++ 'GDTR_READ': 0x067, ++ 'LDTR_READ': 0x068, ++ 'TR_READ': 0x069, ++ 'IDTR_WRITE': 0x06a, ++ 'GDTR_WRITE': 0x06b, ++ 'LDTR_WRITE': 0x06c, ++ 'TR_WRITE': 0x06d, ++ 'RDTSC': 0x06e, ++ 'RDPMC': 0x06f, ++ 'PUSHF': 0x070, ++ 'POPF': 0x071, ++ 'CPUID': 0x072, ++ 'RSM': 0x073, ++ 'IRET': 0x074, ++ 'SWINT': 0x075, ++ 'INVD': 0x076, ++ 'PAUSE': 0x077, ++ 'HLT': 0x078, ++ 'INVLPG': 0x079, ++ 'INVLPGA': 0x07a, ++ 'IOIO': 0x07b, ++ 'MSR': 0x07c, ++ 'TASK_SWITCH': 0x07d, ++ 'FERR_FREEZE': 0x07e, ++ 'SHUTDOWN': 0x07f, ++ 'VMRUN': 0x080, ++ 'VMMCALL': 0x081, ++ 'VMLOAD': 0x082, ++ 'VMSAVE': 0x083, ++ 'STGI': 0x084, ++ 'CLGI': 0x085, ++ 'SKINIT': 0x086, ++ 'RDTSCP': 0x087, ++ 'ICEBP': 0x088, ++ 'WBINVD': 0x089, ++ 'MONITOR': 0x08a, ++ 'MWAIT': 0x08b, ++ 'MWAIT_COND': 0x08c, ++ 'XSETBV': 0x08d, ++ 'NPF': 0x400, ++} ++ ++# EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h) ++AARCH64_EXIT_REASONS = { ++ 'UNKNOWN': 0x00, ++ 'WFI': 0x01, ++ 'CP15_32': 0x03, ++ 'CP15_64': 0x04, ++ 'CP14_MR': 0x05, ++ 'CP14_LS': 0x06, ++ 'FP_ASIMD': 0x07, ++ 'CP10_ID': 0x08, ++ 'CP14_64': 0x0C, ++ 'ILL_ISS': 0x0E, ++ 'SVC32': 0x11, ++ 'HVC32': 0x12, ++ 'SMC32': 0x13, ++ 'SVC64': 0x15, ++ 'HVC64': 0x16, ++ 'SMC64': 0x17, ++ 'SYS64': 0x18, ++ 'IABT': 0x20, ++ 'IABT_HYP': 0x21, ++ 'PC_ALIGN': 0x22, ++ 'DABT': 0x24, ++ 'DABT_HYP': 0x25, ++ 'SP_ALIGN': 0x26, ++ 'FP_EXC32': 0x28, ++ 'FP_EXC64': 0x2C, ++ 'SERROR': 0x2F, ++ 'BREAKPT': 0x30, ++ 'BREAKPT_HYP': 0x31, ++ 'SOFTSTP': 0x32, ++ 'SOFTSTP_HYP': 0x33, ++ 'WATCHPT': 0x34, ++ 'WATCHPT_HYP': 0x35, ++ 'BKPT32': 0x38, ++ 'VECTOR32': 0x3A, ++ 'BRK64': 0x3C, ++} ++ ++# From include/uapi/linux/kvm.h, KVM_EXIT_xxx ++USERSPACE_EXIT_REASONS = { ++ 'UNKNOWN': 0, ++ 'EXCEPTION': 1, ++ 'IO': 2, ++ 'HYPERCALL': 3, ++ 'DEBUG': 4, ++ 'HLT': 5, ++ 'MMIO': 6, ++ 'IRQ_WINDOW_OPEN': 7, ++ 'SHUTDOWN': 8, ++ 'FAIL_ENTRY': 9, ++ 'INTR': 10, ++ 'SET_TPR': 11, ++ 'TPR_ACCESS': 12, ++ 'S390_SIEIC': 13, ++ 'S390_RESET': 14, ++ 'DCR': 15, ++ 'NMI': 16, ++ 'INTERNAL_ERROR': 17, ++ 'OSI': 18, ++ 'PAPR_HCALL': 19, ++ 'S390_UCONTROL': 20, ++ 'WATCHDOG': 21, ++ 'S390_TSCH': 22, ++ 'EPR': 23, ++ 'SYSTEM_EVENT': 24, ++} ++ ++IOCTL_NUMBERS = { ++ 'SET_FILTER': 0x40082406, ++ 'ENABLE': 0x00002400, ++ 'DISABLE': 0x00002401, ++ 'RESET': 0x00002403, ++} ++ ++ ++class Arch(object): ++ """Encapsulates global architecture specific data. ++ ++ Contains the performance event open syscall and ioctl numbers, as ++ well as the VM exit reasons for the architecture it runs on. ++ ++ """ ++ @staticmethod ++ def get_arch(): ++ machine = os.uname()[4] ++ ++ if machine.startswith('ppc'): ++ return ArchPPC() ++ elif machine.startswith('aarch64'): ++ return ArchA64() ++ elif machine.startswith('s390'): ++ return ArchS390() ++ else: ++ # X86_64 ++ for line in open('/proc/cpuinfo'): ++ if not line.startswith('flags'): ++ continue ++ ++ flags = line.split() ++ if 'vmx' in flags: ++ return ArchX86(VMX_EXIT_REASONS) ++ if 'svm' in flags: ++ return ArchX86(SVM_EXIT_REASONS) ++ return ++ ++ ++class ArchX86(Arch): ++ def __init__(self, exit_reasons): ++ self.sc_perf_evt_open = 298 ++ self.ioctl_numbers = IOCTL_NUMBERS ++ self.exit_reasons = exit_reasons ++ ++ ++class ArchPPC(Arch): ++ def __init__(self): ++ self.sc_perf_evt_open = 319 ++ self.ioctl_numbers = IOCTL_NUMBERS ++ self.ioctl_numbers['ENABLE'] = 0x20002400 ++ self.ioctl_numbers['DISABLE'] = 0x20002401 ++ self.ioctl_numbers['RESET'] = 0x20002403 ++ ++ # PPC comes in 32 and 64 bit and some generated ioctl ++ # numbers depend on the wordsize. ++ char_ptr_size = ctypes.sizeof(ctypes.c_char_p) ++ self.ioctl_numbers['SET_FILTER'] = 0x80002406 | char_ptr_size << 16 ++ self.exit_reasons = {} ++ ++ ++class ArchA64(Arch): ++ def __init__(self): ++ self.sc_perf_evt_open = 241 ++ self.ioctl_numbers = IOCTL_NUMBERS ++ self.exit_reasons = AARCH64_EXIT_REASONS ++ ++ ++class ArchS390(Arch): ++ def __init__(self): ++ self.sc_perf_evt_open = 331 ++ self.ioctl_numbers = IOCTL_NUMBERS ++ self.exit_reasons = None ++ ++ARCH = Arch.get_arch() ++ ++ ++class perf_event_attr(ctypes.Structure): ++ """Struct that holds the necessary data to set up a trace event. ++ ++ For an extensive explanation see perf_event_open(2) and ++ include/uapi/linux/perf_event.h, struct perf_event_attr ++ ++ All fields that are not initialized in the constructor are 0. ++ ++ """ ++ _fields_ = [('type', ctypes.c_uint32), ++ ('size', ctypes.c_uint32), ++ ('config', ctypes.c_uint64), ++ ('sample_freq', ctypes.c_uint64), ++ ('sample_type', ctypes.c_uint64), ++ ('read_format', ctypes.c_uint64), ++ ('flags', ctypes.c_uint64), ++ ('wakeup_events', ctypes.c_uint32), ++ ('bp_type', ctypes.c_uint32), ++ ('bp_addr', ctypes.c_uint64), ++ ('bp_len', ctypes.c_uint64), ++ ] ++ ++ def __init__(self): ++ super(self.__class__, self).__init__() ++ self.type = PERF_TYPE_TRACEPOINT ++ self.size = ctypes.sizeof(self) ++ self.read_format = PERF_FORMAT_GROUP ++ ++ ++PERF_TYPE_TRACEPOINT = 2 ++PERF_FORMAT_GROUP = 1 << 3 ++ ++PATH_DEBUGFS_TRACING = '/sys/kernel/debug/tracing' ++PATH_DEBUGFS_KVM = '/sys/kernel/debug/kvm' ++ ++ ++class Group(object): ++ """Represents a perf event group.""" ++ ++ def __init__(self): ++ self.events = [] ++ ++ def add_event(self, event): ++ self.events.append(event) ++ ++ def read(self): ++ """Returns a dict with 'event name: value' for all events in the ++ group. ++ ++ Values are read by reading from the file descriptor of the ++ event that is the group leader. See perf_event_open(2) for ++ details. ++ ++ Read format for the used event configuration is: ++ struct read_format { ++ u64 nr; /* The number of events */ ++ struct { ++ u64 value; /* The value of the event */ ++ } values[nr]; ++ }; ++ ++ """ ++ length = 8 * (1 + len(self.events)) ++ read_format = 'xxxxxxxx' + 'Q' * len(self.events) ++ return dict(zip([event.name for event in self.events], ++ struct.unpack(read_format, ++ os.read(self.events[0].fd, length)))) ++ ++ ++class Event(object): ++ """Represents a performance event and manages its life cycle.""" ++ def __init__(self, name, group, trace_cpu, trace_pid, trace_point, ++ trace_filter, trace_set='kvm'): ++ self.libc = ctypes.CDLL('libc.so.6', use_errno=True) ++ self.syscall = self.libc.syscall ++ self.name = name ++ self.fd = None ++ self.setup_event(group, trace_cpu, trace_pid, trace_point, ++ trace_filter, trace_set) ++ ++ def __del__(self): ++ """Closes the event's file descriptor. ++ ++ As no python file object was created for the file descriptor, ++ python will not reference count the descriptor and will not ++ close it itself automatically, so we do it. ++ ++ """ ++ if self.fd: ++ os.close(self.fd) ++ ++ def perf_event_open(self, attr, pid, cpu, group_fd, flags): ++ """Wrapper for the sys_perf_evt_open() syscall. ++ ++ Used to set up performance events, returns a file descriptor or -1 ++ on error. ++ ++ Attributes are: ++ - syscall number ++ - struct perf_event_attr * ++ - pid or -1 to monitor all pids ++ - cpu number or -1 to monitor all cpus ++ - The file descriptor of the group leader or -1 to create a group. ++ - flags ++ ++ """ ++ return self.syscall(ARCH.sc_perf_evt_open, ctypes.pointer(attr), ++ ctypes.c_int(pid), ctypes.c_int(cpu), ++ ctypes.c_int(group_fd), ctypes.c_long(flags)) ++ ++ def setup_event_attribute(self, trace_set, trace_point): ++ """Returns an initialized ctype perf_event_attr struct.""" ++ ++ id_path = os.path.join(PATH_DEBUGFS_TRACING, 'events', trace_set, ++ trace_point, 'id') ++ ++ event_attr = perf_event_attr() ++ event_attr.config = int(open(id_path).read()) ++ return event_attr ++ ++ def setup_event(self, group, trace_cpu, trace_pid, trace_point, ++ trace_filter, trace_set): ++ """Sets up the perf event in Linux. ++ ++ Issues the syscall to register the event in the kernel and ++ then sets the optional filter. ++ ++ """ ++ ++ event_attr = self.setup_event_attribute(trace_set, trace_point) ++ ++ # First event will be group leader. ++ group_leader = -1 ++ ++ # All others have to pass the leader's descriptor instead. ++ if group.events: ++ group_leader = group.events[0].fd ++ ++ fd = self.perf_event_open(event_attr, trace_pid, ++ trace_cpu, group_leader, 0) ++ if fd == -1: ++ err = ctypes.get_errno() ++ raise OSError(err, os.strerror(err), ++ 'while calling sys_perf_event_open().') ++ ++ if trace_filter: ++ fcntl.ioctl(fd, ARCH.ioctl_numbers['SET_FILTER'], ++ trace_filter) ++ ++ self.fd = fd ++ ++ def enable(self): ++ """Enables the trace event in the kernel. ++ ++ Enabling the group leader makes reading counters from it and the ++ events under it possible. ++ ++ """ ++ fcntl.ioctl(self.fd, ARCH.ioctl_numbers['ENABLE'], 0) ++ ++ def disable(self): ++ """Disables the trace event in the kernel. ++ ++ Disabling the group leader makes reading all counters under it ++ impossible. ++ ++ """ ++ fcntl.ioctl(self.fd, ARCH.ioctl_numbers['DISABLE'], 0) ++ ++ def reset(self): ++ """Resets the count of the trace event in the kernel.""" ++ fcntl.ioctl(self.fd, ARCH.ioctl_numbers['RESET'], 0) ++ ++ ++class Provider(object): ++ """Encapsulates functionalities used by all providers.""" ++ @staticmethod ++ def is_field_wanted(fields_filter, field): ++ """Indicate whether field is valid according to fields_filter.""" ++ if not fields_filter or fields_filter == "help": ++ return True ++ return re.match(fields_filter, field) is not None ++ ++ @staticmethod ++ def walkdir(path): ++ """Returns os.walk() data for specified directory. ++ ++ As it is only a wrapper it returns the same 3-tuple of (dirpath, ++ dirnames, filenames). ++ """ ++ return next(os.walk(path)) ++ ++ ++class TracepointProvider(Provider): ++ """Data provider for the stats class. ++ ++ Manages the events/groups from which it acquires its data. ++ ++ """ ++ def __init__(self, pid, fields_filter): ++ self.group_leaders = [] ++ self.filters = self.get_filters() ++ self.update_fields(fields_filter) ++ self.pid = pid ++ ++ @staticmethod ++ def get_filters(): ++ """Returns a dict of trace events, their filter ids and ++ the values that can be filtered. ++ ++ Trace events can be filtered for special values by setting a ++ filter string via an ioctl. The string normally has the format ++ identifier==value. For each filter a new event will be created, to ++ be able to distinguish the events. ++ ++ """ ++ filters = {} ++ filters['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS) ++ if ARCH.exit_reasons: ++ filters['kvm_exit'] = ('exit_reason', ARCH.exit_reasons) ++ return filters ++ ++ def get_available_fields(self): ++ """Returns a list of available event's of format 'event name(filter ++ name)'. ++ ++ All available events have directories under ++ /sys/kernel/debug/tracing/events/ which export information ++ about the specific event. Therefore, listing the dirs gives us ++ a list of all available events. ++ ++ Some events like the vm exit reasons can be filtered for ++ specific values. To take account for that, the routine below ++ creates special fields with the following format: ++ event name(filter name) ++ ++ """ ++ path = os.path.join(PATH_DEBUGFS_TRACING, 'events', 'kvm') ++ fields = self.walkdir(path)[1] ++ extra = [] ++ for field in fields: ++ if field in self.filters: ++ filter_name_, filter_dicts = self.filters[field] ++ for name in filter_dicts: ++ extra.append(field + '(' + name + ')') ++ fields += extra ++ return fields ++ ++ def update_fields(self, fields_filter): ++ """Refresh fields, applying fields_filter""" ++ self._fields = [field for field in self.get_available_fields() ++ if self.is_field_wanted(fields_filter, field)] ++ ++ @staticmethod ++ def get_online_cpus(): ++ """Returns a list of cpu id integers.""" ++ def parse_int_list(list_string): ++ """Returns an int list from a string of comma separated integers and ++ integer ranges.""" ++ integers = [] ++ members = list_string.split(',') ++ ++ for member in members: ++ if '-' not in member: ++ integers.append(int(member)) ++ else: ++ int_range = member.split('-') ++ integers.extend(range(int(int_range[0]), ++ int(int_range[1]) + 1)) ++ ++ return integers ++ ++ with open('/sys/devices/system/cpu/online') as cpu_list: ++ cpu_string = cpu_list.readline() ++ return parse_int_list(cpu_string) ++ ++ def setup_traces(self): ++ """Creates all event and group objects needed to be able to retrieve ++ data.""" ++ fields = self.get_available_fields() ++ if self._pid > 0: ++ # Fetch list of all threads of the monitored pid, as qemu ++ # starts a thread for each vcpu. ++ path = os.path.join('/proc', str(self._pid), 'task') ++ groupids = self.walkdir(path)[1] ++ else: ++ groupids = self.get_online_cpus() ++ ++ # The constant is needed as a buffer for python libs, std ++ # streams and other files that the script opens. ++ newlim = len(groupids) * len(fields) + 50 ++ try: ++ softlim_, hardlim = resource.getrlimit(resource.RLIMIT_NOFILE) ++ ++ if hardlim < newlim: ++ # Now we need CAP_SYS_RESOURCE, to increase the hard limit. ++ resource.setrlimit(resource.RLIMIT_NOFILE, (newlim, newlim)) ++ else: ++ # Raising the soft limit is sufficient. ++ resource.setrlimit(resource.RLIMIT_NOFILE, (newlim, hardlim)) ++ ++ except ValueError: ++ sys.exit("NOFILE rlimit could not be raised to {0}".format(newlim)) ++ ++ for groupid in groupids: ++ group = Group() ++ for name in fields: ++ tracepoint = name ++ tracefilter = None ++ match = re.match(r'(.*)\((.*)\)', name) ++ if match: ++ tracepoint, sub = match.groups() ++ tracefilter = ('%s==%d\0' % ++ (self.filters[tracepoint][0], ++ self.filters[tracepoint][1][sub])) ++ ++ # From perf_event_open(2): ++ # pid > 0 and cpu == -1 ++ # This measures the specified process/thread on any CPU. ++ # ++ # pid == -1 and cpu >= 0 ++ # This measures all processes/threads on the specified CPU. ++ trace_cpu = groupid if self._pid == 0 else -1 ++ trace_pid = int(groupid) if self._pid != 0 else -1 ++ ++ group.add_event(Event(name=name, ++ group=group, ++ trace_cpu=trace_cpu, ++ trace_pid=trace_pid, ++ trace_point=tracepoint, ++ trace_filter=tracefilter)) ++ ++ self.group_leaders.append(group) ++ ++ @property ++ def fields(self): ++ return self._fields ++ ++ @fields.setter ++ def fields(self, fields): ++ """Enables/disables the (un)wanted events""" ++ self._fields = fields ++ for group in self.group_leaders: ++ for index, event in enumerate(group.events): ++ if event.name in fields: ++ event.reset() ++ event.enable() ++ else: ++ # Do not disable the group leader. ++ # It would disable all of its events. ++ if index != 0: ++ event.disable() ++ ++ @property ++ def pid(self): ++ return self._pid ++ ++ @pid.setter ++ def pid(self, pid): ++ """Changes the monitored pid by setting new traces.""" ++ self._pid = pid ++ # The garbage collector will get rid of all Event/Group ++ # objects and open files after removing the references. ++ self.group_leaders = [] ++ self.setup_traces() ++ self.fields = self._fields ++ ++ def read(self, by_guest=0): ++ """Returns 'event name: current value' for all enabled events.""" ++ ret = defaultdict(int) ++ for group in self.group_leaders: ++ for name, val in group.read().iteritems(): ++ if name in self._fields: ++ ret[name] += val ++ return ret ++ ++ def reset(self): ++ """Reset all field counters""" ++ for group in self.group_leaders: ++ for event in group.events: ++ event.reset() ++ ++ ++class DebugfsProvider(Provider): ++ """Provides data from the files that KVM creates in the kvm debugfs ++ folder.""" ++ def __init__(self, pid, fields_filter, include_past): ++ self.update_fields(fields_filter) ++ self._baseline = {} ++ self.do_read = True ++ self.paths = [] ++ self.pid = pid ++ if include_past: ++ self.restore() ++ ++ def get_available_fields(self): ++ """"Returns a list of available fields. ++ ++ The fields are all available KVM debugfs files ++ ++ """ ++ return self.walkdir(PATH_DEBUGFS_KVM)[2] ++ ++ def update_fields(self, fields_filter): ++ """Refresh fields, applying fields_filter""" ++ self._fields = [field for field in self.get_available_fields() ++ if self.is_field_wanted(fields_filter, field)] ++ ++ @property ++ def fields(self): ++ return self._fields ++ ++ @fields.setter ++ def fields(self, fields): ++ self._fields = fields ++ self.reset() ++ ++ @property ++ def pid(self): ++ return self._pid ++ ++ @pid.setter ++ def pid(self, pid): ++ self._pid = pid ++ if pid != 0: ++ vms = self.walkdir(PATH_DEBUGFS_KVM)[1] ++ if len(vms) == 0: ++ self.do_read = False ++ ++ self.paths = filter(lambda x: "{}-".format(pid) in x, vms) ++ ++ else: ++ self.paths = [] ++ self.do_read = True ++ self.reset() ++ ++ def read(self, reset=0, by_guest=0): ++ """Returns a dict with format:'file name / field -> current value'. ++ ++ Parameter 'reset': ++ 0 plain read ++ 1 reset field counts to 0 ++ 2 restore the original field counts ++ ++ """ ++ results = {} ++ ++ # If no debugfs filtering support is available, then don't read. ++ if not self.do_read: ++ return results ++ ++ paths = self.paths ++ if self._pid == 0: ++ paths = [] ++ for entry in os.walk(PATH_DEBUGFS_KVM): ++ for dir in entry[1]: ++ paths.append(dir) ++ for path in paths: ++ for field in self._fields: ++ value = self.read_field(field, path) ++ key = path + field ++ if reset == 1: ++ self._baseline[key] = value ++ if reset == 2: ++ self._baseline[key] = 0 ++ if self._baseline.get(key, -1) == -1: ++ self._baseline[key] = value ++ increment = (results.get(field, 0) + value - ++ self._baseline.get(key, 0)) ++ if by_guest: ++ pid = key.split('-')[0] ++ if pid in results: ++ results[pid] += increment ++ else: ++ results[pid] = increment ++ else: ++ results[field] = increment ++ ++ return results ++ ++ def read_field(self, field, path): ++ """Returns the value of a single field from a specific VM.""" ++ try: ++ return int(open(os.path.join(PATH_DEBUGFS_KVM, ++ path, ++ field)) ++ .read()) ++ except IOError: ++ return 0 ++ ++ def reset(self): ++ """Reset field counters""" ++ self._baseline = {} ++ self.read(1) ++ ++ def restore(self): ++ """Reset field counters""" ++ self._baseline = {} ++ self.read(2) ++ ++ ++class Stats(object): ++ """Manages the data providers and the data they provide. ++ ++ It is used to set filters on the provider's data and collect all ++ provider data. ++ ++ """ ++ def __init__(self, options): ++ self.providers = self.get_providers(options) ++ self._pid_filter = options.pid ++ self._fields_filter = options.fields ++ self.values = {} ++ ++ @staticmethod ++ def get_providers(options): ++ """Returns a list of data providers depending on the passed options.""" ++ providers = [] ++ ++ if options.debugfs: ++ providers.append(DebugfsProvider(options.pid, options.fields, ++ options.dbgfs_include_past)) ++ if options.tracepoints or not providers: ++ providers.append(TracepointProvider(options.pid, options.fields)) ++ ++ return providers ++ ++ def update_provider_filters(self): ++ """Propagates fields filters to providers.""" ++ # As we reset the counters when updating the fields we can ++ # also clear the cache of old values. ++ self.values = {} ++ for provider in self.providers: ++ provider.update_fields(self._fields_filter) ++ ++ def reset(self): ++ self.values = {} ++ for provider in self.providers: ++ provider.reset() ++ ++ @property ++ def fields_filter(self): ++ return self._fields_filter ++ ++ @fields_filter.setter ++ def fields_filter(self, fields_filter): ++ if fields_filter != self._fields_filter: ++ self._fields_filter = fields_filter ++ self.update_provider_filters() ++ ++ @property ++ def pid_filter(self): ++ return self._pid_filter ++ ++ @pid_filter.setter ++ def pid_filter(self, pid): ++ if pid != self._pid_filter: ++ self._pid_filter = pid ++ self.values = {} ++ for provider in self.providers: ++ provider.pid = self._pid_filter ++ ++ def get(self, by_guest=0): ++ """Returns a dict with field -> (value, delta to last value) of all ++ provider data.""" ++ for provider in self.providers: ++ new = provider.read(by_guest=by_guest) ++ for key in new if by_guest else provider.fields: ++ oldval = self.values.get(key, (0, 0))[0] ++ newval = new.get(key, 0) ++ newdelta = newval - oldval ++ self.values[key] = (newval, newdelta) ++ return self.values ++ ++ def toggle_display_guests(self, to_pid): ++ """Toggle between collection of stats by individual event and by ++ guest pid ++ ++ Events reported by DebugfsProvider change when switching to/from ++ reading by guest values. Hence we have to remove the excess event ++ names from self.values. ++ ++ """ ++ if any(isinstance(ins, TracepointProvider) for ins in self.providers): ++ return 1 ++ if to_pid: ++ for provider in self.providers: ++ if isinstance(provider, DebugfsProvider): ++ for key in provider.fields: ++ if key in self.values.keys(): ++ del self.values[key] ++ else: ++ oldvals = self.values.copy() ++ for key in oldvals: ++ if key.isdigit(): ++ del self.values[key] ++ # Update oldval (see get()) ++ self.get(to_pid) ++ return 0 ++ ++DELAY_DEFAULT = 3.0 ++MAX_GUEST_NAME_LEN = 48 ++MAX_REGEX_LEN = 44 ++DEFAULT_REGEX = r'^[^\(]*$' ++SORT_DEFAULT = 0 ++ ++ ++class Tui(object): ++ """Instruments curses to draw a nice text ui.""" ++ def __init__(self, stats): ++ self.stats = stats ++ self.screen = None ++ self._delay_initial = 0.25 ++ self._delay_regular = DELAY_DEFAULT ++ self._sorting = SORT_DEFAULT ++ self._display_guests = 0 ++ ++ def __enter__(self): ++ """Initialises curses for later use. Based on curses.wrapper ++ implementation from the Python standard library.""" ++ self.screen = curses.initscr() ++ curses.noecho() ++ curses.cbreak() ++ ++ # The try/catch works around a minor bit of ++ # over-conscientiousness in the curses module, the error ++ # return from C start_color() is ignorable. ++ try: ++ curses.start_color() ++ except curses.error: ++ pass ++ ++ # Hide cursor in extra statement as some monochrome terminals ++ # might support hiding but not colors. ++ try: ++ curses.curs_set(0) ++ except curses.error: ++ pass ++ ++ curses.use_default_colors() ++ return self ++ ++ def __exit__(self, *exception): ++ """Resets the terminal to its normal state. Based on curses.wrapper ++ implementation from the Python standard library.""" ++ if self.screen: ++ self.screen.keypad(0) ++ curses.echo() ++ curses.nocbreak() ++ curses.endwin() ++ ++ @staticmethod ++ def get_all_gnames(): ++ """Returns a list of (pid, gname) tuples of all running guests""" ++ res = [] ++ try: ++ child = subprocess.Popen(['ps', '-A', '--format', 'pid,args'], ++ stdout=subprocess.PIPE) ++ except: ++ raise Exception ++ for line in child.stdout: ++ line = line.lstrip().split(' ', 1) ++ # perform a sanity check before calling the more expensive ++ # function to possibly extract the guest name ++ if ' -name ' in line[1]: ++ res.append((line[0], Tui.get_gname_from_pid(line[0]))) ++ child.stdout.close() ++ ++ return res ++ ++ def print_all_gnames(self, row): ++ """Print a list of all running guests along with their pids.""" ++ self.screen.addstr(row, 2, '%8s %-60s' % ++ ('Pid', 'Guest Name (fuzzy list, might be ' ++ 'inaccurate!)'), ++ curses.A_UNDERLINE) ++ row += 1 ++ try: ++ for line in self.get_all_gnames(): ++ self.screen.addstr(row, 2, '%8s %-60s' % (line[0], line[1])) ++ row += 1 ++ if row >= self.screen.getmaxyx()[0]: ++ break ++ except Exception: ++ self.screen.addstr(row + 1, 2, 'Not available') ++ ++ @staticmethod ++ def get_pid_from_gname(gname): ++ """Fuzzy function to convert guest name to QEMU process pid. ++ ++ Returns a list of potential pids, can be empty if no match found. ++ Throws an exception on processing errors. ++ ++ """ ++ pids = [] ++ for line in Tui.get_all_gnames(): ++ if gname == line[1]: ++ pids.append(int(line[0])) ++ ++ return pids ++ ++ @staticmethod ++ def get_gname_from_pid(pid): ++ """Returns the guest name for a QEMU process pid. ++ ++ Extracts the guest name from the QEMU comma line by processing the ++ '-name' option. Will also handle names specified out of sequence. ++ ++ """ ++ name = '' ++ try: ++ line = open('/proc/{}/cmdline' ++ .format(pid), 'rb').read().split('\0') ++ parms = line[line.index('-name') + 1].split(',') ++ while '' in parms: ++ # commas are escaped (i.e. ',,'), hence e.g. 'foo,bar' results ++ # in # ['foo', '', 'bar'], which we revert here ++ idx = parms.index('') ++ parms[idx - 1] += ',' + parms[idx + 1] ++ del parms[idx:idx+2] ++ # the '-name' switch allows for two ways to specify the guest name, ++ # where the plain name overrides the name specified via 'guest=' ++ for arg in parms: ++ if '=' not in arg: ++ name = arg ++ break ++ if arg[:6] == 'guest=': ++ name = arg[6:] ++ except (ValueError, IOError, IndexError): ++ pass ++ ++ return name ++ ++ def update_drilldown(self): ++ """Sets or removes a filter that only allows fields without braces.""" ++ if not self.stats.fields_filter: ++ self.stats.fields_filter = DEFAULT_REGEX ++ ++ elif self.stats.fields_filter == DEFAULT_REGEX: ++ self.stats.fields_filter = None ++ ++ def update_pid(self, pid): ++ """Propagates pid selection to stats object.""" ++ self.stats.pid_filter = pid ++ ++ def refresh_header(self, pid=None): ++ """Refreshes the header.""" ++ if pid is None: ++ pid = self.stats.pid_filter ++ self.screen.erase() ++ gname = self.get_gname_from_pid(pid) ++ if gname: ++ gname = ('({})'.format(gname[:MAX_GUEST_NAME_LEN] + '...' ++ if len(gname) > MAX_GUEST_NAME_LEN ++ else gname)) ++ if pid > 0: ++ self.screen.addstr(0, 0, 'kvm statistics - pid {0} {1}' ++ .format(pid, gname), curses.A_BOLD) ++ else: ++ self.screen.addstr(0, 0, 'kvm statistics - summary', curses.A_BOLD) ++ if self.stats.fields_filter and self.stats.fields_filter \ ++ != DEFAULT_REGEX: ++ regex = self.stats.fields_filter ++ if len(regex) > MAX_REGEX_LEN: ++ regex = regex[:MAX_REGEX_LEN] + '...' ++ self.screen.addstr(1, 17, 'regex filter: {0}'.format(regex)) ++ if self._display_guests: ++ col_name = 'Guest Name' ++ else: ++ col_name = 'Event' ++ self.screen.addstr(2, 1, '%-40s %10s%7s %8s' % ++ (col_name, 'Total', '%Total', 'CurAvg/s'), ++ curses.A_STANDOUT) ++ self.screen.addstr(4, 1, 'Collecting data...') ++ self.screen.refresh() ++ ++ def refresh_body(self, sleeptime): ++ row = 3 ++ self.screen.move(row, 0) ++ self.screen.clrtobot() ++ stats = self.stats.get(self._display_guests) ++ ++ def sortCurAvg(x): ++ # sort by current events if available ++ if stats[x][1]: ++ return (-stats[x][1], -stats[x][0]) ++ else: ++ return (0, -stats[x][0]) ++ ++ def sortTotal(x): ++ # sort by totals ++ return (0, -stats[x][0]) ++ total = 0. ++ for val in stats.values(): ++ total += val[0] ++ if self._sorting == SORT_DEFAULT: ++ sortkey = sortCurAvg ++ else: ++ sortkey = sortTotal ++ for key in sorted(stats.keys(), key=sortkey): ++ ++ if row >= self.screen.getmaxyx()[0]: ++ break ++ values = stats[key] ++ if not values[0] and not values[1]: ++ break ++ if values[0] is not None: ++ cur = int(round(values[1] / sleeptime)) if values[1] else '' ++ if self._display_guests: ++ key = self.get_gname_from_pid(key) ++ self.screen.addstr(row, 1, '%-40s %10d%7.1f %8s' % ++ (key, values[0], values[0] * 100 / total, ++ cur)) ++ row += 1 ++ if row == 3: ++ self.screen.addstr(4, 1, 'No matching events reported yet') ++ self.screen.refresh() ++ ++ def show_msg(self, text): ++ """Display message centered text and exit on key press""" ++ hint = 'Press any key to continue' ++ curses.cbreak() ++ self.screen.erase() ++ (x, term_width) = self.screen.getmaxyx() ++ row = 2 ++ for line in text: ++ start = (term_width - len(line)) / 2 ++ self.screen.addstr(row, start, line) ++ row += 1 ++ self.screen.addstr(row + 1, (term_width - len(hint)) / 2, hint, ++ curses.A_STANDOUT) ++ self.screen.getkey() ++ ++ def show_help_interactive(self): ++ """Display help with list of interactive commands""" ++ msg = (' b toggle events by guests (debugfs only, honors' ++ ' filters)', ++ ' c clear filter', ++ ' f filter by regular expression', ++ ' g filter by guest name', ++ ' h display interactive commands reference', ++ ' o toggle sorting order (Total vs CurAvg/s)', ++ ' p filter by PID', ++ ' q quit', ++ ' r reset stats', ++ ' s set update interval', ++ ' x toggle reporting of stats for individual child trace' ++ ' events', ++ 'Any other key refreshes statistics immediately') ++ curses.cbreak() ++ self.screen.erase() ++ self.screen.addstr(0, 0, "Interactive commands reference", ++ curses.A_BOLD) ++ self.screen.addstr(2, 0, "Press any key to exit", curses.A_STANDOUT) ++ row = 4 ++ for line in msg: ++ self.screen.addstr(row, 0, line) ++ row += 1 ++ self.screen.getkey() ++ self.refresh_header() ++ ++ def show_filter_selection(self): ++ """Draws filter selection mask. ++ ++ Asks for a valid regex and sets the fields filter accordingly. ++ ++ """ ++ while True: ++ self.screen.erase() ++ self.screen.addstr(0, 0, ++ "Show statistics for events matching a regex.", ++ curses.A_BOLD) ++ self.screen.addstr(2, 0, ++ "Current regex: {0}" ++ .format(self.stats.fields_filter)) ++ self.screen.addstr(3, 0, "New regex: ") ++ curses.echo() ++ regex = self.screen.getstr() ++ curses.noecho() ++ if len(regex) == 0: ++ self.stats.fields_filter = DEFAULT_REGEX ++ self.refresh_header() ++ return ++ try: ++ re.compile(regex) ++ self.stats.fields_filter = regex ++ self.refresh_header() ++ return ++ except re.error: ++ continue ++ ++ def show_vm_selection_by_pid(self): ++ """Draws PID selection mask. ++ ++ Asks for a pid until a valid pid or 0 has been entered. ++ ++ """ ++ msg = '' ++ while True: ++ self.screen.erase() ++ self.screen.addstr(0, 0, ++ 'Show statistics for specific pid.', ++ curses.A_BOLD) ++ self.screen.addstr(1, 0, ++ 'This might limit the shown data to the trace ' ++ 'statistics.') ++ self.screen.addstr(5, 0, msg) ++ self.print_all_gnames(7) ++ ++ curses.echo() ++ self.screen.addstr(3, 0, "Pid [0 or pid]: ") ++ pid = self.screen.getstr() ++ curses.noecho() ++ ++ try: ++ if len(pid) > 0: ++ pid = int(pid) ++ if pid != 0 and not os.path.isdir(os.path.join('/proc/', ++ str(pid))): ++ msg = '"' + str(pid) + '": Not a running process' ++ continue ++ else: ++ pid = 0 ++ self.refresh_header(pid) ++ self.update_pid(pid) ++ break ++ except ValueError: ++ msg = '"' + str(pid) + '": Not a valid pid' ++ ++ def show_set_update_interval(self): ++ """Draws update interval selection mask.""" ++ msg = '' ++ while True: ++ self.screen.erase() ++ self.screen.addstr(0, 0, 'Set update interval (defaults to %fs).' % ++ DELAY_DEFAULT, curses.A_BOLD) ++ self.screen.addstr(4, 0, msg) ++ self.screen.addstr(2, 0, 'Change delay from %.1fs to ' % ++ self._delay_regular) ++ curses.echo() ++ val = self.screen.getstr() ++ curses.noecho() ++ ++ try: ++ if len(val) > 0: ++ delay = float(val) ++ if delay < 0.1: ++ msg = '"' + str(val) + '": Value must be >=0.1' ++ continue ++ if delay > 25.5: ++ msg = '"' + str(val) + '": Value must be <=25.5' ++ continue ++ else: ++ delay = DELAY_DEFAULT ++ self._delay_regular = delay ++ break ++ ++ except ValueError: ++ msg = '"' + str(val) + '": Invalid value' ++ self.refresh_header() ++ ++ def show_vm_selection_by_guest_name(self): ++ """Draws guest selection mask. ++ ++ Asks for a guest name until a valid guest name or '' is entered. ++ ++ """ ++ msg = '' ++ while True: ++ self.screen.erase() ++ self.screen.addstr(0, 0, ++ 'Show statistics for specific guest.', ++ curses.A_BOLD) ++ self.screen.addstr(1, 0, ++ 'This might limit the shown data to the trace ' ++ 'statistics.') ++ self.screen.addstr(5, 0, msg) ++ self.print_all_gnames(7) ++ curses.echo() ++ self.screen.addstr(3, 0, "Guest [ENTER or guest]: ") ++ gname = self.screen.getstr() ++ curses.noecho() ++ ++ if not gname: ++ self.refresh_header(0) ++ self.update_pid(0) ++ break ++ else: ++ pids = [] ++ try: ++ pids = self.get_pid_from_gname(gname) ++ except: ++ msg = '"' + gname + '": Internal error while searching, ' \ ++ 'use pid filter instead' ++ continue ++ if len(pids) == 0: ++ msg = '"' + gname + '": Not an active guest' ++ continue ++ if len(pids) > 1: ++ msg = '"' + gname + '": Multiple matches found, use pid ' \ ++ 'filter instead' ++ continue ++ self.refresh_header(pids[0]) ++ self.update_pid(pids[0]) ++ break ++ ++ def show_stats(self): ++ """Refreshes the screen and processes user input.""" ++ sleeptime = self._delay_initial ++ self.refresh_header() ++ start = 0.0 # result based on init value never appears on screen ++ while True: ++ self.refresh_body(time.time() - start) ++ curses.halfdelay(int(sleeptime * 10)) ++ start = time.time() ++ sleeptime = self._delay_regular ++ try: ++ char = self.screen.getkey() ++ if char == 'b': ++ self._display_guests = not self._display_guests ++ if self.stats.toggle_display_guests(self._display_guests): ++ self.show_msg(['Command not available with tracepoints' ++ ' enabled', 'Restart with debugfs only ' ++ '(see option \'-d\') and try again!']) ++ self._display_guests = not self._display_guests ++ self.refresh_header() ++ if char == 'c': ++ self.stats.fields_filter = DEFAULT_REGEX ++ self.refresh_header(0) ++ self.update_pid(0) ++ if char == 'f': ++ curses.curs_set(1) ++ self.show_filter_selection() ++ curses.curs_set(0) ++ sleeptime = self._delay_initial ++ if char == 'g': ++ curses.curs_set(1) ++ self.show_vm_selection_by_guest_name() ++ curses.curs_set(0) ++ sleeptime = self._delay_initial ++ if char == 'h': ++ self.show_help_interactive() ++ if char == 'o': ++ self._sorting = not self._sorting ++ if char == 'p': ++ curses.curs_set(1) ++ self.show_vm_selection_by_pid() ++ curses.curs_set(0) ++ sleeptime = self._delay_initial ++ if char == 'q': ++ break ++ if char == 'r': ++ self.stats.reset() ++ if char == 's': ++ curses.curs_set(1) ++ self.show_set_update_interval() ++ curses.curs_set(0) ++ sleeptime = self._delay_initial ++ if char == 'x': ++ self.update_drilldown() ++ # prevents display of current values on next refresh ++ self.stats.get() ++ except KeyboardInterrupt: ++ break ++ except curses.error: ++ continue ++ ++ ++def batch(stats): ++ """Prints statistics in a key, value format.""" ++ try: ++ s = stats.get() ++ time.sleep(1) ++ s = stats.get() ++ for key in sorted(s.keys()): ++ values = s[key] ++ print '%-42s%10d%10d' % (key, values[0], values[1]) ++ except KeyboardInterrupt: ++ pass ++ ++ ++def log(stats): ++ """Prints statistics as reiterating key block, multiple value blocks.""" ++ keys = sorted(stats.get().iterkeys()) ++ ++ def banner(): ++ for k in keys: ++ print '%s' % k, ++ print ++ ++ def statline(): ++ s = stats.get() ++ for k in keys: ++ print ' %9d' % s[k][1], ++ print ++ line = 0 ++ banner_repeat = 20 ++ while True: ++ try: ++ time.sleep(1) ++ if line % banner_repeat == 0: ++ banner() ++ statline() ++ line += 1 ++ except KeyboardInterrupt: ++ break ++ ++ ++def get_options(): ++ """Returns processed program arguments.""" ++ description_text = """ ++This script displays various statistics about VMs running under KVM. ++The statistics are gathered from the KVM debugfs entries and / or the ++currently available perf traces. ++ ++The monitoring takes additional cpu cycles and might affect the VM's ++performance. ++ ++Requirements: ++- Access to: ++ %s ++ %s/events/* ++ /proc/pid/task ++- /proc/sys/kernel/perf_event_paranoid < 1 if user has no ++ CAP_SYS_ADMIN and perf events are used. ++- CAP_SYS_RESOURCE if the hard limit is not high enough to allow ++ the large number of files that are possibly opened. ++ ++Interactive Commands: ++ b toggle events by guests (debugfs only, honors filters) ++ c clear filter ++ f filter by regular expression ++ g filter by guest name ++ h display interactive commands reference ++ o toggle sorting order (Total vs CurAvg/s) ++ p filter by PID ++ q quit ++ r reset stats ++ s set update interval ++ x toggle reporting of stats for individual child trace events ++Press any other key to refresh statistics immediately. ++""" % (PATH_DEBUGFS_KVM, PATH_DEBUGFS_TRACING) ++ ++ class PlainHelpFormatter(optparse.IndentedHelpFormatter): ++ def format_description(self, description): ++ if description: ++ return description + "\n" ++ else: ++ return "" ++ ++ def cb_guest_to_pid(option, opt, val, parser): ++ try: ++ pids = Tui.get_pid_from_gname(val) ++ except: ++ raise optparse.OptionValueError('Error while searching for guest ' ++ '"{}", use "-p" to specify a pid ' ++ 'instead'.format(val)) ++ if len(pids) == 0: ++ raise optparse.OptionValueError('No guest by the name "{}" ' ++ 'found'.format(val)) ++ if len(pids) > 1: ++ raise optparse.OptionValueError('Multiple processes found (pids: ' ++ '{}) - use "-p" to specify a pid ' ++ 'instead'.format(" ".join(pids))) ++ parser.values.pid = pids[0] ++ ++ optparser = optparse.OptionParser(description=description_text, ++ formatter=PlainHelpFormatter()) ++ optparser.add_option('-1', '--once', '--batch', ++ action='store_true', ++ default=False, ++ dest='once', ++ help='run in batch mode for one second', ++ ) ++ optparser.add_option('-i', '--debugfs-include-past', ++ action='store_true', ++ default=False, ++ dest='dbgfs_include_past', ++ help='include all available data on past events for ' ++ 'debugfs', ++ ) ++ optparser.add_option('-l', '--log', ++ action='store_true', ++ default=False, ++ dest='log', ++ help='run in logging mode (like vmstat)', ++ ) ++ optparser.add_option('-t', '--tracepoints', ++ action='store_true', ++ default=False, ++ dest='tracepoints', ++ help='retrieve statistics from tracepoints', ++ ) ++ optparser.add_option('-d', '--debugfs', ++ action='store_true', ++ default=False, ++ dest='debugfs', ++ help='retrieve statistics from debugfs', ++ ) ++ optparser.add_option('-f', '--fields', ++ action='store', ++ default=DEFAULT_REGEX, ++ dest='fields', ++ help='''fields to display (regex) ++ "-f help" for a list of available events''', ++ ) ++ optparser.add_option('-p', '--pid', ++ action='store', ++ default=0, ++ type='int', ++ dest='pid', ++ help='restrict statistics to pid', ++ ) ++ optparser.add_option('-g', '--guest', ++ action='callback', ++ type='string', ++ dest='pid', ++ metavar='GUEST', ++ help='restrict statistics to guest by name', ++ callback=cb_guest_to_pid, ++ ) ++ (options, _) = optparser.parse_args(sys.argv) ++ return options ++ ++ ++def check_access(options): ++ """Exits if the current user can't access all needed directories.""" ++ if not os.path.exists('/sys/kernel/debug'): ++ sys.stderr.write('Please enable CONFIG_DEBUG_FS in your kernel.') ++ sys.exit(1) ++ ++ if not os.path.exists(PATH_DEBUGFS_KVM): ++ sys.stderr.write("Please make sure, that debugfs is mounted and " ++ "readable by the current user:\n" ++ "('mount -t debugfs debugfs /sys/kernel/debug')\n" ++ "Also ensure, that the kvm modules are loaded.\n") ++ sys.exit(1) ++ ++ if not os.path.exists(PATH_DEBUGFS_TRACING) and (options.tracepoints or ++ not options.debugfs): ++ sys.stderr.write("Please enable CONFIG_TRACING in your kernel " ++ "when using the option -t (default).\n" ++ "If it is enabled, make {0} readable by the " ++ "current user.\n" ++ .format(PATH_DEBUGFS_TRACING)) ++ if options.tracepoints: ++ sys.exit(1) ++ ++ sys.stderr.write("Falling back to debugfs statistics!\n") ++ options.debugfs = True ++ time.sleep(5) ++ ++ return options ++ ++ ++def main(): ++ options = get_options() ++ options = check_access(options) ++ ++ if (options.pid > 0 and ++ not os.path.isdir(os.path.join('/proc/', ++ str(options.pid)))): ++ sys.stderr.write('Did you use a (unsupported) tid instead of a pid?\n') ++ sys.exit('Specified pid does not exist.') ++ ++ stats = Stats(options) ++ ++ if options.fields == "help": ++ event_list = "\n" ++ s = stats.get() ++ for key in s.keys(): ++ if key.find('(') != -1: ++ key = key[0:key.find('(')] ++ if event_list.find('\n' + key + '\n') == -1: ++ event_list += key + '\n' ++ sys.stdout.write(event_list) ++ return "" ++ ++ if options.log: ++ log(stats) ++ elif not options.once: ++ with Tui(stats) as tui: ++ tui.show_stats() ++ else: ++ batch(stats) ++ ++if __name__ == "__main__": ++ main() +diff --git a/scripts/kvm/kvm_stat.texi b/scripts/kvm/kvm_stat.texi +new file mode 100644 +index 0000000..5d964f6 +--- /dev/null ++++ b/scripts/kvm/kvm_stat.texi +@@ -0,0 +1,104 @@ ++@example ++@c man begin SYNOPSIS ++usage: kvm_stat [OPTION]... ++@c man end ++@end example ++ ++@c man begin DESCRIPTION ++ ++kvm_stat prints counts of KVM kernel module trace events. These events signify ++state transitions such as guest mode entry and exit. ++ ++This tool is useful for observing guest behavior from the host perspective. ++Often conclusions about performance or buggy behavior can be drawn from the ++output. ++ ++The set of KVM kernel module trace events may be specific to the kernel version ++or architecture. It is best to check the KVM kernel module source code for the ++meaning of events. ++ ++Use batch and logging modes for scripting purposes. ++ ++@section Interactive Commands ++ ++While running in regular (interactive) mode, use any of the following keys: ++ ++@table @key ++@item b ++@kindex b ++toggle events by guests (debugfs only, honors filters) ++@item c ++@kindex c ++clear filter ++@item f ++@kindex f ++filter by regular expression ++@item g ++@kindex g ++filter by guest name ++@item h ++@kindex h ++display interactive commands reference ++@item o ++@kindex o ++toggle sorting order (Total vs CurAvg/s) ++@item p ++@kindex p ++filter by PID ++@item q ++@kindex q ++quit ++@item r ++@kindex r ++reset stats ++@item s ++@kindex s ++set update interval ++@item x ++@kindex x ++toggle reporting of stats for child trace events ++@end table ++ ++Press any other key to refresh statistics immediately. ++ ++@c man end ++ ++ ++@c man begin OPTIONS ++@table @option ++@item -1, --once, --batch ++ run in batch mode for one second ++@item -l, --log ++ run in logging mode (like vmstat) ++@item -t, --tracepoints ++ retrieve statistics from tracepoints ++@item -d, --debugfs ++ retrieve statistics from debugfs ++@item -p, --pid=@var{pid} ++ limit statistics to one virtual machine (pid) ++@item -i, --debugfs-include-past ++ include all available data on past events for debugfs ++@item -g, --guest=@var{guest_name} ++ limit statistics to one virtual machine (guest name) ++@item -f, --fields=@var{fields} ++ fields to display (regex) ++@item -h, --help ++ show help message ++@end table ++ ++@c man end ++ ++@ignore ++ ++@setfilename kvm_stat ++@settitle Report KVM kernel module event counters. ++ ++@c man begin AUTHOR ++Stefan Hajnoczi ++@c man end ++ ++@c man begin SEEALSO ++perf(1), trace-cmd(1) ++@c man end ++ ++@end ignore +-- +1.8.3.1 + diff --git a/SOURCES/0007-Use-kvm-by-default.patch b/SOURCES/0007-Use-kvm-by-default.patch new file mode 100644 index 0000000..1dd0a03 --- /dev/null +++ b/SOURCES/0007-Use-kvm-by-default.patch @@ -0,0 +1,44 @@ +From 5723fea739b8f883cbd7e158292199a01b566e55 Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Thu, 18 Dec 2014 06:27:49 +0100 +Subject: Use kvm by default + +Bugzilla: 906185 + +RHEL uses kvm accelerator by default, if available. + +Signed-off-by: Miroslav Rezanina + +Rebase notes (2.10.0) +- variable rename (upstream) + +Rebase notes (2.2.0): +- Move code from vl.c to accel.c + +(cherry picked from commit abcd662eb8e516ebe4a6b401e83a62f749491a15) +(cherry picked from commit eca6d5766d956c37e3f7f28d70903d357308c846) +(cherry picked from commit 0672656bb595f3d864b2e108a0278324ee2e7250) +(cherry picked from commit b3c79df62c3d7b1c9355dddb3c4fde285fdfc8c3) +(cherry picked from commit c303c9746b7ccfa46c4b1938ab00a29fea6ced9a) +--- + accel/accel.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/accel/accel.c b/accel/accel.c +index 93e2434..5f3d73f 100644 +--- a/accel/accel.c ++++ b/accel/accel.c +@@ -79,8 +79,8 @@ void configure_accelerator(MachineState *ms) + + accel = qemu_opt_get(qemu_get_machine_opts(), "accel"); + if (accel == NULL) { +- /* Use the default "accelerator", tcg */ +- accel = "tcg"; ++ /* RHEL uses kvm as the default accelerator, fallback to tcg */ ++ accel = "kvm:tcg"; + } + + p = accel; +-- +1.8.3.1 + diff --git a/SOURCES/0008-add-qxl_screendump-monitor-command.patch b/SOURCES/0008-add-qxl_screendump-monitor-command.patch new file mode 100644 index 0000000..bf69df2 --- /dev/null +++ b/SOURCES/0008-add-qxl_screendump-monitor-command.patch @@ -0,0 +1,196 @@ +From 24fd80b15ac39339607e43f9dca80f645b436557 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 14 Mar 2017 13:21:06 +0100 +Subject: add qxl_screendump monitor command + +RH-Author: Gerd Hoffmann +Message-id: <1375866764-17766-2-git-send-email-kraxel@redhat.com> +Patchwork-id: 53033 +O-Subject: [RHEL-7 qemu-kvm PATCH 1/1] add qxl_screendump monitor command +Bugzilla: 903910 +RH-Acked-by: Hans de Goede +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Michal Novotny + +This patch ports the rhel-6 specific qxl_screendump command to rhel-7. +qxl_screendump takes the device id as additional argument and thus can +be used to take screenshots from non-primary displays. + +The plan to get that functionality upstream in time failed, so we go for +plan b and carry forward the rhel-6 specific qxl_screendump command. +Thanks to the major console subsystem cleanups which made it upstream +the implementation is (a) alot less hackier than the rhel-6 one and (b) +not qxl-specific any more. Given that qxl is the only graphic device +which can work as secondary vga card the later is only a theoretical +benefit though ;) + +RHEL-6 commit: 1c6074d107dff93c7c7b0edfb5da871504802946 + +bugzilla: #903910 - RHEL7 does not have equivalent functionality for +__com.redhat_qxl_screendump + +Signed-off-by: Gerd Hoffmann +(cherry picked from commit 211321c693f46d283205830c6c49b54d7250e98c) + +Rebase notes (2.12.0): +- added prototype before definition + +Rebase notes (2.9.0): +- documentation moved to qapi schema + +Rebase notes (2.8.0): +- qmp-commands.hx replaced by docs/qmp-commands.txt (commit bd6092e) +- mhandler.cmd attribute renamed to cmd (commit 2b9e357) + +Rebase notes (2.4.0): +- replace QERR_DEVICE_NOT_FOUND with ERROR_CLASS_DEVICE_NOT_FOUND + +Merged patches (2.9.0): +- a3b59c0 HMP: Fix user manual typo of __com.redhat_qxl_screendump + +Merged patches (2.6.0): +- f12846f __com.redhat_qxl_screendump: add docs + +(cherry picked from commit 9ff701a5129653d6bd27c0b3cc249691cb6ce6a7) +(cherry picked from commit fd2ce5e462ee97f4538981f50e9bebc222fbe157) +(cherry picked from commit da2738aa88ba79de34abf9ce7de88920d0089fb3) +(cherry picked from commit 61fc62e6b6f217da00376b486c8927a5586c27b8) +(cherry picked from commit 4092812ff2d7a7dc7f2f62d49fa3a568ba78781f) +--- + hmp-commands.hx | 14 ++++++++++++++ + hmp.c | 10 ++++++++++ + hmp.h | 1 + + qapi/misc.json | 22 ++++++++++++++++++++++ + ui/console.c | 25 +++++++++++++++++++++++++ + 5 files changed, 72 insertions(+) + +diff --git a/hmp-commands.hx b/hmp-commands.hx +index 3918831..01dcbb2 100644 +--- a/hmp-commands.hx ++++ b/hmp-commands.hx +@@ -272,6 +272,20 @@ Save screen into PPM image @var{filename}. + ETEXI + + { ++ .name = "__com.redhat_qxl_screendump", ++ .args_type = "id:s,filename:F", ++ .params = "id filename", ++ .help = "save screen from qxl device 'id' into PPM image 'filename'", ++ .cmd = hmp___com_redhat_qxl_screen_dump, ++ }, ++ ++STEXI ++@item __com.redhat_qxl_screendump @var{id} @var{filename} ++@findex __com.redhat_qxl_screendump ++Save screen from qxl device @var{id} into PPM image @var{filename}. ++ETEXI ++ ++ { + .name = "logfile", + .args_type = "filename:F", + .params = "filename", +diff --git a/hmp.c b/hmp.c +index 6c92198..7a53e63 100644 +--- a/hmp.c ++++ b/hmp.c +@@ -2160,6 +2160,16 @@ void hmp_screendump(Monitor *mon, const QDict *qdict) + hmp_handle_error(mon, &err); + } + ++void hmp___com_redhat_qxl_screen_dump(Monitor *mon, const QDict *qdict) ++{ ++ const char *id = qdict_get_str(qdict, "id"); ++ const char *filename = qdict_get_str(qdict, "filename"); ++ Error *err = NULL; ++ ++ qmp___com_redhat_qxl_screendump(id, filename, &err); ++ hmp_handle_error(mon, &err); ++} ++ + void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) + { + const char *uri = qdict_get_str(qdict, "uri"); +diff --git a/hmp.h b/hmp.h +index 4e2ec37..f85318c 100644 +--- a/hmp.h ++++ b/hmp.h +@@ -97,6 +97,7 @@ void hmp_getfd(Monitor *mon, const QDict *qdict); + void hmp_closefd(Monitor *mon, const QDict *qdict); + void hmp_sendkey(Monitor *mon, const QDict *qdict); + void hmp_screendump(Monitor *mon, const QDict *qdict); ++void hmp___com_redhat_qxl_screen_dump(Monitor *mon, const QDict *qdict); + void hmp_nbd_server_start(Monitor *mon, const QDict *qdict); + void hmp_nbd_server_add(Monitor *mon, const QDict *qdict); + void hmp_nbd_server_remove(Monitor *mon, const QDict *qdict); +diff --git a/qapi/misc.json b/qapi/misc.json +index 5636f4a..045eb7c 100644 +--- a/qapi/misc.json ++++ b/qapi/misc.json +@@ -2445,6 +2445,28 @@ + { 'command': 'query-fdsets', 'returns': ['FdsetInfo'] } + + ## ++# @__com.redhat_qxl_screendump: ++# ++# Write a PPM of secondary qxl devices to a file. ++# ++# @id: qxl device id ++# @filename: the path of a new PPM file to store the image ++# ++# Returns: Nothing on success ++# ++# Since: never (rhel-only, not upstream) ++# ++# Example: ++# ++# -> { "execute": "__com.redhat_qxl_screendump", ++# "arguments": { "id": video1", "filename": "v1.ppm" } } ++# <- { "return": {} } ++# ++## ++{ 'command': '__com.redhat_qxl_screendump', 'data': { 'id' : 'str', ++ 'filename': 'str' } } ++ ++## + # @TargetInfo: + # + # Information describing the QEMU target. +diff --git a/ui/console.c b/ui/console.c +index 3fb2f4e..73b2d3c 100644 +--- a/ui/console.c ++++ b/ui/console.c +@@ -373,6 +373,31 @@ void qmp_screendump(const char *filename, bool has_device, const char *device, + ppm_save(filename, surface, errp); + } + ++void qmp___com_redhat_qxl_screendump(const char *id, const char *filename, Error **errp); ++void qmp___com_redhat_qxl_screendump(const char *id, const char *filename, Error **errp) ++{ ++ DeviceState *dev; ++ QemuConsole *con; ++ DisplaySurface *surface; ++ ++ dev = qdev_find_recursive(sysbus_get_default(), id); ++ if (NULL == dev) { ++ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, ++ "Device '%s' not found", id); ++ return; ++ } ++ ++ con = qemu_console_lookup_by_device(dev, 0); ++ if (con == NULL) { ++ error_setg(errp, "Device %s has no QemuConsole attached to it.", id); ++ return; ++ } ++ ++ graphic_hw_update(con); ++ surface = qemu_console_surface(con); ++ ppm_save(filename, surface, errp); ++} ++ + void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata) + { + if (!con) { +-- +1.8.3.1 + diff --git a/SOURCES/0009-seabios-paravirt-allow-more-than-1TB-in-x86-guest.patch b/SOURCES/0009-seabios-paravirt-allow-more-than-1TB-in-x86-guest.patch new file mode 100644 index 0000000..01bfaff --- /dev/null +++ b/SOURCES/0009-seabios-paravirt-allow-more-than-1TB-in-x86-guest.patch @@ -0,0 +1,43 @@ +From 0a103b3e40c37d33a57442ab41882e9f776ca8e7 Mon Sep 17 00:00:00 2001 +From: Andrea Arcangeli +Date: Tue, 8 Oct 2013 17:05:45 +0200 +Subject: seabios paravirt: allow more than 1TB in x86 guest + +RH-Author: Andrea Arcangeli +Message-id: <1381251945-13402-2-git-send-email-aarcange@redhat.com> +Patchwork-id: 54784 +O-Subject: [RHEL-7.0 qemu-kvm PATCH] seabios paravirt: allow more than 1TB in x86 guest +Bugzilla: 989677 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Gleb Natapov +RH-Acked-by: Laszlo Ersek + +This patch should be applied to the qemu-kvm rpm package at the same +time of the other one for seabios, so qemu will forward the ram_size +bits over 40 to seabios without losing them. + +Signed-off-by: Andrea Arcangeli +(cherry picked from commit 85123a6939a536f81470ad2e8afa5a7c72584dc0) +(cherry picked from commit 5bbfbb1394759b58edd32134cd5c43871938dac8) +(cherry picked from commit 506774310f8e4238a93dbd6be0e8d2a725b7305f) +(cherry picked from commit fa79d5440dd3e237f186963c5609f203737e68fc) +(cherry picked from commit 0d289939196d2ea236c11e3a2363f09077fa20f0) +--- + hw/i386/pc.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/hw/i386/pc.c b/hw/i386/pc.c +index d38a328..265decf 100644 +--- a/hw/i386/pc.c ++++ b/hw/i386/pc.c +@@ -479,6 +479,7 @@ void pc_cmos_init(PCMachineState *pcms, + rtc_set_memory(s, 0x5b, val); + rtc_set_memory(s, 0x5c, val >> 8); + rtc_set_memory(s, 0x5d, val >> 16); ++ rtc_set_memory(s, 0x5e, val >> 24); + + object_property_add_link(OBJECT(pcms), "rtc_state", + TYPE_ISA_DEVICE, +-- +1.8.3.1 + diff --git a/SOURCES/0010-vfio-cap-number-of-devices-that-can-be-assigned.patch b/SOURCES/0010-vfio-cap-number-of-devices-that-can-be-assigned.patch new file mode 100644 index 0000000..e0ae0ef --- /dev/null +++ b/SOURCES/0010-vfio-cap-number-of-devices-that-can-be-assigned.patch @@ -0,0 +1,77 @@ +From dcf1c9f485cefd4dad8040efb9555f33a118074b Mon Sep 17 00:00:00 2001 +From: Bandan Das +Date: Tue, 3 Dec 2013 20:05:13 +0100 +Subject: vfio: cap number of devices that can be assigned + +RH-Author: Bandan Das +Message-id: <1386101113-31560-3-git-send-email-bsd@redhat.com> +Patchwork-id: 55984 +O-Subject: [PATCH RHEL7 qemu-kvm v2 2/2] vfio: cap number of devices that can be assigned +Bugzilla: 678368 +RH-Acked-by: Alex Williamson +RH-Acked-by: Marcelo Tosatti +RH-Acked-by: Michael S. Tsirkin + +Go through all groups to get count of total number of devices +active to enforce limit + +Reasoning from Alex for the limit(32) - Assuming 3 slots per +device, with 125 slots (number of memory slots for RHEL 7), +we can support almost 40 devices and still have few slots left +for other uses. Stepping down a bit, the number 32 arbitrarily +matches the number of slots on a PCI bus and is also a nice power +of two. + +Signed-off-by: Bandan Das + +Rebase notes (2.8.0): +- removed return value for vfio_realize (commit 1a22aca) + +Merged patches (2.9.0): +- 17eb774 vfio: Use error_setg when reporting max assigned device overshoot + +(cherry picked from commit 9fa3c9fc6dfcde76d80db1aa601b2d577f72ceec) +(cherry picked from commit 3cb35556dc7d994f203d732fe952f95fcdb03c0a) +(cherry picked from commit 17e46c50515e8935c0f99a868fe07bd3c92a4586) +(cherry picked from commit 726bd82965a9e1a58065a3226597b3c2bafdbf68) +(cherry picked from commit efd8c5d78242c2e0892e165b75d2c8f1afb172a6) +--- + hw/vfio/pci.c | 15 ++++++++++++++- + 1 file changed, 14 insertions(+), 1 deletion(-) + +diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c +index b9bc6cd..34b9d19 100644 +--- a/hw/vfio/pci.c ++++ b/hw/vfio/pci.c +@@ -35,6 +35,7 @@ + #include "qapi/error.h" + + #define MSIX_CAP_LENGTH 12 ++#define MAX_DEV_ASSIGN_CMDLINE 32 + + static void vfio_disable_interrupts(VFIOPCIDevice *vdev); + static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled); +@@ -2807,7 +2808,19 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) + ssize_t len; + struct stat st; + int groupid; +- int i, ret; ++ int ret, i = 0; ++ ++ QLIST_FOREACH(group, &vfio_group_list, next) { ++ QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { ++ i++; ++ } ++ } ++ ++ if (i >= MAX_DEV_ASSIGN_CMDLINE) { ++ error_setg(errp, "Maximum supported vfio devices (%d) " ++ "already attached", MAX_DEV_ASSIGN_CMDLINE); ++ return; ++ } + + if (!vdev->vbasedev.sysfsdev) { + if (!(~vdev->host.domain || ~vdev->host.bus || +-- +1.8.3.1 + diff --git a/SOURCES/0011-QMP-Forward-port-__com.redhat_drive_del-from-RHEL-6.patch b/SOURCES/0011-QMP-Forward-port-__com.redhat_drive_del-from-RHEL-6.patch new file mode 100644 index 0000000..2f603b1 --- /dev/null +++ b/SOURCES/0011-QMP-Forward-port-__com.redhat_drive_del-from-RHEL-6.patch @@ -0,0 +1,172 @@ +From 0c3a08560e7aecb443e2f4a70d909aef413ca66f Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Tue, 14 Mar 2017 14:03:39 +0100 +Subject: QMP: Forward-port __com.redhat_drive_del from RHEL-6 + +RH-Author: Markus Armbruster +Message-id: <1387262799-10350-3-git-send-email-armbru@redhat.com> +Patchwork-id: 56292 +O-Subject: [PATCH v2 2/6] QMP: Forward-port __com.redhat_drive_del from RHEL-6 +Bugzilla: 889051 +RH-Acked-by: Fam Zheng +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Luiz Capitulino + +From: Markus Armbruster + +Upstream has drive_del, but only in HMP. The backport to RHEL-6 added +it to QMP as well. Since the QMP command is a downstream extension, +it needs the __com.redhat_ prefix. Since RHEL-6 doesn't have separate +definition of QMP and HMP commands, both the QMP and the HMP command +got the prefix. + +RHEL-7 inherits HMP command drive_del from upstream. Add QMP command +__com.redhat_drive_del for RHEL-6 compatibility. + +If we needed similar compatibility for the HMP command, we'd have to +add __com.redhat_drive_del as alias for drive_del. But we don't. + +Code copied from RHEL-6's qemu-monitor.hx as of +qemu-kvm-0.12.1.2-2.418.el6. It has a "drive_del" without the prefix +in the documentation. Fixed here. Hardly worth fixing in RHEL-6 now. + +Signed-off-by: Markus Armbruster + +Rebase notes (2.9.0): +- documentation moved from docs/qmp-commands.txt to qapi/block.json +- replace qmp_x_blockdev_del with qmp_blockdev_del (due to 79b7a77) + +Rebase notes (2.8.0): +- qmp-commands.hx replaced by docs/qmp-commands.txt (commit bd6092e) +- Changed qmp_x_blockdev_del arguments (upstream) + +Rebase notes (2.4.0): +- use traditional cmd for qmp +- remove user_print + +Merged patches (2.11.0): +- bacc223630 blockdev: Report proper error class in __com.redhat.drive_del + +Merged patches (2.9.0): +- 4831182 QMP: Fix forward port of __com.redhat_drive_del + +Merged patches (2.7.0): +- 85786e0 Fix crash with __com.redhat_drive_del + +(cherry picked from commit b7a0cafd6494cd3855fe10934314b6b1d2df5d2d) +(cherry picked from commit 60b62c9d02e5e19e4cfa6eaeb6652339d4c4ede5) +(cherry picked from commit 9534cde84567c6e83bf925911781737b4c15b145) +(cherry picked from commit a5aa34fcd3f532e91535a4c568c66e30671120ca) +(cherry picked from commit 26bcd54a059c1949d6f1f2d72cd04794776446e1) +--- + blockdev.c | 29 +++++++++++++++++------------ + qapi/block.json | 23 +++++++++++++++++++++++ + 2 files changed, 40 insertions(+), 12 deletions(-) + +diff --git a/blockdev.c b/blockdev.c +index c31bf3d..f65d37c 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -2948,32 +2948,28 @@ BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, + return ret; + } + +-void hmp_drive_del(Monitor *mon, const QDict *qdict) ++void qmp___com_redhat_drive_del(const char *id, Error **errp) + { +- const char *id = qdict_get_str(qdict, "id"); + BlockBackend *blk; + BlockDriverState *bs; + AioContext *aio_context; +- Error *local_err = NULL; + + bs = bdrv_find_node(id); + if (bs) { +- qmp_blockdev_del(id, &local_err); +- if (local_err) { +- error_report_err(local_err); +- } ++ qmp_blockdev_del(id, errp); + return; + } + + blk = blk_by_name(id); + if (!blk) { +- error_report("Device '%s' not found", id); ++ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, ++ "Device '%s' not found", id); + return; + } + + if (!blk_legacy_dinfo(blk)) { +- error_report("Deleting device added with blockdev-add" +- " is not supported"); ++ error_setg(errp, "Deleting device added with blockdev-add" ++ " is not supported"); + return; + } + +@@ -2982,8 +2978,7 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict) + + bs = blk_bs(blk); + if (bs) { +- if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) { +- error_report_err(local_err); ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, errp)) { + aio_context_release(aio_context); + return; + } +@@ -3008,6 +3003,16 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict) + aio_context_release(aio_context); + } + ++void hmp_drive_del(Monitor *mon, const QDict *qdict) ++{ ++ Error *local_err = NULL; ++ ++ qmp___com_redhat_drive_del(qdict_get_str(qdict, "id"), &local_err); ++ if (local_err) { ++ error_report_err(local_err); ++ } ++} ++ + void qmp_block_resize(bool has_device, const char *device, + bool has_node_name, const char *node_name, + int64_t size, Error **errp) +diff --git a/qapi/block.json b/qapi/block.json +index c694524..e1fe18e 100644 +--- a/qapi/block.json ++++ b/qapi/block.json +@@ -188,6 +188,29 @@ + '*force': 'bool' } } + + ## ++# @__com.redhat_drive_del: ++# ++# Remove host block device. ++# ++# Remove host block device. The result is that guest generated IO is no longer ++# submitted against the host device underlying the disk. Once a drive has ++# been deleted, the QEMU Block layer returns -EIO which results in IO ++# errors in the guest for applications that are reading/writing to the device. ++# These errors are always reported to the guest, regardless of the drive's error ++# actions (drive options rerror, werror). ++# ++# @id: the device's ID ++# ++# Example: ++# ++# -> { "execute": "__com.redhat_drive_del", "arguments": { "id": "block1" } } ++# <- { "return": {} } ++# ++## ++{ 'command': '__com.redhat_drive_del', ++ 'data': { 'id': 'str' } } ++ ++## + # @nbd-server-start: + # + # Start an NBD server listening on the given host and port. Block +-- +1.8.3.1 + diff --git a/SOURCES/0012-QMP-Forward-port-__com.redhat_drive_add-from-RHEL-6.patch b/SOURCES/0012-QMP-Forward-port-__com.redhat_drive_add-from-RHEL-6.patch new file mode 100644 index 0000000..3f19da3 --- /dev/null +++ b/SOURCES/0012-QMP-Forward-port-__com.redhat_drive_add-from-RHEL-6.patch @@ -0,0 +1,233 @@ +From f8f33512aefd597c9664eeb26a97cafc12d958da Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Tue, 14 Mar 2017 14:16:45 +0100 +Subject: QMP: Forward-port __com.redhat_drive_add from RHEL-6 + +RH-Author: Markus Armbruster +Message-id: <1387262799-10350-4-git-send-email-armbru@redhat.com> +Patchwork-id: 56294 +O-Subject: [PATCH v2 3/6] QMP: Forward-port __com.redhat_drive_add from RHEL-6 +Bugzilla: 889051 +RH-Acked-by: Fam Zheng +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Luiz Capitulino + +From: Markus Armbruster + +Code taken from RHEL-6 as of qemu-kvm-0.12.1.2-2.418.el6, backported +and fixed up as follows: + +* Update simple_drive_add() for commit 4e89978 "qemu-option: + qemu_opts_from_qdict(): use error_set()". + +* Update simple_drive_add() for commit 2d0d283 "Support default block + interfaces per QEMUMachine". + +* Add comment explaining drive_init() error reporting hacks to + simple_drive_add(). + +* qemu-monitor.hx has been split into qmp-commands.hx and + hmp-commands.hx. Copy the QMP parts to qmp-commands.hx. Clean up + second example slightly. + +* Trailing whitespace cleaned up. + +Signed-off-by: Markus Armbruster + +Rebase notes (2.9.0): +- documentation moved from docs/qmp-commands.txt to qapi/block.json +- added argument to qmp_register_command +- explicit argument listing for correct documentation checking + +Rebase notes (2.8.0): +- qmp-commands.hx replaced by docs/qmp-commands.txt (commit bd6092e) + +Rebase notes (2.6.0): +- Added qapi/error.h to device-hotplug.c + +Rebase notes (2.4.0): +- removed qerror_report +- removed user_print + +Merged patches (2.9.0): +- 599ec5f QMP: Fix forward port of __com.redhat_drive_add + +Merged patches (2.12.0): +- 8679189 qmp: Report __com.redhat_drive_add error to monitor + +Merged patches (2.7.0): +- 20af75a QMP: Relax __com.redhat_drive_add parameter checking + +(cherry picked from commit 6b8c0495aa317dfc5caa6d204373140811880d1a) +(cherry picked from commit 5d293a214ca093a133011d0edc9039b1e71b4219) +(cherry picked from commit 45f0a917e663aab47217bb8b6cc5bca3a8b1baac) +(cherry picked from commit e15f0bd3c4ccce535ae9a08156aebcd780c38132) +(cherry picked from commit c7ae434601e7439371012494cd026179369af330) +--- + device-hotplug.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++ + include/sysemu/blockdev.h | 2 ++ + monitor.c | 2 ++ + qapi/block.json | 45 +++++++++++++++++++++++++++++++++ + 4 files changed, 112 insertions(+) + +diff --git a/device-hotplug.c b/device-hotplug.c +index 23fd665..5575eb7 100644 +--- a/device-hotplug.c ++++ b/device-hotplug.c +@@ -33,6 +33,10 @@ + #include "sysemu/sysemu.h" + #include "monitor/monitor.h" + #include "block/block_int.h" ++#include "qemu/error-report.h" ++#include "qapi/qmp/qerror.h" ++#include "qapi/error.h" ++ + + static DriveInfo *add_init_drive(const char *optstr) + { +@@ -91,3 +95,62 @@ err: + blk_unref(blk); + } + } ++ ++static void check_parm(const char *key, QObject *obj, void *opaque) ++{ ++ static const char *unwanted_keys[] = { ++ "bus", "unit", "index", "if", "boot", "addr", ++ NULL ++ ++ }; ++ int *stopped = opaque; ++ const char **p; ++ ++ if (*stopped) { ++ return; ++ } ++ ++ for (p = unwanted_keys; *p; p++) { ++ if (!strcmp(key, *p)) { ++ error_report(QERR_INVALID_PARAMETER, key); ++ *stopped = 1; ++ return; ++ } ++ } ++} ++ ++void simple_drive_add(QDict *qdict, QObject **ret_data, Error **errp) ++{ ++ int stopped; ++ Error *local_err = NULL; ++ QemuOpts *opts; ++ DriveInfo *dinfo; ++ MachineClass *mc; ++ ++ if (!qdict_haskey(qdict, "id")) { ++ error_setg(errp, QERR_MISSING_PARAMETER, "id"); ++ return; ++ } ++ ++ stopped = 0; ++ qdict_iter(qdict, check_parm, &stopped); ++ if (stopped) { ++ return; ++ } ++ ++ opts = qemu_opts_from_qdict(&qemu_drive_opts, qdict, &local_err); ++ if (!opts) { ++ error_propagate(errp, local_err); ++ return; ++ } ++ qemu_opt_set(opts, "if", "none", &error_abort); ++ mc = MACHINE_GET_CLASS(current_machine); ++ dinfo = drive_new(opts, mc->block_default_type); ++ if (!dinfo) { ++ error_setg(errp, QERR_DEVICE_INIT_FAILED, qemu_opts_id(opts)); ++ qemu_opts_del(opts); ++ return; ++ } ++ ++ return; ++} +diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h +index ac22f2a..3bda14c 100644 +--- a/include/sysemu/blockdev.h ++++ b/include/sysemu/blockdev.h +@@ -63,4 +63,6 @@ DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type); + + void hmp_commit(Monitor *mon, const QDict *qdict); + void hmp_drive_del(Monitor *mon, const QDict *qdict); ++ ++void simple_drive_add(QDict *qdict, QObject **ret_data, Error **errp); + #endif +diff --git a/monitor.c b/monitor.c +index 1813d34..c587f21 100644 +--- a/monitor.c ++++ b/monitor.c +@@ -1204,6 +1204,8 @@ static void monitor_init_qmp_commands(void) + QCO_NO_OPTIONS); + qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add, + QCO_NO_OPTIONS); ++ qmp_register_command(&qmp_commands, "__com.redhat_drive_add", simple_drive_add, ++ QCO_NO_OPTIONS); + + qmp_unregister_commands_hack(); + +diff --git a/qapi/block.json b/qapi/block.json +index e1fe18e..48c732f 100644 +--- a/qapi/block.json ++++ b/qapi/block.json +@@ -211,6 +211,51 @@ + 'data': { 'id': 'str' } } + + ## ++# @__com.redhat_drive_add: ++# ++# Create a drive similar to -drive if=none. ++# ++# @id: Drive ID, must be unique ++# @file: Disk image ++# @format: Disk format ++# @aio: How to perform asynchronous disk I/O ++# @cache: Host cache use policy ++# @cyls, "heads", "secs": Disk geometry ++# @trans: BIOS translation mode ++# @media: Media type ++# @readonly: Open image read-only ++# @rerror: What to do on read error ++# @werror: What to do on write error ++# @serial: Drive serial number ++# @snapshot: Enable snapshot mode ++# @copy-on-read: Enable copy-on-read mode ++# ++# Example: ++# ++# 1. Add a drive without medium: ++# ++# -> { "execute": "__com.redhat_drive_add", "arguments": { "id": "foo" } } ++# <- {"return": {}} ++# ++# 2. Add a drive with medium: ++# ++# -> { "execute": "__com.redhat_drive_add", ++# "arguments": { "id": "bar", "file": "tmp.qcow2", "format": "qcow2" } } ++# <- {"return": {}} ++# ++## ++{ 'command': '__com.redhat_drive_add', ++ 'data': { 'id': 'str', '*file': 'str', '*format': 'str', ++ '*aio': 'BlockdevAioOptions', ++ '*cache': 'BlockdevCacheOptions', ++ '*cyls': 'int', '*heads': 'int', '*secs': 'int', ++ '*trans': 'str', '*media': 'str', ++ '*readonly': 'bool', '*snapshot': 'bool', ++ '*rerror': 'str', '*werror': 'str', ++ '*copy-on-read': 'bool', '*serial': 'str' }, ++ 'gen': false } # just to minimize porting churn ++ ++## + # @nbd-server-start: + # + # Start an NBD server listening on the given host and port. Block +-- +1.8.3.1 + diff --git a/SOURCES/0013-HMP-Forward-port-__com.redhat_drive_add-from-RHEL-6.patch b/SOURCES/0013-HMP-Forward-port-__com.redhat_drive_add-from-RHEL-6.patch new file mode 100644 index 0000000..47b941f --- /dev/null +++ b/SOURCES/0013-HMP-Forward-port-__com.redhat_drive_add-from-RHEL-6.patch @@ -0,0 +1,175 @@ +From 4825ef2a732c3df196b681474a5aba44622da0a6 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Tue, 14 Mar 2017 14:25:44 +0100 +Subject: HMP: Forward-port __com.redhat_drive_add from RHEL-6 + +RH-Author: Markus Armbruster +Message-id: <1387262799-10350-5-git-send-email-armbru@redhat.com> +Patchwork-id: 56295 +O-Subject: [PATCH v2 4/6] HMP: Forward-port __com.redhat_drive_add from RHEL-6 +Bugzilla: 889051 +RH-Acked-by: Fam Zheng +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Luiz Capitulino + +From: Markus Armbruster + +Signed-off-by: Markus Armbruster + +Rebase notes (2.8.0): +- qmp-commands.hx replaced by docs/qmp-commands.txt (commit bd6092e) +- mhandler.cmd attribute renamed to cmd (commit 2b9e357) + +Rebase notes (2.4.0): +- removed user_print + +Merged patches (2.9.0): +- 854d5bf Drop macro RFQDN_REDHAT (partially) +- 4804631 HMP: Clean up botched conflict resolution in user manual +- bb06f4 HMP: Fix documentation of __com.redhat.drive_add + +Merged patches (2.7.0): +- a28dcc5 Fix crash bug in rebase of__com.redhat_drive_add + +(cherry picked from commit 913177df4933b58f50ba55ad2e1205b03b61fc54) +(cherry picked from commit 365e9ae133897df34817774c93b40ecee0821cf1) +(cherry picked from commit d1b35add1a3e78f8b3fd321f251f8c2ab098a3a4) +(cherry picked from commit d208565cba99223f730bcf7fc3598d02d1746462) +(cherry picked from commit 8fae16b0f498343c87c1dd945936857b45ba9ee8) +--- + blockdev.c | 14 ++++++++++++++ + device-hotplug.c | 12 +++++++++++- + hmp-commands.hx | 14 ++++++++++++++ + include/sysemu/blockdev.h | 4 +++- + include/sysemu/sysemu.h | 1 + + monitor.c | 2 +- + vl.c | 1 + + 7 files changed, 45 insertions(+), 3 deletions(-) + +diff --git a/blockdev.c b/blockdev.c +index f65d37c..e941b99 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -4361,3 +4361,17 @@ QemuOptsList qemu_drive_opts = { + { /* end of list */ } + }, + }; ++ ++QemuOptsList qemu_simple_drive_opts = { ++ .name = "simple-drive", ++ .implied_opt_name = "format", ++ .head = QTAILQ_HEAD_INITIALIZER(qemu_simple_drive_opts.head), ++ .desc = { ++ /* ++ * no elements => accept any ++ * sanity checking will happen later ++ * when setting device properties ++ */ ++ { /* end if list */ } ++ } ++}; +diff --git a/device-hotplug.c b/device-hotplug.c +index 5575eb7..cff46ee 100644 +--- a/device-hotplug.c ++++ b/device-hotplug.c +@@ -119,7 +119,7 @@ static void check_parm(const char *key, QObject *obj, void *opaque) + } + } + +-void simple_drive_add(QDict *qdict, QObject **ret_data, Error **errp) ++void qmp_simple_drive_add(QDict *qdict, QObject **ret_data, Error **errp) + { + int stopped; + Error *local_err = NULL; +@@ -154,3 +154,13 @@ void simple_drive_add(QDict *qdict, QObject **ret_data, Error **errp) + + return; + } ++ ++void hmp_simple_drive_add(Monitor *mon, const QDict *qdict) ++{ ++ Error *err = NULL; ++ ++ qmp_simple_drive_add((QDict *)qdict, NULL, &err); ++ if (err) { ++ error_report_err(err); ++ } ++} +diff --git a/hmp-commands.hx b/hmp-commands.hx +index 01dcbb2..399e427 100644 +--- a/hmp-commands.hx ++++ b/hmp-commands.hx +@@ -1295,6 +1295,20 @@ Add drive to PCI storage controller. + ETEXI + + { ++ .name = "__com.redhat_drive_add", ++ .args_type = "simple-drive:O", ++ .params = "id=name,[file=file][,format=f][,media=d]...", ++ .help = "Create a drive similar to -drive if=none.", ++ .cmd = hmp_simple_drive_add, ++ }, ++ ++STEXI ++@item __com.redhat_drive_add ++@findex __com.redhat_drive_add ++Create a drive similar to -drive if=none. ++ETEXI ++ ++ { + .name = "pcie_aer_inject_error", + .args_type = "advisory_non_fatal:-a,correctable:-c," + "id:s,error_status:s," +diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h +index 3bda14c..c19c36a 100644 +--- a/include/sysemu/blockdev.h ++++ b/include/sysemu/blockdev.h +@@ -64,5 +64,7 @@ DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type); + void hmp_commit(Monitor *mon, const QDict *qdict); + void hmp_drive_del(Monitor *mon, const QDict *qdict); + +-void simple_drive_add(QDict *qdict, QObject **ret_data, Error **errp); ++void hmp_simple_drive_add(Monitor *mon, const QDict *qdict); ++void qmp_simple_drive_add(QDict *qdict, QObject **ret_data, Error **errp); ++ + #endif +diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h +index 5832c38..5d8634b 100644 +--- a/include/sysemu/sysemu.h ++++ b/include/sysemu/sysemu.h +@@ -200,6 +200,7 @@ extern QemuOptsList qemu_legacy_drive_opts; + extern QemuOptsList qemu_common_drive_opts; + extern QemuOptsList qemu_drive_opts; + extern QemuOptsList bdrv_runtime_opts; ++extern QemuOptsList qemu_simple_drive_opts; + extern QemuOptsList qemu_chardev_opts; + extern QemuOptsList qemu_device_opts; + extern QemuOptsList qemu_netdev_opts; +diff --git a/monitor.c b/monitor.c +index c587f21..4f595ae 100644 +--- a/monitor.c ++++ b/monitor.c +@@ -1204,7 +1204,7 @@ static void monitor_init_qmp_commands(void) + QCO_NO_OPTIONS); + qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add, + QCO_NO_OPTIONS); +- qmp_register_command(&qmp_commands, "__com.redhat_drive_add", simple_drive_add, ++ qmp_register_command(&qmp_commands, "__com.redhat_drive_add", qmp_simple_drive_add, + QCO_NO_OPTIONS); + + qmp_unregister_commands_hack(); +diff --git a/vl.c b/vl.c +index 03950fc..8dee9d0 100644 +--- a/vl.c ++++ b/vl.c +@@ -3078,6 +3078,7 @@ int main(int argc, char **argv, char **envp) + qemu_add_drive_opts(&qemu_common_drive_opts); + qemu_add_drive_opts(&qemu_drive_opts); + qemu_add_drive_opts(&bdrv_runtime_opts); ++ qemu_add_opts(&qemu_simple_drive_opts); + qemu_add_opts(&qemu_chardev_opts); + qemu_add_opts(&qemu_device_opts); + qemu_add_opts(&qemu_netdev_opts); +-- +1.8.3.1 + diff --git a/SOURCES/0014-Add-support-statement-to-help-output.patch b/SOURCES/0014-Add-support-statement-to-help-output.patch new file mode 100644 index 0000000..a8a8e4c --- /dev/null +++ b/SOURCES/0014-Add-support-statement-to-help-output.patch @@ -0,0 +1,60 @@ +From e93d5457df2dde297089b44d185f5fe5b608d744 Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Wed, 4 Dec 2013 18:53:17 +0100 +Subject: Add support statement to -help output + +RH-Author: Eduardo Habkost +Message-id: <1386183197-27761-1-git-send-email-ehabkost@redhat.com> +Patchwork-id: 55994 +O-Subject: [qemu-kvm RHEL7 PATCH] Add support statement to -help output +Bugzilla: 972773 +RH-Acked-by: Miroslav Rezanina +RH-Acked-by: knoel@redhat.com +RH-Acked-by: Paolo Bonzini + +Add support statement to -help output, reporting direct qemu-kvm usage +as unsupported by Red Hat, and advising users to use libvirt instead. + +Signed-off-by: Eduardo Habkost +(cherry picked from commit 2a07700936e39856cc9f149c6a6517f0715536a6) +(cherry picked from commit 5dd2f4706e2fef945771949e59a8fcc1b5452de9) +(cherry picked from commit d660aafa98635de49f7f7a416a4e910d99b10bce) +(cherry picked from commit 6a5d4b1f602318a5b6f4e49bed90624c67848629) +(cherry picked from commit 506816e81f899d3daa6202e50ef12a21dcee7b96) +--- + vl.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/vl.c b/vl.c +index 8dee9d0..7fce42d 100644 +--- a/vl.c ++++ b/vl.c +@@ -1953,9 +1953,17 @@ static void version(void) + QEMU_COPYRIGHT "\n"); + } + ++static void print_rh_warning(void) ++{ ++ printf("\nWARNING: Direct use of qemu-kvm from the command line is not supported by Red Hat.\n" ++ "WARNING: Use libvirt as the stable management interface.\n" ++ "WARNING: Some command line options listed here may not be available in future releases.\n\n"); ++} ++ + static void help(int exitcode) + { + version(); ++ print_rh_warning(); + printf("usage: %s [options] [disk_image]\n\n" + "'disk_image' is a raw hard disk image for IDE hard disk 0\n\n", + error_get_progname()); +@@ -1972,6 +1980,7 @@ static void help(int exitcode) + "\n" + QEMU_HELP_BOTTOM "\n"); + ++ print_rh_warning(); + exit(exitcode); + } + +-- +1.8.3.1 + diff --git a/SOURCES/0015-vl-Round-memory-sizes-below-2MiB-up-to-2MiB.patch b/SOURCES/0015-vl-Round-memory-sizes-below-2MiB-up-to-2MiB.patch new file mode 100644 index 0000000..3212d85 --- /dev/null +++ b/SOURCES/0015-vl-Round-memory-sizes-below-2MiB-up-to-2MiB.patch @@ -0,0 +1,50 @@ +From dd20dff28940349e9fdca87583c8878607ef3734 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 7 Jul 2014 10:28:38 +0200 +Subject: vl: Round memory sizes below 2MiB up to 2MiB + +RH-Author: Markus Armbruster +Message-id: <1387459965-19517-2-git-send-email-armbru@redhat.com> +Patchwork-id: 56389 +O-Subject: [PATCH 7.0 qemu-kvm 1/1] vl: Round memory sizes below 2MiB up to 2MiB +Bugzilla: 999836 +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Luiz Capitulino +RH-Acked-by: Igor Mammedov + +From: Markus Armbruster + +SeaBIOS requires at least 1MiB of RAM, but doesn't doesn't check for +it. It simply assumes it's there, and crashes when it isn't, often +without any indication what's wrong. No upstream SeaBIOS fix +expected. + +In RHEL-6, we round memory sizes below 2MiB up to 2MiB to protect +SeaBIOS (commit 551c098 and commit b9d6c40). Do the same for RHEL-7. +Not wanted upstream. + +Signed-off-by: Markus Armbruster +(cherry picked from commit 5c401c750c8e52fe5c67b4e60143a862a0d584c1) +(cherry picked from commit 29e44e4e1f10ac7d2f9ac824b928518f3a2ccc10) +(cherry picked from commit 07a664c4bc80f9db8fa027a654214e7f09ee81e0) +(cherry picked from commit be20ffb392aae07927923f8377bc59fb6a5707aa) +(cherry picked from commit 91707a42f802c7bd20f37959849c127d94c71f75) +--- + vl.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/vl.c b/vl.c +index 7fce42d..cbd6bcc 100644 +--- a/vl.c ++++ b/vl.c +@@ -2932,6 +2932,7 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, + } + + sz = QEMU_ALIGN_UP(sz, 8192); ++ sz = MAX(sz, 2 * 1024 * 1024); + ram_size = sz; + if (ram_size != sz) { + error_report("ram size too large"); +-- +1.8.3.1 + diff --git a/SOURCES/0016-use-recommended-max-vcpu-count.patch b/SOURCES/0016-use-recommended-max-vcpu-count.patch new file mode 100644 index 0000000..7134dcd --- /dev/null +++ b/SOURCES/0016-use-recommended-max-vcpu-count.patch @@ -0,0 +1,115 @@ +From 0abcd4ae8344b523f2b462a182fddf181d2b2fa5 Mon Sep 17 00:00:00 2001 +From: Andrew Jones +Date: Tue, 21 Jan 2014 10:46:52 +0100 +Subject: use recommended max vcpu count + +RH-Author: Andrew Jones +Message-id: <1390301212-15344-1-git-send-email-drjones@redhat.com> +Patchwork-id: 56862 +O-Subject: [RHEL7.0 qemu-kvm PATCH v6] use recommended max vcpu count +Bugzilla: 998708 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Marcelo Tosatti + +The recommended vcpu max limit (KVM_CAP_NR_VCPUS) should be used instead +of the actual max vcpu limit (KVM_CAP_MAX_VCPUS) to give an error. + +This commit matches the limit to current KVM_CAP_NR_VCPUS value. + +Rebase notes (2.11.0): +- Update commit log + +Merged patches 2.12.0: +- 6553300 redhat: globally limit the maximum number of CPUs +- 8453db7 redhat: remove manual max_cpus limitations for ppc + +Merged patches (2.11.0): +- fdeef3c1c7 RHEL: Set vcpus hard limit to 240 for Power +- 0584216921 Match POWER max cpus to x86 + +Signed-off-by: Andrew Jones +(cherry picked from commit a4ceb63bdc5cbac19f5f633ec761b9de0dedb55e) +(cherry picked from commit a1f26d85171b4d554225150053700e93ba6eba10) +(cherry picked from commit 00b09fb8ae2fa23abfaba4e89f7daa1ba0ef9ec5) +(cherry picked from commit bd420a814a24c56d1ff1564d92f36219106ef81b) +(cherry picked from commit 79b73eacc15f2c06587734c6259b49d911327369) +--- + accel/kvm/kvm-all.c | 12 ++++++++++++ + vl.c | 23 +++++++++++++++++++++++ + 2 files changed, 35 insertions(+) + +diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c +index ffee68e..3f1c06e 100644 +--- a/accel/kvm/kvm-all.c ++++ b/accel/kvm/kvm-all.c +@@ -1587,6 +1587,18 @@ static int kvm_init(MachineState *ms) + soft_vcpus_limit = kvm_recommended_vcpus(s); + hard_vcpus_limit = kvm_max_vcpus(s); + ++#ifdef HOST_PPC64 ++ /* ++ * On POWER, the kernel advertises a soft limit based on the ++ * number of CPU threads on the host. We want to allow exceeding ++ * this for testing purposes, so we don't want to set hard limit ++ * to soft limit as on x86. ++ */ ++#else ++ /* RHEL doesn't support nr_vcpus > soft_vcpus_limit */ ++ hard_vcpus_limit = soft_vcpus_limit; ++#endif ++ + while (nc->name) { + if (nc->num > soft_vcpus_limit) { + warn_report("Number of %s cpus requested (%d) exceeds " +diff --git a/vl.c b/vl.c +index cbd6bcc..f3acab3 100644 +--- a/vl.c ++++ b/vl.c +@@ -135,6 +135,12 @@ int main(int argc, char **argv) + #define MAX_VIRTIO_CONSOLES 1 + #define MAX_SCLP_CONSOLES 1 + ++#if defined(CONFIG_RHV) ++#define RHEL_MAX_CPUS 384 ++#else ++#define RHEL_MAX_CPUS 240 ++#endif ++ + static const char *data_dir[16]; + static int data_dir_idx; + const char *bios_name = NULL; +@@ -1520,6 +1526,20 @@ MachineClass *find_default_machine(void) + return mc; + } + ++/* Maximum number of CPUs limited for Red Hat Enterprise Linux */ ++static void limit_max_cpus_in_machines(void) ++{ ++ GSList *el, *machines = object_class_get_list(TYPE_MACHINE, false); ++ ++ for (el = machines; el; el = el->next) { ++ MachineClass *mc = el->data; ++ ++ if (mc->max_cpus > RHEL_MAX_CPUS) { ++ mc->max_cpus = RHEL_MAX_CPUS; ++ } ++ } ++} ++ + MachineInfoList *qmp_query_machines(Error **errp) + { + GSList *el, *machines = object_class_get_list(TYPE_MACHINE, false); +@@ -4084,6 +4104,9 @@ int main(int argc, char **argv, char **envp) + + replay_configure(icount_opts); + ++ /* Maximum number of CPUs limited for Red Hat Enterprise Linux */ ++ limit_max_cpus_in_machines(); ++ + machine_class = select_machine(); + + set_memory_options(&ram_slots, &maxram_size, machine_class); +-- +1.8.3.1 + diff --git a/SOURCES/0017-Add-support-for-simpletrace.patch b/SOURCES/0017-Add-support-for-simpletrace.patch new file mode 100644 index 0000000..a89736f --- /dev/null +++ b/SOURCES/0017-Add-support-for-simpletrace.patch @@ -0,0 +1,121 @@ +From 7fde5fc94f5007470c1f0dc985fd0e7f1a59cfe4 Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Thu, 8 Oct 2015 09:50:17 +0200 +Subject: Add support for simpletrace + +As simpletrace is upstream, we just need to properly handle it during rpmbuild. + +Signed-off-by: Miroslav Rezanina + +Rebase notes (2.9.0): +- Added group argument for tracetool.py (upstream) + +Rebase notes (2.8.0): +- Changed tracetool.py parameters + +Merged patches (2.3.0): +- db959d6 redhat/qemu-kvm.spec.template: Install qemu-kvm-simpletrace.stp +- 5292fc3 trace: add SystemTap init scripts for simpletrace bridge +- eda9e5e simpletrace: install simpletrace.py +- 85c4c8f trace: add systemtap-initscript README file to RPM + +(cherry picked from commit bfc1d7f3628f2ffbabbae71d57a506cea6663ddf) +(cherry picked from commit 4aca5a1b194d1beb690daf4ca9d51b9f791dbf2e) +(cherry picked from commit f09ad4d02a20aa0d3060d0ff9933b74c78b45ea1) +(cherry picked from commit 4705eac2bb8026c929bc33c87fe691eeb3c7cffc) +--- + .gitignore | 2 ++ + Makefile | 4 +++ + README.systemtap | 43 +++++++++++++++++++++++++++++++++ + redhat/qemu-kvm.spec.template | 27 +++++++++++++++++++-- + scripts/systemtap/conf.d/qemu_kvm.conf | 4 +++ + scripts/systemtap/script.d/qemu_kvm.stp | 1 + + 6 files changed, 79 insertions(+), 2 deletions(-) + create mode 100644 README.systemtap + create mode 100644 scripts/systemtap/conf.d/qemu_kvm.conf + create mode 100644 scripts/systemtap/script.d/qemu_kvm.stp + +diff --git a/Makefile b/Makefile +index 2ffac2b..bdd8223 100644 +--- a/Makefile ++++ b/Makefile +@@ -867,6 +867,10 @@ endif + $(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(qemu_datadir)/keymaps"; \ + done + $(INSTALL_DATA) $(BUILD_DIR)/trace-events-all "$(DESTDIR)$(qemu_datadir)/trace-events-all" ++ $(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/systemtap/script.d" ++ $(INSTALL_DATA) $(SRC_PATH)/scripts/systemtap/script.d/qemu_kvm.stp "$(DESTDIR)$(qemu_datadir)/systemtap/script.d/" ++ $(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/systemtap/conf.d" ++ $(INSTALL_DATA) $(SRC_PATH)/scripts/systemtap/conf.d/qemu_kvm.conf "$(DESTDIR)$(qemu_datadir)/systemtap/conf.d/" + for d in $(TARGET_DIRS); do \ + $(MAKE) $(SUBDIR_MAKEFLAGS) TARGET_DIR=$$d/ -C $$d $@ || exit 1 ; \ + done +diff --git a/README.systemtap b/README.systemtap +new file mode 100644 +index 0000000..ad913fc +--- /dev/null ++++ b/README.systemtap +@@ -0,0 +1,43 @@ ++QEMU tracing using systemtap-initscript ++--------------------------------------- ++ ++You can capture QEMU trace data all the time using systemtap-initscript. This ++uses SystemTap's flight recorder mode to trace all running guests to a ++fixed-size buffer on the host. Old trace entries are overwritten by new ++entries when the buffer size wraps. ++ ++1. Install the systemtap-initscript package: ++ # yum install systemtap-initscript ++ ++2. Install the systemtap scripts and the conf file: ++ # cp /usr/share/qemu-kvm/systemtap/script.d/qemu_kvm.stp /etc/systemtap/script.d/ ++ # cp /usr/share/qemu-kvm/systemtap/conf.d/qemu_kvm.conf /etc/systemtap/conf.d/ ++ ++The set of trace events to enable is given in qemu_kvm.stp. This SystemTap ++script can be customized to add or remove trace events provided in ++/usr/share/systemtap/tapset/qemu-kvm-simpletrace.stp. ++ ++SystemTap customizations can be made to qemu_kvm.conf to control the flight ++recorder buffer size and whether to store traces in memory only or disk too. ++See stap(1) for option documentation. ++ ++3. Start the systemtap service. ++ # service systemtap start qemu_kvm ++ ++4. Make the service start at boot time. ++ # chkconfig systemtap on ++ ++5. Confirm that the service works. ++ # service systemtap status qemu_kvm ++ qemu_kvm is running... ++ ++When you want to inspect the trace buffer, perform the following steps: ++ ++1. Dump the trace buffer. ++ # staprun -A qemu_kvm >/tmp/trace.log ++ ++2. Start the systemtap service because the preceding step stops the service. ++ # service systemtap start qemu_kvm ++ ++3. Translate the trace record to readable format. ++ # /usr/share/qemu-kvm/simpletrace.py --no-header /usr/share/qemu-kvm/trace-events /tmp/trace.log +diff --git a/scripts/systemtap/conf.d/qemu_kvm.conf b/scripts/systemtap/conf.d/qemu_kvm.conf +new file mode 100644 +index 0000000..372d816 +--- /dev/null ++++ b/scripts/systemtap/conf.d/qemu_kvm.conf +@@ -0,0 +1,4 @@ ++# Force load uprobes (see BZ#1118352) ++stap -e 'probe process("/usr/libexec/qemu-kvm").function("main") { printf("") }' -c true ++ ++qemu_kvm_OPT="-s4" # per-CPU buffer size, in megabytes +diff --git a/scripts/systemtap/script.d/qemu_kvm.stp b/scripts/systemtap/script.d/qemu_kvm.stp +new file mode 100644 +index 0000000..c04abf9 +--- /dev/null ++++ b/scripts/systemtap/script.d/qemu_kvm.stp +@@ -0,0 +1 @@ ++probe qemu.kvm.simpletrace.handle_qmp_command,qemu.kvm.simpletrace.monitor_protocol_*,qemu.kvm.simpletrace.migrate_set_state {} +-- +1.8.3.1 + diff --git a/SOURCES/0018-Use-qemu-kvm-in-documentation-instead-of-qemu-system.patch b/SOURCES/0018-Use-qemu-kvm-in-documentation-instead-of-qemu-system.patch new file mode 100644 index 0000000..6888a9b --- /dev/null +++ b/SOURCES/0018-Use-qemu-kvm-in-documentation-instead-of-qemu-system.patch @@ -0,0 +1,944 @@ +From 28978c6f26cf430252237c9573c2d5d61908d826 Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Fri, 14 Nov 2014 08:51:50 +0100 +Subject: Use qemu-kvm in documentation instead of qemu-system- + +Patchwork-id: 62380 +O-Subject: [RHEV-7.1 qemu-kvm-rhev PATCHv4] Use qemu-kvm in documentation instead of qemu-system-i386 +Bugzilla: 1140620 +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Markus Armbruster +RH-Acked-by: Stefan Hajnoczi + +From: Miroslav Rezanina + +We change the name and location of qemu-kvm binaries. Update documentation +to reflect this change. Only architectures available in RHEL are updated. + +Signed-off-by: Miroslav Rezanina + +Rebase Notes (2.12.0): +- Additional fixes included + +Rebase Notes (2.11.0): +- Fixing docs/qemu-block-drivers.texi + +Rebase Notes (2.10.0): +- Changed patch name and updated commit message. + +Rebase Notes (2.9.0): +- fixed chunks missed on 2.8 rebase + +(cherry picked from commit 1c2dac56d5e710faebe25b7aa9cac594ec0f9d4b) +(cherry picked from commit dfa2037d390047a7d7c7b13f779443bfc6c3709d) + +Conflicts: + qemu-options.hx + +(cherry picked from commit c7985367ba8258c99526549ab94ef066ae52da14) + +Conflicts: + qemu-options.hx + +(cherry picked from commit e7dc2155506c1ead844f1faef85e5f71bc2adf9e) +(cherry picked from commit 834b4c22e11c86038ec0581a86a83920569dafbd) +--- + docs/can.txt | 10 +-- + docs/pr-manager.rst | 4 +- + docs/qemu-block-drivers.texi | 70 ++++++++++----------- + docs/replay.txt | 4 +- + docs/specs/tpm.txt | 4 +- + qemu-doc.texi | 40 ++++++------ + qemu-options.hx | 144 ++++++++++++++++++++++--------------------- + 7 files changed, 140 insertions(+), 136 deletions(-) + +diff --git a/docs/can.txt b/docs/can.txt +index a357105..0c0fc11 100644 +--- a/docs/can.txt ++++ b/docs/can.txt +@@ -50,9 +50,9 @@ CAN boards can be selected + The ''kvaser_pci'' board/device model is compatible with and has been tested with + ''kvaser_pci'' driver included in mainline Linux kernel. + The tested setup was Linux 4.9 kernel on the host and guest side. +-Example for qemu-system-x86_64: ++Example for qemu-kvm (intel architecture): + +- qemu-system-x86_64 -enable-kvm -kernel /boot/vmlinuz-4.9.0-4-amd64 \ ++ qemu-kvm -enable-kvm -kernel /boot/vmlinuz-4.9.0-4-amd64 \ + -initrd ramdisk.cpio \ + -virtfs local,path=shareddir,security_model=none,mount_tag=shareddir \ + -object can-bus,id=canbus0 \ +@@ -60,9 +60,9 @@ Example for qemu-system-x86_64: + -device kvaser_pci,canbus=canbus0 \ + -nographic -append "console=ttyS0" + +-Example for qemu-system-arm: ++Example for qemu-kvm (arm architecture): + +- qemu-system-arm -cpu arm1176 -m 256 -M versatilepb \ ++ qemu-kvm -cpu arm1176 -m 256 -M versatilepb \ + -kernel kernel-qemu-arm1176-versatilepb \ + -hda rpi-wheezy-overlay \ + -append "console=ttyAMA0 root=/dev/sda2 ro init=/sbin/init-overlay" \ +@@ -104,4 +104,4 @@ Links to other resources + Slides + http://rtime.felk.cvut.cz/publications/public/rtlws2015-qemu-can-slides.pdf + (5) Linux SocketCAN utilities +- https://github.com/linux-can/can-utils/ +\ No newline at end of file ++ https://github.com/linux-can/can-utils/ +diff --git a/docs/pr-manager.rst b/docs/pr-manager.rst +index 9b1de19..45cb8be 100644 +--- a/docs/pr-manager.rst ++++ b/docs/pr-manager.rst +@@ -36,7 +36,7 @@ accepts the path to the helper program's Unix socket. For example, + the following command line defines a ``pr-manager-helper`` object and + attaches it to a SCSI passthrough device:: + +- $ qemu-system-x86_64 ++ $ qemu-kvm + -device virtio-scsi \ + -object pr-manager-helper,id=helper0,path=/var/run/qemu-pr-helper.sock + -drive if=none,id=hd,driver=raw,file.filename=/dev/sdb,file.pr-manager=helper0 +@@ -44,7 +44,7 @@ attaches it to a SCSI passthrough device:: + + Alternatively, using ``-blockdev``:: + +- $ qemu-system-x86_64 ++ $ qemu-kvm + -device virtio-scsi \ + -object pr-manager-helper,id=helper0,path=/var/run/qemu-pr-helper.sock + -blockdev node-name=hd,driver=raw,file.driver=host_device,file.filename=/dev/sdb,file.pr-manager=helper0 +diff --git a/docs/qemu-block-drivers.texi b/docs/qemu-block-drivers.texi +index f179369..e0d752a 100644 +--- a/docs/qemu-block-drivers.texi ++++ b/docs/qemu-block-drivers.texi +@@ -405,7 +405,7 @@ QEMU can automatically create a virtual FAT disk image from a + directory tree. In order to use it, just type: + + @example +-qemu-system-i386 linux.img -hdb fat:/my_directory ++qemu-kvm linux.img -hdb fat:/my_directory + @end example + + Then you access access to all the files in the @file{/my_directory} +@@ -415,14 +415,14 @@ them via SAMBA or NFS. The default access is @emph{read-only}. + Floppies can be emulated with the @code{:floppy:} option: + + @example +-qemu-system-i386 linux.img -fda fat:floppy:/my_directory ++qemu-kvm linux.img -fda fat:floppy:/my_directory + @end example + + A read/write support is available for testing (beta stage) with the + @code{:rw:} option: + + @example +-qemu-system-i386 linux.img -fda fat:floppy:rw:/my_directory ++qemu-kvm linux.img -fda fat:floppy:rw:/my_directory + @end example + + What you should @emph{never} do: +@@ -440,14 +440,14 @@ QEMU can access directly to block device exported using the Network Block Device + protocol. + + @example +-qemu-system-i386 linux.img -hdb nbd://my_nbd_server.mydomain.org:1024/ ++qemu-kvm linux.img -hdb nbd://my_nbd_server.mydomain.org:1024/ + @end example + + If the NBD server is located on the same host, you can use an unix socket instead + of an inet socket: + + @example +-qemu-system-i386 linux.img -hdb nbd+unix://?socket=/tmp/my_socket ++qemu-kvm linux.img -hdb nbd+unix://?socket=/tmp/my_socket + @end example + + In this case, the block device must be exported using qemu-nbd: +@@ -464,23 +464,23 @@ qemu-nbd --socket=/tmp/my_socket --share=2 my_disk.qcow2 + @noindent + and then you can use it with two guests: + @example +-qemu-system-i386 linux1.img -hdb nbd+unix://?socket=/tmp/my_socket +-qemu-system-i386 linux2.img -hdb nbd+unix://?socket=/tmp/my_socket ++qemu-kvm linux1.img -hdb nbd+unix://?socket=/tmp/my_socket ++qemu-kvm linux2.img -hdb nbd+unix://?socket=/tmp/my_socket + @end example + + If the nbd-server uses named exports (supported since NBD 2.9.18, or with QEMU's + own embedded NBD server), you must specify an export name in the URI: + @example +-qemu-system-i386 -cdrom nbd://localhost/debian-500-ppc-netinst +-qemu-system-i386 -cdrom nbd://localhost/openSUSE-11.1-ppc-netinst ++qemu-kvm -cdrom nbd://localhost/debian-500-ppc-netinst ++qemu-kvm -cdrom nbd://localhost/openSUSE-11.1-ppc-netinst + @end example + + The URI syntax for NBD is supported since QEMU 1.3. An alternative syntax is + also available. Here are some example of the older syntax: + @example +-qemu-system-i386 linux.img -hdb nbd:my_nbd_server.mydomain.org:1024 +-qemu-system-i386 linux2.img -hdb nbd:unix:/tmp/my_socket +-qemu-system-i386 -cdrom nbd:localhost:10809:exportname=debian-500-ppc-netinst ++qemu-kvm linux.img -hdb nbd:my_nbd_server.mydomain.org:1024 ++qemu-kvm linux2.img -hdb nbd:unix:/tmp/my_socket ++qemu-kvm -cdrom nbd:localhost:10809:exportname=debian-500-ppc-netinst + @end example + + @node disk_images_sheepdog +@@ -505,7 +505,7 @@ qemu-img convert @var{filename} sheepdog:///@var{image} + + You can boot from the Sheepdog disk image with the command: + @example +-qemu-system-i386 sheepdog:///@var{image} ++qemu-kvm sheepdog:///@var{image} + @end example + + You can also create a snapshot of the Sheepdog image like qcow2. +@@ -517,7 +517,7 @@ where @var{tag} is a tag name of the newly created snapshot. + To boot from the Sheepdog snapshot, specify the tag name of the + snapshot. + @example +-qemu-system-i386 sheepdog:///@var{image}#@var{tag} ++qemu-kvm sheepdog:///@var{image}#@var{tag} + @end example + + You can create a cloned image from the existing snapshot. +@@ -530,14 +530,14 @@ is its tag name. + You can use an unix socket instead of an inet socket: + + @example +-qemu-system-i386 sheepdog+unix:///@var{image}?socket=@var{path} ++qemu-kvm sheepdog+unix:///@var{image}?socket=@var{path} + @end example + + If the Sheepdog daemon doesn't run on the local host, you need to + specify one of the Sheepdog servers to connect to. + @example + qemu-img create sheepdog://@var{hostname}:@var{port}/@var{image} @var{size} +-qemu-system-i386 sheepdog://@var{hostname}:@var{port}/@var{image} ++qemu-kvm sheepdog://@var{hostname}:@var{port}/@var{image} + @end example + + @node disk_images_iscsi +@@ -627,7 +627,7 @@ cat >iscsi.conf < /sys/bus/pci/devices/0000:06:0d.0/driver/unbind + # echo 1102 0002 > /sys/bus/pci/drivers/vfio-pci/new_id + +-# qemu-system-x86_64 -drive file=nvme://@var{host}:@var{bus}:@var{slot}.@var{func}/@var{namespace} ++# qemu-kvm -drive file=nvme://@var{host}:@var{bus}:@var{slot}.@var{func}/@var{namespace} + @end example + + Alternative syntax using properties: + + @example +-qemu-system-x86_64 -drive file.driver=nvme,file.device=@var{host}:@var{bus}:@var{slot}.@var{func},file.namespace=@var{namespace} ++qemu-kvm -drive file.driver=nvme,file.device=@var{host}:@var{bus}:@var{slot}.@var{func},file.namespace=@var{namespace} + @end example + + @var{host}:@var{bus}:@var{slot}.@var{func} is the NVMe controller's PCI device +diff --git a/docs/replay.txt b/docs/replay.txt +index 2e21e9c..f1923e8 100644 +--- a/docs/replay.txt ++++ b/docs/replay.txt +@@ -25,7 +25,7 @@ Deterministic replay has the following features: + + Usage of the record/replay: + * First, record the execution with the following command line: +- qemu-system-i386 \ ++ qemu-kvm \ + -icount shift=7,rr=record,rrfile=replay.bin \ + -drive file=disk.qcow2,if=none,id=img-direct \ + -drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \ +@@ -33,7 +33,7 @@ Usage of the record/replay: + -netdev user,id=net1 -device rtl8139,netdev=net1 \ + -object filter-replay,id=replay,netdev=net1 + * After recording, you can replay it by using another command line: +- qemu-system-i386 \ ++ qemu-kvm \ + -icount shift=7,rr=replay,rrfile=replay.bin \ + -drive file=disk.qcow2,if=none,id=img-direct \ + -drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \ +diff --git a/docs/specs/tpm.txt b/docs/specs/tpm.txt +index d1d7157..897c300 100644 +--- a/docs/specs/tpm.txt ++++ b/docs/specs/tpm.txt +@@ -98,7 +98,7 @@ QEMU files related to the TPM passthrough device: + Command line to start QEMU with the TPM passthrough device using the host's + hardware TPM /dev/tpm0: + +-qemu-system-x86_64 -display sdl -enable-kvm \ ++qemu-kvm -display vnc -enable-kvm \ + -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ + -tpmdev passthrough,id=tpm0,path=/dev/tpm0 \ + -device tpm-tis,tpmdev=tpm0 test.img +@@ -164,7 +164,7 @@ swtpm socket --tpmstate dir=/tmp/mytpm1 \ + Command line to start QEMU with the TPM emulator device communicating with + the swtpm: + +-qemu-system-x86_64 -display sdl -enable-kvm \ ++qemu-kvm -display sdl -enable-kvm \ + -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ + -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ +diff --git a/qemu-doc.texi b/qemu-doc.texi +index 5813d27..de5097a 100644 +--- a/qemu-doc.texi ++++ b/qemu-doc.texi +@@ -203,12 +203,12 @@ Note that, by default, GUS shares IRQ(7) with parallel ports and so + QEMU must be told to not have parallel ports to have working GUS. + + @example +-qemu-system-i386 dos.img -soundhw gus -parallel none ++qemu-kvm dos.img -soundhw gus -parallel none + @end example + + Alternatively: + @example +-qemu-system-i386 dos.img -device gus,irq=5 ++qemu-kvm dos.img -device gus,irq=5 + @end example + + Or some other unclaimed IRQ. +@@ -224,7 +224,7 @@ CS4231A is the chip used in Windows Sound System and GUSMAX products + Download and uncompress the linux image (@file{linux.img}) and type: + + @example +-qemu-system-i386 linux.img ++qemu-kvm linux.img + @end example + + Linux should boot and give you a prompt. +@@ -234,7 +234,7 @@ Linux should boot and give you a prompt. + + @example + @c man begin SYNOPSIS +-@command{qemu-system-i386} [@var{options}] [@var{disk_image}] ++@command{qemu-kvm} [@var{options}] [@var{disk_image}] + @c man end + @end example + +@@ -813,7 +813,7 @@ On Linux hosts, a shared memory device is available. The basic syntax + is: + + @example +-qemu-system-x86_64 -device ivshmem-plain,memdev=@var{hostmem} ++qemu-kvm -device ivshmem-plain,memdev=@var{hostmem} + @end example + + where @var{hostmem} names a host memory backend. For a POSIX shared +@@ -834,7 +834,7 @@ memory server is: + ivshmem-server -p @var{pidfile} -S @var{path} -m @var{shm-name} -l @var{shm-size} -n @var{vectors} + + # Then start your qemu instances with matching arguments +-qemu-system-x86_64 -device ivshmem-doorbell,vectors=@var{vectors},chardev=@var{id} ++qemu-kvm -device ivshmem-doorbell,vectors=@var{vectors},chardev=@var{id} + -chardev socket,path=@var{path},id=@var{id} + @end example + +@@ -859,7 +859,7 @@ Instead of specifying the using POSIX shm, you may specify + a memory backend that has hugepage support: + + @example +-qemu-system-x86_64 -object memory-backend-file,size=1G,mem-path=/dev/hugepages/my-shmem-file,share,id=mb1 ++qemu-kvm -object memory-backend-file,size=1G,mem-path=/dev/hugepages/my-shmem-file,share,id=mb1 + -device ivshmem-plain,memdev=mb1 + @end example + +@@ -875,7 +875,7 @@ kernel testing. + + The syntax is: + @example +-qemu-system-i386 -kernel arch/i386/boot/bzImage -hda root-2.4.20.img -append "root=/dev/hda" ++qemu-kvm -kernel arch/i386/boot/bzImage -hda root-2.4.20.img -append "root=/dev/hda" + @end example + + Use @option{-kernel} to provide the Linux kernel image and +@@ -890,7 +890,7 @@ If you do not need graphical output, you can disable it and redirect + the virtual serial port and the QEMU monitor to the console with the + @option{-nographic} option. The typical command line is: + @example +-qemu-system-i386 -kernel arch/i386/boot/bzImage -hda root-2.4.20.img \ ++qemu-kvm -kernel arch/i386/boot/bzImage -hda root-2.4.20.img \ + -append "root=/dev/hda console=ttyS0" -nographic + @end example + +@@ -956,7 +956,7 @@ Network adapter that supports CDC ethernet and RNDIS protocols. @var{id} + specifies a netdev defined with @code{-netdev @dots{},id=@var{id}}. + For instance, user-mode networking can be used with + @example +-qemu-system-i386 [...] -netdev user,id=net0 -device usb-net,netdev=net0 ++qemu-kvm [...] -netdev user,id=net0 -device usb-net,netdev=net0 + @end example + @item usb-ccid + Smartcard reader device +@@ -975,7 +975,7 @@ no type is given, the HCI logic corresponds to @code{-bt hci,vlan=0}. + This USB device implements the USB Transport Layer of HCI. Example + usage: + @example +-@command{qemu-system-i386} [...@var{OPTIONS}...] @option{-usbdevice} bt:hci,vlan=3 @option{-bt} device:keyboard,vlan=3 ++@command{qemu-kvm} [...@var{OPTIONS}...] @option{-usbdevice} bt:hci,vlan=3 @option{-bt} device:keyboard,vlan=3 + @end example + @end table + +@@ -1052,7 +1052,7 @@ For this setup it is recommended to restrict it to listen on a UNIX domain + socket only. For example + + @example +-qemu-system-i386 [...OPTIONS...] -vnc unix:/home/joebloggs/.qemu-myvm-vnc ++qemu-kvm [...OPTIONS...] -vnc unix:/home/joebloggs/.qemu-myvm-vnc + @end example + + This ensures that only users on local box with read/write access to that +@@ -1075,7 +1075,7 @@ is running the password is set with the monitor. Until the monitor is used to + set the password all clients will be rejected. + + @example +-qemu-system-i386 [...OPTIONS...] -vnc :1,password -monitor stdio ++qemu-kvm [...OPTIONS...] -vnc :1,password -monitor stdio + (qemu) change vnc password + Password: ******** + (qemu) +@@ -1092,7 +1092,7 @@ support provides a secure session, but no authentication. This allows any + client to connect, and provides an encrypted session. + + @example +-qemu-system-i386 [...OPTIONS...] -vnc :1,tls,x509=/etc/pki/qemu -monitor stdio ++qemu-kvm [...OPTIONS...] -vnc :1,tls,x509=/etc/pki/qemu -monitor stdio + @end example + + In the above example @code{/etc/pki/qemu} should contain at least three files, +@@ -1110,7 +1110,7 @@ then validate against the CA certificate. This is a good choice if deploying + in an environment with a private internal certificate authority. + + @example +-qemu-system-i386 [...OPTIONS...] -vnc :1,tls,x509verify=/etc/pki/qemu -monitor stdio ++qemu-kvm [...OPTIONS...] -vnc :1,tls,x509verify=/etc/pki/qemu -monitor stdio + @end example + + +@@ -1121,7 +1121,7 @@ Finally, the previous method can be combined with VNC password authentication + to provide two layers of authentication for clients. + + @example +-qemu-system-i386 [...OPTIONS...] -vnc :1,password,tls,x509verify=/etc/pki/qemu -monitor stdio ++qemu-kvm [...OPTIONS...] -vnc :1,password,tls,x509verify=/etc/pki/qemu -monitor stdio + (qemu) change vnc password + Password: ******** + (qemu) +@@ -1144,7 +1144,7 @@ used for authentication, but assuming use of one supporting SSF, + then QEMU can be launched with: + + @example +-qemu-system-i386 [...OPTIONS...] -vnc :1,sasl -monitor stdio ++qemu-kvm [...OPTIONS...] -vnc :1,sasl -monitor stdio + @end example + + @node vnc_sec_certificate_sasl +@@ -1158,7 +1158,7 @@ credentials. This can be enabled, by combining the 'sasl' option + with the aforementioned TLS + x509 options: + + @example +-qemu-system-i386 [...OPTIONS...] -vnc :1,tls,x509,sasl -monitor stdio ++qemu-kvm [...OPTIONS...] -vnc :1,tls,x509,sasl -monitor stdio + @end example + + @node vnc_setup_sasl +@@ -1514,7 +1514,7 @@ QEMU has a primitive support to work with gdb, so that you can do + In order to use gdb, launch QEMU with the '-s' option. It will wait for a + gdb connection: + @example +-qemu-system-i386 -s -kernel arch/i386/boot/bzImage -hda root-2.4.20.img \ ++qemu-kvm -s -kernel arch/i386/boot/bzImage -hda root-2.4.20.img \ + -append "root=/dev/hda" + Connected to host network interface: tun0 + Waiting gdb connection on port 1234 +@@ -1760,7 +1760,7 @@ Set the initial VGA graphic mode. The default is 800x600x32. + Set OpenBIOS variables in NVRAM, for example: + + @example +-qemu-system-ppc -prom-env 'auto-boot?=false' \ ++qemu-kvm -prom-env 'auto-boot?=false' \ + -prom-env 'boot-device=hd:2,\yaboot' \ + -prom-env 'boot-args=conf=hd:2,\yaboot.conf' + @end example +diff --git a/qemu-options.hx b/qemu-options.hx +index 2042dba..43f10b1 100644 +--- a/qemu-options.hx ++++ b/qemu-options.hx +@@ -263,7 +263,7 @@ This option defines a free-form string that can be used to describe @var{fd}. + + You can open an image using pre-opened file descriptors from an fd set: + @example +-qemu-system-i386 ++qemu-kvm + -add-fd fd=3,set=2,opaque="rdwr:/path/to/file" + -add-fd fd=4,set=2,opaque="rdonly:/path/to/file" + -drive file=/dev/fdset/2,index=0,media=disk +@@ -292,7 +292,7 @@ STEXI + Set default value of @var{driver}'s property @var{prop} to @var{value}, e.g.: + + @example +-qemu-system-i386 -global ide-hd.physical_block_size=4096 disk-image.img ++qemu-kvm -global ide-hd.physical_block_size=4096 disk-image.img + @end example + + In particular, you can use this to set driver properties for devices which are +@@ -346,11 +346,11 @@ bootindex options. The default is non-strict boot. + + @example + # try to boot from network first, then from hard disk +-qemu-system-i386 -boot order=nc ++qemu-kvm -boot order=nc + # boot from CD-ROM first, switch back to default order after reboot +-qemu-system-i386 -boot once=d ++qemu-kvm -boot once=d + # boot with a splash picture for 5 seconds. +-qemu-system-i386 -boot menu=on,splash=/root/boot.bmp,splash-time=5000 ++qemu-kvm -boot menu=on,splash=/root/boot.bmp,splash-time=5000 + @end example + + Note: The legacy format '-boot @var{drives}' is still supported but its +@@ -379,7 +379,7 @@ For example, the following command-line sets the guest startup RAM size to + memory the guest can reach to 4GB: + + @example +-qemu-system-x86_64 -m 1G,slots=3,maxmem=4G ++qemu-kvm -m 1G,slots=3,maxmem=4G + @end example + + If @var{slots} and @var{maxmem} are not specified, memory hotplug won't +@@ -448,12 +448,12 @@ Enable audio and selected sound hardware. Use 'help' to print all + available sound hardware. + + @example +-qemu-system-i386 -soundhw sb16,adlib disk.img +-qemu-system-i386 -soundhw es1370 disk.img +-qemu-system-i386 -soundhw ac97 disk.img +-qemu-system-i386 -soundhw hda disk.img +-qemu-system-i386 -soundhw all disk.img +-qemu-system-i386 -soundhw help ++qemu-kvm -soundhw sb16,adlib disk.img ++qemu-kvm -soundhw es1370 disk.img ++qemu-kvm -soundhw ac97 disk.img ++qemu-kvm -soundhw hda disk.img ++qemu-kvm -soundhw all disk.img ++qemu-kvm -soundhw help + @end example + + Note that Linux's i810_audio OSS kernel (for AC97) module might +@@ -946,21 +946,21 @@ is off. + + Instead of @option{-cdrom} you can use: + @example +-qemu-system-i386 -drive file=file,index=2,media=cdrom ++qemu-kvm -drive file=file,index=2,media=cdrom + @end example + + Instead of @option{-hda}, @option{-hdb}, @option{-hdc}, @option{-hdd}, you can + use: + @example +-qemu-system-i386 -drive file=file,index=0,media=disk +-qemu-system-i386 -drive file=file,index=1,media=disk +-qemu-system-i386 -drive file=file,index=2,media=disk +-qemu-system-i386 -drive file=file,index=3,media=disk ++qemu-kvm -drive file=file,index=0,media=disk ++qemu-kvm -drive file=file,index=1,media=disk ++qemu-kvm -drive file=file,index=2,media=disk ++qemu-kvm -drive file=file,index=3,media=disk + @end example + + You can open an image using pre-opened file descriptors from an fd set: + @example +-qemu-system-i386 ++qemu-kvm + -add-fd fd=3,set=2,opaque="rdwr:/path/to/file" + -add-fd fd=4,set=2,opaque="rdonly:/path/to/file" + -drive file=/dev/fdset/2,index=0,media=disk +@@ -968,28 +968,28 @@ qemu-system-i386 + + You can connect a CDROM to the slave of ide0: + @example +-qemu-system-i386 -drive file=file,if=ide,index=1,media=cdrom ++qemu-kvm -drive file=file,if=ide,index=1,media=cdrom + @end example + + If you don't specify the "file=" argument, you define an empty drive: + @example +-qemu-system-i386 -drive if=ide,index=1,media=cdrom ++qemu-kvm -drive if=ide,index=1,media=cdrom + @end example + + Instead of @option{-fda}, @option{-fdb}, you can use: + @example +-qemu-system-i386 -drive file=file,index=0,if=floppy +-qemu-system-i386 -drive file=file,index=1,if=floppy ++qemu-kvm -drive file=file,index=0,if=floppy ++qemu-kvm -drive file=file,index=1,if=floppy + @end example + + By default, @var{interface} is "ide" and @var{index} is automatically + incremented: + @example +-qemu-system-i386 -drive file=a -drive file=b" ++qemu-kvm -drive file=a -drive file=b" + @end example + is interpreted like: + @example +-qemu-system-i386 -hda a -hdb b ++qemu-kvm -hda a -hdb b + @end example + ETEXI + +@@ -2056,8 +2056,8 @@ The following two example do exactly the same, to show how @option{-nic} can + be used to shorten the command line length (note that the e1000 is the default + on i386, so the @option{model=e1000} parameter could even be omitted here, too): + @example +-qemu-system-i386 -netdev user,id=n1,ipv6=off -device e1000,netdev=n1,mac=52:54:98:76:54:32 +-qemu-system-i386 -nic user,ipv6=off,model=e1000,mac=52:54:98:76:54:32 ++qemu-kvm -netdev user,id=n1,ipv6=off -device e1000,netdev=n1,mac=52:54:98:76:54:32 ++qemu-kvm -nic user,ipv6=off,model=e1000,mac=52:54:98:76:54:32 + @end example + + @item -nic none +@@ -2128,7 +2128,7 @@ can not be resolved. + + Example: + @example +-qemu-system-i386 -nic user,dnssearch=mgmt.example.org,dnssearch=example.org ++qemu-kvm -nic user,dnssearch=mgmt.example.org,dnssearch=example.org + @end example + + @item tftp=@var{dir} +@@ -2144,7 +2144,7 @@ a guest from a local directory. + + Example (using pxelinux): + @example +-qemu-system-i386 -hda linux.img -boot n -device e1000,netdev=n1 \ ++qemu-kvm -hda linux.img -boot n -device e1000,netdev=n1 \ + -netdev user,id=n1,tftp=/path/to/tftp/files,bootfile=/pxelinux.0 + @end example + +@@ -2178,7 +2178,7 @@ screen 0, use the following: + + @example + # on the host +-qemu-system-i386 -nic user,hostfwd=tcp:127.0.0.1:6001-:6000 ++qemu-kvm -nic user,hostfwd=tcp:127.0.0.1:6001-:6000 + # this host xterm should open in the guest X11 server + xterm -display :1 + @end example +@@ -2188,7 +2188,7 @@ the guest, use the following: + + @example + # on the host +-qemu-system-i386 -nic user,hostfwd=tcp::5555-:23 ++qemu-kvm -nic user,hostfwd=tcp::5555-:23 + telnet localhost 5555 + @end example + +@@ -2207,7 +2207,7 @@ lifetime, like in the following example: + @example + # open 10.10.1.1:4321 on bootup, connect 10.0.2.100:1234 to it whenever + # the guest accesses it +-qemu-system-i386 -nic user,guestfwd=tcp:10.0.2.100:1234-tcp:10.10.1.1:4321 ++qemu-kvm -nic user,guestfwd=tcp:10.0.2.100:1234-tcp:10.10.1.1:4321 + @end example + + Or you can execute a command on every TCP connection established by the guest, +@@ -2216,7 +2216,7 @@ so that QEMU behaves similar to an inetd process for that virtual server: + @example + # call "netcat 10.10.1.1 4321" on every TCP connection to 10.0.2.100:1234 + # and connect the TCP stream to its stdin/stdout +-qemu-system-i386 -nic 'user,id=n1,guestfwd=tcp:10.0.2.100:1234-cmd:netcat 10.10.1.1 4321' ++qemu-kvm -nic 'user,id=n1,guestfwd=tcp:10.0.2.100:1234-cmd:netcat 10.10.1.1 4321' + @end example + + @end table +@@ -2248,21 +2248,22 @@ Examples: + + @example + #launch a QEMU instance with the default network script +-qemu-system-i386 linux.img -nic tap ++qemu-kvm linux.img -nic tap + @end example + + @example + #launch a QEMU instance with two NICs, each one connected + #to a TAP device +-qemu-system-i386 linux.img \ ++qemu-kvm linux.img \ + -netdev tap,id=nd0,ifname=tap0 -device e1000,netdev=nd0 \ + -netdev tap,id=nd1,ifname=tap1 -device rtl8139,netdev=nd1 ++ -net nic,vlan=1 -net tap,vlan=1,ifname=tap1 + @end example + + @example + #launch a QEMU instance with the default network helper to + #connect a TAP device to bridge br0 +-qemu-system-i386 linux.img -device virtio-net-pci,netdev=n1 \ ++qemu-kvm linux.img -device virtio-net-pci,netdev=n1 \ + -netdev tap,id=n1,"helper=/path/to/qemu-bridge-helper" + @end example + +@@ -2279,13 +2280,13 @@ Examples: + @example + #launch a QEMU instance with the default network helper to + #connect a TAP device to bridge br0 +-qemu-system-i386 linux.img -netdev bridge,id=n1 -device virtio-net,netdev=n1 ++qemu-kvm linux.img -netdev bridge,id=n1 -device virtio-net,netdev=n1 + @end example + + @example + #launch a QEMU instance with the default network helper to + #connect a TAP device to bridge qemubr0 +-qemu-system-i386 linux.img -netdev bridge,br=qemubr0,id=n1 -device virtio-net,netdev=n1 ++qemu-kvm linux.img -netdev bridge,br=qemubr0,id=n1 -device virtio-net,netdev=n1 + @end example + + @item -netdev socket,id=@var{id}[,fd=@var{h}][,listen=[@var{host}]:@var{port}][,connect=@var{host}:@var{port}] +@@ -2300,13 +2301,13 @@ specifies an already opened TCP socket. + Example: + @example + # launch a first QEMU instance +-qemu-system-i386 linux.img \ +- -device e1000,netdev=n1,mac=52:54:00:12:34:56 \ +- -netdev socket,id=n1,listen=:1234 ++qemu-kvm linux.img \ ++ -device e1000,netdev=n1,mac=52:54:00:12:34:56 \ ++ -netdev socket,id=n1,listen=:1234 + # connect the network of this instance to the network of the first instance +-qemu-system-i386 linux.img \ +- -device e1000,netdev=n2,mac=52:54:00:12:34:57 \ +- -netdev socket,id=n2,connect=127.0.0.1:1234 ++qemu-kvm linux.img \ ++ -device e1000,netdev=n2,mac=52:54:00:12:34:57 \ ++ -netdev socket,id=n2,connect=127.0.0.1:1234 + @end example + + @item -netdev socket,id=@var{id}[,fd=@var{h}][,mcast=@var{maddr}:@var{port}[,localaddr=@var{addr}]] +@@ -2329,23 +2330,23 @@ Use @option{fd=h} to specify an already opened UDP multicast socket. + Example: + @example + # launch one QEMU instance +-qemu-system-i386 linux.img \ +- -device e1000,netdev=n1,mac=52:54:00:12:34:56 \ +- -netdev socket,id=n1,mcast=230.0.0.1:1234 ++qemu-kvm linux.img \ ++ -device e1000,netdev=n1,mac=52:54:00:12:34:56 \ ++ -netdev socket,id=n1,mcast=230.0.0.1:1234 + # launch another QEMU instance on same "bus" +-qemu-system-i386 linux.img \ +- -device e1000,netdev=n2,mac=52:54:00:12:34:57 \ +- -netdev socket,id=n2,mcast=230.0.0.1:1234 ++qemu-kvm linux.img \ ++ -device e1000,netdev=n2,mac=52:54:00:12:34:57 \ ++ -netdev socket,id=n2,mcast=230.0.0.1:1234 + # launch yet another QEMU instance on same "bus" +-qemu-system-i386 linux.img \ +- -device e1000,netdev=n3,macaddr=52:54:00:12:34:58 \ +- -netdev socket,id=n3,mcast=230.0.0.1:1234 ++qemu-kvm linux.img \ ++ -device e1000,netdev=n3,macaddr=52:54:00:12:34:58 \ ++ -netdev socket,id=n3,mcast=230.0.0.1:1234 + @end example + + Example (User Mode Linux compat.): + @example + # launch QEMU instance (note mcast address selected is UML's default) +-qemu-system-i386 linux.img \ ++qemu-kvm linux.img \ + -device e1000,netdev=n1,mac=52:54:00:12:34:56 \ + -netdev socket,id=n1,mcast=239.192.168.1:1102 + # launch UML +@@ -2354,9 +2355,12 @@ qemu-system-i386 linux.img \ + + Example (send packets from host's 1.2.3.4): + @example +-qemu-system-i386 linux.img \ +- -device e1000,netdev=n1,mac=52:54:00:12:34:56 \ +- -netdev socket,id=n1,mcast=239.192.168.1:1102,localaddr=1.2.3.4 ++qemu-kvm linux.img \ ++ -device e1000,netdev=n1,mac=52:54:00:12:34:56 \ ++ -netdev socket,id=n1,mcast=239.192.168.1:1102,localaddr=1.2.3.4 ++qemu-kvm linux.img \ ++ -net nic,macaddr=52:54:00:12:34:56 \ ++ -net socket,mcast=239.192.168.1:1102,localaddr=1.2.3.4 + @end example + + @item -netdev l2tpv3,id=@var{id},src=@var{srcaddr},dst=@var{dstaddr}[,srcport=@var{srcport}][,dstport=@var{dstport}],txsession=@var{txsession}[,rxsession=@var{rxsession}][,ipv6][,udp][,cookie64][,counter][,pincounter][,txcookie=@var{txcookie}][,rxcookie=@var{rxcookie}][,offset=@var{offset}] +@@ -2414,7 +2418,7 @@ brctl addif br-lan vmtunnel0 + # on 4.3.2.1 + # launch QEMU instance - if your network has reorder or is very lossy add ,pincounter + +-qemu-system-i386 linux.img -device e1000,netdev=n1 \ ++qemu-kvm linux.img -device e1000,netdev=n1 \ + -netdev l2tpv3,id=n1,src=4.2.3.1,dst=1.2.3.4,udp,srcport=16384,dstport=16384,rxsession=0xffffffff,txsession=0xffffffff,counter + + @end example +@@ -2431,7 +2435,7 @@ Example: + # launch vde switch + vde_switch -F -sock /tmp/myswitch + # launch QEMU instance +-qemu-system-i386 linux.img -nic vde,sock=/tmp/myswitch ++qemu-kvm linux.img -nic vde,sock=/tmp/myswitch + @end example + + @item -netdev vhost-user,chardev=@var{id}[,vhostforce=on|off][,queues=n] +@@ -2445,11 +2449,11 @@ be created for multiqueue vhost-user. + + Example: + @example +-qemu -m 512 -object memory-backend-file,id=mem,size=512M,mem-path=/hugetlbfs,share=on \ +- -numa node,memdev=mem \ +- -chardev socket,id=chr0,path=/path/to/socket \ +- -netdev type=vhost-user,id=net0,chardev=chr0 \ +- -device virtio-net-pci,netdev=net0 ++qemu-kvm -m 512 -object memory-backend-file,id=mem,size=512M,mem-path=/hugetlbfs,share=on \ ++ -numa node,memdev=mem \ ++ -chardev socket,id=chr0,path=/path/to/socket \ ++ -netdev type=vhost-user,id=net0,chardev=chr0 \ ++ -device virtio-net-pci,netdev=net0 + @end example + + @item -netdev hubport,id=@var{id},hubid=@var{hubid}[,netdev=@var{nd}] +@@ -2879,7 +2883,7 @@ and communicate. Requires the Linux @code{vhci} driver installed. Can + be used as following: + + @example +-qemu-system-i386 [...OPTIONS...] -bt hci,vlan=5 -bt vhci,vlan=5 ++qemu-kvm [...OPTIONS...] -bt hci,vlan=5 -bt vhci,vlan=5 + @end example + + @item -bt device:@var{dev}[,vlan=@var{n}] +@@ -3310,14 +3314,14 @@ ETEXI + + DEF("realtime", HAS_ARG, QEMU_OPTION_realtime, + "-realtime [mlock=on|off]\n" +- " run qemu with realtime features\n" ++ " run qemu-kvm with realtime features\n" + " mlock=on|off controls mlock support (default: on)\n", + QEMU_ARCH_ALL) + STEXI + @item -realtime mlock=on|off + @findex -realtime +-Run qemu with realtime features. +-mlocking qemu and guest memory can be enabled via @option{mlock=on} ++Run qemu-kvm with realtime features. ++mlocking qemu-kvm and guest memory can be enabled via @option{mlock=on} + (enabled by default). + ETEXI + +@@ -3331,7 +3335,7 @@ connections will likely be TCP-based, but also UDP, pseudo TTY, or even + stdio are reasonable use case. The latter is allowing to start QEMU from + within gdb and establish the connection via a pipe: + @example +-(gdb) target remote | exec qemu-system-i386 -gdb stdio ... ++(gdb) target remote | exec qemu-kvm -gdb stdio ... + @end example + ETEXI + +@@ -4251,7 +4255,7 @@ which specify the queue number of cryptodev backend, the default of + + @example + +- # qemu-system-x86_64 \ ++ # qemu-kvm \ + [...] \ + -object cryptodev-backend-builtin,id=cryptodev0 \ + -device virtio-crypto-pci,id=crypto0,cryptodev=cryptodev0 \ +@@ -4271,7 +4275,7 @@ of cryptodev backend for multiqueue vhost-user, the default of @var{queues} is 1 + + @example + +- # qemu-system-x86_64 \ ++ # qemu-kvm \ + [...] \ + -chardev socket,id=chardev0,path=/path/to/socket \ + -object cryptodev-vhost-user,id=cryptodev0,chardev=chardev0 \ +-- +1.8.3.1 + diff --git a/SOURCES/0019-qmp-add-__com.redhat_reason-to-the-BLOCK_IO_ERROR-ev.patch b/SOURCES/0019-qmp-add-__com.redhat_reason-to-the-BLOCK_IO_ERROR-ev.patch new file mode 100644 index 0000000..7598393 --- /dev/null +++ b/SOURCES/0019-qmp-add-__com.redhat_reason-to-the-BLOCK_IO_ERROR-ev.patch @@ -0,0 +1,167 @@ +From 171f9db1f1217a217c7c13b3282b66eec7a165d1 Mon Sep 17 00:00:00 2001 +From: Luiz Capitulino +Date: Wed, 15 Mar 2017 13:25:17 +0100 +Subject: qmp: add __com.redhat_reason to the BLOCK_IO_ERROR event + +Patchwork-id: 64969 +O-Subject: [RHEL7.2 qemu-kvm-rhev PATCH 1/2] qmp: add error reason to the BLOCK_IO_ERROR event +Bugzilla: 1199174 +RH-Acked-by: Markus Armbruster +RH-Acked-by: Kevin Wolf +RH-Acked-by: Eric Blake + +This commit forward ports the following RHEL-7.0 commit to RHEL-7.2: + + commit 771a3a333eb0c9299a69a78ddb9c4181850b827d + Author: Laszlo Ersek + Date: Thu Nov 21 16:27:18 2013 +0100 + + error reason in BLOCK_IO_ERROR / BLOCK_JOB_ERROR events (RHEL 6->7 fwd) + +I had to redo the work because now events use the QAPI, but it +was straightforward. + +There's one significant difference though: this commit does not +extend the BLOCK_JOB_ERROR event as did the RHEL-7.0 commit. The +reason is that this extension was supposed to be used only by vdsm +and I don't think there's a requirement to have the extension +in BLOCK_JOB_ERROR too. Let's not spread it. + +Signed-off-by: Luiz Capitulino +Signed-off-by: Miroslav Rezanina + +Rebase notes (2.10.0): +- Properly updated example + +Rebase notes (2.9.0): +- moved documentation to schema + +(cherry picked from commit 2f9487b60f700de33551208c543f5d3b4fa89236) +(cherry picked from commit e2bf476c86cf5697ef3ea6aab2249aa85b8be2cd) +(cherry picked from commit 025b8d9385cab3c7c00e0caf9af84722a9ac914c) +(cherry picked from commit 264ccd5fc938bc26362046a697ad0d535531e69b) +(cherry picked from commit 88802f4552223d31f0ea9cf6c943ad3b091120cd) +--- + block/block-backend.c | 27 +++++++++++++++++++++++---- + qapi/block-core.json | 20 ++++++++++++++++++-- + 2 files changed, 41 insertions(+), 6 deletions(-) + +diff --git a/block/block-backend.c b/block/block-backend.c +index 681b240..fd342db 100644 +--- a/block/block-backend.c ++++ b/block/block-backend.c +@@ -1650,9 +1650,25 @@ BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, + } + } + ++/* https://bugzilla.redhat.com/show_bug.cgi?id=1199174 */ ++static RHEL7BlockErrorReason get_rhel7_error_reason(int error) ++{ ++ switch (error) { ++ case ENOSPC: ++ return RHEL7_BLOCK_ERROR_REASON_ENOSPC; ++ case EPERM: ++ return RHEL7_BLOCK_ERROR_REASON_EPERM; ++ case EIO: ++ return RHEL7_BLOCK_ERROR_REASON_EIO; ++ default: ++ return RHEL7_BLOCK_ERROR_REASON_EOTHER; ++ } ++} ++ + static void send_qmp_error_event(BlockBackend *blk, + BlockErrorAction action, +- bool is_read, int error) ++ bool is_read, int error, ++ RHEL7BlockErrorReason res) + { + IoOperationType optype; + BlockDriverState *bs = blk_bs(blk); +@@ -1662,7 +1678,7 @@ static void send_qmp_error_event(BlockBackend *blk, + bs ? bdrv_get_node_name(bs) : NULL, optype, + action, blk_iostatus_is_enabled(blk), + error == ENOSPC, strerror(error), +- &error_abort); ++ res, &error_abort); + } + + /* This is done by device models because, while the block layer knows +@@ -1672,7 +1688,10 @@ static void send_qmp_error_event(BlockBackend *blk, + void blk_error_action(BlockBackend *blk, BlockErrorAction action, + bool is_read, int error) + { ++ RHEL7BlockErrorReason res; ++ + assert(error >= 0); ++ res = get_rhel7_error_reason(error); + + if (action == BLOCK_ERROR_ACTION_STOP) { + /* First set the iostatus, so that "info block" returns an iostatus +@@ -1690,10 +1709,10 @@ void blk_error_action(BlockBackend *blk, BlockErrorAction action, + * 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); ++ send_qmp_error_event(blk, action, is_read, error, res); + qemu_system_vmstop_request(RUN_STATE_IO_ERROR); + } else { +- send_qmp_error_event(blk, action, is_read, error); ++ send_qmp_error_event(blk, action, is_read, error, res); + } + } + +diff --git a/qapi/block-core.json b/qapi/block-core.json +index c50517b..b38d5d6 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -4404,6 +4404,19 @@ + 'fatal' : 'bool' } } + + ## ++# @RHEL7BlockErrorReason: ++# ++# Block I/O error reason ++# ++# @eio: errno EIO ++# @eperm: errno EPERM ++# @enospc: errno ENOSPC ++# @eother: any errno other than EIO, EPERM, ENOSPC ++## ++{ 'enum': 'RHEL7BlockErrorReason', ++ 'data': [ 'enospc', 'eperm', 'eio', 'eother' ] } ++ ++## + # @BLOCK_IO_ERROR: + # + # Emitted when a disk I/O error occurs +@@ -4430,6 +4443,8 @@ + # (This field is a debugging aid for humans, it should not + # be parsed by applications) (since: 2.2) + # ++# @__com.redhat_reason: error reason ++# + # Note: If action is "stop", a STOP event will eventually follow the + # BLOCK_IO_ERROR event + # +@@ -4441,7 +4456,8 @@ + # "data": { "device": "ide0-hd1", + # "node-name": "#block212", + # "operation": "write", +-# "action": "stop" }, ++# "action": "stop", ++# "__com.redhat_reason": "enospc" }, + # "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } + # + ## +@@ -4449,7 +4465,7 @@ + 'data': { 'device': 'str', '*node-name': 'str', + 'operation': 'IoOperationType', + 'action': 'BlockErrorAction', '*nospace': 'bool', +- 'reason': 'str' } } ++ 'reason': 'str', '__com.redhat_reason': 'RHEL7BlockErrorReason' } } + + ## + # @BLOCK_JOB_COMPLETED: +-- +1.8.3.1 + diff --git a/SOURCES/0020-Migration-compat-for-pckbd.patch b/SOURCES/0020-Migration-compat-for-pckbd.patch new file mode 100644 index 0000000..4ca6bb1 --- /dev/null +++ b/SOURCES/0020-Migration-compat-for-pckbd.patch @@ -0,0 +1,68 @@ +From a13b85a600a676b75777d71dd6f11c233a5d8849 Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Thu, 25 Jun 2015 16:34:25 +0200 +Subject: Migration compat for pckbd + +Patchwork-id: 66497 +O-Subject: [RHEL-7.2 qemu-kvm-rhev PATCH 1/1] Migration compat for pckbd +Bugzilla: 1215092 +RH-Acked-by: Juan Quintela +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Paolo Bonzini + +From: "Dr. David Alan Gilbert" + +2.2 added a new subsection to the pc keyboard model to preserve +the value of 'outport' across migration; to maintain migration +backwards compatibility this patch disables it on older machine types. +This leaves us no-worse-off than in older versions. + +Even with the new code, the value migrated in 'outport' isn't used +anywhere in the pckbd model; for example the value migrated doesn't +change the state of the A20 line or potentially do a reset. + +The only effect, as far as I can tell is if the guest were to +explicitly read the outport value it might get a more sensible reply. + +Signed-off-by: Dr. David Alan Gilbert +Signed-off-by: Miroslav Rezanina +(cherry picked from commit d2aba8a17b40c081f2ce29bc12b3945b7fb91d53) +(cherry picked from commit 583b819622f77c9e62bf3cd4f44b08de8aa46dcd) + +Conflicts: + hw/input/pckbd.c + +(cherry picked from commit b26d8202c6a76320865d3a9dad113e570f4e1bad) +(cherry picked from commit fd17a12528099d896cbdb032425f08d393fca145) +(cherry picked from commit 9fb05d3c53161d680e58dc44b3db75d4c9a43453) +--- + hw/input/pckbd.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c +index 66adb83..bbd4112 100644 +--- a/hw/input/pckbd.c ++++ b/hw/input/pckbd.c +@@ -27,6 +27,7 @@ + #include "hw/i386/pc.h" + #include "hw/input/ps2.h" + #include "hw/input/i8042.h" ++#include "migration/migration.h" + #include "sysemu/sysemu.h" + + /* debug PC keyboard */ +@@ -390,6 +391,11 @@ static int kbd_outport_post_load(void *opaque, int version_id) + static bool kbd_outport_needed(void *opaque) + { + KBDState *s = opaque; ++ ++ if (migrate_pre_2_2) { ++ return false; ++ } ++ + return s->outport != kbd_outport_default(s); + } + +-- +1.8.3.1 + diff --git a/SOURCES/0021-Migration-compat-for-fdc.patch b/SOURCES/0021-Migration-compat-for-fdc.patch new file mode 100644 index 0000000..c1f3355 --- /dev/null +++ b/SOURCES/0021-Migration-compat-for-fdc.patch @@ -0,0 +1,130 @@ +From 81b63caf259fac3ec0906f6d7712a831206d0dd5 Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Fri, 26 Jun 2015 16:19:47 +0200 +Subject: Migration compat for fdc + +Patchwork-id: 66534 +O-Subject: [RHEL-7.2 qemu-kvm-rhev PATCH 1/1] Migration compat for fdc +Bugzilla: 1215091 +RH-Acked-by: Amit Shah +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Juan Quintela + +From: "Dr. David Alan Gilbert" + +2.2 added some sections into the fdc and floppy drive, this patch +disables those new sections for reverse migration compatibility. + +There are three pieces of data added to the migration: + 1) 'perpendicular mode' on the drive - i.e. 2.88MB mode, that + was rare as hens teeth and the flag isn't actually used anywhere. + + 2) fdc_reset_sensei + This relates to the state of the fdc just after a reset command; + the fdc produces four 'sense' states internally (corresponding to + one external interrupt) that is there for backwards compatibility + to an older fdc (and to 8" drives!!). This compatibility code + was added to qemu to fix SCO Openserver floppy problems, ~2009. + Migration just after an fdc-reset would get the initial interrupt + but lose the extra 3 sense states. Print a log message since + that's guest visible, but it's not knowingly caused us a problem + so don't fail migration. + + 3) result-timer + The emulation models a delay after the 'read id' command which + is handled by a timer; if we migrate before the timer goes off + we probably wont complete the command. + I'm worried that the most likely time that a 'read id' would be + used would be in a background probe to see if there's a floppy + present, so again, don't fail the migrate, but do print a log + message. With any luck any sane floppy driver will have a + timeout; if we hit problems then a work around would be to + make the pre-save mark the command as finished with error. + +Signed-off-by: Dr. David Alan Gilbert +Signed-off-by: Miroslav Rezanina +(cherry picked from commit b18d89c56aa26e86fb6194f77a15a72244d5ff88) +(cherry picked from commit 50613448205a2e85e90a488be1c83d0f2b5f2d52) +(cherry picked from commit 5bdbc1fe6b4574c78c80e664c539c51d7719f84d) +(cherry picked from commit 069889e404721c5439ee7d69aa90f9d4580f4f21) +(cherry picked from commit fd31805201e8da37645c5a34f7990a281afce10f) +--- + hw/block/fdc.c | 39 +++++++++++++++++++++++++++++++++++++-- + 1 file changed, 37 insertions(+), 2 deletions(-) + +diff --git a/hw/block/fdc.c b/hw/block/fdc.c +index 3964096..6943263 100644 +--- a/hw/block/fdc.c ++++ b/hw/block/fdc.c +@@ -36,6 +36,7 @@ + #include "hw/isa/isa.h" + #include "hw/sysbus.h" + #include "hw/block/block.h" ++#include "migration/migration.h" + #include "sysemu/block-backend.h" + #include "sysemu/blockdev.h" + #include "sysemu/sysemu.h" +@@ -1036,6 +1037,10 @@ static bool fdrive_perpendicular_needed(void *opaque) + { + FDrive *drive = opaque; + ++ if (migrate_pre_2_2) { ++ return false; ++ } ++ + return drive->perpendicular != 0; + } + +@@ -1130,8 +1135,20 @@ static int fdc_post_load(void *opaque, int version_id) + static bool fdc_reset_sensei_needed(void *opaque) + { + FDCtrl *s = opaque; ++ bool needed = s->reset_sensei != 0; ++ ++ if (migrate_pre_2_2) { ++ /* ++ * This probably wont matter for most OSs, but it's good to log ++ * it just incase we find it causes problems. ++ */ ++ if (needed) { ++ error_report("INFO: fdc migration just after reset (sensei!=0)"); ++ } ++ return false; ++ } + +- return s->reset_sensei != 0; ++ return needed; + } + + static const VMStateDescription vmstate_fdc_reset_sensei = { +@@ -1148,8 +1165,26 @@ static const VMStateDescription vmstate_fdc_reset_sensei = { + static bool fdc_result_timer_needed(void *opaque) + { + FDCtrl *s = opaque; ++ bool needed = timer_pending(s->result_timer); ++ ++ if (migrate_pre_2_2) { ++ /* ++ * This could upset some OSs if their read-id command doesn't ++ * complete, so lets log something. ++ */ ++ if (needed) { ++ error_report("INFO: fdc migration just after read-id (timer!=0)"); ++ } ++ /* ++ * However, since it's not apparently caused us problems for many ++ * years, don't fail the migration, especially as this could ++ * happen as part of a background drive-probe which if it fails ++ * won't be a problem. ++ */ ++ return false; ++ } + +- return timer_pending(s->result_timer); ++ return needed; + } + + static const VMStateDescription vmstate_fdc_result_timer = { +-- +1.8.3.1 + diff --git a/SOURCES/0022-spapr-Reduce-advertised-max-LUNs-for-spapr_vscsi.patch b/SOURCES/0022-spapr-Reduce-advertised-max-LUNs-for-spapr_vscsi.patch new file mode 100644 index 0000000..5793a1d --- /dev/null +++ b/SOURCES/0022-spapr-Reduce-advertised-max-LUNs-for-spapr_vscsi.patch @@ -0,0 +1,52 @@ +From ee04d835a5a44c7f2ca4cc42f5603879b64a9ebd Mon Sep 17 00:00:00 2001 +From: David Gibson +Date: Wed, 9 Sep 2015 02:03:04 +0200 +Subject: spapr: Reduce advertised max LUNs for spapr_vscsi + +Patchwork-id: 67711 +O-Subject: [RHEL7.2 qemu-kvm-rhev PATCH] spapr: Reduce advertised max LUNs for spapr_vscsi +Bugzilla: 1260464 +RH-Acked-by: Miroslav Rezanina +RH-Acked-by: Laurent Vivier +RH-Acked-by: Thomas Huth + +The implementation of the PAPR paravirtual SCSI adapter currently +allows up to 32 LUNs (max_lun == 31). However the adapter isn't really +designed to support lots of devices - the PowerVM implementation only +ever puts one disk per vSCSI controller. + +More specifically, the Linux guest side vscsi driver (the only one we +really care about) is hardcoded to allow a maximum of 8 LUNs. + +So, reduce the number of LUNs on the qemu side to 8, so we don't +falsely advertise that more LUNs can work. + +Signed-off-by: David Gibson + +Signed-off-by: David Gibson +Signed-off-by: Miroslav Rezanina +(cherry picked from commit 6c166e382c232ba4e527b4fc5ca9fdea151498d9) +(cherry picked from commit 9eadef5817e22891efc509ff1af6f0d14545b2b0) +(cherry picked from commit dea0aabfbf919f28cf04db7343c42dc423fdf79b) +(cherry picked from commit 6aef4d0da0a410a8224dec70743c32d4e10eee8b) +(cherry picked from commit 50fdb5661adeeb0867bbaae8cd9ac55d71499231) +--- + hw/scsi/spapr_vscsi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c +index a9e49c7..3a06b2f 100644 +--- a/hw/scsi/spapr_vscsi.c ++++ b/hw/scsi/spapr_vscsi.c +@@ -1178,7 +1178,7 @@ static const struct SCSIBusInfo vscsi_scsi_info = { + .tcq = true, + .max_channel = 7, /* logical unit addressing format */ + .max_target = 63, +- .max_lun = 31, ++ .max_lun = 7, + + .transfer_data = vscsi_transfer_data, + .complete = vscsi_command_complete, +-- +1.8.3.1 + diff --git a/SOURCES/0023-RHEL-only-hw-char-pl011-fix-SBSA-reset.patch b/SOURCES/0023-RHEL-only-hw-char-pl011-fix-SBSA-reset.patch new file mode 100644 index 0000000..f3d422f --- /dev/null +++ b/SOURCES/0023-RHEL-only-hw-char-pl011-fix-SBSA-reset.patch @@ -0,0 +1,61 @@ +From 098bbd9ed847849834ff1f21766d23ea240c5bf0 Mon Sep 17 00:00:00 2001 +From: Andrew Jones +Date: Mon, 1 Aug 2016 14:27:09 +0200 +Subject: RHEL-only: hw/char/pl011: fix SBSA reset + +RH-Author: Andrew Jones +Message-id: <1470061629-6395-1-git-send-email-drjones@redhat.com> +Patchwork-id: 71697 +O-Subject: [AArch64 RHEL-7.3 qemu-kvm-rhev PATCH] RHEL-only: hw/char/pl011: fix SBSA reset +Bugzilla: 1266048 +RH-Acked-by: Auger Eric +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Wei Huang + +When booting Linux with an SBSA UART, e.g. when booting mach-virt +with ACPI, if the user types on the console during boot, then when +the login prompt appears she won't be able to log in. This is +because during boot the SBSA UART needs to be reset, but the SBSA +specification doesn't provide registers to enable/disable the FIFOs. +This patch observes a couple registers the SBSA UART does write to +in order to attempt to guess when a reset is needed, and then do it. +We risk losing some characters from the FIFO if the guess is wrong, +but the risk of that should be quite low. + +Signed-off-by: Andrew Jones +Signed-off-by: Miroslav Rezanina +(cherry picked from commit 15ee295534f654d6b6ba9499cdd380aa9c954920) +(cherry picked from commit 49be481336c227fdad2f7edc02fa088f3d88c9a2) +(cherry picked from commit 9fcede24f35378e2c9113440a692d2c96cc94865) +(cherry picked from commit b721e7ff2bdd42b0a786b8630c1ba8b1d3560da3) +(cherry picked from commit 88f3eb70f909b03ed18074aadd12f32f28ad8437) +--- + hw/char/pl011.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/hw/char/pl011.c b/hw/char/pl011.c +index 2aa277f..23fe047 100644 +--- a/hw/char/pl011.c ++++ b/hw/char/pl011.c +@@ -209,6 +209,18 @@ static void pl011_write(void *opaque, hwaddr offset, + pl011_update(s); + break; + case 17: /* UARTICR */ ++ /* ++ * RHEL-only, fixes BZ1266048 ++ * ++ * Look for the "signature" of a driver init or shutdown in ++ * order to know that we need to reset the SBSA UART. Yes, ++ * this is hacky, but as SBSA drivers aren't required to write ++ * UARTLCR_H or UARTCR, then we don't have much choice... ++ */ ++ if (s->int_enabled == 0 && value == 0xffff) { ++ s->read_count = 0; ++ s->read_pos = 0; ++ } + s->int_level &= ~value; + pl011_update(s); + break; +-- +1.8.3.1 + diff --git a/SOURCES/0024-blockdev-ignore-cache-options-for-empty-CDROM-drives.patch b/SOURCES/0024-blockdev-ignore-cache-options-for-empty-CDROM-drives.patch new file mode 100644 index 0000000..4c5c575 --- /dev/null +++ b/SOURCES/0024-blockdev-ignore-cache-options-for-empty-CDROM-drives.patch @@ -0,0 +1,94 @@ +From 8d74e190054f8a2254f9da5a0eb2e17c66df94db Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 16 Sep 2016 22:06:28 +0200 +Subject: blockdev: ignore cache options for empty CDROM drives + +RH-Author: John Snow +Message-id: <1474063588-6370-2-git-send-email-jsnow@redhat.com> +Patchwork-id: 72377 +O-Subject: [RHEV-7.3 qemu-kvm-rhev PATCH 1/1] blockdev: ignore cache options for empty CDROM drives +Bugzilla: 1342999 +RH-Acked-by: Max Reitz +RH-Acked-by: Kevin Wolf +RH-Acked-by: Stefan Hajnoczi + +In qemu-kvm-rhev-2.3.0, QEMU will accept cache options for empty CDROM +devices, but silently ignore them as they will be overwritten when the +next CDROM is inserted. + +Libvirt and VMM are capable of generating XML configurations which +attempt to specify these cache options to QEMU, though they don't have +any effect. + +Upstream, a refactoring of cache option mechanisms means that we have +started rejecting invalid configurations where cache options are supplied +without any target to actually apply them to. + +This means that there are combinations of QEMU and libvirt that will fail +to start a VM if a user selects a cache option. + +This patch is a downstream-only workaround until libvirt can stop +supplying cache settings for empty CDROMs and/or until libvirt can take +advantage of the new QMP tray/medium manipulation mechanisms that will +allow proper cache specification for removable media. + +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +(cherry picked from commit 89b162019bfd202bbbd00563d03a030c2f7c1395) +(cherry picked from commit 454f60447bfea904d561ef282fc0446229484c02) +(cherry picked from commit 50e4b1d94ce41a9c83034d372caea6ed20d6fcfb) +(cherry picked from commit 54994db40273e0b4bec5f703459fdb71a453a373) +(cherry picked from commit 0f4f6f39126ba96a4b288bb13bcd0f13eb46fa35) +--- + blockdev.c | 28 +++++++++++++++++++++++++++- + 1 file changed, 27 insertions(+), 1 deletion(-) + +diff --git a/blockdev.c b/blockdev.c +index e941b99..cf10108 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -460,6 +460,32 @@ static void extract_common_blockdev_options(QemuOpts *opts, int *bdrv_flags, + } + } + ++/** ++ * libvirt expects to be able to pass cache options for CDROM drives without ++ * inserted media. Historically, QEMU eventually ignores these cache options as ++ * they are lost when media is inserted. Recently, QEMU started rejecting these ++ * configurations. Libvirt however still generates such configurations. ++ * ++ * To prevent QEMU from being unable to start, pretend there are no options ++ * present if the only options present are cache options for the BDS. ++ */ ++static bool __redhat_com_has_bs_opts(QDict *bs_opts) ++{ ++ size_t n, s; ++ s = qdict_size(bs_opts); ++ ++ if (s == 0) { ++ return false; ++ } else if (s > 2) { ++ return true; ++ } ++ ++ n = qdict_haskey(bs_opts, BDRV_OPT_CACHE_DIRECT); ++ n += qdict_haskey(bs_opts, BDRV_OPT_CACHE_NO_FLUSH); ++ ++ return s != n; ++} ++ + /* Takes the ownership of bs_opts */ + static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, + Error **errp) +@@ -567,7 +593,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, + read_only = qemu_opt_get_bool(opts, BDRV_OPT_READ_ONLY, false); + + /* init */ +- if ((!file || !*file) && !qdict_size(bs_opts)) { ++ if ((!file || !*file) && !__redhat_com_has_bs_opts(bs_opts)) { + BlockBackendRootState *blk_rs; + + blk = blk_new(0, BLK_PERM_ALL); +-- +1.8.3.1 + diff --git a/SOURCES/0025-usb-xhci-Fix-PCI-capability-order.patch b/SOURCES/0025-usb-xhci-Fix-PCI-capability-order.patch new file mode 100644 index 0000000..0fc8c0d --- /dev/null +++ b/SOURCES/0025-usb-xhci-Fix-PCI-capability-order.patch @@ -0,0 +1,96 @@ +From 273826f0427e0e62be20ea42349dfb591dbcdf14 Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Fri, 5 May 2017 19:06:14 +0200 +Subject: usb-xhci: Fix PCI capability order + +RH-Author: Dr. David Alan Gilbert +Message-id: <20170505190614.15987-2-dgilbert@redhat.com> +Patchwork-id: 75038 +O-Subject: [RHEL-7.4 qemu-kvm-rhev PATCH 1/1] usb-xhci: Fix PCI capability order +Bugzilla: 1447874 +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Gerd Hoffmann +RH-Acked-by: Juan Quintela + +From: "Dr. David Alan Gilbert" + +Upstream commit 1108b2f8a9 in 2.7.0 changed the order +of the PCI capability chain in the XHCI pci device in the case +where the device has the PCIe endpoint capability (i.e. only +older machine types, pc-i440fx-2.0 upstream, pc-i440fx-rhel7.0.0 +apparently for us). + +Changing the order breaks migration compatibility; fixing this +upstream would mean breaking the same case going from 2.7.0->current +that currently works 2.7.0->2.9.0 - so upstream it's a choice +of two breakages. + +Since we never released 2.7.0/2.8.0 we can fix this downstream. + +This reverts the order so that we create the capabilities in the +order: + PCIe + MSI + MSI-X + +The symptom is: +qemu-kvm: get_pci_config_device: Bad config data: i=0x71 read: a0 device: 0 cmask: ff wmask: 0 w1cmask:0 +qemu-kvm: Failed to load PCIDevice:config +qemu-kvm: Failed to load xhci:parent_obj +qemu-kvm: error while loading state for instance 0x0 of device '0000:00:0d.0/xhci' +qemu-kvm: load of migration failed: Invalid argument + +Signed-off-by: Dr. David Alan Gilbert +Signed-off-by: Miroslav Rezanina + +-- +Rebase notes (2.9.0): +- Change in assert condition (upstream) + +(cherry picked from commit aad727a5ecde1ad4935eb8427604d4df5a1f1f35) +(cherry picked from commit 2dd7402227e77d748a7375233ac9e7feab244bda) + +Conflicts: + hw/usb/hcd-xhci.c + +(cherry picked from commit a42f86dc906cc7d2c16d02bf125ed76847b469cb) +(cherry picked from commit 992ab2e4f6e15d3e51bc716763aa8d6f45c6d29d) +(cherry picked from commit 5b7b0303b6dd70c32bd03a9d8facd7eb7cf72be8) +--- + hw/usb/hcd-xhci.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index 883141f..181e803 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -3368,6 +3368,12 @@ static void usb_xhci_realize(struct PCIDevice *dev, Error **errp) + xhci->max_pstreams_mask = 0; + } + ++ if (pci_bus_is_express(pci_get_bus(dev)) || ++ xhci_get_flag(xhci, XHCI_FLAG_FORCE_PCIE_ENDCAP)) { ++ ret = pcie_endpoint_cap_init(dev, 0xa0); ++ assert(ret > 0); ++ } ++ + if (xhci->msi != ON_OFF_AUTO_OFF) { + ret = msi_init(dev, 0x70, xhci->numintrs, true, false, &err); + /* Any error other than -ENOTSUP(board's MSI support is broken) +@@ -3416,12 +3422,6 @@ static void usb_xhci_realize(struct PCIDevice *dev, Error **errp) + PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64, + &xhci->mem); + +- if (pci_bus_is_express(pci_get_bus(dev)) || +- xhci_get_flag(xhci, XHCI_FLAG_FORCE_PCIE_ENDCAP)) { +- ret = pcie_endpoint_cap_init(dev, 0xa0); +- assert(ret > 0); +- } +- + if (xhci->msix != ON_OFF_AUTO_OFF) { + /* TODO check for errors, and should fail when msix=on */ + msix_init(dev, xhci->numintrs, +-- +1.8.3.1 + diff --git a/SOURCES/0026-blockdev-ignore-aio-native-for-empty-drives.patch b/SOURCES/0026-blockdev-ignore-aio-native-for-empty-drives.patch new file mode 100644 index 0000000..e459de3 --- /dev/null +++ b/SOURCES/0026-blockdev-ignore-aio-native-for-empty-drives.patch @@ -0,0 +1,78 @@ +From d4fe1e7fd6002d201088870c1e019b607c168454 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Thu, 11 May 2017 20:53:51 +0200 +Subject: blockdev: ignore aio=native for empty drives + +RH-Author: John Snow +Message-id: <20170511205351.6337-2-jsnow@redhat.com> +Patchwork-id: 75070 +O-Subject: [RHV-7.4 qemu-kvm-rhev PATCH 1/1] blockdev: ignore aio=native for empty drives +Bugzilla: 1402645 +RH-Acked-by: Kevin Wolf +RH-Acked-by: Eric Blake +RH-Acked-by: Max Reitz + +This is a bit of a gross one; Upstream QEMU changed the way it handles +cache options with regards to removable media, associating options more +with the medium instead of the device. As part of that, it became +impossible to specify cache options on empty drives. + +In the future, one would use blockdev-add instead to choose cache options +per-medium instead of per-device, but we're not there yet in libvirt so +we added a workaround downstream to simply ignore cache options on empty +CDROMs under the premise of "It actually never worked anyway." + +As fallout from this decision, it is now no longer possible to specify +aio=native on empty CDROM devices either, as that requires the use of a +cache option that you can no longer specify. As a bad, gross, disgusting +workaround, simply ignore aio=native on empty drives until such time that +libvirt stops providing such configurations. + +Signed-off-by: Miroslav Rezanina +(cherry picked from commit cda174ad842c9a61bc275315bf3155139ba19bc0) +(cherry picked from commit a72f1c0f7b02ea571ec2ce35dfbe8c17c4dfa6d9) +(cherry picked from commit 313ffb6aba9419fe620b898c9b4a5ab4b0bff205) +(cherry picked from commit fa1666d4f59412a324067a1bd9b4fed7ca8c71f2) +(cherry picked from commit 62a7f6391872a32c3fdcbce988e2b7b760fca0c9) +--- + blockdev.c | 17 ++++++++++++++++- + 1 file changed, 16 insertions(+), 1 deletion(-) + +diff --git a/blockdev.c b/blockdev.c +index cf10108..60b37dc 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -486,6 +486,21 @@ static bool __redhat_com_has_bs_opts(QDict *bs_opts) + return s != n; + } + ++/** ++ * libvirt expects to be able to pass io driver options (aio=native) for CDROM ++ * drives without inserted media. While this has worked historically, given the ++ * above workaround and lack of a supported alternative in current versions of ++ * libvirt, certain options such as aio=native cannot be supported as it ++ * requires the use of an accompanying cache option, which we also ignore. ++ * Until libvirt learns how to supply cache options to inserted media, ignore ++ * the aio= preference on empty CDROMs with the understanding that un-tuned ++ * performance is preferable to being unable to use the CDROM at all. ++ */ ++static int __redhat_com_filter_flags(int flags) ++{ ++ return flags & ~BDRV_O_NATIVE_AIO; ++} ++ + /* Takes the ownership of bs_opts */ + static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, + Error **errp) +@@ -598,7 +613,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, + + blk = blk_new(0, BLK_PERM_ALL); + blk_rs = blk_get_root_state(blk); +- blk_rs->open_flags = bdrv_flags; ++ blk_rs->open_flags = __redhat_com_filter_flags(bdrv_flags); + blk_rs->read_only = read_only; + blk_rs->detect_zeroes = detect_zeroes; + +-- +1.8.3.1 + diff --git a/SOURCES/0027-virtio-scsi-Reject-scsi-cd-if-data-plane-enabled-RHE.patch b/SOURCES/0027-virtio-scsi-Reject-scsi-cd-if-data-plane-enabled-RHE.patch new file mode 100644 index 0000000..4d9b5f3 --- /dev/null +++ b/SOURCES/0027-virtio-scsi-Reject-scsi-cd-if-data-plane-enabled-RHE.patch @@ -0,0 +1,70 @@ +From db9aa5263282c293ffd22dd80a273590fcde92f2 Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Wed, 14 Jun 2017 15:37:01 +0200 +Subject: virtio-scsi: Reject scsi-cd if data plane enabled [RHEL only] + +RH-Author: Fam Zheng +Message-id: <20170614153701.14757-1-famz@redhat.com> +Patchwork-id: 75613 +O-Subject: [RHV-7.4 qemu-kvm-rhev PATCH v3] virtio-scsi: Reject scsi-cd if data plane enabled [RHEL only] +Bugzilla: 1378816 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz + +We need a fix for RHEL 7.4 and 7.3.z, but unfortunately upstream isn't +ready. If it were, the changes will be too invasive. To have an idea: + +https://lists.gnu.org/archive/html/qemu-devel/2017-05/msg05400.html + +is an incomplete attempt to fix part of the issue, and the remaining +work unfortunately involve even more complex changes. + +As a band-aid, this partially reverts the effect of ef8875b +(virtio-scsi: Remove op blocker for dataplane, since v2.7). We cannot +simply revert that commit as a whole because we already shipped it in +qemu-kvm-rhev 7.3, since when, block jobs has been possible. We should +only block what has been broken. Also, faithfully reverting the above +commit means adding back the removed op blocker, but that is not enough, +because it still crashes when inserting media into an initially empty +scsi-cd. + +All in all, scsi-cd on virtio-scsi-dataplane has basically been unusable +unless the scsi-cd never enters an empty state, so, disable it +altogether. Otherwise it would be much more difficult to avoid +crashing. + +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +(cherry picked from commit b0caf00bbc35c7d89e02999bdce86e1f867728e8) +(cherry picked from commit c9c4f117d8b507c2f86035c282d537c0a327364f) +(cherry picked from commit be16f8328fe109bab1ebc8f17b16b604e6e139d8) +(cherry picked from commit 39884820a2d464918ea383df0519c2de0731b923) +(cherry picked from commit f1e2dabe06a7f4d0b5ef4c96e5ace53b9df14296) +--- + hw/scsi/virtio-scsi.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c +index 3aa9971..9f754c4 100644 +--- a/hw/scsi/virtio-scsi.c ++++ b/hw/scsi/virtio-scsi.c +@@ -790,6 +790,15 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, + VirtIOSCSI *s = VIRTIO_SCSI(vdev); + SCSIDevice *sd = SCSI_DEVICE(dev); + ++ /* XXX: Remove this check once block backend is capable of handling ++ * AioContext change upon eject/insert. ++ * s->ctx is NULL if ioeventfd is off, s->ctx is qemu_get_aio_context() if ++ * data plane is not used, both cases are safe for scsi-cd. */ ++ if (s->ctx && s->ctx != qemu_get_aio_context() && ++ object_dynamic_cast(OBJECT(dev), "scsi-cd")) { ++ error_setg(errp, "scsi-cd is not supported by data plane"); ++ return; ++ } + if (s->ctx && !s->dataplane_fenced) { + if (blk_op_is_blocked(sd->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { + return; +-- +1.8.3.1 + diff --git a/SOURCES/0028-osdep-Force-define-F_OFD_GETLK-RHEL-only.patch b/SOURCES/0028-osdep-Force-define-F_OFD_GETLK-RHEL-only.patch new file mode 100644 index 0000000..6337e92 --- /dev/null +++ b/SOURCES/0028-osdep-Force-define-F_OFD_GETLK-RHEL-only.patch @@ -0,0 +1,56 @@ +From 0901cd0ef66131ea6fe93654b0ceb1feaad9a0e5 Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Thu, 12 Oct 2017 13:54:45 +0200 +Subject: osdep: Force define F_OFD_GETLK (RHEL only) + +RH-Author: Fam Zheng +Message-id: <20171012135445.4214-1-famz@redhat.com> +Patchwork-id: 77220 +O-Subject: [RHV7.5 qemu-kvm-ma PATCH] osdep: Force define F_OFD_GETLK (RHEL only) +Bugzilla: 1378241 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Thomas Huth + +BZ: 1378241 +Brew: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=14254218 + +glibc is not ready yet (BZ 1461231, which is deferred to 7.6 due to +capacity), so the OFD constants are not defined in the system headers we +pull in. (They do exist in the headers of latest kernel-headers package, +but we don't want to include that anyway.) + +Actually the constants are all that are missing before we can call image +locking done in 7.5, so there is no reason to wait for glibc. + +This patch can be reverted once the new glibc headers are in place. + +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +(cherry picked from commit ac74b9067d079b03f3fe4236270f9eb34121009b) +(cherry picked from commit ae5b5b95b3a6ba4d1d9fb424cf95205e43f2ad67) +(cherry picked from commit f380fac7d927459c3675dabd125be8a379f43a2c) +(cherry picked from commit fb92cc1c98ff118afbc18ca1c22379ab3cf76f7a) +--- + util/osdep.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/util/osdep.c b/util/osdep.c +index a73de0e..0dbe6ec 100644 +--- a/util/osdep.c ++++ b/util/osdep.c +@@ -23,6 +23,11 @@ + */ + #include "qemu/osdep.h" + ++#ifndef F_OFD_SETLK ++#define F_OFD_GETLK 36 ++#define F_OFD_SETLK 37 ++#endif ++ + /* Needed early for CONFIG_BSD etc. */ + + #ifdef CONFIG_SOLARIS +-- +1.8.3.1 + diff --git a/SOURCES/0029-spapr-disable-cpu-hot-remove.patch b/SOURCES/0029-spapr-disable-cpu-hot-remove.patch new file mode 100644 index 0000000..1779e45 --- /dev/null +++ b/SOURCES/0029-spapr-disable-cpu-hot-remove.patch @@ -0,0 +1,60 @@ +From 695c0b884eb5e6f35023b0e32928741c044b0498 Mon Sep 17 00:00:00 2001 +From: Igor Mammedov +Date: Thu, 19 Oct 2017 15:28:13 +0200 +Subject: spapr: disable cpu hot-remove + +RH-Author: Igor Mammedov +Message-id: <1508426893-172020-1-git-send-email-imammedo@redhat.com> +Patchwork-id: 77378 +O-Subject: [RHV7.5 qemu-kvm-ma PATCH v2] spapr: disable cpu hot-remove +Bugzilla: 1499320 +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Thomas Huth +RH-Acked-by: David Gibson + +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1499320 +Brew: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=14301295 +Upstream: RHEL-only + +Rebase to 2.10 brought in cpu hot-remove with it, disable it +for qemu-kvm-ma variant of QEMU where it hasn't been supported. +Use CONFIG_RHV to switch unplug off only for qemu-kvm-ma and +not to affect RHEV variant built from the same source code. + +Signed-off-by: Igor Mammedov + +Conflicts: + hw/ppc/spapr.c + +(cherry picked from commit 56c058f25caa085fb22a92d89976afb529a391d8) +(cherry picked from commit c5448e8e407c2275d5b74e93fbf67eafb93b5a56) +(cherry picked from commit 7556463d8bfe3f357960962c6da2f6dc692b289f) +--- + hw/ppc/spapr.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c +index 3ea94de..f71a62e 100644 +--- a/hw/ppc/spapr.c ++++ b/hw/ppc/spapr.c +@@ -3315,6 +3315,7 @@ static + void spapr_core_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) + { ++#if defined(CONFIG_RHV) + sPAPRMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); + int index; + sPAPRDRConnector *drc; +@@ -3337,6 +3338,9 @@ void spapr_core_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, + spapr_drc_detach(drc); + + spapr_hotplug_req_remove_by_index(drc); ++#else ++ error_setg(errp, "this feature or command is not currently supported"); ++#endif /* CONFIG_RHV */ + } + + static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, +-- +1.8.3.1 + diff --git a/SOURCES/0030-migration-Reenable-incoming-live-block-migration.patch b/SOURCES/0030-migration-Reenable-incoming-live-block-migration.patch new file mode 100644 index 0000000..be676f1 --- /dev/null +++ b/SOURCES/0030-migration-Reenable-incoming-live-block-migration.patch @@ -0,0 +1,92 @@ +From ed076b2838fb9c9b7e137352e72408d9267fd702 Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Wed, 22 Nov 2017 15:41:32 +0100 +Subject: migration: Reenable incoming live-block-migration + +RH-Author: Dr. David Alan Gilbert +Message-id: <20171122154132.15363-1-dgilbert@redhat.com> +Patchwork-id: 77779 +O-Subject: [RHEL-7.5 qemu-kvm-rhev PATCH 1/1] migration: Reenable incoming live-block-migration +Bugzilla: 1515173 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Fam Zheng +RH-Acked-by: Juan Quintela + +From: "Dr. David Alan Gilbert" + +rhel7 has always disabled outgoing old-style live block migration +(but in -rhev enabled nbd block migration); however it allows +incoming old-style live block migration to allow reception of a stream +from rhel6. + +I added --disable-live-block-migration to upstream in ed1701c6a5a, +however that really did disable it completely. + +In the 7.5 world we've inherited the upstream version and lost the +incoming support. + +Reenable incoming support even when outgoing is disabled. + +Signed-off-by: Dr. David Alan Gilbert +Signed-off-by: Miroslav Rezanina +(cherry picked from commit e6f62c7eb81f164ad5aef99a4f7ff48200928938) +(cherry picked from commit d5fb1ed0bc8fe06f7f9ffa50df033f1be6a19335) +(cherry picked from commit d4f3d9c5f333b1b97e0eea3eff665988d92f7a24) +(cherry picked from commit 011f4eb1ad9f4a3b358a904348340b010cf54512) +--- + include/migration/misc.h | 7 +++---- + migration/Makefile.objs | 2 +- + migration/block.h | 6 +++++- + 3 files changed, 9 insertions(+), 6 deletions(-) + +diff --git a/include/migration/misc.h b/include/migration/misc.h +index 4ebf24c..10084e0 100644 +--- a/include/migration/misc.h ++++ b/include/migration/misc.h +@@ -22,11 +22,10 @@ void ram_mig_init(void); + + /* migration/block.c */ + +-#ifdef CONFIG_LIVE_BLOCK_MIGRATION ++/* RHEL7 allows incoming block migration even with ++ * --disable-live-block-migration to allow RHEL6->7 migration. ++ */ + void blk_mig_init(void); +-#else +-static inline void blk_mig_init(void) {} +-#endif + + #define SELF_ANNOUNCE_ROUNDS 5 + +diff --git a/migration/Makefile.objs b/migration/Makefile.objs +index c83ec47..5cb9fc0 100644 +--- a/migration/Makefile.objs ++++ b/migration/Makefile.objs +@@ -10,6 +10,6 @@ common-obj-y += block-dirty-bitmap.o + + common-obj-$(CONFIG_RDMA) += rdma.o + +-common-obj-$(CONFIG_LIVE_BLOCK_MIGRATION) += block.o ++common-obj-y += block.o + + rdma.o-libs := $(RDMA_LIBS) +diff --git a/migration/block.h b/migration/block.h +index 3178609..1cc81b0 100644 +--- a/migration/block.h ++++ b/migration/block.h +@@ -14,7 +14,11 @@ + #ifndef MIGRATION_BLOCK_H + #define MIGRATION_BLOCK_H + +-#ifdef CONFIG_LIVE_BLOCK_MIGRATION ++/* RHEL7: live block migration is still compiled in even ++ * with --disable-live-block-migration since we must ++ * allow inbound migration from RHEL6. ++ */ ++#if 1 /* CONFIG_LIVE_BLOCK_MIGRATION */ + int blk_mig_active(void); + int blk_mig_bulk_active(void); + uint64_t blk_mig_bytes_transferred(void); +-- +1.8.3.1 + diff --git a/SOURCES/0031-block-vxhs-improve-error-message-for-missing-bad-vxh.patch b/SOURCES/0031-block-vxhs-improve-error-message-for-missing-bad-vxh.patch new file mode 100644 index 0000000..b5430bc --- /dev/null +++ b/SOURCES/0031-block-vxhs-improve-error-message-for-missing-bad-vxh.patch @@ -0,0 +1,65 @@ +From f2d1e27cdec37a9658bcb0262fb266ab3df4b695 Mon Sep 17 00:00:00 2001 +From: Jeffrey Cody +Date: Mon, 11 Dec 2017 22:38:12 +0100 +Subject: block/vxhs: improve error message for missing / bad vxhs module + +RH-Author: Jeffrey Cody +Message-id: <59af10d83125fff42beacd30dbca83d50409bbed.1513031708.git.jcody@redhat.com> +Patchwork-id: 78305 +O-Subject: [RHV7.5 qemu-kvm-rhev PATCH 1/1] block/vxhs: improve error message for missing / bad vxhs module +Bugzilla: 1505654 +RH-Acked-by: Markus Armbruster +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Laszlo Ersek + +[Downstream only, as the module load of libvxhs is downstream only] + +In the case of missing libvxhs libraries, the original error message, +while technically accurate, may lead a user to think there is a QEMU bug +if trying to using the VXHS protocol. Update the message so that it is +clear that the likely issue is that the Veritas QEMU libvxhs RPM is not +installed (or not installed correctly, if there are permission or file +corruption issues, etc.). + +An example error message before this change: + +> qemu-img info vxhs://localhost/test +qemu-img: Could not open 'vxhs://localhost/test': \ + error loading libvxhs: /usr/lib64/qemu/libvxhs.so.1: \ + cannot open shared object file: No such file or directory + +An example error message after this change: + +> qemu-img info vxhs://localhost/test +qemu-img: Could not open 'vxhs://localhost/test': \ + The VXHS library from Veritas might not be installed correctly \ + (/usr/lib64/qemu/libvxhs.so.1: \ + cannot open shared object file: No such file or directory) + +Signed-off-by: Jeff Cody +Signed-off-by: Miroslav Rezanina +(cherry picked from commit 363bf9a1aea6d4af394df27fa4fcaba95b00af98) +(cherry picked from commit c7d20788c4b8a3b285a1cb7ddcee790b74c1621f) +(cherry picked from commit dc13bdde418c8447859f4630f01af680119c14bb) +--- + block/vxhs.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/block/vxhs.c b/block/vxhs.c +index a18154c..68edb51 100644 +--- a/block/vxhs.c ++++ b/block/vxhs.c +@@ -115,7 +115,8 @@ static void bdrv_vxhs_load_libs(Error **errp) + libvxhs_handle = g_module_open(LIBVXHS_FULL_PATHNAME, + G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); + if (!libvxhs_handle) { +- error_setg(errp, "error loading libvxhs: %s", g_module_error()); ++ error_setg(errp, "The VXHS library from Veritas might not be installed " ++ "correctly (%s)", g_module_error()); + return; + } + +-- +1.8.3.1 + diff --git a/SOURCES/0032-serial-always-transmit-send-receive-buffers-on-migra.patch b/SOURCES/0032-serial-always-transmit-send-receive-buffers-on-migra.patch new file mode 100644 index 0000000..613977f --- /dev/null +++ b/SOURCES/0032-serial-always-transmit-send-receive-buffers-on-migra.patch @@ -0,0 +1,95 @@ +From c782c6646a2806a95a953bcfdb7c4801edfe65a4 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Thu, 11 Jan 2018 13:56:44 +0100 +Subject: serial: always transmit send/receive buffers on migration + +RH-Author: Paolo Bonzini +Message-id: <20180111135644.16253-1-pbonzini@redhat.com> +Patchwork-id: 78551 +O-Subject: [RHEL7.5 qemu-kvm-rhev PATCH v2] serial: always transmit send/receive buffers on migration +Bugzilla: 1459945 +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Miroslav Rezanina + +When subsections were added in qemu 2.3, they were all disabled +for machine types that require pre-2.3 compatibility. The commit +message says "disabling these subsections on older machine types should +leave it no worse than existing qemu", but actually migrating from +new QEMU to new QEMU (but with old machine type) will detect an +inconsistent migration state and fail the migration: + + qemu-system-x86_64: inconsistent state in serial device (tsr not empty, tsr_retry=0 + qemu-system-x86_64: Failed to load serial:state + qemu-system-x86_64: error while loading state for instance 0x0 of device 'serial' + +In fact, this shows that migration from new source to old destination +might have also eaten the data in the FIFO or transmit/receive buffers. + +It's actually pretty easy to trigger the failure by connecting a console +to a hung-up reader (not a *disconnected* reader!). The fix is to +handle the subsections the same as we did in the qemu-kvm BZ1452067. +The data registers are migrated, which may indeed cause some more migrations +to fail to old qemu-kvm-rhev, but it will fix migration to new qemu-kvm-rhev. +Some subsections are still keyed on migrate_pre_2_2; from the commit message +of downstream commit 7d2e8f9662feb64c0b15b6fd53e06e3c56921f27: + + thr_ipending can be reconstructed fairly + reliably by serial_post_load. The others are features that are + unlikely to be used in RHEL, respectively receive timeout (Linux + does not even have the UART_IIR_CTI symbol in the driver) and + physical serial ports connected to a modem + +I consider this okay because nobody has yet complained about it for +qemu-kvm. It's also safer because the failure avoids serial data loss +on migration. This is consistent with the intended use of subsections. + +Signed-off-by: Paolo Bonzini +Signed-off-by: Miroslav Rezanina +(cherry picked from commit 10564d0bb819113f925e32b989b24fb26dca45ef) +(cherry picked from commit 9e1104c955c05c7b850cda3a02f69bf3e931c765) +(cherry picked from commit 04e1727ca5a6cef2e23d387cef32ad18f4f1b6a7) +--- + hw/char/serial.c | 12 ------------ + 1 file changed, 12 deletions(-) + +diff --git a/hw/char/serial.c b/hw/char/serial.c +index 7647fac..d6d9b18 100644 +--- a/hw/char/serial.c ++++ b/hw/char/serial.c +@@ -722,10 +722,6 @@ static const VMStateDescription vmstate_serial_thr_ipending = { + static bool serial_tsr_needed(void *opaque) + { + SerialState *s = (SerialState *)opaque; +- if (migrate_pre_2_2) { +- return false; +- } +- + return s->tsr_retry != 0; + } + +@@ -745,10 +741,6 @@ static const VMStateDescription vmstate_serial_tsr = { + static bool serial_recv_fifo_needed(void *opaque) + { + SerialState *s = (SerialState *)opaque; +- if (migrate_pre_2_2) { +- return false; +- } +- + return !fifo8_is_empty(&s->recv_fifo); + + } +@@ -767,10 +759,6 @@ static const VMStateDescription vmstate_serial_recv_fifo = { + static bool serial_xmit_fifo_needed(void *opaque) + { + SerialState *s = (SerialState *)opaque; +- if (migrate_pre_2_2) { +- return false; +- } +- + return !fifo8_is_empty(&s->xmit_fifo); + } + +-- +1.8.3.1 + diff --git a/SOURCES/0033-target-i386-sanitize-x86-MSR_PAT-loaded-from-another.patch b/SOURCES/0033-target-i386-sanitize-x86-MSR_PAT-loaded-from-another.patch new file mode 100644 index 0000000..88668ba --- /dev/null +++ b/SOURCES/0033-target-i386-sanitize-x86-MSR_PAT-loaded-from-another.patch @@ -0,0 +1,79 @@ +From ebba9a277c36df319d1ec59f25d8b8d596fb8ab4 Mon Sep 17 00:00:00 2001 +From: Wei Huang +Date: Wed, 17 Jan 2018 22:13:23 +0100 +Subject: target-i386: sanitize x86 MSR_PAT loaded from another source + +RH-Author: Wei Huang +Message-id: <20180117221323.1008-1-wei@redhat.com> +Patchwork-id: 78659 +O-Subject: [RHEL-7.5 qemu-kvm-rhev PATCH 1/1] target-i386: sanitize x86 MSR_PAT loaded from another source +Bugzilla: 1529461 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Miroslav Rezanina + +The RHEL 7 downstream commit a94f33258 honors guest VM's writes of MSR_PAT +for SVM machines. But this cause a problem when an x86 VM is migrated from +an old host, such as RHEL 6.9. This is because older system doesn't save +the guest's PAT field during migration; Instead 0x0 is saved and migrated. +At the destination, it will use 0x0 as guest PAT because of a94f33258. +This causes the guest VM's performance to drop significatly. + +This patch solves the problem by sanitizing the PAT field. If it is zero, +we use the default MSR_PAT value (0x0007040600070406ULL) to prevent +performance drop. This solution should work with different types of +(old or new) VM sources. + +Signed-off-by: Wei Huang +Signed-off-by: Miroslav Rezanina +(cherry picked from commit 09fbed03321a5b7a2ecd55ba37bed53db552b0b9) +(cherry picked from commit e883fc66d38107233b26acc588fb7af9a2afc8a2) +(cherry picked from commit afd4296db6ae47e5f073a4dd07ea256b660f60de) +--- + target/i386/cpu.c | 2 +- + target/i386/cpu.h | 1 + + target/i386/machine.c | 3 +++ + 3 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index a9db495..0fc7fb0 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -3785,7 +3785,7 @@ static void x86_cpu_reset(CPUState *s) + /* All units are in INIT state. */ + env->xstate_bv = 0; + +- env->pat = 0x0007040600070406ULL; ++ env->pat = MSR_PAT_DEFAULT; + env->msr_ia32_misc_enable = MSR_IA32_MISC_ENABLE_DEFAULT; + + memset(env->dr, 0, sizeof(env->dr)); +diff --git a/target/i386/cpu.h b/target/i386/cpu.h +index 1b219fa..0c7a3d6 100644 +--- a/target/i386/cpu.h ++++ b/target/i386/cpu.h +@@ -401,6 +401,7 @@ typedef enum X86Seg { + #define MSR_MTRRfix4K_F8000 0x26f + + #define MSR_PAT 0x277 ++#define MSR_PAT_DEFAULT 0x0007040600070406ULL + + #define MSR_MTRRdefType 0x2ff + +diff --git a/target/i386/machine.c b/target/i386/machine.c +index c9a3b5c..f86abe7 100644 +--- a/target/i386/machine.c ++++ b/target/i386/machine.c +@@ -277,6 +277,9 @@ static int cpu_post_load(void *opaque, int version_id) + env->hflags &= ~HF_CPL_MASK; + env->hflags |= (env->segs[R_SS].flags >> DESC_DPL_SHIFT) & HF_CPL_MASK; + ++ if (!(env->pat)) ++ env->pat = MSR_PAT_DEFAULT; ++ + env->fpstt = (env->fpus_vmstate >> 11) & 7; + env->fpus = env->fpus_vmstate & ~0x3800; + env->fptag_vmstate ^= 0xff; +-- +1.8.3.1 + diff --git a/SOURCES/0034-spapr-disable-memory-hotplug.patch b/SOURCES/0034-spapr-disable-memory-hotplug.patch new file mode 100644 index 0000000..12a183a --- /dev/null +++ b/SOURCES/0034-spapr-disable-memory-hotplug.patch @@ -0,0 +1,82 @@ +From d3f4feea8023be7c0c3db09e4ea1a24a34f8c4a9 Mon Sep 17 00:00:00 2001 +From: Igor Mammedov +Date: Wed, 31 Jan 2018 10:44:31 +0100 +Subject: spapr: disable memory hotplug + +RH-Author: Igor Mammedov +Message-id: <1517395471-44118-1-git-send-email-imammedo@redhat.com> +Patchwork-id: 78748 +O-Subject: [RHV7.5 qemu-kvm-ma PATCH v3] spapr: disable memory hotplug +Bugzilla: 1535952 +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Laurent Vivier +RH-Acked-by: Dr. David Alan Gilbert + +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1535952 +Brew: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=15146188 +Upstream: RHEL-only + +Disable memory hotplug for qemu-kvm-ma variant of QEMU where +shouldn't be supported. +Use CONFIG_RHV to switch feature off only for qemu-kvm-ma and +not to affect RHEV variant built from the same source code. + +PS: +Disable only (un)plug entry points from device_add/del and +leave the rest of hotplug hw intact so that -ma machine +could be migrated to -rhev. +Note: backward migration would be broken if source -rhev +machine used pc-dimm devices (either with -device/device_add) + +Signed-off-by: Igor Mammedov +Signed-off-by: Miroslav Rezanina +(cherry picked from commit bf05b668e4248781d24cfba88bfb9647ca6ca14e) +(cherry picked from commit dd0871512012fbbe343361bd3ff6fa21545c41d5) +(cherry picked from commit d42b9faa86dfd70e4ba6b8f2bf108d0e21a1a65d) +--- + hw/ppc/spapr.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c +index f71a62e..360a258 100644 +--- a/hw/ppc/spapr.c ++++ b/hw/ppc/spapr.c +@@ -3070,6 +3070,9 @@ out: + static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) + { ++#if !defined(CONFIG_RHV) ++ error_setg(errp, "Memory hotplug not supported for this machine"); ++#else + PCDIMMDevice *dimm = PC_DIMM(dev); + PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); + MemoryRegion *mr; +@@ -3097,6 +3100,7 @@ static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + + out: + g_free(mem_dev); ++#endif + } + + struct sPAPRDIMMState { +@@ -3210,6 +3214,9 @@ void spapr_lmb_release(DeviceState *dev) + static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) + { ++#if !defined(CONFIG_RHV) ++ error_setg(errp, "Memory hot unplug not supported for this machine"); ++#else + sPAPRMachineState *spapr = SPAPR_MACHINE(hotplug_dev); + Error *local_err = NULL; + PCDIMMDevice *dimm = PC_DIMM(dev); +@@ -3264,6 +3271,7 @@ static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev, + nr_lmbs, spapr_drc_index(drc)); + out: + error_propagate(errp, local_err); ++#endif + } + + static void *spapr_populate_hotplug_cpu_dt(CPUState *cs, int *fdt_offset, +-- +1.8.3.1 + diff --git a/SOURCES/80-kvm.rules b/SOURCES/80-kvm.rules new file mode 100644 index 0000000..4ad7d43 --- /dev/null +++ b/SOURCES/80-kvm.rules @@ -0,0 +1 @@ +KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm" diff --git a/SOURCES/85-kvm.preset b/SOURCES/85-kvm.preset new file mode 100644 index 0000000..8024052 --- /dev/null +++ b/SOURCES/85-kvm.preset @@ -0,0 +1,5 @@ +# Enable kvm-setup by default. This can have odd side effects on +# PowerNV systems that aren't intended as KVM hosts, but at present we +# only support RHEL on PowerNV for the purpose of being a RHEV host. + +enable kvm-setup.service diff --git a/SOURCES/95-kvm-memlock.conf b/SOURCES/95-kvm-memlock.conf new file mode 100644 index 0000000..fc59dbe --- /dev/null +++ b/SOURCES/95-kvm-memlock.conf @@ -0,0 +1,10 @@ +# The KVM HV implementation on Power can require a significant amount +# of unswappable memory (about half of which also needs to be host +# physically contiguous) to hold the guest's Hash Page Table (HPT) - +# roughly 1/64th of the guest's RAM size, minimum 16MiB. +# +# These limits allow unprivileged users to start smallish VMs, such as +# those used by libguestfs. +# +* hard memlock 65536 +* soft memlock 65536 diff --git a/SOURCES/99-qemu-guest-agent.rules b/SOURCES/99-qemu-guest-agent.rules new file mode 100644 index 0000000..8a290ab --- /dev/null +++ b/SOURCES/99-qemu-guest-agent.rules @@ -0,0 +1,2 @@ +SUBSYSTEM=="virtio-ports", ATTR{name}=="org.qemu.guest_agent.0", \ + TAG+="systemd" ENV{SYSTEMD_WANTS}="qemu-guest-agent.service" diff --git a/SOURCES/README.rhel6-gpxe-source b/SOURCES/README.rhel6-gpxe-source new file mode 100644 index 0000000..959ac38 --- /dev/null +++ b/SOURCES/README.rhel6-gpxe-source @@ -0,0 +1,9 @@ +The ROM images on /usr/share/qemu-kvm/rhel6-*.rom come from the +Red Hat Enterprise Linux 6.4 package gpxe-roms-qemu-0.9.7-6.16.el6.noarch.rpm. + +The source code for those images can be downloaded from: +http://ftp.redhat.com/pub/redhat/linux/enterprise/6Server/en/os/SRPMS/gpxe-0.9.7-6.16.el6.src.rpm + +For more information on how to obtain source code for Red Hat Enterprise Linux +software, you can refer to Chapter 1. Obtaining Red Hat Enterprise Linux of the +Red Hat Enterprise Linux Installation Guide. diff --git a/SOURCES/bridge.conf b/SOURCES/bridge.conf new file mode 100644 index 0000000..a573665 --- /dev/null +++ b/SOURCES/bridge.conf @@ -0,0 +1 @@ +allow virbr0 diff --git a/SOURCES/build_configure.sh b/SOURCES/build_configure.sh new file mode 100755 index 0000000..a07c930 --- /dev/null +++ b/SOURCES/build_configure.sh @@ -0,0 +1,182 @@ +#!/bin/sh + +_prefix=$1 +shift +_libdir=$1 +shift +_sysconfdir=$1 +shift +_localstatedir=$1 +shift +_libexecdir=$1 +shift +qemudocdir=$1 +shift +pkgname=$1 +shift +arch=$1 +shift +nvr=$1 +shift +optflags=$1 +shift +have_fdt=$1 +shift +have_gluster=$1 +shift +have_guest_agent=$1 +shift +have_numa=$1 +shift +have_rbd=$1 +shift +have_rdma=$1 +shift +have_seccomp=$1 +shift +have_spice=$1 +shift +have_opengl=$1 +shift +have_usbredir=$1 +shift +have_tcmalloc=$1 +shift +have_vxhs=$1 +shift +have_vtd=$1 +shift +have_live_block_ops=$1 +shift +have_vhost_user=$1 +shift +is_rhv=$1 +shift +have_malloc_trim=$1 +shift + +if [ "$have_rbd" == "enable" ]; then + rbd_driver=rbd, +fi + +if [ "$have_gluster" == "enable" ]; then + gluster_driver=gluster, +fi + +if [ "$have_vxhs" == "enable" ]; then + vxhs_driver=vxhs, +fi + +if [ "$is_rhv" == "enable" ]; then + rhel_target=rhv +else + rhel_target=rhel +fi + +./configure \ + --prefix=${_prefix} \ + --libdir=${_libdir} \ + --sysconfdir=${_sysconfdir} \ + --interp-prefix=${_prefix}/qemu-%M \ + --localstatedir=${_localstatedir} \ + --docdir=${qemudocdir} \ + --libexecdir=${_libexecdir} \ + --firmwarepath=${_prefix}/share/qemu-firmware \ + --extra-ldflags="$extraldflags -pie -Wl,-z,relro -Wl,-z,now" \ + --extra-cflags="${optflags} -fPIE -DPIE" \ + --with-pkgversion=${nvr} \ + --with-confsuffix=/${pkgname} \ + --with-git=git \ + --with-coroutine=ucontext \ + --tls-priority=NORMAL \ + --disable-bluez \ + --disable-brlapi \ + --disable-cap-ng \ + --enable-coroutine-pool \ + --enable-curl \ + --disable-curses \ + --disable-debug-tcg \ + --enable-docs \ + --disable-gtk \ + --enable-kvm \ + --enable-libiscsi \ + --disable-libnfs \ + --enable-libssh2 \ + --enable-libusb \ + --disable-bzip2 \ + --enable-linux-aio \ + --disable-live-block-migration \ + --enable-lzo \ + --enable-pie \ + --disable-qom-cast-debug \ + --disable-sdl \ + --enable-snappy \ + --disable-sparse \ + --disable-strip \ + --disable-tpm \ + --enable-trace-backend=dtrace \ + --disable-vde \ + --disable-vhost-scsi \ + --disable-virtfs \ + --disable-vnc-jpeg \ + --disable-vte \ + --enable-vnc-png \ + --enable-vnc-sasl \ + --enable-werror \ + --disable-xen \ + --disable-xfsctl \ + --enable-gnutls \ + --enable-gcrypt \ + --disable-nettle \ + --enable-attr \ + --disable-bsd-user \ + --disable-cocoa \ + --enable-debug-info \ + --disable-guest-agent-msi \ + --disable-hax \ + --disable-jemalloc \ + --disable-linux-user \ + --disable-modules \ + --disable-netmap \ + --disable-replication \ + --enable-system \ + --enable-tools \ + --disable-user \ + --enable-vhost-net \ + --enable-vhost-vsock \ + --enable-vnc \ + --enable-mpath \ + --disable-virglrenderer \ + --disable-xen-pci-passthrough \ + --enable-tcg \ + --disable-crypto-afalg \ + --${have_fdt}-fdt \ + --${have_gluster}-glusterfs \ + --${have_guest_agent}-guest-agent \ + --${have_numa}-numa \ + --${have_rbd}-rbd \ + --${have_rdma}-rdma \ + --${have_seccomp}-seccomp \ + --${have_spice}-spice \ + --${have_spice}-smartcard \ + --${have_opengl}-opengl \ + --${have_usbredir}-usb-redir \ + --${have_tcmalloc}-tcmalloc \ + --${have_vxhs}-vxhs \ + --${have_vtd}-vtd \ + --${have_live_block_ops}-live-block-ops \ + --${have_vhost_user}-vhost-user \ + --disable-sanitizers \ + --disable-hvf \ + --disable-whpx \ + --${have_malloc_trim}-malloc-trim \ + --disable-membarrier \ + --enable-vhost-crypto \ + --disable-libxml2 \ + --enable-capstone \ + --audio-drv-list= \ + --enable-git-update \ + --block-drv-rw-whitelist=qcow2,raw,file,host_device,nbd,iscsi,${gluster_driver}${rbd_driver}${vxhs_driver}blkdebug,luks,null-co,nvme,copy-on-read,throttle \ + --block-drv-ro-whitelist=vmdk,vhdx,vpc,https,ssh \ + --rhel-target=${rhel_target} \ + "$@" diff --git a/SOURCES/ksm.service b/SOURCES/ksm.service new file mode 100644 index 0000000..35c6f1d --- /dev/null +++ b/SOURCES/ksm.service @@ -0,0 +1,13 @@ +[Unit] +Description=Kernel Samepage Merging +ConditionPathExists=/sys/kernel/mm/ksm + +[Service] +Type=oneshot +RemainAfterExit=yes +EnvironmentFile=-/etc/sysconfig/ksm +ExecStart=/usr/libexec/ksmctl start +ExecStop=/usr/libexec/ksmctl stop + +[Install] +WantedBy=multi-user.target diff --git a/SOURCES/ksm.sysconfig b/SOURCES/ksm.sysconfig new file mode 100644 index 0000000..d99656d --- /dev/null +++ b/SOURCES/ksm.sysconfig @@ -0,0 +1,4 @@ +# The maximum number of unswappable kernel pages +# which may be allocated by ksm (0 for unlimited) +# If unset, defaults to half of total memory +# KSM_MAX_KERNEL_PAGES= diff --git a/SOURCES/ksmctl.c b/SOURCES/ksmctl.c new file mode 100644 index 0000000..af39591 --- /dev/null +++ b/SOURCES/ksmctl.c @@ -0,0 +1,77 @@ +/* Start/stop KSM, for systemd. + * Copyright (C) 2009, 2011 Red Hat, Inc. + * Written by Paolo Bonzini . + * Based on the original sysvinit script by Dan Kenigsberg + * This file is distributed under the GNU General Public License, version 2 + * or later. */ + +#include +#include +#include +#include +#include +#include + +#define KSM_MAX_KERNEL_PAGES_FILE "/sys/kernel/mm/ksm/max_kernel_pages" +#define KSM_RUN_FILE "/sys/kernel/mm/ksm/run" + +char *program_name; + +int usage(void) +{ + fprintf(stderr, "Usage: %s {start|stop}\n", program_name); + return 1; +} + +int write_value(uint64_t value, char *filename) +{ + FILE *fp; + if (!(fp = fopen(filename, "w")) || + fprintf(fp, "%llu\n", (unsigned long long) value) == EOF || + fflush(fp) == EOF || + fclose(fp) == EOF) + return 1; + + return 0; +} + +uint64_t ksm_max_kernel_pages() +{ + char *var = getenv("KSM_MAX_KERNEL_PAGES"); + char *endptr; + uint64_t value; + if (var && *var) { + value = strtoll(var, &endptr, 0); + if (value < LLONG_MAX && !*endptr) + return value; + } + /* Unless KSM_MAX_KERNEL_PAGES is set, let KSM munch up to half of + * total memory. */ + return sysconf(_SC_PHYS_PAGES) / 2; +} + +int start(void) +{ + if (access(KSM_MAX_KERNEL_PAGES_FILE, R_OK) >= 0) + write_value(ksm_max_kernel_pages(), KSM_MAX_KERNEL_PAGES_FILE); + return write_value(1, KSM_RUN_FILE); +} + +int stop(void) +{ + return write_value(0, KSM_RUN_FILE); +} + +int main(int argc, char **argv) +{ + program_name = argv[0]; + if (argc < 2) { + return usage(); + } else if (!strcmp(argv[1], "start")) { + return start(); + } else if (!strcmp(argv[1], "stop")) { + return stop(); + } else { + return usage(); + } +} diff --git a/SOURCES/ksmtuned b/SOURCES/ksmtuned new file mode 100644 index 0000000..320ce74 --- /dev/null +++ b/SOURCES/ksmtuned @@ -0,0 +1,138 @@ +#!/bin/bash +# +# Copyright 2009 Red Hat, Inc. and/or its affiliates. +# Released under the GPL +# +# Author: Dan Kenigsberg +# +# ksmtuned - a simple script that controls whether (and with what vigor) ksm +# should search for duplicated pages. +# +# starts ksm when memory commited to qemu processes exceeds a threshold, and +# make ksm work harder and harder untill memory load falls below that +# threshold. +# +# send SIGUSR1 to this process right after a new qemu process is started, or +# following its death, to retune ksm accordingly +# +# needs testing and ironing. contact danken@redhat.com if something breaks. + +if [ -f /etc/ksmtuned.conf ]; then + . /etc/ksmtuned.conf +fi + +debug() { + if [ -n "$DEBUG" ]; then + s="`/bin/date`: $*" + [ -n "$LOGFILE" ] && echo "$s" >> "$LOGFILE" || echo "$s" + fi +} + + +KSM_MONITOR_INTERVAL=${KSM_MONITOR_INTERVAL:-60} +KSM_NPAGES_BOOST=${KSM_NPAGES_BOOST:-300} +KSM_NPAGES_DECAY=${KSM_NPAGES_DECAY:--50} + +KSM_NPAGES_MIN=${KSM_NPAGES_MIN:-64} +KSM_NPAGES_MAX=${KSM_NPAGES_MAX:-1250} +# millisecond sleep between ksm scans for 16Gb server. Smaller servers sleep +# more, bigger sleep less. +KSM_SLEEP_MSEC=${KSM_SLEEP_MSEC:-10} + +KSM_THRES_COEF=${KSM_THRES_COEF:-20} +KSM_THRES_CONST=${KSM_THRES_CONST:-2048} + +total=`awk '/^MemTotal:/ {print $2}' /proc/meminfo` +debug total $total + +npages=0 +sleep=$[KSM_SLEEP_MSEC * 16 * 1024 * 1024 / total] +[ $sleep -le 10 ] && sleep=10 +debug sleep $sleep +thres=$[total * KSM_THRES_COEF / 100] +if [ $KSM_THRES_CONST -gt $thres ]; then + thres=$KSM_THRES_CONST +fi +debug thres $thres + +KSMCTL () { + case x$1 in + xstop) + echo 0 > /sys/kernel/mm/ksm/run + ;; + xstart) + echo $2 > /sys/kernel/mm/ksm/pages_to_scan + echo $3 > /sys/kernel/mm/ksm/sleep_millisecs + echo 1 > /sys/kernel/mm/ksm/run + ;; + esac +} + +committed_memory () { + local pidlist + pidlist=$(pgrep -d ' ' -- '^qemu(-kvm|:.{1,11})$') + if [ -n "$pidlist" ]; then + ps -p "$pidlist" -o rsz= + fi | awk '{ sum += $1 }; END { print 0+sum }' +} + +free_memory () { + awk '/^(MemFree|Buffers|Cached):/ {free += $2}; END {print free}' \ + /proc/meminfo +} + +increase_npages() { + local delta + delta=${1:-0} + npages=$[npages + delta] + if [ $npages -lt $KSM_NPAGES_MIN ]; then + npages=$KSM_NPAGES_MIN + elif [ $npages -gt $KSM_NPAGES_MAX ]; then + npages=$KSM_NPAGES_MAX + fi + echo $npages +} + + +adjust () { + local free committed + free=`free_memory` + committed=`committed_memory` + debug committed $committed free $free + if [ $[committed + thres] -lt $total -a $free -gt $thres ]; then + KSMCTL stop + debug "$[committed + thres] < $total and free > $thres, stop ksm" + return 1 + fi + debug "$[committed + thres] > $total, start ksm" + if [ $free -lt $thres ]; then + npages=`increase_npages $KSM_NPAGES_BOOST` + debug "$free < $thres, boost" + else + npages=`increase_npages $KSM_NPAGES_DECAY` + debug "$free > $thres, decay" + fi + KSMCTL start $npages $sleep + debug "KSMCTL start $npages $sleep" + return 0 +} + +function nothing () { + : +} + +loop () { + trap nothing SIGUSR1 + while true + do + sleep $KSM_MONITOR_INTERVAL & + wait $! + adjust + done +} + +PIDFILE=${PIDFILE-/var/run/ksmtune.pid} +if touch "$PIDFILE"; then + loop & + echo $! > "$PIDFILE" +fi diff --git a/SOURCES/ksmtuned.conf b/SOURCES/ksmtuned.conf new file mode 100644 index 0000000..fc4518c --- /dev/null +++ b/SOURCES/ksmtuned.conf @@ -0,0 +1,21 @@ +# Configuration file for ksmtuned. + +# How long ksmtuned should sleep between tuning adjustments +# KSM_MONITOR_INTERVAL=60 + +# Millisecond sleep between ksm scans for 16Gb server. +# Smaller servers sleep more, bigger sleep less. +# KSM_SLEEP_MSEC=10 + +# KSM_NPAGES_BOOST=300 +# KSM_NPAGES_DECAY=-50 +# KSM_NPAGES_MIN=64 +# KSM_NPAGES_MAX=1250 + +# KSM_THRES_COEF=20 +# KSM_THRES_CONST=2048 + +# uncomment the following if you want ksmtuned debug info + +# LOGFILE=/var/log/ksmtuned +# DEBUG=1 diff --git a/SOURCES/ksmtuned.service b/SOURCES/ksmtuned.service new file mode 100644 index 0000000..39febcc --- /dev/null +++ b/SOURCES/ksmtuned.service @@ -0,0 +1,12 @@ +[Unit] +Description=Kernel Samepage Merging (KSM) Tuning Daemon +After=ksm.service +Requires=ksm.service + +[Service] +ExecStart=/usr/sbin/ksmtuned +ExecReload=/bin/kill -USR1 $MAINPID +Type=forking + +[Install] +WantedBy=multi-user.target diff --git a/SOURCES/kvm-AArch64-Add-virt-rhel7.6-machine-type.patch b/SOURCES/kvm-AArch64-Add-virt-rhel7.6-machine-type.patch new file mode 100644 index 0000000..eed9fc0 --- /dev/null +++ b/SOURCES/kvm-AArch64-Add-virt-rhel7.6-machine-type.patch @@ -0,0 +1,51 @@ +From a35c8f28c25c62d5b371a77156a5b810fa6455da Mon Sep 17 00:00:00 2001 +From: Wei Huang +Date: Wed, 28 Mar 2018 18:58:55 +0200 +Subject: [PATCH 01/13] AArch64: Add virt-rhel7.6 machine type + +RH-Author: Wei Huang +Message-id: <20180328185856.20056-2-wei@redhat.com> +Patchwork-id: 79427 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/2] AArch64: Add virt-rhel7.6 machine type +Bugzilla: 1558723 +RH-Acked-by: Andrew Jones +RH-Acked-by: Auger Eric +RH-Acked-by: Laszlo Ersek + +This patch adds "virt-rhel7.6" machine type. Because RHEL 7.5 virt-arm +was a preview product, we remove the "virt-rhel7.5" type to avoid the +legacy support burden. + +Signed-off-by: Wei Huang +Signed-off-by: Miroslav Rezanina +--- + hw/arm/virt.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/hw/arm/virt.c b/hw/arm/virt.c +index 806eb1e..a4d0f52 100644 +--- a/hw/arm/virt.c ++++ b/hw/arm/virt.c +@@ -1820,7 +1820,7 @@ static void rhel_machine_init(void) + } + type_init(rhel_machine_init); + +-static void rhel750_virt_instance_init(Object *obj) ++static void rhel760_virt_instance_init(Object *obj) + { + VirtMachineState *vms = VIRT_MACHINE(obj); + VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); +@@ -1862,8 +1862,8 @@ static void rhel750_virt_instance_init(Object *obj) + vms->irqmap=a15irqmap; + } + +-static void rhel750_virt_options(MachineClass *mc) ++static void rhel760_virt_options(MachineClass *mc) + { + SET_MACHINE_COMPAT(mc, ARM_RHEL_COMPAT); + } +-DEFINE_RHEL_MACHINE_AS_LATEST(7, 5, 0) ++DEFINE_RHEL_MACHINE_AS_LATEST(7, 6, 0) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Acceptance-tests-add-Linux-kernel-boot-and-console-c.patch b/SOURCES/kvm-Acceptance-tests-add-Linux-kernel-boot-and-console-c.patch new file mode 100644 index 0000000..1f53c82 --- /dev/null +++ b/SOURCES/kvm-Acceptance-tests-add-Linux-kernel-boot-and-console-c.patch @@ -0,0 +1,102 @@ +From fd8563f0e3d4697334188aa25ff92f0655752602 Mon Sep 17 00:00:00 2001 +From: Yash Mankad +Date: Tue, 17 Jul 2018 23:38:08 +0200 +Subject: [PATCH 51/89] Acceptance tests: add Linux kernel boot and console + checking test + +RH-Author: Yash Mankad +Message-id: <3a22fbea648a71abf84257d69931075cafe3340e.1531870629.git.ymankad@redhat.com> +Patchwork-id: 81380 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 5/5] Acceptance tests: add Linux kernel boot and console checking test +Bugzilla: +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow +RH-Acked-by: Eduardo Habkost + +From: Cleber Rosa + +This test boots a Linux kernel, and checks that the given command +line was effective in two ways: + + * It makes the kernel use the set "console device" as a console + * The kernel records the command line as expected in the console + +Given that way too many error conditions may occur, and detecting the +kernel boot progress status may not be trivial, this test relies on a +timeout to handle unexpected situations. Also, it's *not* tagged as a +quick test for obvious reasons. + +It may be useful, while interactively running/debugging this test, or +tests similar to this one, to show some of the logging channels. +Example: + + $ avocado --show=QMP,console run boot_linux_console.py + +Signed-off-by: Cleber Rosa +Message-Id: <20180530184156.15634-6-crosa@redhat.com> +Reviewed-by: Stefan Hajnoczi +Signed-off-by: Eduardo Habkost +(cherry picked from commit c1cc73f407b890c4e7ab5bf520c0637e0364e92a) +Signed-off-by: Yash Mankad +Signed-off-by: Miroslav Rezanina +--- + tests/acceptance/boot_linux_console.py | 47 ++++++++++++++++++++++++++++++++++ + 1 file changed, 47 insertions(+) + create mode 100644 tests/acceptance/boot_linux_console.py + +diff --git a/tests/acceptance/boot_linux_console.py b/tests/acceptance/boot_linux_console.py +new file mode 100644 +index 0000000..98324f7 +--- /dev/null ++++ b/tests/acceptance/boot_linux_console.py +@@ -0,0 +1,47 @@ ++# Functional test that boots a Linux kernel and checks the console ++# ++# Copyright (c) 2018 Red Hat, Inc. ++# ++# Author: ++# Cleber Rosa ++# ++# This work is licensed under the terms of the GNU GPL, version 2 or ++# later. See the COPYING file in the top-level directory. ++ ++import logging ++ ++from avocado_qemu import Test ++ ++ ++class BootLinuxConsole(Test): ++ """ ++ Boots a x86_64 Linux kernel and checks that the console is operational ++ and the kernel command line is properly passed from QEMU to the kernel ++ ++ :avocado: enable ++ :avocado: tags=x86_64 ++ """ ++ ++ timeout = 60 ++ ++ def test(self): ++ kernel_url = ('https://mirrors.kernel.org/fedora/releases/28/' ++ 'Everything/x86_64/os/images/pxeboot/vmlinuz') ++ kernel_hash = '238e083e114c48200f80d889f7e32eeb2793e02a' ++ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) ++ ++ self.vm.set_machine('pc') ++ self.vm.set_console() ++ kernel_command_line = 'console=ttyS0' ++ self.vm.add_args('-kernel', kernel_path, ++ '-append', kernel_command_line) ++ self.vm.launch() ++ console = self.vm.console_socket.makefile() ++ console_logger = logging.getLogger('console') ++ while True: ++ msg = console.readline() ++ console_logger.debug(msg.strip()) ++ if 'Kernel command line: %s' % kernel_command_line in msg: ++ break ++ if 'Kernel panic - not syncing' in msg: ++ self.fail("Kernel panic reached") +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Acceptance-tests-add-quick-VNC-tests.patch b/SOURCES/kvm-Acceptance-tests-add-quick-VNC-tests.patch new file mode 100644 index 0000000..46c3d24 --- /dev/null +++ b/SOURCES/kvm-Acceptance-tests-add-quick-VNC-tests.patch @@ -0,0 +1,104 @@ +From 6d28e6c096b3015387788dc1f140df0be3c28a6f Mon Sep 17 00:00:00 2001 +From: Yash Mankad +Date: Tue, 17 Jul 2018 23:38:06 +0200 +Subject: [PATCH 49/89] Acceptance tests: add quick VNC tests +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Yash Mankad +Message-id: <51cad6473c9d59a780befb5d96c8534affd938cf.1531870629.git.ymankad@redhat.com> +Patchwork-id: 81383 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 3/5] Acceptance tests: add quick VNC tests +Bugzilla: +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow +RH-Acked-by: Eduardo Habkost + +From: Cleber Rosa + +This patch adds a few simple behavior tests for VNC. + +Signed-off-by: Cleber Rosa +Reviewed-by: Stefan Hajnoczi +Message-Id: <20180530184156.15634-4-crosa@redhat.com> +Reviewed-by: Philippe Mathieu-Daudé +Tested-by: Philippe Mathieu-Daudé +Signed-off-by: Eduardo Habkost +(cherry picked from commit 7b1bd11cff0915a1266c34bdfb66d70f6372340d) +Signed-off-by: Yash Mankad +Signed-off-by: Miroslav Rezanina +--- + tests/acceptance/vnc.py | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 60 insertions(+) + create mode 100644 tests/acceptance/vnc.py + +diff --git a/tests/acceptance/vnc.py b/tests/acceptance/vnc.py +new file mode 100644 +index 0000000..b1ef9d7 +--- /dev/null ++++ b/tests/acceptance/vnc.py +@@ -0,0 +1,60 @@ ++# Simple functional tests for VNC functionality ++# ++# Copyright (c) 2018 Red Hat, Inc. ++# ++# Author: ++# Cleber Rosa ++# ++# This work is licensed under the terms of the GNU GPL, version 2 or ++# later. See the COPYING file in the top-level directory. ++ ++from avocado_qemu import Test ++ ++ ++class Vnc(Test): ++ """ ++ :avocado: enable ++ :avocado: tags=vnc,quick ++ """ ++ def test_no_vnc(self): ++ self.vm.add_args('-nodefaults', '-S') ++ self.vm.launch() ++ self.assertFalse(self.vm.qmp('query-vnc')['return']['enabled']) ++ ++ def test_no_vnc_change_password(self): ++ self.vm.add_args('-nodefaults', '-S') ++ self.vm.launch() ++ self.assertFalse(self.vm.qmp('query-vnc')['return']['enabled']) ++ set_password_response = self.vm.qmp('change', ++ device='vnc', ++ target='password', ++ arg='new_password') ++ self.assertIn('error', set_password_response) ++ self.assertEqual(set_password_response['error']['class'], ++ 'GenericError') ++ self.assertEqual(set_password_response['error']['desc'], ++ 'Could not set password') ++ ++ def test_vnc_change_password_requires_a_password(self): ++ self.vm.add_args('-nodefaults', '-S', '-vnc', ':0') ++ self.vm.launch() ++ self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled']) ++ set_password_response = self.vm.qmp('change', ++ device='vnc', ++ target='password', ++ arg='new_password') ++ self.assertIn('error', set_password_response) ++ self.assertEqual(set_password_response['error']['class'], ++ 'GenericError') ++ self.assertEqual(set_password_response['error']['desc'], ++ 'Could not set password') ++ ++ def test_vnc_change_password(self): ++ self.vm.add_args('-nodefaults', '-S', '-vnc', ':0,password') ++ self.vm.launch() ++ self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled']) ++ set_password_response = self.vm.qmp('change', ++ device='vnc', ++ target='password', ++ arg='new_password') ++ self.assertEqual(set_password_response['return'], {}) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Add-functional-acceptance-tests-infrastructure.patch b/SOURCES/kvm-Add-functional-acceptance-tests-infrastructure.patch new file mode 100644 index 0000000..b696dfc --- /dev/null +++ b/SOURCES/kvm-Add-functional-acceptance-tests-infrastructure.patch @@ -0,0 +1,359 @@ +From b041318485f538aa5bc9a34a010a629d45bbc73d Mon Sep 17 00:00:00 2001 +From: Yash Mankad +Date: Tue, 17 Jul 2018 23:38:04 +0200 +Subject: [PATCH 47/89] Add functional/acceptance tests infrastructure +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Yash Mankad +Message-id: +Patchwork-id: 81382 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/5] Add functional/acceptance tests infrastructure +Bugzilla: +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow +RH-Acked-by: Eduardo Habkost + +From: Cleber Rosa + +This patch adds the very minimum infrastructure necessary for writing +and running functional/acceptance tests, including: + + * Documentation + * The avocado_qemu.Test base test class + * One example tests (version.py) + +Additional functionality is expected to be added along the tests that +require them. + +Signed-off-by: Cleber Rosa +Message-Id: <20180530184156.15634-2-crosa@redhat.com> +Reviewed-by: Philippe Mathieu-Daudé +Tested-by: Philippe Mathieu-Daudé +[ehabkost: fix typo on testing.rst] +Reviewed-by: Stefan Hajnoczi +Signed-off-by: Eduardo Habkost +(cherry picked from commit c3d7e8c90db208b1d876f8d6458c2dfca169137f) +Signed-off-by: Yash Mankad + +Signed-off-by: Miroslav Rezanina +--- + docs/devel/testing.rst | 192 ++++++++++++++++++++++++++++++ + tests/acceptance/README.rst | 10 ++ + tests/acceptance/avocado_qemu/__init__.py | 54 +++++++++ + tests/acceptance/version.py | 24 ++++ + 4 files changed, 280 insertions(+) + create mode 100644 tests/acceptance/README.rst + create mode 100644 tests/acceptance/avocado_qemu/__init__.py + create mode 100644 tests/acceptance/version.py + +diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst +index 0ca1a2d..f33e5a8 100644 +--- a/docs/devel/testing.rst ++++ b/docs/devel/testing.rst +@@ -484,3 +484,195 @@ supported. To start the fuzzer, run + + Alternatively, some command different from "qemu-img info" can be tested, by + changing the ``-c`` option. ++ ++Acceptance tests using the Avocado Framework ++============================================ ++ ++The ``tests/acceptance`` directory hosts functional tests, also known ++as acceptance level tests. They're usually higher level tests, and ++may interact with external resources and with various guest operating ++systems. ++ ++These tests are written using the Avocado Testing Framework (which must ++be installed separately) in conjunction with a the ``avocado_qemu.Test`` ++class, implemented at ``tests/acceptance/avocado_qemu``. ++ ++Tests based on ``avocado_qemu.Test`` can easily: ++ ++ * Customize the command line arguments given to the convenience ++ ``self.vm`` attribute (a QEMUMachine instance) ++ ++ * Interact with the QEMU monitor, send QMP commands and check ++ their results ++ ++ * Interact with the guest OS, using the convenience console device ++ (which may be useful to assert the effectiveness and correctness of ++ command line arguments or QMP commands) ++ ++ * Interact with external data files that accompany the test itself ++ (see ``self.get_data()``) ++ ++ * Download (and cache) remote data files, such as firmware and kernel ++ images ++ ++ * Have access to a library of guest OS images (by means of the ++ ``avocado.utils.vmimage`` library) ++ ++ * Make use of various other test related utilities available at the ++ test class itself and at the utility library: ++ ++ - http://avocado-framework.readthedocs.io/en/latest/api/test/avocado.html#avocado.Test ++ - http://avocado-framework.readthedocs.io/en/latest/api/utils/avocado.utils.html ++ ++Installation ++------------ ++ ++To install Avocado and its dependencies, run: ++ ++.. code:: ++ ++ pip install --user avocado-framework ++ ++Alternatively, follow the instructions on this link: ++ ++ http://avocado-framework.readthedocs.io/en/latest/GetStartedGuide.html#installing-avocado ++ ++Overview ++-------- ++ ++This directory provides the ``avocado_qemu`` Python module, containing ++the ``avocado_qemu.Test`` class. Here's a simple usage example: ++ ++.. code:: ++ ++ from avocado_qemu import Test ++ ++ ++ class Version(Test): ++ """ ++ :avocado: enable ++ :avocado: tags=quick ++ """ ++ def test_qmp_human_info_version(self): ++ self.vm.launch() ++ res = self.vm.command('human-monitor-command', ++ command_line='info version') ++ self.assertRegexpMatches(res, r'^(\d+\.\d+\.\d)') ++ ++To execute your test, run: ++ ++.. code:: ++ ++ avocado run version.py ++ ++Tests may be classified according to a convention by using docstring ++directives such as ``:avocado: tags=TAG1,TAG2``. To run all tests ++in the current directory, tagged as "quick", run: ++ ++.. code:: ++ ++ avocado run -t quick . ++ ++The ``avocado_qemu.Test`` base test class ++----------------------------------------- ++ ++The ``avocado_qemu.Test`` class has a number of characteristics that ++are worth being mentioned right away. ++ ++First of all, it attempts to give each test a ready to use QEMUMachine ++instance, available at ``self.vm``. Because many tests will tweak the ++QEMU command line, launching the QEMUMachine (by using ``self.vm.launch()``) ++is left to the test writer. ++ ++At test "tear down", ``avocado_qemu.Test`` handles the QEMUMachine ++shutdown. ++ ++QEMUMachine ++~~~~~~~~~~~ ++ ++The QEMUMachine API is already widely used in the Python iotests, ++device-crash-test and other Python scripts. It's a wrapper around the ++execution of a QEMU binary, giving its users: ++ ++ * the ability to set command line arguments to be given to the QEMU ++ binary ++ ++ * a ready to use QMP connection and interface, which can be used to ++ send commands and inspect its results, as well as asynchronous ++ events ++ ++ * convenience methods to set commonly used command line arguments in ++ a more succinct and intuitive way ++ ++QEMU binary selection ++~~~~~~~~~~~~~~~~~~~~~ ++ ++The QEMU binary used for the ``self.vm`` QEMUMachine instance will ++primarily depend on the value of the ``qemu_bin`` parameter. If it's ++not explicitly set, its default value will be the result of a dynamic ++probe in the same source tree. A suitable binary will be one that ++targets the architecture matching host machine. ++ ++Based on this description, test writers will usually rely on one of ++the following approaches: ++ ++1) Set ``qemu_bin``, and use the given binary ++ ++2) Do not set ``qemu_bin``, and use a QEMU binary named like ++ "${arch}-softmmu/qemu-system-${arch}", either in the current ++ working directory, or in the current source tree. ++ ++The resulting ``qemu_bin`` value will be preserved in the ++``avocado_qemu.Test`` as an attribute with the same name. ++ ++Attribute reference ++------------------- ++ ++Besides the attributes and methods that are part of the base ++``avocado.Test`` class, the following attributes are available on any ++``avocado_qemu.Test`` instance. ++ ++vm ++~~ ++ ++A QEMUMachine instance, initially configured according to the given ++``qemu_bin`` parameter. ++ ++qemu_bin ++~~~~~~~~ ++ ++The preserved value of the ``qemu_bin`` parameter or the result of the ++dynamic probe for a QEMU binary in the current working directory or ++source tree. ++ ++Parameter reference ++------------------- ++ ++To understand how Avocado parameters are accessed by tests, and how ++they can be passed to tests, please refer to:: ++ ++ http://avocado-framework.readthedocs.io/en/latest/WritingTests.html#accessing-test-parameters ++ ++Parameter values can be easily seen in the log files, and will look ++like the following: ++ ++.. code:: ++ ++ PARAMS (key=qemu_bin, path=*, default=x86_64-softmmu/qemu-system-x86_64) => 'x86_64-softmmu/qemu-system-x86_64 ++ ++qemu_bin ++~~~~~~~~ ++ ++The exact QEMU binary to be used on QEMUMachine. ++ ++Uninstalling Avocado ++-------------------- ++ ++If you've followed the installation instructions above, you can easily ++uninstall Avocado. Start by listing the packages you have installed:: ++ ++ pip list --user ++ ++And remove any package you want with:: ++ ++ pip uninstall +diff --git a/tests/acceptance/README.rst b/tests/acceptance/README.rst +new file mode 100644 +index 0000000..89260fa +--- /dev/null ++++ b/tests/acceptance/README.rst +@@ -0,0 +1,10 @@ ++============================================ ++Acceptance tests using the Avocado Framework ++============================================ ++ ++This directory contains functional tests, also known as acceptance ++level tests. They're usually higher level, and may interact with ++external resources and with various guest operating systems. ++ ++For more information, please refer to ``docs/devel/testing.rst``, ++section "Acceptance tests using the Avocado Framework". +diff --git a/tests/acceptance/avocado_qemu/__init__.py b/tests/acceptance/avocado_qemu/__init__.py +new file mode 100644 +index 0000000..1e54fd5 +--- /dev/null ++++ b/tests/acceptance/avocado_qemu/__init__.py +@@ -0,0 +1,54 @@ ++# Test class and utilities for functional tests ++# ++# Copyright (c) 2018 Red Hat, Inc. ++# ++# Author: ++# Cleber Rosa ++# ++# This work is licensed under the terms of the GNU GPL, version 2 or ++# later. See the COPYING file in the top-level directory. ++ ++import os ++import sys ++ ++import avocado ++ ++SRC_ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ++SRC_ROOT_DIR = os.path.abspath(os.path.dirname(SRC_ROOT_DIR)) ++sys.path.append(os.path.join(SRC_ROOT_DIR, 'scripts')) ++ ++from qemu import QEMUMachine ++ ++def is_readable_executable_file(path): ++ return os.path.isfile(path) and os.access(path, os.R_OK | os.X_OK) ++ ++ ++def pick_default_qemu_bin(): ++ """ ++ Picks the path of a QEMU binary, starting either in the current working ++ directory or in the source tree root directory. ++ """ ++ arch = os.uname()[4] ++ qemu_bin_relative_path = os.path.join("%s-softmmu" % arch, ++ "qemu-system-%s" % arch) ++ if is_readable_executable_file(qemu_bin_relative_path): ++ return qemu_bin_relative_path ++ ++ qemu_bin_from_src_dir_path = os.path.join(SRC_ROOT_DIR, ++ qemu_bin_relative_path) ++ if is_readable_executable_file(qemu_bin_from_src_dir_path): ++ return qemu_bin_from_src_dir_path ++ ++ ++class Test(avocado.Test): ++ def setUp(self): ++ self.vm = None ++ self.qemu_bin = self.params.get('qemu_bin', ++ default=pick_default_qemu_bin()) ++ if self.qemu_bin is None: ++ self.cancel("No QEMU binary defined or found in the source tree") ++ self.vm = QEMUMachine(self.qemu_bin) ++ ++ def tearDown(self): ++ if self.vm is not None: ++ self.vm.shutdown() +diff --git a/tests/acceptance/version.py b/tests/acceptance/version.py +new file mode 100644 +index 0000000..13b0a74 +--- /dev/null ++++ b/tests/acceptance/version.py +@@ -0,0 +1,24 @@ ++# Version check example test ++# ++# Copyright (c) 2018 Red Hat, Inc. ++# ++# Author: ++# Cleber Rosa ++# ++# This work is licensed under the terms of the GNU GPL, version 2 or ++# later. See the COPYING file in the top-level directory. ++ ++ ++from avocado_qemu import Test ++ ++ ++class Version(Test): ++ """ ++ :avocado: enable ++ :avocado: tags=quick ++ """ ++ def test_qmp_human_info_version(self): ++ self.vm.launch() ++ res = self.vm.command('human-monitor-command', ++ command_line='info version') ++ self.assertRegexpMatches(res, r'^(\d+\.\d+\.\d)') +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Add-support-to-KVM_GET_MSR_FEATURE_INDEX_LIST-an.patch b/SOURCES/kvm-Add-support-to-KVM_GET_MSR_FEATURE_INDEX_LIST-an.patch new file mode 100644 index 0000000..056fb0d --- /dev/null +++ b/SOURCES/kvm-Add-support-to-KVM_GET_MSR_FEATURE_INDEX_LIST-an.patch @@ -0,0 +1,160 @@ +From 23dd23db9af68c2076239087f505c88b138ca409 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Tue, 4 Jun 2019 21:47:22 +0200 +Subject: [PATCH 03/23] kvm: Add support to KVM_GET_MSR_FEATURE_INDEX_LIST and + KVM_GET_MSRS system ioctl + +RH-Author: plai@redhat.com +Message-id: <1559684847-10889-4-git-send-email-plai@redhat.com> +Patchwork-id: 88531 +O-Subject: [RHEL7.7 qemu-kvm-rhev PATCH v4 3/8] kvm: Add support to KVM_GET_MSR_FEATURE_INDEX_LIST and KVM_GET_MSRS system ioctl +Bugzilla: 1709972 +RH-Acked-by: Eduardo Habkost +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Miroslav Rezanina + +From: Robert Hoo + +Add kvm_get_supported_feature_msrs() to get supported MSR feature index list. +Add kvm_arch_get_supported_msr_feature() to get each MSR features value. + +Signed-off-by: Robert Hoo +Message-Id: <1539578845-37944-2-git-send-email-robert.hu@linux.intel.com> +Reviewed-by: Eduardo Habkost +Signed-off-by: Eduardo Habkost +(cherry picked from commit f57bceb6ab5163ddd6c41ff4344ab8cf28a9c63d) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + include/sysemu/kvm.h | 2 ++ + target/i386/kvm.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 82 insertions(+) + +diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h +index 23669c4..3d8f294 100644 +--- a/include/sysemu/kvm.h ++++ b/include/sysemu/kvm.h +@@ -464,6 +464,8 @@ int kvm_vm_check_extension(KVMState *s, unsigned int extension); + + uint32_t kvm_arch_get_supported_cpuid(KVMState *env, uint32_t function, + uint32_t index, int reg); ++uint32_t kvm_arch_get_supported_msr_feature(KVMState *s, uint32_t index); ++ + + void kvm_set_sigmask_len(KVMState *s, unsigned int sigmask_len); + +diff --git a/target/i386/kvm.c b/target/i386/kvm.c +index 00f2141..0ecec4a 100644 +--- a/target/i386/kvm.c ++++ b/target/i386/kvm.c +@@ -106,6 +106,7 @@ static int has_pit_state2; + static bool has_msr_mcg_ext_ctl; + + static struct kvm_cpuid2 *cpuid_cache; ++static struct kvm_msr_list *kvm_feature_msrs; + + int kvm_has_pit_state2(void) + { +@@ -405,6 +406,42 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, + return ret; + } + ++uint32_t kvm_arch_get_supported_msr_feature(KVMState *s, uint32_t index) ++{ ++ struct { ++ struct kvm_msrs info; ++ struct kvm_msr_entry entries[1]; ++ } msr_data; ++ uint32_t ret; ++ ++ if (kvm_feature_msrs == NULL) { /* Host doesn't support feature MSRs */ ++ return 0; ++ } ++ ++ /* Check if requested MSR is supported feature MSR */ ++ int i; ++ for (i = 0; i < kvm_feature_msrs->nmsrs; i++) ++ if (kvm_feature_msrs->indices[i] == index) { ++ break; ++ } ++ if (i == kvm_feature_msrs->nmsrs) { ++ return 0; /* if the feature MSR is not supported, simply return 0 */ ++ } ++ ++ msr_data.info.nmsrs = 1; ++ msr_data.entries[0].index = index; ++ ++ ret = kvm_ioctl(s, KVM_GET_MSRS, &msr_data); ++ if (ret != 1) { ++ error_report("KVM get MSR (index=0x%x) feature failed, %s", ++ index, strerror(-ret)); ++ exit(1); ++ } ++ ++ return msr_data.entries[0].data; ++} ++ ++ + typedef struct HWPoisonPage { + ram_addr_t ram_addr; + QLIST_ENTRY(HWPoisonPage) list; +@@ -1164,6 +1201,47 @@ void kvm_arch_do_init_vcpu(X86CPU *cpu) + } + } + ++static int kvm_get_supported_feature_msrs(KVMState *s) ++{ ++ int ret = 0; ++ ++ if (kvm_feature_msrs != NULL) { ++ return 0; ++ } ++ ++ if (!kvm_check_extension(s, KVM_CAP_GET_MSR_FEATURES)) { ++ return 0; ++ } ++ ++ struct kvm_msr_list msr_list; ++ ++ msr_list.nmsrs = 0; ++ ret = kvm_ioctl(s, KVM_GET_MSR_FEATURE_INDEX_LIST, &msr_list); ++ if (ret < 0 && ret != -E2BIG) { ++ error_report("Fetch KVM feature MSR list failed: %s", ++ strerror(-ret)); ++ return ret; ++ } ++ ++ assert(msr_list.nmsrs > 0); ++ kvm_feature_msrs = (struct kvm_msr_list *) \ ++ g_malloc0(sizeof(msr_list) + ++ msr_list.nmsrs * sizeof(msr_list.indices[0])); ++ ++ kvm_feature_msrs->nmsrs = msr_list.nmsrs; ++ ret = kvm_ioctl(s, KVM_GET_MSR_FEATURE_INDEX_LIST, kvm_feature_msrs); ++ ++ if (ret < 0) { ++ error_report("Fetch KVM feature MSR list failed: %s", ++ strerror(-ret)); ++ g_free(kvm_feature_msrs); ++ kvm_feature_msrs = NULL; ++ return ret; ++ } ++ ++ return 0; ++} ++ + static int kvm_get_supported_msrs(KVMState *s) + { + static int kvm_supported_msrs; +@@ -1320,6 +1398,8 @@ int kvm_arch_init(MachineState *ms, KVMState *s) + return ret; + } + ++ kvm_get_supported_feature_msrs(s); ++ + uname(&utsname); + lm_capable_kernel = strcmp(utsname.machine, "x86_64") == 0; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Disable-AT24Cx-i2c-eeprom.patch b/SOURCES/kvm-Disable-AT24Cx-i2c-eeprom.patch new file mode 100644 index 0000000..a51edd4 --- /dev/null +++ b/SOURCES/kvm-Disable-AT24Cx-i2c-eeprom.patch @@ -0,0 +1,40 @@ +From 2cb498c245b670f98ee4d05c16766d95811ff2f3 Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Thu, 7 Jun 2018 07:43:11 +0200 +Subject: [PATCH 03/15] Disable AT24Cx i2c eeprom + +RH-Author: Miroslav Rezanina +Message-id: +Patchwork-id: 80598 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 3/7] Disable AT24Cx i2c eeprom +Bugzilla: 1586357 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Markus Armbruster + +From: Miroslav Rezanina + +We do not want to support new eeprom_at24c device. It is protected +by CONFIG_I2C option but we can't remove it as it cover other supported +devices. Manually remove module with the device. + +Signed-off-by: Miroslav Rezanina +--- + hw/nvram/Makefile.objs | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/nvram/Makefile.objs b/hw/nvram/Makefile.objs +index a912d25..cbc8bba 100644 +--- a/hw/nvram/Makefile.objs ++++ b/hw/nvram/Makefile.objs +@@ -1,6 +1,6 @@ + common-obj-$(CONFIG_DS1225Y) += ds1225y.o + common-obj-y += eeprom93xx.o +-common-obj-$(CONFIG_I2C) += eeprom_at24c.o ++#common-obj-$(CONFIG_I2C) += eeprom_at24c.o + common-obj-y += fw_cfg.o + common-obj-y += chrp_nvram.o + common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Disable-CAN-bus-devices.patch b/SOURCES/kvm-Disable-CAN-bus-devices.patch new file mode 100644 index 0000000..cdac317 --- /dev/null +++ b/SOURCES/kvm-Disable-CAN-bus-devices.patch @@ -0,0 +1,40 @@ +From 354b15f627251bf85363350a529cface7eef208d Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Thu, 7 Jun 2018 07:43:12 +0200 +Subject: [PATCH 04/15] Disable CAN bus devices + +RH-Author: Miroslav Rezanina +Message-id: <706a21b4d25946165513ff99619c0fed0852dc50.1528355911.git.mrezanin@redhat.com> +Patchwork-id: 80593 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 4/7] Disable CAN bus devices +Bugzilla: 1586357 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Markus Armbruster + +From: Miroslav Rezanina + +We do not want to support new CAN devices (kvaser_pci, mioe3680_pci +and pcm3680_pci). + +Signed-off-by: Miroslav Rezanina +--- + default-configs/pci.mak | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/default-configs/pci.mak b/default-configs/pci.mak +index 4c8c296..25fc382 100644 +--- a/default-configs/pci.mak ++++ b/default-configs/pci.mak +@@ -33,7 +33,7 @@ CONFIG_SERIAL_ISA=y + CONFIG_SERIAL_PCI=y + CONFIG_CAN_BUS=y + CONFIG_CAN_SJA1000=y +-CONFIG_CAN_PCI=y ++#CONFIG_CAN_PCI=y + #CONFIG_IPACK=y + CONFIG_WDT_IB6300ESB=y + CONFIG_PCI_TESTDEV=y +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Disable-PCIe-to-PCI-bridge-device.patch b/SOURCES/kvm-Disable-PCIe-to-PCI-bridge-device.patch new file mode 100644 index 0000000..2525c64 --- /dev/null +++ b/SOURCES/kvm-Disable-PCIe-to-PCI-bridge-device.patch @@ -0,0 +1,38 @@ +From d496bec5b14df55d49658a283326ad720f974a0a Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Thu, 7 Jun 2018 07:43:15 +0200 +Subject: [PATCH 07/15] Disable PCIe-to-PCI bridge device + +RH-Author: Miroslav Rezanina +Message-id: +Patchwork-id: 80594 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 7/7] Disable PCIe-to-PCI bridge device +Bugzilla: 1586357 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Laurent Vivier + +From: Miroslav Rezanina + +There's new pcie-to-pci device we do not want to support it. + +Signed-off-by: Miroslav Rezanina +--- + hw/pci-bridge/Makefile.objs | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/hw/pci-bridge/Makefile.objs b/hw/pci-bridge/Makefile.objs +index 47065f8..68db0da 100644 +--- a/hw/pci-bridge/Makefile.objs ++++ b/hw/pci-bridge/Makefile.objs +@@ -1,5 +1,6 @@ + common-obj-y += pci_bridge_dev.o +-common-obj-$(CONFIG_PCIE_PORT) += pcie_root_port.o gen_pcie_root_port.o pcie_pci_bridge.o ++common-obj-$(CONFIG_PCIE_PORT) += pcie_root_port.o gen_pcie_root_port.o ++#common-obj-$(CONFIG_PCIE_PORT) += pcie_pci_bridge.o + common-obj-$(CONFIG_PXB) += pci_expander_bridge.o + common-obj-$(CONFIG_XIO3130) += xio3130_upstream.o xio3130_downstream.o + common-obj-$(CONFIG_IOH3420) += ioh3420.o +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Disable-aarch64-devices-reappeared-after-2.12-rebase.patch b/SOURCES/kvm-Disable-aarch64-devices-reappeared-after-2.12-rebase.patch new file mode 100644 index 0000000..ff255f7 --- /dev/null +++ b/SOURCES/kvm-Disable-aarch64-devices-reappeared-after-2.12-rebase.patch @@ -0,0 +1,43 @@ +From db40ec43617c802b5dff4af43c095924bea17ac6 Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Thu, 7 Jun 2018 07:43:09 +0200 +Subject: [PATCH 01/15] Disable aarch64 devices reappeared after 2.12 rebase + +RH-Author: Miroslav Rezanina +Message-id: +Patchwork-id: 80592 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/7] Disable aarch64 devices reappeared after 2.12 rebase +Bugzilla: 1586357 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Laurent Vivier + +From: Miroslav Rezanina + +Up to QEMU 2.11 we disabled aarch64 devices by removing files from +Makefile. +As default_config options were added, we use them to disable devices +since 2.12. However, we can't turn CONFIG_ARM_V7M (turning it off will +break build) so we have to manually remove armv7m module from Makefile. + +Signed-off-by: Miroslav Rezanina +--- + hw/arm/Makefile.objs | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs +index 2885e3e..3e8b167 100644 +--- a/hw/arm/Makefile.objs ++++ b/hw/arm/Makefile.objs +@@ -16,7 +16,7 @@ obj-$(CONFIG_STRONGARM) += collie.o + obj-$(CONFIG_VERSATILE) += vexpress.o versatilepb.o + obj-$(CONFIG_ZYNQ) += xilinx_zynq.o + +-obj-$(CONFIG_ARM_V7M) += armv7m.o ++#obj-$(CONFIG_ARM_V7M) += armv7m.o + obj-$(CONFIG_EXYNOS4) += exynos4210.o + obj-$(CONFIG_PXA2XX) += pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o + obj-$(CONFIG_DIGIC) += digic.o +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Disable-new-pvrdma-device.patch b/SOURCES/kvm-Disable-new-pvrdma-device.patch new file mode 100644 index 0000000..b0ffdf3 --- /dev/null +++ b/SOURCES/kvm-Disable-new-pvrdma-device.patch @@ -0,0 +1,39 @@ +From dda389bad731375b9308e4fc6db7a44cdadc7b1d Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Thu, 7 Jun 2018 07:43:14 +0200 +Subject: [PATCH 06/15] Disable new pvrdma device + +RH-Author: Miroslav Rezanina +Message-id: <5d290e25879e3ac42c247ff3c3a524001ceb0be1.1528355911.git.mrezanin@redhat.com> +Patchwork-id: 80597 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 6/7] Disable new pvrdma device +Bugzilla: 1586357 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Markus Armbruster + +From: Miroslav Rezanina + +New pvrdma device was introduced in rebase to QEMU 2.12. We do not want to +support this device. + +Signed-off-by: Miroslav Rezanina +--- + hw/rdma/Makefile.objs | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/hw/rdma/Makefile.objs b/hw/rdma/Makefile.objs +index 3504c39..02ca2a9 100644 +--- a/hw/rdma/Makefile.objs ++++ b/hw/rdma/Makefile.objs +@@ -1,5 +1,6 @@ + ifeq ($(CONFIG_RDMA),y) + obj-$(CONFIG_PCI) += rdma_utils.o rdma_backend.o rdma_rm.o + obj-$(CONFIG_PCI) += vmw/pvrdma_dev_ring.o vmw/pvrdma_cmd.o \ +- vmw/pvrdma_qp_ops.o vmw/pvrdma_main.o ++ vmw/pvrdma_qp_ops.o ++#obj-$(CONFIG_PCI) += vmw/pvrdma_main.o + endif +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Disable-new-superio-devices.patch b/SOURCES/kvm-Disable-new-superio-devices.patch new file mode 100644 index 0000000..2936dec --- /dev/null +++ b/SOURCES/kvm-Disable-new-superio-devices.patch @@ -0,0 +1,38 @@ +From 5ce455e319c4e34fbe7fef78b72898d3dbc1c8c6 Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Thu, 7 Jun 2018 07:43:13 +0200 +Subject: [PATCH 05/15] Disable new superio devices + +RH-Author: Miroslav Rezanina +Message-id: <51483d3f0091abc80d52485ab076581d4ca914c5.1528355911.git.mrezanin@redhat.com> +Patchwork-id: 80595 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 5/7] Disable new superio devices +Bugzilla: 1586357 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Markus Armbruster + +From: Miroslav Rezanina + +We do not want to support new superio devices (fdc37m81x-superio and +smc37c669-superio). + +Signed-off-by: Miroslav Rezanina +--- + hw/isa/Makefile.objs | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/isa/Makefile.objs b/hw/isa/Makefile.objs +index 83e06f6..7de4f44 100644 +--- a/hw/isa/Makefile.objs ++++ b/hw/isa/Makefile.objs +@@ -1,5 +1,5 @@ + common-obj-$(CONFIG_ISA_BUS) += isa-bus.o +-common-obj-$(CONFIG_ISA_BUS) += isa-superio.o smc37c669-superio.o ++#common-obj-$(CONFIG_ISA_BUS) += isa-superio.o smc37c669-superio.o + common-obj-$(CONFIG_APM) += apm.o + common-obj-$(CONFIG_I82378) += i82378.o + common-obj-$(CONFIG_PC87312) += pc87312.o +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Disable-split-irq-device.patch b/SOURCES/kvm-Disable-split-irq-device.patch new file mode 100644 index 0000000..4c36b39 --- /dev/null +++ b/SOURCES/kvm-Disable-split-irq-device.patch @@ -0,0 +1,40 @@ +From 1055193a52857bd42f7a3961f5f6dcf7edb14cef Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Thu, 7 Jun 2018 07:43:10 +0200 +Subject: [PATCH 02/15] Disable split-irq device + +RH-Author: Miroslav Rezanina +Message-id: <63c6336171256528761f3b4aa22c058d472b048b.1528355911.git.mrezanin@redhat.com> +Patchwork-id: 80596 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 2/7] Disable split-irq device +Bugzilla: 1586357 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Markus Armbruster + +From: Miroslav Rezanina + +There's new split-irq device introduced in QEMU 2.12. We do not want to +support this device. + +Signed-off-by: Miroslav Rezanina +--- + hw/core/Makefile.objs | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs +index 2b4491f..e967fb2 100644 +--- a/hw/core/Makefile.objs ++++ b/hw/core/Makefile.objs +@@ -20,7 +20,7 @@ common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o + # common-obj-$(CONFIG_SOFTMMU) += register.o + # obj-$(CONFIG_SOFTMMU) += generic-loader.o + # common-obj-$(CONFIG_SOFTMMU) += or-irq.o +-common-obj-$(CONFIG_SOFTMMU) += split-irq.o ++#common-obj-$(CONFIG_SOFTMMU) += split-irq.o + common-obj-$(CONFIG_PLATFORM_BUS) += platform-bus.o + + obj-$(CONFIG_SOFTMMU) += null-machine.o +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Fix-heap-overflow-in-ip_reass-on-big-packet-input.patch b/SOURCES/kvm-Fix-heap-overflow-in-ip_reass-on-big-packet-input.patch new file mode 100644 index 0000000..9ca9864 --- /dev/null +++ b/SOURCES/kvm-Fix-heap-overflow-in-ip_reass-on-big-packet-input.patch @@ -0,0 +1,56 @@ +From 903fa86ac4e6c03d135777213f06943bbc2ffd16 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= +Date: Wed, 31 Jul 2019 20:13:05 +0200 +Subject: [PATCH] Fix heap overflow in ip_reass on big packet input +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Philippe Mathieu-Daudé +Message-id: <20190731201305.28657-2-philmd@redhat.com> +Patchwork-id: 89840 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/1] Fix heap overflow in ip_reass on big packet input +Bugzilla: 1734753 1735653 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Thomas Huth +RH-Acked-by: Marc-André Lureau + +From: Samuel Thibault + +When the first fragment does not fit in the preallocated buffer, q will +already be pointing to the ext buffer, so we mustn't try to update it. + +Signed-off-by: Samuel Thibault +(cherry picked from libslirp commit 126c04acbabd7ad32c2b018fe10dfac2a3bc1210) +Signed-off-by: Philippe Mathieu-Daudé + +Signed-off-by: Miroslav Rezanina +--- + slirp/ip_input.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/slirp/ip_input.c b/slirp/ip_input.c +index 348e1dc..07d8808 100644 +--- a/slirp/ip_input.c ++++ b/slirp/ip_input.c +@@ -334,6 +334,8 @@ insert: + q = fp->frag_link.next; + m = dtom(slirp, q); + ++ int was_ext = m->m_flags & M_EXT; ++ + q = (struct ipasfrag *) q->ipf_next; + while (q != (struct ipasfrag*)&fp->frag_link) { + struct mbuf *t = dtom(slirp, q); +@@ -356,7 +358,7 @@ insert: + * the old buffer (in the mbuf), so we must point ip + * into the new buffer. + */ +- if (m->m_flags & M_EXT) { ++ if (!was_ext && m->m_flags & M_EXT) { + int delta = (char *)q - m->m_dat; + q = (struct ipasfrag *)(m->m_ext + delta); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Fix-permissions-for-iotest-218.patch b/SOURCES/kvm-Fix-permissions-for-iotest-218.patch new file mode 100644 index 0000000..94ea738 --- /dev/null +++ b/SOURCES/kvm-Fix-permissions-for-iotest-218.patch @@ -0,0 +1,16 @@ +From e19d458c0569611dee562575e03968844bea0252 Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Wed, 27 Jun 2018 14:42:36 +0200 +Subject: [PATCH 01/89] Fix permissions for iotest 218 + +--- + tests/qemu-iotests/218 | 0 + 1 file changed, 0 insertions(+), 0 deletions(-) + mode change 100644 => 100755 tests/qemu-iotests/218 + +diff --git a/tests/qemu-iotests/218 b/tests/qemu-iotests/218 +old mode 100644 +new mode 100755 +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Fix-x-hv-max-vps-compat-value-for-7.4-machine-type.patch b/SOURCES/kvm-Fix-x-hv-max-vps-compat-value-for-7.4-machine-type.patch new file mode 100644 index 0000000..c5672ce --- /dev/null +++ b/SOURCES/kvm-Fix-x-hv-max-vps-compat-value-for-7.4-machine-type.patch @@ -0,0 +1,60 @@ +From b6128137787bb3c5e05c41013cb4f6cf6c0edf6a Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Thu, 31 May 2018 06:36:35 +0200 +Subject: [PATCH 1/9] Fix x-hv-max-vps compat value for 7.4 machine type + +RH-Author: Miroslav Rezanina +Message-id: <1527748595-28488-1-git-send-email-mrezanin@redhat.com> +Patchwork-id: 80541 +O-Subject: [RHEL qemu-kvm/qemu-kvm-rhev PATCH] Fix x-hv-max-vps compat value for 7.4 machine type +Bugzilla: 1583959 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Laszlo Ersek + +Commit b2f9f4fcaad9c64f4551ab1dbe9e474c3dc6b2b4 increased the limit of vcpus +for windows guest. In addition, it keep old limit for older machine types. +However, due to conflict was compatibility part incorrectly placed and +we keep this limit for 7.3 and older machine types instead of 7.4 and older. + +Moving the chunk to correct spot so we have correct limit. + +Signed-off-by: Miroslav Rezanina +--- + include/hw/i386/pc.h | 11 +++++------ + 1 file changed, 5 insertions(+), 6 deletions(-) + +diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h +index e94424f..d5a0827 100644 +--- a/include/hw/i386/pc.h ++++ b/include/hw/i386/pc.h +@@ -1003,9 +1003,13 @@ extern void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id); + .driver = "q35-pcihost",\ + .property = "x-pci-hole64-fix",\ + .value = "off",\ ++ },\ ++ { /* PC_RHEL7_4_COMPAT from PC_COMPAT_2_10 */ \ ++ .driver = TYPE_X86_CPU,\ ++ .property = "x-hv-max-vps",\ ++ .value = "0x40",\ + }, + +- + #define PC_RHEL7_3_COMPAT \ + HW_COMPAT_RHEL7_3 \ + { /* PC_RHEL7_3_COMPAT from PC_COMPAT_2_8 */ \ +@@ -1067,11 +1071,6 @@ extern void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id); + .driver = TYPE_X86_CPU,\ + .property = "kvm-no-smi-migration",\ + .value = "on",\ +- },\ +- { /* PC_RHEL7_4_COMPAT from PC_COMPAT_2_10 */ \ +- .driver = TYPE_X86_CPU,\ +- .property = "x-hv-max-vps",\ +- .value = "0x40",\ + }, + + #define PC_RHEL7_2_COMPAT \ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Introduce-new-no_guest_reset-parameter-for-usb-host-.patch b/SOURCES/kvm-Introduce-new-no_guest_reset-parameter-for-usb-host-.patch new file mode 100644 index 0000000..2c94790 --- /dev/null +++ b/SOURCES/kvm-Introduce-new-no_guest_reset-parameter-for-usb-host-.patch @@ -0,0 +1,140 @@ +From 8db99b422a6de4d19a93429a221e26afa03f32ef Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Mon, 3 Jun 2019 13:45:47 +0200 +Subject: [PATCH 4/9] Introduce new "no_guest_reset" parameter for usb-host + device + +RH-Author: Gerd Hoffmann +Message-id: <20190603134550.30153-2-kraxel@redhat.com> +Patchwork-id: 88446 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/4] Introduce new "no_guest_reset" parameter for usb-host device +Bugzilla: 1710861 +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Alexander Kappner + +With certain USB devices passed through via usb-host, a guest attempting to +reset a usb-host device can trigger a reset loop that renders the USB device +unusable. In my use case, the device was an iPhone XR that was passed through to +a Mac OS X Mojave guest. Upon connecting the device, the following happens: + +1) Guest recognizes new device, sends reset to emulated USB host +2) QEMU's USB host sends reset to host kernel +3) Host kernel resets device +4) After reset, host kernel determines that some part of the device descriptor +has changed ("device firmware changed" in dmesg), so host kernel decides to +re-enumerate the device. +5) Re-enumeration causes QEMU to disconnect and reconnect the device in the +guest. +6) goto 1) + +Here's from the host kernel (note the "device firmware changed" lines") + +[3677704.473050] usb 1-1.3: new high-speed USB device number 53 using ehci-pci +[3677704.555594] usb 1-1.3: New USB device found, idVendor=05ac, idProduct=12a8, bcdDevice=11.08 +[3677704.555599] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3 +[3677704.555602] usb 1-1.3: Product: iPhone +[3677704.555605] usb 1-1.3: Manufacturer: Apple Inc. +[3677704.555607] usb 1-1.3: SerialNumber: [[removed]] +[3677709.401040] usb 1-1.3: reset high-speed USB device number 53 using ehci-pci +[3677709.479486] usb 1-1.3: device firmware changed +[3677709.479842] usb 1-1.3: USB disconnect, device number 53 +[3677709.546039] usb 1-1.3: new high-speed USB device number 54 using ehci-pci +[3677709.627471] usb 1-1.3: New USB device found, idVendor=05ac, idProduct=12a8, bcdDevice=11.08 +[3677709.627476] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3 +[3677709.627479] usb 1-1.3: Product: iPhone +[3677709.627481] usb 1-1.3: Manufacturer: Apple Inc. +[3677709.627483] usb 1-1.3: SerialNumber: [[removed]] +[3677762.320044] usb 1-1.3: reset high-speed USB device number 54 using ehci-pci +[3677762.615630] usb 1-1.3: USB disconnect, device number 54 +[3677762.787043] usb 1-1.3: new high-speed USB device number 55 using ehci-pci +[3677762.869016] usb 1-1.3: New USB device found, idVendor=05ac, idProduct=12a8, bcdDevice=11.08 +[3677762.869024] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3 +[3677762.869028] usb 1-1.3: Product: iPhone +[3677762.869032] usb 1-1.3: Manufacturer: Apple Inc. +[3677762.869035] usb 1-1.3: SerialNumber: [[removed]] +[3677815.662036] usb 1-1.3: reset high-speed USB device number 55 using ehci-pci + +Here's from QEMU: + +libusb: error [_get_usbfs_fd] libusb couldn't open USB device /dev/bus/usb/005/022: No such file or directory +libusb: error [udev_hotplug_event] ignoring udev action bind +libusb: error [udev_hotplug_event] ignoring udev action bind +libusb: error [_open_sysfs_attr] open /sys/bus/usb/devices/5-1/bConfigurationValue failed ret=-1 errno=2 +libusb: error [_get_usbfs_fd] File doesn't exist, wait 10 ms and try again + +libusb: error [_get_usbfs_fd] libusb couldn't open USB device /dev/bus/usb/005/024: No such file or directory +libusb: error [udev_hotplug_event] ignoring udev action bind +libusb: error [udev_hotplug_event] ignoring udev action bind +libusb: error [_open_sysfs_attr] open /sys/bus/usb/devices/5-1/bConfigurationValue failed ret=-1 errno=2 +libusb: error [_get_usbfs_fd] File doesn't exist, wait 10 ms and try again + +libusb: error [_get_usbfs_fd] libusb couldn't open USB device /dev/bus/usb/005/026: No such file or directory + +The result of this is that the device remains permanently unusable in the guest. +The same problem has been previously reported for an iPad: +https://stackoverflow.com/questions/52617634/how-do-i-get-qemu-usb-passthrough-to-work-for-ipad-iphone + +This problem can be elegantly solved by interrupting step 2) above. Instead of +passing through the reset, QEMU simply ignores it. To allow this to be +configured on a per-device level, a new parameter "no_guest_reset" is +introduced for the usb-host device. I can confirm that the configuration +described above (iPhone XS + Mojave guest) works flawlessly with +no_guest_reset=True specified. + +Working command line for my scenario: +device_add usb-host,vendorid=0x05ac,productid=0x12a8,no_guest_reset=True,id=iphone + +Best regards +Alexander + +Signed-off-by: Alexander Kappner +Signed-off-by: Gerd Hoffmann +Message-id: 20190128140027.9448-1-kraxel@redhat.com + +[ kraxel: rename parameter to "guest-reset" ] + +Signed-off-by: Gerd Hoffmann +(cherry picked from commit ba4c735b4fc74e309ce4b2551d258e442ef513a5) +Signed-off-by: Miroslav Rezanina +--- + hw/usb/host-libusb.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c +index 0290fb8..0425f0e 100644 +--- a/hw/usb/host-libusb.c ++++ b/hw/usb/host-libusb.c +@@ -82,7 +82,7 @@ struct USBHostDevice { + uint32_t options; + uint32_t loglevel; + bool needs_autoscan; +- ++ bool allow_guest_reset; + /* state */ + QTAILQ_ENTRY(USBHostDevice) next; + int seen, errcount; +@@ -1447,6 +1447,10 @@ static void usb_host_handle_reset(USBDevice *udev) + USBHostDevice *s = USB_HOST_DEVICE(udev); + int rc; + ++ if (!s->allow_guest_reset) { ++ return; ++ } ++ + trace_usb_host_reset(s->bus_num, s->addr); + + rc = libusb_reset_device(s->dh); +@@ -1564,6 +1568,7 @@ static Property usb_host_dev_properties[] = { + DEFINE_PROP_UINT32("productid", USBHostDevice, match.product_id, 0), + DEFINE_PROP_UINT32("isobufs", USBHostDevice, iso_urb_count, 4), + DEFINE_PROP_UINT32("isobsize", USBHostDevice, iso_urb_frames, 32), ++ DEFINE_PROP_BOOL("guest-reset", USBHostDevice, allow_guest_reset, true), + DEFINE_PROP_UINT32("loglevel", USBHostDevice, loglevel, + LIBUSB_LOG_LEVEL_WARNING), + DEFINE_PROP_BIT("pipeline", USBHostDevice, options, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Migration-TLS-Fix-crash-due-to-double-cleanup.patch b/SOURCES/kvm-Migration-TLS-Fix-crash-due-to-double-cleanup.patch new file mode 100644 index 0000000..13da7be --- /dev/null +++ b/SOURCES/kvm-Migration-TLS-Fix-crash-due-to-double-cleanup.patch @@ -0,0 +1,65 @@ +From c70d2938f2acf89ca872061086f1fe26e6c5a69d Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Fri, 22 Jun 2018 19:00:01 +0200 +Subject: [PATCH 22/57] Migration+TLS: Fix crash due to double cleanup +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Dr. David Alan Gilbert +Message-id: <20180622190005.21297-15-dgilbert@redhat.com> +Patchwork-id: 81004 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 14/18] Migration+TLS: Fix crash due to double cleanup +Bugzilla: 1584139 +RH-Acked-by: Juan Quintela +RH-Acked-by: Peter Xu +RH-Acked-by: Laurent Vivier + +From: "Dr. David Alan Gilbert" + +During a TLS connect we see: + migration_channel_connect calls + migration_tls_channel_connect + (calls after TLS setup) + migration_channel_connect + +My previous error handling fix made migration_channel_connect +call migrate_fd_connect in all cases; unfortunately the above +means it gets called twice and crashes doing double cleanup. + +Fixes: 688a3dcba98 + +Reported-by: Peter Krempa +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Message-Id: <20180430185943.35714-1-dgilbert@redhat.com> +Signed-off-by: Juan Quintela +(cherry picked from commit 8b7bf2badac25c0a52aff1b181ad75fdb304dd0c) +Signed-off-by: Miroslav Rezanina +--- + migration/channel.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/migration/channel.c b/migration/channel.c +index c5eaf0f..7a32b5a 100644 +--- a/migration/channel.c ++++ b/migration/channel.c +@@ -71,6 +71,15 @@ void migration_channel_connect(MigrationState *s, + !object_dynamic_cast(OBJECT(ioc), + TYPE_QIO_CHANNEL_TLS)) { + migration_tls_channel_connect(s, ioc, hostname, &error); ++ ++ if (!error) { ++ /* tls_channel_connect will call back to this ++ * function after the TLS handshake, ++ * so we mustn't call migrate_fd_connect until then ++ */ ++ ++ return; ++ } + } else { + QEMUFile *f = qemu_fopen_channel_output(ioc); + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-RHEL-7.6-Add-pseries-rhel7.6.0-sxxm-machine-type.patch b/SOURCES/kvm-RHEL-7.6-Add-pseries-rhel7.6.0-sxxm-machine-type.patch new file mode 100644 index 0000000..b033b5f --- /dev/null +++ b/SOURCES/kvm-RHEL-7.6-Add-pseries-rhel7.6.0-sxxm-machine-type.patch @@ -0,0 +1,70 @@ +From 5aec9f19936a96f0140e8ede00fb1a5753477dc7 Mon Sep 17 00:00:00 2001 +From: David Gibson +Date: Mon, 25 Jun 2018 05:11:41 +0200 +Subject: [PATCH 01/89] RHEL-7.6: Add pseries-rhel7.6.0-sxxm machine type + +RH-Author: David Gibson +Message-id: <20180625051141.8203-1-dgibson@redhat.com> +Patchwork-id: 81034 +O-Subject: [RHEL-7.6 qemu-kvm-rhv PATCH] RHEL-7.6: Add pseries-rhel7.6.0-sxxm machine type +Bugzilla: 1592648 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Serhii Popovych +RH-Acked-by: Thomas Huth + +For the pseries-rhel7.3.0 .. pseries-rhel7.5.0 machine types we have -sxxm +variants, the only difference being that the -sxxm variants have the +Spectre and Meltdown mitigations available to guests by default. + +We'd delayed on adding a similar variant for RHEL 7.6, in the hope that we +might be able to enable the mitigations by default for the ordinary 7.6 +machine type. This requires that updated POWER8 firmware (FW860.51 or +newer) be installed on the host. + +The updated firmware was only released late May, and it's not clear how +quickly and widely it will be deployed. For that reason, plus for +consistency in how things need to be configured across rhel-7.y, we're now +adding a pseries-rhel7.6.0-sxxm machine type. + +Signed-off-by: David Gibson +Signed-off-by: Miroslav Rezanina +--- + hw/ppc/spapr.c | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c +index e2df370..a580334 100644 +--- a/hw/ppc/spapr.c ++++ b/hw/ppc/spapr.c +@@ -4369,6 +4369,28 @@ static void spapr_machine_rhel760_class_options(MachineClass *mc) + DEFINE_SPAPR_MACHINE(rhel760, "rhel7.6.0", true); + + /* ++ * pseries-rhel7.6.0-sxxm ++ * ++ * pseries-rhel7.6.0 with speculative execution exploit mitigations enabled by default ++ */ ++static void spapr_machine_rhel760sxxm_instance_options(MachineState *machine) ++{ ++ spapr_machine_rhel760_instance_options(machine); ++} ++ ++static void spapr_machine_rhel760sxxm_class_options(MachineClass *mc) ++{ ++ sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); ++ ++ spapr_machine_rhel760_class_options(mc); ++ smc->default_caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_WORKAROUND; ++ smc->default_caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_WORKAROUND; ++ smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_FIXED_CCD; ++} ++ ++DEFINE_SPAPR_MACHINE(rhel760sxxm, "rhel7.6.0-sxxm", false); ++ ++/* + * pseries-rhel7.5.0 + * like SPAPR_COMPAT_2_11 and SPAPR_COMPAT_2_10 + * SPAPR_CAP_HTM already enabled in 7.4 +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Re-enable-disabled-Hyper-V-enlightenments.patch b/SOURCES/kvm-Re-enable-disabled-Hyper-V-enlightenments.patch new file mode 100644 index 0000000..8bb5959 --- /dev/null +++ b/SOURCES/kvm-Re-enable-disabled-Hyper-V-enlightenments.patch @@ -0,0 +1,48 @@ +From 5967994372c21a8504d7f1b7a61275a647a52c52 Mon Sep 17 00:00:00 2001 +From: Vitaly Kuznetsov +Date: Fri, 12 Oct 2018 07:44:45 +0200 +Subject: [PATCH] Re-enable disabled Hyper-V enlightenments + +RH-Author: Vitaly Kuznetsov +Message-id: <20181012074445.29864-1-vkuznets@redhat.com> +Patchwork-id: 82672 +O-Subject: [RHEL7.7 qemu-kvm-rhev PATCH] Re-enable disabled Hyper-V enlightenments +Bugzilla: 1638835 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: vrozenfe +RH-Acked-by: Miroslav Rezanina + +RHEL-only. + +With the latest Win10 update stimer/synic enlightenments are a must, the +rest is re-enabled for consistency. + +Partially revert 9746c405955 ("Enable/disable devices for RHEL 7"). + +Signed-off-by: Vitaly Kuznetsov +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 91f5a97..8454532 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -5366,13 +5366,11 @@ static Property x86_cpu_properties[] = { + DEFINE_PROP_BOOL("hv-vapic", X86CPU, hyperv_vapic, false), + DEFINE_PROP_BOOL("hv-time", X86CPU, hyperv_time, false), + DEFINE_PROP_BOOL("hv-crash", X86CPU, hyperv_crash, false), +-#if 0 /* Disabled for Red Hat Enterprise Linux */ + DEFINE_PROP_BOOL("hv-reset", X86CPU, hyperv_reset, false), + DEFINE_PROP_BOOL("hv-vpindex", X86CPU, hyperv_vpindex, false), + DEFINE_PROP_BOOL("hv-runtime", X86CPU, hyperv_runtime, false), + DEFINE_PROP_BOOL("hv-synic", X86CPU, hyperv_synic, false), + DEFINE_PROP_BOOL("hv-stimer", X86CPU, hyperv_stimer, false), +-#endif + DEFINE_PROP_BOOL("hv-frequencies", X86CPU, hyperv_frequencies, false), + DEFINE_PROP_BOOL("check", X86CPU, check_cpuid, true), + DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false), +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Revert-Disable-PCIe-to-PCI-bridge-device.patch b/SOURCES/kvm-Revert-Disable-PCIe-to-PCI-bridge-device.patch new file mode 100644 index 0000000..cd2dffb --- /dev/null +++ b/SOURCES/kvm-Revert-Disable-PCIe-to-PCI-bridge-device.patch @@ -0,0 +1,42 @@ +From ec6f06f14094727dbe049343b1f815c40a6f05b4 Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Thu, 9 Aug 2018 18:23:25 +0200 +Subject: [PATCH 3/5] Revert "Disable PCIe-to-PCI bridge device" + +RH-Author: Eduardo Habkost +Message-id: <20180809182325.21534-2-ehabkost@redhat.com> +Patchwork-id: 81691 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/1] Revert "Disable PCIe-to-PCI bridge device" +Bugzilla: 1390329 +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Thomas Huth +RH-Acked-by: Laszlo Ersek + +This reverts commit d496bec5b14df55d49658a283326ad720f974a0a. + +Without pcie-pci-bridge, libvirt will keep using the DMI bridge +(i82801b11-bridge) to plug Conventional PCI devices, and we want +to discourage its usage. + +Signed-off-by: Eduardo Habkost +Signed-off-by: Miroslav Rezanina +--- + hw/pci-bridge/Makefile.objs | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/hw/pci-bridge/Makefile.objs b/hw/pci-bridge/Makefile.objs +index 68db0da..47065f8 100644 +--- a/hw/pci-bridge/Makefile.objs ++++ b/hw/pci-bridge/Makefile.objs +@@ -1,6 +1,5 @@ + common-obj-y += pci_bridge_dev.o +-common-obj-$(CONFIG_PCIE_PORT) += pcie_root_port.o gen_pcie_root_port.o +-#common-obj-$(CONFIG_PCIE_PORT) += pcie_pci_bridge.o ++common-obj-$(CONFIG_PCIE_PORT) += pcie_root_port.o gen_pcie_root_port.o pcie_pci_bridge.o + common-obj-$(CONFIG_PXB) += pci_expander_bridge.o + common-obj-$(CONFIG_XIO3130) += xio3130_upstream.o xio3130_downstream.o + common-obj-$(CONFIG_IOH3420) += ioh3420.o +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Revert-block-dirty-bitmap-Add-bdrv_dirty_iter_next_a.patch b/SOURCES/kvm-Revert-block-dirty-bitmap-Add-bdrv_dirty_iter_next_a.patch new file mode 100644 index 0000000..e4f7296 --- /dev/null +++ b/SOURCES/kvm-Revert-block-dirty-bitmap-Add-bdrv_dirty_iter_next_a.patch @@ -0,0 +1,113 @@ +From 4ff0b9ce2eca5f96d7c99b0d95bafdfae917958f Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 21:48:33 +0100 +Subject: [PATCH 040/163] Revert "block/dirty-bitmap: Add + bdrv_dirty_iter_next_area" + +RH-Author: John Snow +Message-id: <20190320214838.22027-6-jsnow@redhat.com> +Patchwork-id: 84997 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 05/10] Revert "block/dirty-bitmap: Add bdrv_dirty_iter_next_area" +Bugzilla: 1691048 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +This reverts commit 72d10a94213a954ad569095cb4491f2ae0853c40. + +The function is unused now. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: John Snow +(cherry picked from commit 166cd551254f4ea6226d7e687b19928747247500) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 55 -------------------------------------------- + include/block/dirty-bitmap.h | 2 -- + 2 files changed, 57 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index c151837..4d9a8af 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -518,61 +518,6 @@ int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) + return hbitmap_iter_next(&iter->hbi, true); + } + +-/** +- * Return the next consecutively dirty area in the dirty bitmap +- * belonging to the given iterator @iter. +- * +- * @max_offset: Maximum value that may be returned for +- * *offset + *bytes +- * @offset: Will contain the start offset of the next dirty area +- * @bytes: Will contain the length of the next dirty area +- * +- * Returns: True if a dirty area could be found before max_offset +- * (which means that *offset and *bytes then contain valid +- * values), false otherwise. +- * +- * Note that @iter is never advanced if false is returned. If an area +- * is found (which means that true is returned), it will be advanced +- * past that area. +- */ +-bool bdrv_dirty_iter_next_area(BdrvDirtyBitmapIter *iter, uint64_t max_offset, +- uint64_t *offset, int *bytes) +-{ +- uint32_t granularity = bdrv_dirty_bitmap_granularity(iter->bitmap); +- uint64_t gran_max_offset; +- int64_t ret; +- int size; +- +- if (max_offset == iter->bitmap->size) { +- /* If max_offset points to the image end, round it up by the +- * bitmap granularity */ +- gran_max_offset = ROUND_UP(max_offset, granularity); +- } else { +- gran_max_offset = max_offset; +- } +- +- ret = hbitmap_iter_next(&iter->hbi, false); +- if (ret < 0 || ret + granularity > gran_max_offset) { +- return false; +- } +- +- *offset = ret; +- size = 0; +- +- assert(granularity <= INT_MAX); +- +- do { +- /* Advance iterator */ +- ret = hbitmap_iter_next(&iter->hbi, true); +- size += granularity; +- } while (ret + granularity <= gran_max_offset && +- hbitmap_iter_next(&iter->hbi, false) == ret + granularity && +- size <= INT_MAX - granularity); +- +- *bytes = MIN(size, max_offset - *offset); +- return true; +-} +- + /* Called within bdrv_dirty_bitmap_lock..unlock */ + void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, + int64_t offset, int64_t bytes) +diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h +index 4ef00ca..04a117f 100644 +--- a/include/block/dirty-bitmap.h ++++ b/include/block/dirty-bitmap.h +@@ -83,8 +83,6 @@ void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, + void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, + int64_t offset, int64_t bytes); + int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter); +-bool bdrv_dirty_iter_next_area(BdrvDirtyBitmapIter *iter, uint64_t max_offset, +- uint64_t *offset, int *bytes); + void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *hbi, int64_t offset); + int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap); + int64_t bdrv_get_meta_dirty_count(BdrvDirtyBitmap *bitmap); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Revert-hbitmap-Add-advance-param-to-hbitmap_iter_nex.patch b/SOURCES/kvm-Revert-hbitmap-Add-advance-param-to-hbitmap_iter_nex.patch new file mode 100644 index 0000000..0004c51 --- /dev/null +++ b/SOURCES/kvm-Revert-hbitmap-Add-advance-param-to-hbitmap_iter_nex.patch @@ -0,0 +1,193 @@ +From b404e1ea2e0315a8031fe67005e2921eab23e7a4 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 21:48:35 +0100 +Subject: [PATCH 042/163] Revert "hbitmap: Add @advance param to + hbitmap_iter_next()" + +RH-Author: John Snow +Message-id: <20190320214838.22027-8-jsnow@redhat.com> +Patchwork-id: 84995 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 07/10] Revert "hbitmap: Add @advance param to hbitmap_iter_next()" +Bugzilla: 1691048 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +This reverts commit a33fbb4f8b64226becf502a123733776ce319b24. + +The functionality is unused. + +Note: in addition to automatic revert, drop second parameter in +hbitmap_iter_next() call from hbitmap_next_dirty_area() too. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: John Snow +(cherry picked from commit 19c021e1948a81c4ba19b3ff735432b45b6aebee) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 2 +- + block/dirty-bitmap.c | 2 +- + include/qemu/hbitmap.h | 5 +---- + tests/test-hbitmap.c | 26 +++++++++++++------------- + util/hbitmap.c | 12 ++++-------- + 5 files changed, 20 insertions(+), 27 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index 6a66b1c..90680c4 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -409,7 +409,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) + HBitmapIter hbi; + + hbitmap_iter_init(&hbi, job->copy_bitmap, 0); +- while ((cluster = hbitmap_iter_next(&hbi, true)) != -1) { ++ while ((cluster = hbitmap_iter_next(&hbi)) != -1) { + do { + if (yield_and_check(job)) { + return 0; +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index 4d9a8af..00ea36f 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -515,7 +515,7 @@ void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter) + + int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) + { +- return hbitmap_iter_next(&iter->hbi, true); ++ return hbitmap_iter_next(&iter->hbi); + } + + /* Called within bdrv_dirty_bitmap_lock..unlock */ +diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h +index 097dce3..4afbe62 100644 +--- a/include/qemu/hbitmap.h ++++ b/include/qemu/hbitmap.h +@@ -351,14 +351,11 @@ void hbitmap_free_meta(HBitmap *hb); + /** + * hbitmap_iter_next: + * @hbi: HBitmapIter to operate on. +- * @advance: If true, advance the iterator. Otherwise, the next call +- * of this function will return the same result (if that +- * position is still dirty). + * + * 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, bool advance); ++int64_t hbitmap_iter_next(HBitmapIter *hbi); + + /** + * hbitmap_iter_next_word: +diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c +index 6358f35..592d821 100644 +--- a/tests/test-hbitmap.c ++++ b/tests/test-hbitmap.c +@@ -46,7 +46,7 @@ static void hbitmap_test_check(TestHBitmapData *data, + + i = first; + for (;;) { +- next = hbitmap_iter_next(&hbi, true); ++ next = hbitmap_iter_next(&hbi); + if (next < 0) { + next = data->size; + } +@@ -435,25 +435,25 @@ static void test_hbitmap_iter_granularity(TestHBitmapData *data, + /* Note that hbitmap_test_check has to be invoked manually in this test. */ + hbitmap_test_init(data, 131072 << 7, 7); + hbitmap_iter_init(&hbi, data->hb, 0); +- g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); ++ g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + + hbitmap_test_set(data, ((L2 + L1 + 1) << 7) + 8, 8); + hbitmap_iter_init(&hbi, data->hb, 0); +- g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, (L2 + L1 + 1) << 7); +- g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); ++ g_assert_cmpint(hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); ++ g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + + hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); +- g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); ++ g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + + hbitmap_test_set(data, (131072 << 7) - 8, 8); + hbitmap_iter_init(&hbi, data->hb, 0); +- g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, (L2 + L1 + 1) << 7); +- g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, 131071 << 7); +- g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); ++ g_assert_cmpint(hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); ++ g_assert_cmpint(hbitmap_iter_next(&hbi), ==, 131071 << 7); ++ g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + + hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); +- g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, 131071 << 7); +- g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); ++ g_assert_cmpint(hbitmap_iter_next(&hbi), ==, 131071 << 7); ++ g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + } + + static void hbitmap_test_set_boundary_bits(TestHBitmapData *data, ssize_t diff) +@@ -893,7 +893,7 @@ static void test_hbitmap_serialize_zeroes(TestHBitmapData *data, + for (i = 0; i < num_positions; i++) { + hbitmap_deserialize_zeroes(data->hb, positions[i], min_l1, true); + hbitmap_iter_init(&iter, data->hb, 0); +- next = hbitmap_iter_next(&iter, true); ++ next = hbitmap_iter_next(&iter); + if (i == num_positions - 1) { + g_assert_cmpint(next, ==, -1); + } else { +@@ -919,10 +919,10 @@ static void test_hbitmap_iter_and_reset(TestHBitmapData *data, + + hbitmap_iter_init(&hbi, data->hb, BITS_PER_LONG - 1); + +- hbitmap_iter_next(&hbi, true); ++ hbitmap_iter_next(&hbi); + + hbitmap_reset_all(data->hb); +- hbitmap_iter_next(&hbi, true); ++ hbitmap_iter_next(&hbi); + } + + static void test_hbitmap_next_zero_check_range(TestHBitmapData *data, +diff --git a/util/hbitmap.c b/util/hbitmap.c +index fa35652..7905212 100644 +--- a/util/hbitmap.c ++++ b/util/hbitmap.c +@@ -144,7 +144,7 @@ unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi) + return cur; + } + +-int64_t hbitmap_iter_next(HBitmapIter *hbi, bool advance) ++int64_t hbitmap_iter_next(HBitmapIter *hbi) + { + unsigned long cur = hbi->cur[HBITMAP_LEVELS - 1] & + hbi->hb->levels[HBITMAP_LEVELS - 1][hbi->pos]; +@@ -157,12 +157,8 @@ int64_t hbitmap_iter_next(HBitmapIter *hbi, bool advance) + } + } + +- if (advance) { +- /* The next call will resume work from the next bit. */ +- hbi->cur[HBITMAP_LEVELS - 1] = cur & (cur - 1); +- } else { +- hbi->cur[HBITMAP_LEVELS - 1] = cur; +- } ++ /* 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; +@@ -261,7 +257,7 @@ bool hbitmap_next_dirty_area(const HBitmap *hb, uint64_t *start, + end = *count > hb->orig_size - *start ? hb->orig_size : *start + *count; + + hbitmap_iter_init(&hbi, hb, *start); +- firt_dirty_off = hbitmap_iter_next(&hbi, false); ++ firt_dirty_off = hbitmap_iter_next(&hbi); + + if (firt_dirty_off < 0 || firt_dirty_off >= end) { + return false; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Revert-migration-move-only_migratable-to-MigrationSt.patch b/SOURCES/kvm-Revert-migration-move-only_migratable-to-MigrationSt.patch new file mode 100644 index 0000000..f1da546 --- /dev/null +++ b/SOURCES/kvm-Revert-migration-move-only_migratable-to-MigrationSt.patch @@ -0,0 +1,206 @@ +From 99c200d1a05ef50fedcb6b86387af3b60fe54a0e Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:19 +0200 +Subject: [PATCH 52/53] Revert "migration: move only_migratable to + MigrationState" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-31-armbru@redhat.com> +Patchwork-id: 88007 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 30/31] Revert "migration: move only_migratable to MigrationState" +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +This reverts commit 3df663e575f1876d7f3bc684f80e72fca0703d39. +This reverts commit b605c47b57b58e61a901a50a0762dccf43d94783. + +Command line option --only-migratable is for disallowing any +configuration that can block migration. + +Initially, --only-migratable set global variable @only_migratable. + +Commit 3df663e575 "migration: move only_migratable to MigrationState" +replaced it by MigrationState member @only_migratable. That was a +mistake. + +First, it doesn't make sense on the design level. MigrationState +captures the state of an individual migration, but --only-migratable +isn't a property of an individual migration, it's a restriction on +QEMU configuration. With fault tolerance, we could have several +migrations at once. --only-migratable would certainly protect all of +them. Storing it in MigrationState feels inappropriate. + +Second, it contributes to a dependency cycle that manifests itself as +a bug now. + +Putting @only_migratable into MigrationState means its available only +after migration_object_init(). + +We can't set it before migration_object_init(), so we delay setting it +with a global property (this is fixup commit b605c47b57 "migration: +fix handling for --only-migratable"). + +We can't get it before migration_object_init(), so anything that uses +it can only run afterwards. + +Since migrate_add_blocker() needs to obey --only-migratable, any code +adding migration blockers can run only afterwards. This contributes +to the following dependency cycle: + +* configure_blockdev() must run before machine_set_property() + so machine properties can refer to block backends + +* machine_set_property() before configure_accelerator() + so machine properties like kvm-irqchip get applied + +* configure_accelerator() before migration_object_init() + so that Xen's accelerator compat properties get applied. + +* migration_object_init() before configure_blockdev() + so configure_blockdev() can add migration blockers + +The cycle was closed when recent commit cda4aa9a5a0 "Create block +backends before setting machine properties" added the first +dependency, and satisfied it by violating the last one. Broke block +backends that add migration blockers. + +Moving @only_migratable into MigrationState was a mistake. Revert it. + +This doesn't quite break the "migration_object_init() before +configure_blockdev() dependency, since migrate_add_blocker() still has +another dependency on migration_object_init(). To be addressed the +next commit. + +Note that the reverted commit made -only-migratable sugar for -global +migration.only-migratable=on below the hood. Documentation has only +ever mentioned -only-migratable. This commit removes the arcane & +undocumented alternative to -only-migratable again. Nobody should be +using it. + +Signed-off-by: Miroslav Rezanina + +Conflicts: + include/migration/misc.h + migration/migration.c + migration/migration.h + vl.c + +Signed-off-by: Markus Armbruster +Message-Id: <20190401090827.20793-3-armbru@redhat.com> +Reviewed-by: Igor Mammedov +(cherry picked from commit 811f8652712a4ec2ff73c2c5dca35581a25112a4) +[Conflicts in migration/migration.c and vl.c because we lack conflicts +there] +Signed-off-by: Markus Armbruster +--- + include/sysemu/sysemu.h | 1 + + migration/migration.c | 5 ++--- + migration/migration.h | 3 --- + migration/savevm.c | 2 +- + vl.c | 9 ++------- + 5 files changed, 6 insertions(+), 14 deletions(-) + +diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h +index 5d8634b..2a6f4a5 100644 +--- a/include/sysemu/sysemu.h ++++ b/include/sysemu/sysemu.h +@@ -14,6 +14,7 @@ + /* vl.c */ + + extern const char *bios_name; ++extern int only_migratable; + extern const char *qemu_name; + extern QemuUUID qemu_uuid; + extern bool qemu_uuid_set; +diff --git a/migration/migration.c b/migration/migration.c +index edf1c06..83b8d6a 100644 +--- a/migration/migration.c ++++ b/migration/migration.c +@@ -1330,7 +1330,7 @@ static GSList *migration_blockers; + + int migrate_add_blocker(Error *reason, Error **errp) + { +- if (migrate_get_current()->only_migratable) { ++ if (only_migratable) { + error_propagate(errp, error_copy(reason)); + error_prepend(errp, "disallowing migration blocker " + "(--only_migratable) for: "); +@@ -2498,7 +2498,7 @@ void migration_global_dump(Monitor *mon) + monitor_printf(mon, "store-global-state: %s\n", + ms->store_global_state ? "on" : "off"); + monitor_printf(mon, "only-migratable: %s\n", +- ms->only_migratable ? "on" : "off"); ++ only_migratable ? "on" : "off"); + monitor_printf(mon, "send-configuration: %s\n", + ms->send_configuration ? "on" : "off"); + monitor_printf(mon, "send-section-footer: %s\n", +@@ -2513,7 +2513,6 @@ void migration_global_dump(Monitor *mon) + static Property migration_properties[] = { + DEFINE_PROP_BOOL("store-global-state", MigrationState, + store_global_state, true), +- DEFINE_PROP_BOOL("only-migratable", MigrationState, only_migratable, false), + DEFINE_PROP_BOOL("send-configuration", MigrationState, + send_configuration, true), + DEFINE_PROP_BOOL("send-section-footer", MigrationState, +diff --git a/migration/migration.h b/migration/migration.h +index a9c5c7f..84bdcb7 100644 +--- a/migration/migration.h ++++ b/migration/migration.h +@@ -175,9 +175,6 @@ struct MigrationState + */ + bool store_global_state; + +- /* Whether the VM is only allowing for migratable devices */ +- bool only_migratable; +- + /* Whether we send QEMU_VM_CONFIGURATION during migration */ + bool send_configuration; + /* Whether we send section footer during migration */ +diff --git a/migration/savevm.c b/migration/savevm.c +index e5d57fa..6c398d1 100644 +--- a/migration/savevm.c ++++ b/migration/savevm.c +@@ -2530,7 +2530,7 @@ void vmstate_register_ram_global(MemoryRegion *mr) + bool vmstate_check_only_migratable(const VMStateDescription *vmsd) + { + /* check needed if --only-migratable is specified */ +- if (!migrate_get_current()->only_migratable) { ++ if (!only_migratable) { + return true; + } + +diff --git a/vl.c b/vl.c +index 15e87a4..61247eb 100644 +--- a/vl.c ++++ b/vl.c +@@ -192,6 +192,7 @@ bool boot_strict; + uint8_t *boot_splash_filedata; + size_t boot_splash_filedata_size; + uint8_t qemu_extra_params_fw[2]; ++int only_migratable; /* turn it off unless user states otherwise */ + + int icount_align_option; + +@@ -3994,13 +3995,7 @@ int main(int argc, char **argv, char **envp) + incoming = optarg; + break; + case QEMU_OPTION_only_migratable: +- /* +- * TODO: we can remove this option one day, and we +- * should all use: +- * +- * "-global migration.only-migratable=true" +- */ +- qemu_global_option("migration.only-migratable=true"); ++ only_migratable = 1; + break; + case QEMU_OPTION_nodefaults: + has_defaults = 0; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Revert-spapr-Don-t-allow-memory-hotplug-to-memory-le.patch b/SOURCES/kvm-Revert-spapr-Don-t-allow-memory-hotplug-to-memory-le.patch new file mode 100644 index 0000000..58ed66a --- /dev/null +++ b/SOURCES/kvm-Revert-spapr-Don-t-allow-memory-hotplug-to-memory-le.patch @@ -0,0 +1,69 @@ +From 55cc729e37b945bcbb9135fe5553d1bb9a293424 Mon Sep 17 00:00:00 2001 +From: Serhii Popovych +Date: Wed, 2 May 2018 18:52:28 +0200 +Subject: [PATCH 05/13] Revert "spapr: Don't allow memory hotplug to memory + less nodes" + +RH-Author: Serhii Popovych +Message-id: <1525287148-92715-3-git-send-email-spopovyc@redhat.com> +Patchwork-id: 80012 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 2/2] Revert "spapr: Don't allow memory hotplug to memory less nodes" +Bugzilla: 1570525 +RH-Acked-by: David Gibson +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier + +This reverts commit b556854bd8524c26b8be98ab1bfdf0826831e793. + +Leave change @node type from uint32_t to to int from reverted commit +because node < 0 is always false. + +Note that implementing capability or some trick to detect if guest +kernel does not support hot-add to memory: this returns previous +behavour where memory added to first non-empty node. + +Signed-off-by: Serhii Popovych +Signed-off-by: David Gibson +(cherry picked from commit e47f1d2786c3d01a7894a493aafe0efa6b64453c) +Signed-off-by: Serhii Popovych +Signed-off-by: Miroslav Rezanina +--- + hw/ppc/spapr.c | 22 ---------------------- + 1 file changed, 22 deletions(-) + +diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c +index c9561e1..6f005a0 100644 +--- a/hw/ppc/spapr.c ++++ b/hw/ppc/spapr.c +@@ -3501,28 +3501,6 @@ static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, + return; + } + +- /* +- * Currently PowerPC kernel doesn't allow hot-adding memory to +- * memory-less node, but instead will silently add the memory +- * to the first node that has some memory. This causes two +- * unexpected behaviours for the user. +- * +- * - Memory gets hotplugged to a different node than what the user +- * specified. +- * - Since pc-dimm subsystem in QEMU still thinks that memory belongs +- * to memory-less node, a reboot will set things accordingly +- * and the previously hotplugged memory now ends in the right node. +- * This appears as if some memory moved from one node to another. +- * +- * So until kernel starts supporting memory hotplug to memory-less +- * nodes, just prevent such attempts upfront in QEMU. +- */ +- if (nb_numa_nodes && !numa_info[node].node_mem) { +- error_setg(errp, "Can't hotplug memory to memory-less node %d", +- node); +- return; +- } +- + spapr_memory_plug(hotplug_dev, dev, node, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { + spapr_core_plug(hotplug_dev, dev, errp); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Revert-test-hbitmap-Add-non-advancing-iter_next-test.patch b/SOURCES/kvm-Revert-test-hbitmap-Add-non-advancing-iter_next-test.patch new file mode 100644 index 0000000..28ac38d --- /dev/null +++ b/SOURCES/kvm-Revert-test-hbitmap-Add-non-advancing-iter_next-test.patch @@ -0,0 +1,121 @@ +From 882e1e9983aee5f98b3ad7c65ced43d7cacfd000 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 21:48:34 +0100 +Subject: [PATCH 041/163] Revert "test-hbitmap: Add non-advancing iter_next + tests" + +RH-Author: John Snow +Message-id: <20190320214838.22027-7-jsnow@redhat.com> +Patchwork-id: 84999 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 06/10] Revert "test-hbitmap: Add non-advancing iter_next tests" +Bugzilla: 1691048 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +This reverts commit 269576848ec3d57d2d958cf5ac69b08c44adf816. + +The functionality is unused. Drop tests. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: John Snow +(cherry picked from commit 4294c4ab4825a2ce4d816e52f95a6f08b56aa69c) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/test-hbitmap.c | 36 ++++++++++++------------------------ + 1 file changed, 12 insertions(+), 24 deletions(-) + +diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c +index 4f312e9..6358f35 100644 +--- a/tests/test-hbitmap.c ++++ b/tests/test-hbitmap.c +@@ -30,18 +30,6 @@ typedef struct TestHBitmapData { + } TestHBitmapData; + + +-static int64_t check_hbitmap_iter_next(HBitmapIter *hbi) +-{ +- int next0, next1; +- +- next0 = hbitmap_iter_next(hbi, false); +- next1 = hbitmap_iter_next(hbi, true); +- +- g_assert_cmpint(next0, ==, next1); +- +- return next0; +-} +- + /* Check that the HBitmap and the shadow bitmap contain the same data, + * ignoring the same "first" bits. + */ +@@ -58,7 +46,7 @@ static void hbitmap_test_check(TestHBitmapData *data, + + i = first; + for (;;) { +- next = check_hbitmap_iter_next(&hbi); ++ next = hbitmap_iter_next(&hbi, true); + if (next < 0) { + next = data->size; + } +@@ -447,25 +435,25 @@ static void test_hbitmap_iter_granularity(TestHBitmapData *data, + /* Note that hbitmap_test_check has to be invoked manually in this test. */ + hbitmap_test_init(data, 131072 << 7, 7); + hbitmap_iter_init(&hbi, data->hb, 0); +- g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); ++ g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); + + hbitmap_test_set(data, ((L2 + L1 + 1) << 7) + 8, 8); + hbitmap_iter_init(&hbi, data->hb, 0); +- g_assert_cmpint(check_hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); +- g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); ++ g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, (L2 + L1 + 1) << 7); ++ g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); + + hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); + + hbitmap_test_set(data, (131072 << 7) - 8, 8); + hbitmap_iter_init(&hbi, data->hb, 0); +- g_assert_cmpint(check_hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); +- g_assert_cmpint(check_hbitmap_iter_next(&hbi), ==, 131071 << 7); +- g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); ++ g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, (L2 + L1 + 1) << 7); ++ g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, 131071 << 7); ++ g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); + + hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); +- g_assert_cmpint(check_hbitmap_iter_next(&hbi), ==, 131071 << 7); +- g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); ++ g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, 131071 << 7); ++ g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); + } + + static void hbitmap_test_set_boundary_bits(TestHBitmapData *data, ssize_t diff) +@@ -905,7 +893,7 @@ static void test_hbitmap_serialize_zeroes(TestHBitmapData *data, + for (i = 0; i < num_positions; i++) { + hbitmap_deserialize_zeroes(data->hb, positions[i], min_l1, true); + hbitmap_iter_init(&iter, data->hb, 0); +- next = check_hbitmap_iter_next(&iter); ++ next = hbitmap_iter_next(&iter, true); + if (i == num_positions - 1) { + g_assert_cmpint(next, ==, -1); + } else { +@@ -931,10 +919,10 @@ static void test_hbitmap_iter_and_reset(TestHBitmapData *data, + + hbitmap_iter_init(&hbi, data->hb, BITS_PER_LONG - 1); + +- check_hbitmap_iter_next(&hbi); ++ hbitmap_iter_next(&hbi, true); + + hbitmap_reset_all(data->hb); +- check_hbitmap_iter_next(&hbi); ++ hbitmap_iter_next(&hbi, true); + } + + static void test_hbitmap_next_zero_check_range(TestHBitmapData *data, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Revert-usb-release-the-created-buses.patch b/SOURCES/kvm-Revert-usb-release-the-created-buses.patch new file mode 100644 index 0000000..24c4639 --- /dev/null +++ b/SOURCES/kvm-Revert-usb-release-the-created-buses.patch @@ -0,0 +1,99 @@ +From 5969300e27c6a28f1bc345b84cd9e690780e29f8 Mon Sep 17 00:00:00 2001 +From: Serhii Popovych +Date: Mon, 9 Jul 2018 11:31:18 +0200 +Subject: [PATCH 28/89] Revert "usb: release the created buses" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Serhii Popovych +Message-id: <1531135878-18813-4-git-send-email-spopovyc@redhat.com> +Patchwork-id: 81266 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 3/3] Revert "usb: release the created buses" +Bugzilla: 1556678 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Marc-André Lureau +RH-Acked-by: David Gibson + +From: Marc-André Lureau + +The USB device don't hold the bus. There is no ASAN related reports +anymore. + +This reverts commit cd7bc87868d534f95e928cad98e2a52df7695771. + +Signed-off-by: Marc-André Lureau +Message-id: 20180613172815.32738-3-marcandre.lureau@redhat.com +Signed-off-by: Gerd Hoffmann +(cherry picked from commit 9b5c2fd53feb574036747d0284fd7f73dfedc89c) +Signed-off-by: Serhii Popovych +Signed-off-by: Miroslav Rezanina +--- + hw/usb/dev-storage.c | 16 ---------------- + hw/usb/dev-uas.c | 2 -- + 2 files changed, 18 deletions(-) + +diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c +index 68e2062..9fb00a9 100644 +--- a/hw/usb/dev-storage.c ++++ b/hw/usb/dev-storage.c +@@ -589,13 +589,6 @@ static const struct SCSIBusInfo usb_msd_scsi_info_bot = { + .load_request = usb_msd_load_request, + }; + +-static void usb_msd_unrealize_storage(USBDevice *dev, Error **errp) +-{ +- MSDState *s = USB_STORAGE_DEV(dev); +- +- object_unref(OBJECT(&s->bus)); +-} +- + static void usb_msd_storage_realize(USBDevice *dev, Error **errp) + { + MSDState *s = USB_STORAGE_DEV(dev); +@@ -645,13 +638,6 @@ static void usb_msd_storage_realize(USBDevice *dev, Error **errp) + s->scsi_dev = scsi_dev; + } + +-static void usb_msd_bot_unrealize(USBDevice *dev, Error **errp) +-{ +- MSDState *s = USB_STORAGE_DEV(dev); +- +- object_unref(OBJECT(&s->bus)); +-} +- + static void usb_msd_bot_realize(USBDevice *dev, Error **errp) + { + MSDState *s = USB_STORAGE_DEV(dev); +@@ -716,7 +702,6 @@ static void usb_msd_class_storage_initfn(ObjectClass *klass, void *data) + USBDeviceClass *uc = USB_DEVICE_CLASS(klass); + + uc->realize = usb_msd_storage_realize; +- uc->unrealize = usb_msd_unrealize_storage; + dc->props = msd_properties; + } + +@@ -779,7 +764,6 @@ static void usb_msd_class_bot_initfn(ObjectClass *klass, void *data) + USBDeviceClass *uc = USB_DEVICE_CLASS(klass); + + uc->realize = usb_msd_bot_realize; +- uc->unrealize = usb_msd_bot_unrealize; + uc->attached_settable = true; + } + +diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c +index c218b53..eaf515d 100644 +--- a/hw/usb/dev-uas.c ++++ b/hw/usb/dev-uas.c +@@ -896,8 +896,6 @@ static void usb_uas_unrealize(USBDevice *dev, Error **errp) + UASDevice *uas = USB_UAS(dev); + + qemu_bh_delete(uas->status_bh); +- +- object_unref(OBJECT(&uas->bus)); + } + + static void usb_uas_realize(USBDevice *dev, Error **errp) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Use-4-MB-vram-for-cirrus.patch b/SOURCES/kvm-Use-4-MB-vram-for-cirrus.patch new file mode 100644 index 0000000..8dd1c24 --- /dev/null +++ b/SOURCES/kvm-Use-4-MB-vram-for-cirrus.patch @@ -0,0 +1,63 @@ +From 798456424d4941bed5e19e4ee3c712c7bc3766dd Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Wed, 13 Jun 2018 10:40:26 +0200 +Subject: [PATCH 4/9] Use 4 MB vram for cirrus. + +RH-Author: Gerd Hoffmann +Message-id: <20180613104026.4395-3-kraxel@redhat.com> +Patchwork-id: 80652 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 2/2] Use 4 MB vram for cirrus. +Bugzilla: 1542080 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Daniel P. Berrange + +Follow upstream, which switched from 8 MB to 4 MB in release 2.9, +see commit 73c148130b58709f0f2abfedbae92681d87eb404. + +RHEL traditionally defaults to 16 MB video memory, so we had a patch +changing 8 MB -> 16 MB downstream. The 2.9 rebase kept that patch, now +changing 4 MB -> 16 MB. + +This patch effectively reverts the downstream change and adds a compat +property so the video memory will stay at 16 MB for 7.5 and older +machine types. + +Signed-off-by: Gerd Hoffmann +Signed-off-by: Miroslav Rezanina +--- + hw/display/cirrus_vga.c | 2 +- + include/hw/compat.h | 4 ++++ + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c +index feacb45..014268a 100644 +--- a/hw/display/cirrus_vga.c ++++ b/hw/display/cirrus_vga.c +@@ -3133,7 +3133,7 @@ static void pci_cirrus_vga_realize(PCIDevice *dev, Error **errp) + + static Property pci_vga_cirrus_properties[] = { + DEFINE_PROP_UINT32("vgamem_mb", struct PCICirrusVGAState, +- cirrus_vga.vga.vram_size_mb, 16), ++ cirrus_vga.vga.vram_size_mb, 4), + DEFINE_PROP_BOOL("blitter", struct PCICirrusVGAState, + cirrus_vga.enable_blitter, true), + DEFINE_PROP_END_OF_LIST(), +diff --git a/include/hw/compat.h b/include/hw/compat.h +index 666eed9..9e659e9 100644 +--- a/include/hw/compat.h ++++ b/include/hw/compat.h +@@ -476,6 +476,10 @@ + .driver = "virtio-tablet-device",\ + .property = "wheel-axis",\ + .value = "false",\ ++ },{ /* HW_COMPAT_RHEL7_5 */ \ ++ .driver = "cirrus-vga",\ ++ .property = "vgamem_mb",\ ++ .value = "16",\ + }, + + #endif /* HW_COMPAT_H */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Use-KVM_GET_MSR_INDEX_LIST-for-MSR_IA32_ARCH_CAP.patch b/SOURCES/kvm-Use-KVM_GET_MSR_INDEX_LIST-for-MSR_IA32_ARCH_CAP.patch new file mode 100644 index 0000000..690996c --- /dev/null +++ b/SOURCES/kvm-Use-KVM_GET_MSR_INDEX_LIST-for-MSR_IA32_ARCH_CAP.patch @@ -0,0 +1,76 @@ +From d8ed655838cc87d703e1a1419bd9704fceae6a6c Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Tue, 4 Jun 2019 21:47:25 +0200 +Subject: [PATCH 06/23] kvm: Use KVM_GET_MSR_INDEX_LIST for + MSR_IA32_ARCH_CAPABILITIES support + +RH-Author: plai@redhat.com +Message-id: <1559684847-10889-7-git-send-email-plai@redhat.com> +Patchwork-id: 88529 +O-Subject: [RHEL7.7 qemu-kvm-rhev PATCH v4 6/8] kvm: Use KVM_GET_MSR_INDEX_LIST for MSR_IA32_ARCH_CAPABILITIES support +Bugzilla: 1709972 +RH-Acked-by: Eduardo Habkost +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Miroslav Rezanina + +From: Bandan Das + +When writing to guest's MSR_IA32_ARCH_CAPABILITIES, check whether it's +supported in the guest using the KVM_GET_MSR_INDEX_LIST ioctl. + +Fixes: d86f963694df27f11b3681ffd225c9362de1b634 +Suggested-by: Eduardo Habkost +Tested-by: balducci@units.it +Signed-off-by: Bandan Das +Message-Id: +Signed-off-by: Eduardo Habkost +(cherry picked from commit aec5e9c3a94cf8b7920f59bef69a6f426092c4a0) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + target/i386/kvm.c | 15 +++++++-------- + 1 file changed, 7 insertions(+), 8 deletions(-) + +diff --git a/target/i386/kvm.c b/target/i386/kvm.c +index 88a4114..c99c0ef 100644 +--- a/target/i386/kvm.c ++++ b/target/i386/kvm.c +@@ -94,6 +94,7 @@ static bool has_msr_xss; + static bool has_msr_spec_ctrl; + static bool has_msr_virt_ssbd; + static bool has_msr_smi_count; ++static bool has_msr_arch_capabs; + + static uint32_t has_architectural_pmu_version; + static uint32_t num_architectural_pmu_gp_counters; +@@ -1330,6 +1331,9 @@ static int kvm_get_supported_msrs(KVMState *s) + case MSR_VIRT_SSBD: + has_msr_virt_ssbd = true; + break; ++ case MSR_IA32_ARCH_CAPABILITIES: ++ has_msr_arch_capabs = true; ++ break; + } + } + } +@@ -1834,14 +1838,9 @@ static int kvm_put_msrs(X86CPU *cpu, int level) + #endif + + /* If host supports feature MSR, write down. */ +- if (kvm_feature_msrs) { +- int i; +- for (i = 0; i < kvm_feature_msrs->nmsrs; i++) +- if (kvm_feature_msrs->indices[i] == MSR_IA32_ARCH_CAPABILITIES) { +- kvm_msr_entry_add(cpu, MSR_IA32_ARCH_CAPABILITIES, +- env->features[FEAT_ARCH_CAPABILITIES]); +- break; +- } ++ if (has_msr_arch_capabs) { ++ kvm_msr_entry_add(cpu, MSR_IA32_ARCH_CAPABILITIES, ++ env->features[FEAT_ARCH_CAPABILITIES]); + } + + /* +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Use-inhibit-to-prevent-ballooning-without-synchr.patch b/SOURCES/kvm-Use-inhibit-to-prevent-ballooning-without-synchr.patch new file mode 100644 index 0000000..fbdf1ec --- /dev/null +++ b/SOURCES/kvm-Use-inhibit-to-prevent-ballooning-without-synchr.patch @@ -0,0 +1,80 @@ +From c9168134e8e35b61d2b06a1fc34a672a7e9cec85 Mon Sep 17 00:00:00 2001 +From: Alex Williamson +Date: Mon, 3 Dec 2018 21:52:53 +0100 +Subject: [PATCH 19/34] kvm: Use inhibit to prevent ballooning without + synchronous mmu + +RH-Author: Alex Williamson +Message-id: <154387397293.26945.13950211291934514410.stgit@gimli.home> +Patchwork-id: 83228 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 2/7] kvm: Use inhibit to prevent ballooning without synchronous mmu +Bugzilla: 1619778 +RH-Acked-by: Peter Xu +RH-Acked-by: Cornelia Huck +RH-Acked-by: Auger Eric +RH-Acked-by: David Hildenbrand + +Bugzilla: 1619778 + +Remove KVM specific tests in balloon_page(), instead marking +ballooning as inhibited without KVM_CAP_SYNC_MMU support. + +Reviewed-by: David Hildenbrand +Reviewed-by: Peter Xu +Reviewed-by: Cornelia Huck +Acked-by: Paolo Bonzini +Signed-off-by: Alex Williamson +(cherry picked from commit f59489423ab79852e98d9b3025b7d99ba8da584f) +Signed-off-by: Miroslav Rezanina +--- + accel/kvm/kvm-all.c | 4 ++++ + hw/virtio/virtio-balloon.c | 4 +--- + 2 files changed, 5 insertions(+), 3 deletions(-) + +diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c +index 3f1c06e..3a7c8a3 100644 +--- a/accel/kvm/kvm-all.c ++++ b/accel/kvm/kvm-all.c +@@ -39,6 +39,7 @@ + #include "trace.h" + #include "hw/irq.h" + #include "sysemu/sev.h" ++#include "sysemu/balloon.h" + + #include "hw/boards.h" + +@@ -1711,6 +1712,9 @@ static int kvm_init(MachineState *ms) + s->many_ioeventfds = kvm_check_many_ioeventfds(); + + s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU); ++ if (!s->sync_mmu) { ++ qemu_balloon_inhibit(true); ++ } + + return 0; + +diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c +index f456cea..4aab291 100644 +--- a/hw/virtio/virtio-balloon.c ++++ b/hw/virtio/virtio-balloon.c +@@ -21,7 +21,6 @@ + #include "hw/mem/pc-dimm.h" + #include "sysemu/balloon.h" + #include "hw/virtio/virtio-balloon.h" +-#include "sysemu/kvm.h" + #include "exec/address-spaces.h" + #include "qapi/error.h" + #include "qapi/qapi-events-misc.h" +@@ -36,8 +35,7 @@ + + static void balloon_page(void *addr, int deflate) + { +- if (!qemu_balloon_is_inhibited() && (!kvm_enabled() || +- kvm_has_sync_mmu())) { ++ if (!qemu_balloon_is_inhibited()) { + qemu_madvise(addr, BALLOON_PAGE_SIZE, + deflate ? QEMU_MADV_WILLNEED : QEMU_MADV_DONTNEED); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-Using-ip_deq-after-m_free-might-read-pointers-from-a.patch b/SOURCES/kvm-Using-ip_deq-after-m_free-might-read-pointers-from-a.patch new file mode 100644 index 0000000..0f8a77e --- /dev/null +++ b/SOURCES/kvm-Using-ip_deq-after-m_free-might-read-pointers-from-a.patch @@ -0,0 +1,61 @@ +From 3f9fbb23959f82de389fa43848bb28cd2b80a4bb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= +Date: Fri, 6 Sep 2019 14:00:34 +0200 +Subject: [PATCH 1/4] Using ip_deq after m_free might read pointers from an + allocation reuse. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Philippe Mathieu-Daudé +Message-id: <20190906140034.19722-2-philmd@redhat.com> +Patchwork-id: 90306 +O-Subject: [RHEL-7.7 qemu-kvm-ma + RHEL-7.7 qemu-kvm-rhev + RHEL-8.1.0 qemu-kvm PATCH 1/1] Using ip_deq after m_free might read pointers from an allocation reuse. +Bugzilla: 1749723 +RH-Acked-by: Thomas Huth +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Stefan Hajnoczi + +From: Samuel Thibault + +This would be difficult to exploit, but that is still related with +CVE-2019-14378 which generates fragmented IP packets that would trigger this +issue and at least produce a DoS. + +Signed-off-by: Samuel Thibault +(cherry picked from libslirp commit c59279437eda91841b9d26079c70b8a540d41204) +Signed-off-by: Philippe Mathieu-Daudé + +Signed-off-by: Miroslav Rezanina +--- + slirp/ip_input.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/slirp/ip_input.c b/slirp/ip_input.c +index 07d8808..7cf0133 100644 +--- a/slirp/ip_input.c ++++ b/slirp/ip_input.c +@@ -300,6 +300,7 @@ ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp) + */ + while (q != (struct ipasfrag*)&fp->frag_link && + ip->ip_off + ip->ip_len > q->ipf_off) { ++ struct ipasfrag *prev; + i = (ip->ip_off + ip->ip_len) - q->ipf_off; + if (i < q->ipf_len) { + q->ipf_len -= i; +@@ -307,9 +308,10 @@ ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp) + m_adj(dtom(slirp, q), i); + break; + } ++ prev = q; + q = q->ipf_next; +- m_free(dtom(slirp, q->ipf_prev)); +- ip_deq(q->ipf_prev); ++ ip_deq(prev); ++ m_free(dtom(slirp, prev)); + } + + insert: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-ahci-don-t-schedule-unnecessary-BH.patch b/SOURCES/kvm-ahci-don-t-schedule-unnecessary-BH.patch new file mode 100644 index 0000000..dc5aef2 --- /dev/null +++ b/SOURCES/kvm-ahci-don-t-schedule-unnecessary-BH.patch @@ -0,0 +1,46 @@ +From 80d7419e93a1195b46c6c917b4533ca440c12c82 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Tue, 10 Jul 2018 23:06:16 +0200 +Subject: [PATCH 33/89] ahci: don't schedule unnecessary BH + +RH-Author: John Snow +Message-id: <20180710230616.11000-4-jsnow@redhat.com> +Patchwork-id: 81292 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 3/3] ahci: don't schedule unnecessary BH +Bugzilla: 1584914 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Stefan Hajnoczi + +The comment gives us a hint. *Maybe* we still have something to +process. Well, why not check? + +Signed-off-by: John Snow +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Jeff Cody +Message-id: 20180531004323.4611-4-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 42af312adef8afdae11d5f83d12a404b178dbda4) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + hw/ide/ahci.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c +index a9558e4..380366b 100644 +--- a/hw/ide/ahci.c ++++ b/hw/ide/ahci.c +@@ -1427,8 +1427,7 @@ static void ahci_cmd_done(IDEDMA *dma) + /* update d2h status */ + ahci_write_fis_d2h(ad); + +- if (!ad->check_bh) { +- /* maybe we still have something to process, check later */ ++ if (ad->port_regs.cmd_issue && !ad->check_bh) { + ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad); + qemu_bh_schedule(ad->check_bh); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-ahci-fix-PxCI-register-race.patch b/SOURCES/kvm-ahci-fix-PxCI-register-race.patch new file mode 100644 index 0000000..40412a4 --- /dev/null +++ b/SOURCES/kvm-ahci-fix-PxCI-register-race.patch @@ -0,0 +1,81 @@ +From 41f2bce1d8d55b1a35bd7456b4b13b5bc098745f Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Tue, 10 Jul 2018 23:06:15 +0200 +Subject: [PATCH 32/89] ahci: fix PxCI register race +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20180710230616.11000-3-jsnow@redhat.com> +Patchwork-id: 81293 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 2/3] ahci: fix PxCI register race +Bugzilla: 1584914 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Stefan Hajnoczi + +Fixes: https://bugs.launchpad.net/qemu/+bug/1769189 + +AHCI presently signals completion prior to the PxCI register being +cleared to indicate completion. If a guest driver attempts to issue +a new command in its IRQ handler, it might be surprised to learn there +is still a command pending. + +In the case of Windows 10's boot driver, it will actually poll the IRQ +register hoping to find out when the command is done running -- which +will never happen, as there isn't a command running. + +Fix this: clear PxCI in ahci_cmd_done and not in the asynchronous BH. +Because it now runs synchronously, we don't need to check if the command +is actually done by spying on the ATA registers. We know it's done. + +CC: qemu-stable +Reported-by: François Guerraz +Tested-by: Bruce Rogers +Signed-off-by: John Snow +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Jeff Cody +Message-id: 20180531004323.4611-3-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 5694c7eacce6b263ad7497cc1bb76aad746cfd4e) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + hw/ide/ahci.c | 13 ++++++------- + 1 file changed, 6 insertions(+), 7 deletions(-) + +diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c +index b7a6f68..a9558e4 100644 +--- a/hw/ide/ahci.c ++++ b/hw/ide/ahci.c +@@ -532,13 +532,6 @@ static void ahci_check_cmd_bh(void *opaque) + qemu_bh_delete(ad->check_bh); + ad->check_bh = NULL; + +- if ((ad->busy_slot != -1) && +- !(ad->port.ifs[0].status & (BUSY_STAT|DRQ_STAT))) { +- /* no longer busy */ +- ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot); +- ad->busy_slot = -1; +- } +- + check_cmd(ad->hba, ad->port_no); + } + +@@ -1425,6 +1418,12 @@ static void ahci_cmd_done(IDEDMA *dma) + + trace_ahci_cmd_done(ad->hba, ad->port_no); + ++ /* no longer busy */ ++ if (ad->busy_slot != -1) { ++ ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot); ++ ad->busy_slot = -1; ++ } ++ + /* update d2h status */ + ahci_write_fis_d2h(ad); + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-ahci-trim-signatures-on-raise-lower.patch b/SOURCES/kvm-ahci-trim-signatures-on-raise-lower.patch new file mode 100644 index 0000000..7a36f41 --- /dev/null +++ b/SOURCES/kvm-ahci-trim-signatures-on-raise-lower.patch @@ -0,0 +1,66 @@ +From 2b9789734ace2edd99aab3e76a559f29201e0d8d Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Tue, 10 Jul 2018 23:06:14 +0200 +Subject: [PATCH 31/89] ahci: trim signatures on raise/lower + +RH-Author: John Snow +Message-id: <20180710230616.11000-2-jsnow@redhat.com> +Patchwork-id: 81295 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/3] ahci: trim signatures on raise/lower +Bugzilla: 1584914 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Stefan Hajnoczi + +These functions work on the AHCI device, not the individual +AHCI devices, so trim the AHCIDevice argument. + +Signed-off-by: John Snow +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Jeff Cody +Message-id: 20180531004323.4611-2-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit dc5a43eda68fff32c7b0b43847332db325b094f3) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + hw/ide/ahci.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c +index e22d7be..b7a6f68 100644 +--- a/hw/ide/ahci.c ++++ b/hw/ide/ahci.c +@@ -131,7 +131,7 @@ static uint32_t ahci_port_read(AHCIState *s, int port, int offset) + return val; + } + +-static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev) ++static void ahci_irq_raise(AHCIState *s) + { + DeviceState *dev_state = s->container; + PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state), +@@ -146,7 +146,7 @@ static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev) + } + } + +-static void ahci_irq_lower(AHCIState *s, AHCIDevice *dev) ++static void ahci_irq_lower(AHCIState *s) + { + DeviceState *dev_state = s->container; + PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state), +@@ -174,9 +174,9 @@ static void ahci_check_irq(AHCIState *s) + trace_ahci_check_irq(s, old_irq, s->control_regs.irqstatus); + if (s->control_regs.irqstatus && + (s->control_regs.ghc & HOST_CTL_IRQ_EN)) { +- ahci_irq_raise(s, NULL); ++ ahci_irq_raise(s); + } else { +- ahci_irq_lower(s, NULL); ++ ahci_irq_lower(s); + } + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-aio-Do-aio_notify_accept-only-during-blocking-aio_po.patch b/SOURCES/kvm-aio-Do-aio_notify_accept-only-during-blocking-aio_po.patch new file mode 100644 index 0000000..fff781d --- /dev/null +++ b/SOURCES/kvm-aio-Do-aio_notify_accept-only-during-blocking-aio_po.patch @@ -0,0 +1,122 @@ +From a2e385fea51333b9cfbf88b19cbb4f82a5677381 Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Fri, 17 Aug 2018 03:08:36 +0200 +Subject: [PATCH 5/5] aio: Do aio_notify_accept only during blocking aio_poll + +RH-Author: Fam Zheng +Message-id: <20180817030836.20581-3-famz@redhat.com> +Patchwork-id: 81862 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 2/2] aio: Do aio_notify_accept only during blocking aio_poll +Bugzilla: 1562750 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Thomas Huth +RH-Acked-by: Laszlo Ersek + +An aio_notify() pairs with an aio_notify_accept(). The former should +happen in the main thread or a vCPU thread, and the latter should be +done in the IOThread. + +There is one rare case that the main thread or vCPU thread may "steal" +the aio_notify() event just raised by itself, in bdrv_set_aio_context() +[1]. The sequence is like this: + + main thread IO Thread + =============================================================== + bdrv_drained_begin() + aio_disable_external(ctx) + aio_poll(ctx, true) + ctx->notify_me += 2 + ... + bdrv_drained_end() + ... + aio_notify() + ... + bdrv_set_aio_context() + aio_poll(ctx, false) +[1] aio_notify_accept(ctx) + ppoll() /* Hang! */ + +[1] is problematic. It will clear the ctx->notifier event so that +the blocked ppoll() will not return. + +(For the curious, this bug was noticed when booting a number of VMs +simultaneously in RHV. One or two of the VMs will hit this race +condition, making the VIRTIO device unresponsive to I/O commands. When +it hangs, Seabios is busy waiting for a read request to complete (read +MBR), right after initializing the virtio-blk-pci device, using 100% +guest CPU. See also https://bugzilla.redhat.com/show_bug.cgi?id=1562750 +for the original bug analysis.) + +aio_notify() only injects an event when ctx->notify_me is set, +correspondingly aio_notify_accept() is only useful when ctx->notify_me +_was_ set. Move the call to it into the "blocking" branch. This will +effectively skip [1] and fix the hang. + +Furthermore, blocking aio_poll is only allowed on home thread +(in_aio_context_home_thread), because otherwise two blocking +aio_poll()'s can steal each other's ctx->notifier event and cause +hanging just like described above. + +Cc: qemu-stable@nongnu.org +Suggested-by: Paolo Bonzini +Signed-off-by: Fam Zheng +Message-Id: <20180809132259.18402-3-famz@redhat.com> +Signed-off-by: Fam Zheng +(cherry picked from commit b37548fcd1b8ac2e88e185a395bef851f3fc4e65) +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + util/aio-posix.c | 4 ++-- + util/aio-win32.c | 3 ++- + 2 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/util/aio-posix.c b/util/aio-posix.c +index f650c7c..f05d3a8 100644 +--- a/util/aio-posix.c ++++ b/util/aio-posix.c +@@ -591,6 +591,7 @@ bool aio_poll(AioContext *ctx, bool blocking) + * so disable the optimization now. + */ + if (blocking) { ++ assert(in_aio_context_home_thread(ctx)); + atomic_add(&ctx->notify_me, 2); + } + +@@ -633,6 +634,7 @@ bool aio_poll(AioContext *ctx, bool blocking) + + if (blocking) { + atomic_sub(&ctx->notify_me, 2); ++ aio_notify_accept(ctx); + } + + /* Adjust polling time */ +@@ -676,8 +678,6 @@ bool aio_poll(AioContext *ctx, bool blocking) + } + } + +- aio_notify_accept(ctx); +- + /* if we have any readable fds, dispatch event */ + if (ret > 0) { + for (i = 0; i < npfd; i++) { +diff --git a/util/aio-win32.c b/util/aio-win32.c +index a67b00c..ac5524c 100644 +--- a/util/aio-win32.c ++++ b/util/aio-win32.c +@@ -373,11 +373,12 @@ bool aio_poll(AioContext *ctx, bool blocking) + ret = WaitForMultipleObjects(count, events, FALSE, timeout); + if (blocking) { + assert(first); ++ assert(in_aio_context_home_thread(ctx)); + atomic_sub(&ctx->notify_me, 2); ++ aio_notify_accept(ctx); + } + + if (first) { +- aio_notify_accept(ctx); + progress |= aio_bh_poll(ctx); + first = false; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-aio-posix-Don-t-count-ctx-notifier-as-progress-when-.patch b/SOURCES/kvm-aio-posix-Don-t-count-ctx-notifier-as-progress-when-.patch new file mode 100644 index 0000000..a6fd736 --- /dev/null +++ b/SOURCES/kvm-aio-posix-Don-t-count-ctx-notifier-as-progress-when-.patch @@ -0,0 +1,47 @@ +From 8c5d6c7bdc1cd5557503764ff8187d39bed3cbe1 Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Fri, 17 Aug 2018 03:08:35 +0200 +Subject: [PATCH 4/5] aio-posix: Don't count ctx->notifier as progress when + polling + +RH-Author: Fam Zheng +Message-id: <20180817030836.20581-2-famz@redhat.com> +Patchwork-id: 81861 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/2] aio-posix: Don't count ctx->notifier as progress when polling +Bugzilla: 1562750 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Thomas Huth +RH-Acked-by: Laszlo Ersek + +The same logic exists in fd polling. This change is especially important +to avoid busy loop once we limit aio_notify_accept() to blocking +aio_poll(). + +Cc: qemu-stable@nongnu.org +Signed-off-by: Fam Zheng +Message-Id: <20180809132259.18402-2-famz@redhat.com> +Signed-off-by: Fam Zheng +(cherry picked from commit 70232b5253a3c4e03ed1ac47ef9246a8ac66c6fa) +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + util/aio-posix.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/util/aio-posix.c b/util/aio-posix.c +index d8f0cb4..f650c7c 100644 +--- a/util/aio-posix.c ++++ b/util/aio-posix.c +@@ -494,7 +494,8 @@ static bool run_poll_handlers_once(AioContext *ctx) + QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { + if (!node->deleted && node->io_poll && + aio_node_check(ctx, node->is_external) && +- node->io_poll(node->opaque)) { ++ node->io_poll(node->opaque) && ++ node->opaque != &ctx->notifier) { + progress = true; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-aio-posix-compute-timeout-before-polling.patch b/SOURCES/kvm-aio-posix-compute-timeout-before-polling.patch new file mode 100644 index 0000000..3e5b6ed --- /dev/null +++ b/SOURCES/kvm-aio-posix-compute-timeout-before-polling.patch @@ -0,0 +1,182 @@ +From 451a04350505ac3078620cacc1c89389c848e6c5 Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Tue, 18 Sep 2018 09:07:13 +0200 +Subject: [PATCH 2/3] aio-posix: compute timeout before polling + +RH-Author: Fam Zheng +Message-id: <20180918090714.18069-3-famz@redhat.com> +Patchwork-id: 82213 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 2/3] aio-posix: compute timeout before polling +Bugzilla: 1628191 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +From: Paolo Bonzini + +This is a preparation for the next patch, and also a very small +optimization. Compute the timeout only once, before invoking +try_poll_mode, and adjust it in run_poll_handlers. The adjustment +is the polling time when polling fails, or zero (non-blocking) if +polling succeeds. + +Fixes: 70232b5253a3c4e03ed1ac47ef9246a8ac66c6fa +Signed-off-by: Paolo Bonzini +Message-Id: <20180912171040.1732-3-pbonzini@redhat.com> +Reviewed-by: Fam Zheng +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + util/aio-posix.c | 59 ++++++++++++++++++++++++++++++++----------------------- + util/trace-events | 4 ++-- + 2 files changed, 36 insertions(+), 27 deletions(-) + +diff --git a/util/aio-posix.c b/util/aio-posix.c +index 1d7cc53..1b17597 100644 +--- a/util/aio-posix.c ++++ b/util/aio-posix.c +@@ -490,7 +490,7 @@ static void add_pollfd(AioHandler *node) + npfd++; + } + +-static bool run_poll_handlers_once(AioContext *ctx) ++static bool run_poll_handlers_once(AioContext *ctx, int64_t *timeout) + { + bool progress = false; + AioHandler *node; +@@ -500,6 +500,7 @@ static bool run_poll_handlers_once(AioContext *ctx) + aio_node_check(ctx, node->is_external) && + node->io_poll(node->opaque) && + node->opaque != &ctx->notifier) { ++ *timeout = 0; + progress = true; + } + +@@ -522,31 +523,38 @@ static bool run_poll_handlers_once(AioContext *ctx) + * + * Returns: true if progress was made, false otherwise + */ +-static bool run_poll_handlers(AioContext *ctx, int64_t max_ns) ++static bool run_poll_handlers(AioContext *ctx, int64_t max_ns, int64_t *timeout) + { + bool progress; +- int64_t end_time; ++ int64_t start_time, elapsed_time; + + assert(ctx->notify_me); + assert(qemu_lockcnt_count(&ctx->list_lock) > 0); + +- trace_run_poll_handlers_begin(ctx, max_ns); +- +- end_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + max_ns; ++ trace_run_poll_handlers_begin(ctx, max_ns, *timeout); + ++ start_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + do { +- progress = run_poll_handlers_once(ctx); +- } while (!progress && qemu_clock_get_ns(QEMU_CLOCK_REALTIME) < end_time ++ progress = run_poll_handlers_once(ctx, timeout); ++ elapsed_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start_time; ++ } while (!progress && elapsed_time < max_ns + && !atomic_read(&ctx->poll_disable_cnt)); + +- trace_run_poll_handlers_end(ctx, progress); ++ /* 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 +- * @blocking: busy polling is only attempted when blocking is true ++ * @timeout: timeout for blocking wait, computed by the caller and updated if ++ * polling succeeds. + * + * ctx->notify_me must be non-zero so this function can detect aio_notify(). + * +@@ -554,19 +562,16 @@ static bool run_poll_handlers(AioContext *ctx, int64_t max_ns) + * + * Returns: true if progress was made, false otherwise + */ +-static bool try_poll_mode(AioContext *ctx, bool blocking) ++static bool try_poll_mode(AioContext *ctx, int64_t *timeout) + { +- if (blocking && ctx->poll_max_ns && !atomic_read(&ctx->poll_disable_cnt)) { +- /* See qemu_soonest_timeout() uint64_t hack */ +- int64_t max_ns = MIN((uint64_t)aio_compute_timeout(ctx), +- (uint64_t)ctx->poll_ns); ++ /* See qemu_soonest_timeout() uint64_t hack */ ++ int64_t max_ns = MIN((uint64_t)*timeout, (uint64_t)ctx->poll_ns); + +- if (max_ns) { +- poll_set_started(ctx, true); ++ if (max_ns && !atomic_read(&ctx->poll_disable_cnt)) { ++ poll_set_started(ctx, true); + +- if (run_poll_handlers(ctx, max_ns)) { +- return true; +- } ++ if (run_poll_handlers(ctx, max_ns, timeout)) { ++ return true; + } + } + +@@ -575,7 +580,7 @@ static bool try_poll_mode(AioContext *ctx, bool blocking) + /* Even if we don't run busy polling, try polling once in case it can make + * progress and the caller will be able to avoid ppoll(2)/epoll_wait(2). + */ +- return run_poll_handlers_once(ctx); ++ return run_poll_handlers_once(ctx, timeout); + } + + bool aio_poll(AioContext *ctx, bool blocking) +@@ -605,8 +610,14 @@ bool aio_poll(AioContext *ctx, bool blocking) + start = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + } + +- progress = try_poll_mode(ctx, blocking); +- if (!progress) { ++ timeout = blocking ? aio_compute_timeout(ctx) : 0; ++ progress = try_poll_mode(ctx, &timeout); ++ assert(!(timeout && progress)); ++ ++ /* 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 || atomic_read(&ctx->poll_disable_cnt)) { + assert(npfd == 0); + + /* fill pollfds */ +@@ -620,8 +631,6 @@ bool aio_poll(AioContext *ctx, bool blocking) + } + } + +- timeout = blocking ? aio_compute_timeout(ctx) : 0; +- + /* wait until next event */ + if (aio_epoll_check_poll(ctx, pollfds, npfd, timeout)) { + AioHandler epoll_handler; +diff --git a/util/trace-events b/util/trace-events +index 4822434..79569b7 100644 +--- a/util/trace-events ++++ b/util/trace-events +@@ -1,8 +1,8 @@ + # See docs/devel/tracing.txt for syntax documentation. + + # util/aio-posix.c +-run_poll_handlers_begin(void *ctx, int64_t max_ns) "ctx %p max_ns %"PRId64 +-run_poll_handlers_end(void *ctx, bool progress) "ctx %p progress %d" ++run_poll_handlers_begin(void *ctx, int64_t max_ns, int64_t timeout) "ctx %p max_ns %"PRId64 " timeout %"PRId64 ++run_poll_handlers_end(void *ctx, bool progress, int64_t timeout) "ctx %p progress %d new timeout %"PRId64 + poll_shrink(void *ctx, int64_t old, int64_t new) "ctx %p old %"PRId64" new %"PRId64 + poll_grow(void *ctx, int64_t old, int64_t new) "ctx %p old %"PRId64" new %"PRId64 + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-aio-posix-do-skip-system-call-if-ctx-notifier-pollin.patch b/SOURCES/kvm-aio-posix-do-skip-system-call-if-ctx-notifier-pollin.patch new file mode 100644 index 0000000..019141c --- /dev/null +++ b/SOURCES/kvm-aio-posix-do-skip-system-call-if-ctx-notifier-pollin.patch @@ -0,0 +1,61 @@ +From 189e720e9fa5536fb22960f158b3b7f91dcccf2c Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Tue, 18 Sep 2018 09:07:14 +0200 +Subject: [PATCH 3/3] aio-posix: do skip system call if ctx->notifier polling + succeeds + +RH-Author: Fam Zheng +Message-id: <20180918090714.18069-4-famz@redhat.com> +Patchwork-id: 82214 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 3/3] aio-posix: do skip system call if ctx->notifier polling succeeds +Bugzilla: 1628191 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +From: Paolo Bonzini + +Commit 70232b5253 ("aio-posix: Don't count ctx->notifier as progress when +2018-08-15), by not reporting progress, causes aio_poll to execute the +system call when polling succeeds because of ctx->notifier. This introduces +latency before the call to aio_bh_poll() and negates the advantages of +polling, unfortunately. + +The fix builds on the previous patch, separating the effect of polling on +the timeout from the progress reported to aio_poll(). ctx->notifier +does zero the timeout, causing the caller to skip the system call, +but it does not report progress, so that the bug fix of commit 70232b5253 +still stands. + +Fixes: 70232b5253a3c4e03ed1ac47ef9246a8ac66c6fa +Signed-off-by: Paolo Bonzini +Message-Id: <20180912171040.1732-4-pbonzini@redhat.com> +Reviewed-by: Fam Zheng +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + util/aio-posix.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/util/aio-posix.c b/util/aio-posix.c +index 1b17597..357de8a 100644 +--- a/util/aio-posix.c ++++ b/util/aio-posix.c +@@ -498,10 +498,11 @@ static bool run_poll_handlers_once(AioContext *ctx, int64_t *timeout) + QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { + if (!node->deleted && node->io_poll && + aio_node_check(ctx, node->is_external) && +- node->io_poll(node->opaque) && +- node->opaque != &ctx->notifier) { ++ node->io_poll(node->opaque)) { + *timeout = 0; +- progress = true; ++ if (node->opaque != &ctx->notifier) { ++ progress = true; ++ } + } + + /* Caller handles freeing deleted nodes. Don't do it here. */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-aio-posix-fix-concurrent-access-to-poll_disable_cnt.patch b/SOURCES/kvm-aio-posix-fix-concurrent-access-to-poll_disable_cnt.patch new file mode 100644 index 0000000..13cc84a --- /dev/null +++ b/SOURCES/kvm-aio-posix-fix-concurrent-access-to-poll_disable_cnt.patch @@ -0,0 +1,118 @@ +From a9becae1ae86e699ee4a606ff2319799a9fda48c Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Tue, 18 Sep 2018 09:07:12 +0200 +Subject: [PATCH 1/3] aio-posix: fix concurrent access to poll_disable_cnt + +RH-Author: Fam Zheng +Message-id: <20180918090714.18069-2-famz@redhat.com> +Patchwork-id: 82215 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/3] aio-posix: fix concurrent access to poll_disable_cnt +Bugzilla: 1628191 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +From: Paolo Bonzini + +It is valid for an aio_set_fd_handler to happen concurrently with +aio_poll. In that case, poll_disable_cnt can change under the heels +of aio_poll, and the assertion on poll_disable_cnt can fail in +run_poll_handlers. + +Therefore, this patch simply checks the counter on every polling +iteration. There are no particular needs for ordering, since the +polling loop is terminated anyway by aio_notify at the end of +aio_set_fd_handler. + +Signed-off-by: Paolo Bonzini +Message-Id: <20180912171040.1732-2-pbonzini@redhat.com> +Reviewed-by: Fam Zheng +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + util/aio-posix.c | 26 +++++++++++++++----------- + 1 file changed, 15 insertions(+), 11 deletions(-) + +diff --git a/util/aio-posix.c b/util/aio-posix.c +index f05d3a8..1d7cc53 100644 +--- a/util/aio-posix.c ++++ b/util/aio-posix.c +@@ -211,6 +211,7 @@ void aio_set_fd_handler(AioContext *ctx, + AioHandler *node; + bool is_new = false; + bool deleted = false; ++ int poll_disable_change; + + qemu_lockcnt_lock(&ctx->list_lock); + +@@ -244,11 +245,9 @@ void aio_set_fd_handler(AioContext *ctx, + QLIST_REMOVE(node, node); + deleted = true; + } +- +- if (!node->io_poll) { +- ctx->poll_disable_cnt--; +- } ++ poll_disable_change = -!node->io_poll; + } else { ++ poll_disable_change = !io_poll - (node && !node->io_poll); + if (node == NULL) { + /* Alloc and insert if it's not already there */ + node = g_new0(AioHandler, 1); +@@ -257,10 +256,6 @@ void aio_set_fd_handler(AioContext *ctx, + + g_source_add_poll(&ctx->source, &node->pfd); + is_new = true; +- +- ctx->poll_disable_cnt += !io_poll; +- } else { +- ctx->poll_disable_cnt += !io_poll - !node->io_poll; + } + + /* Update handler with latest information */ +@@ -274,6 +269,15 @@ void aio_set_fd_handler(AioContext *ctx, + node->pfd.events |= (io_write ? G_IO_OUT | G_IO_ERR : 0); + } + ++ /* 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. ++ */ ++ atomic_set(&ctx->poll_disable_cnt, ++ atomic_read(&ctx->poll_disable_cnt) + poll_disable_change); ++ + aio_epoll_update(ctx, node, is_new); + qemu_lockcnt_unlock(&ctx->list_lock); + aio_notify(ctx); +@@ -525,7 +529,6 @@ static bool run_poll_handlers(AioContext *ctx, int64_t max_ns) + + assert(ctx->notify_me); + assert(qemu_lockcnt_count(&ctx->list_lock) > 0); +- assert(ctx->poll_disable_cnt == 0); + + trace_run_poll_handlers_begin(ctx, max_ns); + +@@ -533,7 +536,8 @@ static bool run_poll_handlers(AioContext *ctx, int64_t max_ns) + + do { + progress = run_poll_handlers_once(ctx); +- } while (!progress && qemu_clock_get_ns(QEMU_CLOCK_REALTIME) < end_time); ++ } while (!progress && qemu_clock_get_ns(QEMU_CLOCK_REALTIME) < end_time ++ && !atomic_read(&ctx->poll_disable_cnt)); + + trace_run_poll_handlers_end(ctx, progress); + +@@ -552,7 +556,7 @@ static bool run_poll_handlers(AioContext *ctx, int64_t max_ns) + */ + static bool try_poll_mode(AioContext *ctx, bool blocking) + { +- if (blocking && ctx->poll_max_ns && ctx->poll_disable_cnt == 0) { ++ if (blocking && ctx->poll_max_ns && !atomic_read(&ctx->poll_disable_cnt)) { + /* See qemu_soonest_timeout() uint64_t hack */ + int64_t max_ns = MIN((uint64_t)aio_compute_timeout(ctx), + (uint64_t)ctx->poll_ns); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-aio-wait-Increase-num_waiters-even-in-home-thread.patch b/SOURCES/kvm-aio-wait-Increase-num_waiters-even-in-home-thread.patch new file mode 100644 index 0000000..6f65743 --- /dev/null +++ b/SOURCES/kvm-aio-wait-Increase-num_waiters-even-in-home-thread.patch @@ -0,0 +1,62 @@ +From efb378ef6983c9f7cc7cdb2cc0540a52b05ff916 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:26 +0200 +Subject: [PATCH 35/49] aio-wait: Increase num_waiters even in home thread + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-29-kwolf@redhat.com> +Patchwork-id: 82179 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 28/42] aio-wait: Increase num_waiters even in home thread +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Even if AIO_WAIT_WHILE() is called in the home context of the +AioContext, we still want to allow the condition to change depending on +other threads as long as they kick the AioWait. Specfically block jobs +can be running in an I/O thread and should then be able to kick a drain +in the main loop context. + +Signed-off-by: Kevin Wolf +Reviewed-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + include/block/aio-wait.h | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h +index c85a62f..600fad1 100644 +--- a/include/block/aio-wait.h ++++ b/include/block/aio-wait.h +@@ -76,6 +76,8 @@ typedef struct { + bool waited_ = false; \ + AioWait *wait_ = (wait); \ + AioContext *ctx_ = (ctx); \ ++ /* Increment wait_->num_waiters before evaluating cond. */ \ ++ atomic_inc(&wait_->num_waiters); \ + if (ctx_ && in_aio_context_home_thread(ctx_)) { \ + while ((cond)) { \ + aio_poll(ctx_, true); \ +@@ -84,8 +86,6 @@ typedef struct { + } else { \ + assert(qemu_get_current_aio_context() == \ + qemu_get_aio_context()); \ +- /* Increment wait_->num_waiters before evaluating cond. */ \ +- atomic_inc(&wait_->num_waiters); \ + while ((cond)) { \ + if (ctx_) { \ + aio_context_release(ctx_); \ +@@ -96,8 +96,8 @@ typedef struct { + } \ + waited_ = true; \ + } \ +- atomic_dec(&wait_->num_waiters); \ + } \ ++ atomic_dec(&wait_->num_waiters); \ + waited_; }) + + /** +-- +1.8.3.1 + diff --git a/SOURCES/kvm-backup-Use-copy-offloading.patch b/SOURCES/kvm-backup-Use-copy-offloading.patch new file mode 100644 index 0000000..857fb6c --- /dev/null +++ b/SOURCES/kvm-backup-Use-copy-offloading.patch @@ -0,0 +1,260 @@ +From 6e2070840430f3009b201976b0a10a9d627adfed Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:54:52 +0200 +Subject: [PATCH 67/89] backup: Use copy offloading + +RH-Author: John Snow +Message-id: <20180718225511.14878-17-jsnow@redhat.com> +Patchwork-id: 81399 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 16/35] backup: Use copy offloading +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Fam Zheng + +The implementation is similar to the 'qemu-img convert'. In the +beginning of the job, offloaded copy is attempted. If it fails, further +I/O will go through the existing bounce buffer code path. + +Then, as Kevin pointed out, both this and qemu-img convert can benefit +from a local check if one request fails because of, for example, the +offset is beyond EOF, but another may well be accepted by the protocol +layer. This will be implemented separately. + +Reviewed-by: Stefan Hajnoczi +Signed-off-by: Fam Zheng +Message-id: 20180703023758.14422-4-famz@redhat.com +Signed-off-by: Jeff Cody +(cherry picked from commit 9ded4a0114968e98b41494fc035ba14f84cdf700) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 150 ++++++++++++++++++++++++++++++++++++++--------------- + block/trace-events | 1 + + 2 files changed, 110 insertions(+), 41 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index 5661435..d26eeb5 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -45,6 +45,8 @@ typedef struct BackupBlockJob { + QLIST_HEAD(, CowRequest) inflight_reqs; + + HBitmap *copy_bitmap; ++ bool use_copy_range; ++ int64_t copy_range_size; + } BackupBlockJob; + + static const BlockJobDriver backup_job_driver; +@@ -86,19 +88,101 @@ static void cow_request_end(CowRequest *req) + qemu_co_queue_restart_all(&req->wait_queue); + } + ++/* Copy range to target with a bounce buffer and return the bytes copied. If ++ * error occured, return a negative error number */ ++static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, ++ int64_t start, ++ int64_t end, ++ bool is_write_notifier, ++ bool *error_is_read, ++ void **bounce_buffer) ++{ ++ int ret; ++ struct iovec iov; ++ QEMUIOVector qiov; ++ BlockBackend *blk = job->common.blk; ++ int nbytes; ++ ++ hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1); ++ nbytes = MIN(job->cluster_size, job->len - start); ++ if (!*bounce_buffer) { ++ *bounce_buffer = blk_blockalign(blk, job->cluster_size); ++ } ++ iov.iov_base = *bounce_buffer; ++ iov.iov_len = nbytes; ++ qemu_iovec_init_external(&qiov, &iov, 1); ++ ++ ret = blk_co_preadv(blk, start, qiov.size, &qiov, ++ is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0); ++ if (ret < 0) { ++ trace_backup_do_cow_read_fail(job, start, ret); ++ if (error_is_read) { ++ *error_is_read = true; ++ } ++ goto fail; ++ } ++ ++ if (qemu_iovec_is_zero(&qiov)) { ++ ret = blk_co_pwrite_zeroes(job->target, start, ++ qiov.size, BDRV_REQ_MAY_UNMAP); ++ } else { ++ ret = blk_co_pwritev(job->target, start, ++ qiov.size, &qiov, ++ job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0); ++ } ++ if (ret < 0) { ++ trace_backup_do_cow_write_fail(job, start, ret); ++ if (error_is_read) { ++ *error_is_read = false; ++ } ++ goto fail; ++ } ++ ++ return nbytes; ++fail: ++ hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1); ++ return ret; ++ ++} ++ ++/* Copy range to target and return the bytes copied. If error occured, return a ++ * negative error number. */ ++static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job, ++ int64_t start, ++ int64_t end, ++ bool is_write_notifier) ++{ ++ int ret; ++ int nr_clusters; ++ BlockBackend *blk = job->common.blk; ++ int nbytes; ++ ++ assert(QEMU_IS_ALIGNED(job->copy_range_size, job->cluster_size)); ++ nbytes = MIN(job->copy_range_size, end - start); ++ nr_clusters = DIV_ROUND_UP(nbytes, job->cluster_size); ++ hbitmap_reset(job->copy_bitmap, start / job->cluster_size, ++ nr_clusters); ++ ret = blk_co_copy_range(blk, start, job->target, start, nbytes, ++ is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0); ++ if (ret < 0) { ++ trace_backup_do_cow_copy_range_fail(job, start, ret); ++ hbitmap_set(job->copy_bitmap, start / job->cluster_size, ++ nr_clusters); ++ return ret; ++ } ++ ++ return nbytes; ++} ++ + static int coroutine_fn backup_do_cow(BackupBlockJob *job, + int64_t offset, uint64_t bytes, + bool *error_is_read, + bool is_write_notifier) + { +- BlockBackend *blk = job->common.blk; + CowRequest cow_request; +- struct iovec iov; +- QEMUIOVector bounce_qiov; +- void *bounce_buffer = NULL; + int ret = 0; + int64_t start, end; /* bytes */ +- int n; /* bytes */ ++ void *bounce_buffer = NULL; + + qemu_co_rwlock_rdlock(&job->flush_rwlock); + +@@ -110,60 +194,38 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job, + wait_for_overlapping_requests(job, start, end); + cow_request_begin(&cow_request, job, start, end); + +- for (; start < end; start += job->cluster_size) { ++ while (start < end) { + if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) { + trace_backup_do_cow_skip(job, start); ++ start += job->cluster_size; + continue; /* already copied */ + } +- hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1); + + trace_backup_do_cow_process(job, start); + +- n = MIN(job->cluster_size, job->len - start); +- +- if (!bounce_buffer) { +- bounce_buffer = blk_blockalign(blk, job->cluster_size); +- } +- iov.iov_base = bounce_buffer; +- iov.iov_len = n; +- qemu_iovec_init_external(&bounce_qiov, &iov, 1); +- +- ret = blk_co_preadv(blk, start, bounce_qiov.size, &bounce_qiov, +- is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0); +- if (ret < 0) { +- trace_backup_do_cow_read_fail(job, start, ret); +- if (error_is_read) { +- *error_is_read = true; ++ if (job->use_copy_range) { ++ ret = backup_cow_with_offload(job, start, end, is_write_notifier); ++ if (ret < 0) { ++ job->use_copy_range = false; + } +- hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1); +- goto out; + } +- +- if (buffer_is_zero(iov.iov_base, iov.iov_len)) { +- ret = blk_co_pwrite_zeroes(job->target, start, +- bounce_qiov.size, BDRV_REQ_MAY_UNMAP); +- } else { +- ret = blk_co_pwritev(job->target, start, +- bounce_qiov.size, &bounce_qiov, +- job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0); ++ if (!job->use_copy_range) { ++ ret = backup_cow_with_bounce_buffer(job, start, end, is_write_notifier, ++ error_is_read, &bounce_buffer); + } + if (ret < 0) { +- trace_backup_do_cow_write_fail(job, start, ret); +- if (error_is_read) { +- *error_is_read = false; +- } +- hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1); +- goto out; ++ break; + } + + /* Publish progress, guest I/O counts as progress too. Note that the + * offset field is an opaque progress value, it is not a disk offset. + */ +- job->bytes_read += n; +- job_progress_update(&job->common.job, n); ++ start += ret; ++ job->bytes_read += ret; ++ job_progress_update(&job->common.job, ret); ++ ret = 0; + } + +-out: + if (bounce_buffer) { + qemu_vfree(bounce_buffer); + } +@@ -665,6 +727,12 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, + } else { + job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size); + } ++ job->use_copy_range = true; ++ job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk), ++ blk_get_max_transfer(job->target)); ++ job->copy_range_size = MAX(job->cluster_size, ++ QEMU_ALIGN_UP(job->copy_range_size, ++ job->cluster_size)); + + /* Required permissions are already taken with target's blk_new() */ + block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, +diff --git a/block/trace-events b/block/trace-events +index 2d59b53..c35287b 100644 +--- a/block/trace-events ++++ b/block/trace-events +@@ -42,6 +42,7 @@ backup_do_cow_skip(void *job, int64_t start) "job %p start %"PRId64 + backup_do_cow_process(void *job, int64_t start) "job %p start %"PRId64 + backup_do_cow_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d" + backup_do_cow_write_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d" ++backup_do_cow_copy_range_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d" + + # blockdev.c + qmp_block_job_cancel(void *job) "job %p" +-- +1.8.3.1 + diff --git a/SOURCES/kvm-balloon-Allow-multiple-inhibit-users.patch b/SOURCES/kvm-balloon-Allow-multiple-inhibit-users.patch new file mode 100644 index 0000000..a56812b --- /dev/null +++ b/SOURCES/kvm-balloon-Allow-multiple-inhibit-users.patch @@ -0,0 +1,72 @@ +From d3e302f2ab9543e1fb52c9d7b6c2167d054a62f5 Mon Sep 17 00:00:00 2001 +From: Alex Williamson +Date: Mon, 3 Dec 2018 21:52:37 +0100 +Subject: [PATCH 18/34] balloon: Allow multiple inhibit users + +RH-Author: Alex Williamson +Message-id: <154387395708.26945.11708135028435449926.stgit@gimli.home> +Patchwork-id: 83227 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/7] balloon: Allow multiple inhibit users +Bugzilla: 1619778 +RH-Acked-by: Peter Xu +RH-Acked-by: Cornelia Huck +RH-Acked-by: Auger Eric +RH-Acked-by: David Hildenbrand + +Bugzilla: 1619778 + +A simple true/false internal state does not allow multiple users. Fix +this within the existing interface by converting to a counter, so long +as the counter is elevated, ballooning is inhibited. + +Reviewed-by: David Hildenbrand +Reviewed-by: Peter Xu +Reviewed-by: Cornelia Huck +Signed-off-by: Alex Williamson +(cherry picked from commit 01ccbec7bdf6f89f1b7d46dda05e4c1fd2dd5ade) +Signed-off-by: Miroslav Rezanina +--- + balloon.c | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +diff --git a/balloon.c b/balloon.c +index 6bf0a96..9319879 100644 +--- a/balloon.c ++++ b/balloon.c +@@ -26,6 +26,7 @@ + + #include "qemu/osdep.h" + #include "qemu-common.h" ++#include "qemu/atomic.h" + #include "exec/cpu-common.h" + #include "sysemu/kvm.h" + #include "sysemu/balloon.h" +@@ -37,16 +38,22 @@ + static QEMUBalloonEvent *balloon_event_fn; + static QEMUBalloonStatus *balloon_stat_fn; + static void *balloon_opaque; +-static bool balloon_inhibited; ++static int balloon_inhibit_count; + + bool qemu_balloon_is_inhibited(void) + { +- return balloon_inhibited; ++ return atomic_read(&balloon_inhibit_count) > 0; + } + + void qemu_balloon_inhibit(bool state) + { +- balloon_inhibited = state; ++ if (state) { ++ atomic_inc(&balloon_inhibit_count); ++ } else { ++ atomic_dec(&balloon_inhibit_count); ++ } ++ ++ assert(atomic_read(&balloon_inhibit_count) >= 0); + } + + static bool have_balloon(Error **errp) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-bdrv_query_image_info-Error-parameter-added.patch b/SOURCES/kvm-bdrv_query_image_info-Error-parameter-added.patch new file mode 100644 index 0000000..d10454a --- /dev/null +++ b/SOURCES/kvm-bdrv_query_image_info-Error-parameter-added.patch @@ -0,0 +1,202 @@ +From d48c917f7bcc56fce488c129884abee39dbf0df4 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 21:48:36 +0100 +Subject: [PATCH 043/163] bdrv_query_image_info Error parameter added + +RH-Author: John Snow +Message-id: <20190320214838.22027-9-jsnow@redhat.com> +Patchwork-id: 85002 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 08/10] bdrv_query_image_info Error parameter added +Bugzilla: 1691048 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Andrey Shinkevich + +Inform a user in case qcow2_get_specific_info fails to obtain +QCOW2 image specific information. This patch is preliminary to +the one "qcow2: Add list of bitmaps to ImageInfoSpecificQCow2". + +Signed-off-by: Andrey Shinkevich +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: Kevin Wolf +Message-Id: <1549638368-530182-2-git-send-email-andrey.shinkevich@virtuozzo.com> +Signed-off-by: Eric Blake +(cherry picked from commit 1bf6e9ca9234e1dbcaa18baa06eca9d55cc2dbbb) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block.c | 5 +++-- + block/crypto.c | 9 +++------ + block/qapi.c | 7 ++++++- + block/qcow2.c | 10 ++++++++-- + block/vmdk.c | 3 ++- + include/block/block.h | 3 ++- + include/block/block_int.h | 3 ++- + qemu-io-cmds.c | 7 ++++++- + 8 files changed, 32 insertions(+), 15 deletions(-) + +diff --git a/block.c b/block.c +index bcf277d..e3e0e34 100644 +--- a/block.c ++++ b/block.c +@@ -4204,11 +4204,12 @@ int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) + return drv->bdrv_get_info(bs, bdi); + } + +-ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs) ++ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs, ++ Error **errp) + { + BlockDriver *drv = bs->drv; + if (drv && drv->bdrv_get_specific_info) { +- return drv->bdrv_get_specific_info(bs); ++ return drv->bdrv_get_specific_info(bs, errp); + } + return NULL; + } +diff --git a/block/crypto.c b/block/crypto.c +index 0bb0db6..fd88782 100644 +--- a/block/crypto.c ++++ b/block/crypto.c +@@ -664,20 +664,17 @@ static int block_crypto_get_info_luks(BlockDriverState *bs, + } + + static ImageInfoSpecific * +-block_crypto_get_specific_info_luks(BlockDriverState *bs) ++block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp) + { + BlockCrypto *crypto = bs->opaque; + ImageInfoSpecific *spec_info; + QCryptoBlockInfo *info; + +- info = qcrypto_block_get_info(crypto->block, NULL); ++ info = qcrypto_block_get_info(crypto->block, errp); + if (!info) { + return NULL; + } +- if (info->format != Q_CRYPTO_BLOCK_FORMAT_LUKS) { +- qapi_free_QCryptoBlockInfo(info); +- return NULL; +- } ++ assert(info->format == Q_CRYPTO_BLOCK_FORMAT_LUKS); + + spec_info = g_new(ImageInfoSpecific, 1); + spec_info->type = IMAGE_INFO_SPECIFIC_KIND_LUKS; +diff --git a/block/qapi.c b/block/qapi.c +index 339727f..42ce4a3 100644 +--- a/block/qapi.c ++++ b/block/qapi.c +@@ -282,7 +282,12 @@ void bdrv_query_image_info(BlockDriverState *bs, + info->dirty_flag = bdi.is_dirty; + info->has_dirty_flag = true; + } +- info->format_specific = bdrv_get_specific_info(bs); ++ info->format_specific = bdrv_get_specific_info(bs, &err); ++ if (err) { ++ error_propagate(errp, err); ++ qapi_free_ImageInfo(info); ++ goto out; ++ } + info->has_format_specific = info->format_specific != NULL; + + backing_filename = bs->backing_file; +diff --git a/block/qcow2.c b/block/qcow2.c +index 114dcdd..de9872f 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -4166,14 +4166,20 @@ static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) + return 0; + } + +-static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs) ++static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, ++ Error **errp) + { + BDRVQcow2State *s = bs->opaque; + ImageInfoSpecific *spec_info; + QCryptoBlockInfo *encrypt_info = NULL; ++ Error *local_err = NULL; + + if (s->crypto != NULL) { +- encrypt_info = qcrypto_block_get_info(s->crypto, &error_abort); ++ encrypt_info = qcrypto_block_get_info(s->crypto, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return NULL; ++ } + } + + spec_info = g_new(ImageInfoSpecific, 1); +diff --git a/block/vmdk.c b/block/vmdk.c +index 84f8bbe..f52f291 100644 +--- a/block/vmdk.c ++++ b/block/vmdk.c +@@ -2287,7 +2287,8 @@ static int coroutine_fn vmdk_co_check(BlockDriverState *bs, + return ret; + } + +-static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs) ++static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs, ++ Error **errp) + { + int i; + BDRVVmdkState *s = bs->opaque; +diff --git a/include/block/block.h b/include/block/block.h +index 36a702c..5f40140 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -472,7 +472,8 @@ const char *bdrv_get_device_name(const BlockDriverState *bs); + const char *bdrv_get_device_or_node_name(const BlockDriverState *bs); + int bdrv_get_flags(BlockDriverState *bs); + int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); +-ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs); ++ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs, ++ Error **errp); + void bdrv_round_to_clusters(BlockDriverState *bs, + int64_t offset, int64_t bytes, + int64_t *cluster_offset, +diff --git a/include/block/block_int.h b/include/block/block_int.h +index f457acb..f241bd9 100644 +--- a/include/block/block_int.h ++++ b/include/block/block_int.h +@@ -321,7 +321,8 @@ struct BlockDriver { + const char *name, + Error **errp); + int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi); +- ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs); ++ ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs, ++ Error **errp); + + int coroutine_fn (*bdrv_save_vmstate)(BlockDriverState *bs, + QEMUIOVector *qiov, +diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c +index 5bf5f28..9c51e57 100644 +--- a/qemu-io-cmds.c ++++ b/qemu-io-cmds.c +@@ -1660,6 +1660,7 @@ static int info_f(BlockBackend *blk, int argc, char **argv) + BlockDriverState *bs = blk_bs(blk); + BlockDriverInfo bdi; + ImageInfoSpecific *spec_info; ++ Error *local_err = NULL; + char s1[64], s2[64]; + int ret; + +@@ -1681,7 +1682,11 @@ static int info_f(BlockBackend *blk, int argc, char **argv) + printf("cluster size: %s\n", s1); + printf("vm state offset: %s\n", s2); + +- spec_info = bdrv_get_specific_info(bs); ++ spec_info = bdrv_get_specific_info(bs, &local_err); ++ if (local_err) { ++ error_report_err(local_err); ++ return -EIO; ++ } + if (spec_info) { + printf("Format specific information:\n"); + bdrv_image_info_specific_dump(fprintf, stdout, spec_info); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-bitmap-Update-count-after-a-merge.patch b/SOURCES/kvm-bitmap-Update-count-after-a-merge.patch new file mode 100644 index 0000000..a8f8520 --- /dev/null +++ b/SOURCES/kvm-bitmap-Update-count-after-a-merge.patch @@ -0,0 +1,56 @@ +From 56152f8d7cb931e6172a9e44bec7fe274716a240 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:37 +0100 +Subject: [PATCH 27/33] bitmap: Update count after a merge + +RH-Author: John Snow +Message-id: <20190206221243.7407-18-jsnow@redhat.com> +Patchwork-id: 84282 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 17/23] bitmap: Update count after a merge +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +From: Eric Blake + +We need an accurate count of the number of bits set in a bitmap +after a merge. In particular, since the merge operation short-circuits +a merge from an empty source, if you have bitmaps A, B, and C where +B started empty, then merge C into B, and B into A, an inaccurate +count meant that A did not get the contents of C. + +In the worst case, we may falsely regard the bitmap as empty when +it has had new writes merged into it. + +Fixes: be58721db +CC: qemu-stable@nongnu.org +Signed-off-by: Eric Blake +Signed-off-by: John Snow +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20181002233314.30159-1-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit d1dde7149e376d72b422a529ec4bf3ed47f3ba30) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + util/hbitmap.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/util/hbitmap.c b/util/hbitmap.c +index d5aca51..8d402c5 100644 +--- a/util/hbitmap.c ++++ b/util/hbitmap.c +@@ -759,6 +759,9 @@ bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result) + } + } + ++ /* Recompute the dirty count */ ++ result->count = hb_count_between(result, 0, result->size - 1); ++ + return true; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-bitmaps-Fix-typo-in-function-name.patch b/SOURCES/kvm-bitmaps-Fix-typo-in-function-name.patch new file mode 100644 index 0000000..f44c5d6 --- /dev/null +++ b/SOURCES/kvm-bitmaps-Fix-typo-in-function-name.patch @@ -0,0 +1,155 @@ +From e7e2710ac920bb8c62d0b4bb9b95700a087509b5 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 18:18:56 +0200 +Subject: [PATCH 151/163] bitmaps: Fix typo in function name + +RH-Author: John Snow +Message-id: <20190403181857.9693-21-jsnow@redhat.com> +Patchwork-id: 85430 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 20/21] bitmaps: Fix typo in function name +Bugzilla: 1677073 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Sergio Lopez Pascual + +From: Eric Blake + +Commit a88b179f introduced the ability to set and query bitmap +persistence, but with an atypical spelling. + +Signed-off-by: Eric Blake +Message-id: 20190308205845.25734-1-eblake@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 796a3798ab882ae78a8203acd335ded4e10e3afb) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 4 ++-- + block/qcow2-bitmap.c | 6 +++--- + blockdev.c | 4 ++-- + include/block/dirty-bitmap.h | 4 ++-- + migration/block-dirty-bitmap.c | 4 ++-- + 5 files changed, 11 insertions(+), 11 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index 2139354..59e6ebb 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -740,7 +740,7 @@ bool bdrv_has_readonly_bitmaps(BlockDriverState *bs) + } + + /* Called with BQL taken. */ +-void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent) ++void bdrv_dirty_bitmap_set_persistence(BdrvDirtyBitmap *bitmap, bool persistent) + { + qemu_mutex_lock(bitmap->mutex); + bitmap->persistent = persistent; +@@ -765,7 +765,7 @@ void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration) + qemu_mutex_unlock(bitmap->mutex); + } + +-bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap) ++bool bdrv_dirty_bitmap_get_persistence(BdrvDirtyBitmap *bitmap) + { + return bitmap->persistent && !bitmap->migration; + } +diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c +index cbab0e5..4d093da 100644 +--- a/block/qcow2-bitmap.c ++++ b/block/qcow2-bitmap.c +@@ -970,7 +970,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) + goto fail; + } + +- bdrv_dirty_bitmap_set_persistance(bitmap, true); ++ bdrv_dirty_bitmap_set_persistence(bitmap, true); + if (bm->flags & BME_FLAG_IN_USE) { + bdrv_dirty_bitmap_set_inconsistent(bitmap); + } else { +@@ -1428,7 +1428,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp) + uint32_t granularity = bdrv_dirty_bitmap_granularity(bitmap); + Qcow2Bitmap *bm; + +- if (!bdrv_dirty_bitmap_get_persistance(bitmap) || ++ if (!bdrv_dirty_bitmap_get_persistence(bitmap) || + bdrv_dirty_bitmap_readonly(bitmap) || + bdrv_dirty_bitmap_inconsistent(bitmap)) { + continue; +@@ -1546,7 +1546,7 @@ int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp) + for (bitmap = bdrv_dirty_bitmap_next(bs, NULL); bitmap != NULL; + bitmap = bdrv_dirty_bitmap_next(bs, bitmap)) + { +- if (bdrv_dirty_bitmap_get_persistance(bitmap)) { ++ if (bdrv_dirty_bitmap_get_persistence(bitmap)) { + bdrv_dirty_bitmap_set_readonly(bitmap, true); + } + } +diff --git a/blockdev.c b/blockdev.c +index e497939..61218b4 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3014,7 +3014,7 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, + bdrv_disable_dirty_bitmap(bitmap); + } + +- bdrv_dirty_bitmap_set_persistance(bitmap, persistent); ++ bdrv_dirty_bitmap_set_persistence(bitmap, persistent); + out: + if (aio_context) { + aio_context_release(aio_context); +@@ -3039,7 +3039,7 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name, + return; + } + +- if (bdrv_dirty_bitmap_get_persistance(bitmap)) { ++ if (bdrv_dirty_bitmap_get_persistence(bitmap)) { + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + bdrv_remove_persistent_dirty_bitmap(bs, name, &local_err); +diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h +index 2a78243..8044ace 100644 +--- a/include/block/dirty-bitmap.h ++++ b/include/block/dirty-bitmap.h +@@ -78,7 +78,7 @@ void bdrv_dirty_bitmap_deserialize_ones(BdrvDirtyBitmap *bitmap, + void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap); + + void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value); +-void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, ++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); +@@ -103,7 +103,7 @@ 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_dirty_bitmap_get_autoload(const BdrvDirtyBitmap *bitmap); +-bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap); ++bool bdrv_dirty_bitmap_get_persistence(BdrvDirtyBitmap *bitmap); + bool bdrv_dirty_bitmap_inconsistent(const BdrvDirtyBitmap *bitmap); + bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs); + BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs, +diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c +index 06ab58d..3beac29 100644 +--- a/migration/block-dirty-bitmap.c ++++ b/migration/block-dirty-bitmap.c +@@ -321,7 +321,7 @@ static int init_dirty_bitmap_migration(void) + if (bdrv_dirty_bitmap_enabled(bitmap)) { + dbms->flags |= DIRTY_BITMAP_MIG_START_FLAG_ENABLED; + } +- if (bdrv_dirty_bitmap_get_persistance(bitmap)) { ++ if (bdrv_dirty_bitmap_get_persistence(bitmap)) { + dbms->flags |= DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT; + } + +@@ -473,7 +473,7 @@ static int dirty_bitmap_load_start(QEMUFile *f, DirtyBitmapLoadState *s) + } + + if (flags & DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT) { +- bdrv_dirty_bitmap_set_persistance(s->bitmap, true); ++ bdrv_dirty_bitmap_set_persistence(s->bitmap, true); + } + + bdrv_disable_dirty_bitmap(s->bitmap); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-bloc-qcow2-drop-dirty_bitmaps_loaded-state-variable.patch b/SOURCES/kvm-bloc-qcow2-drop-dirty_bitmaps_loaded-state-variable.patch new file mode 100644 index 0000000..aa4900a --- /dev/null +++ b/SOURCES/kvm-bloc-qcow2-drop-dirty_bitmaps_loaded-state-variable.patch @@ -0,0 +1,84 @@ +From 428b1ae675f8018ef457d095d726bc2e4cc5b5d4 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:40 +0100 +Subject: [PATCH 30/33] bloc/qcow2: drop dirty_bitmaps_loaded state variable + +RH-Author: John Snow +Message-id: <20190206221243.7407-21-jsnow@redhat.com> +Patchwork-id: 84279 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 20/23] bloc/qcow2: drop dirty_bitmaps_loaded state variable +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +From: Vladimir Sementsov-Ogievskiy + +This variable doesn't work as it should, because it is actually cleared +in qcow2_co_invalidate_cache() by memset(). Drop it, as the following +patch will introduce new behavior. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: John Snow +Signed-off-by: John Snow +(cherry picked from commit 2ea427effff61efa5d0dc69f9cae126d13879617) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/qcow2.c | 19 ++----------------- + block/qcow2.h | 1 - + 2 files changed, 2 insertions(+), 18 deletions(-) + +diff --git a/block/qcow2.c b/block/qcow2.c +index 5c5530d..d260cd6 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -1149,7 +1149,6 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, + uint64_t ext_end; + uint64_t l1_vm_state_index; + bool update_header = false; +- bool header_updated = false; + + ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); + if (ret < 0) { +@@ -1488,23 +1487,9 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, + s->autoclear_features &= QCOW2_AUTOCLEAR_MASK; + } + +- if (s->dirty_bitmaps_loaded) { +- /* It's some kind of reopen. There are no known cases where we need to +- * reload bitmaps in such a situation, so it's safer to skip them. +- * +- * Moreover, if we have some readonly bitmaps and we are reopening for +- * rw we should reopen bitmaps correspondingly. +- */ +- if (bdrv_has_readonly_bitmaps(bs) && +- !bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE)) +- { +- qcow2_reopen_bitmaps_rw_hint(bs, &header_updated, &local_err); +- } +- } else { +- header_updated = qcow2_load_dirty_bitmaps(bs, &local_err); +- s->dirty_bitmaps_loaded = true; ++ if (qcow2_load_dirty_bitmaps(bs, &local_err)) { ++ update_header = false; + } +- update_header = update_header && !header_updated; + if (local_err != NULL) { + error_propagate(errp, local_err); + ret = -EINVAL; +diff --git a/block/qcow2.h b/block/qcow2.h +index d2c63e4..43163b2 100644 +--- a/block/qcow2.h ++++ b/block/qcow2.h +@@ -299,7 +299,6 @@ typedef struct BDRVQcow2State { + uint32_t nb_bitmaps; + uint64_t bitmap_directory_size; + uint64_t bitmap_directory_offset; +- bool dirty_bitmaps_loaded; + + int flags; + int qcow_version; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Add-BDRV_REQ_NO_FALLBACK.patch b/SOURCES/kvm-block-Add-BDRV_REQ_NO_FALLBACK.patch new file mode 100644 index 0000000..81a2375 --- /dev/null +++ b/SOURCES/kvm-block-Add-BDRV_REQ_NO_FALLBACK.patch @@ -0,0 +1,109 @@ +From 5ad7c32387034a02c9a932018e60580872431db9 Mon Sep 17 00:00:00 2001 +From: Maxim Levitsky +Date: Wed, 5 Jun 2019 13:56:58 +0200 +Subject: [PATCH 10/23] block: Add BDRV_REQ_NO_FALLBACK + +RH-Author: Maxim Levitsky +Message-id: <20190605135705.24526-3-mlevitsk@redhat.com> +Patchwork-id: 88557 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 2/9] block: Add BDRV_REQ_NO_FALLBACK +Bugzilla: 1648622 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow + +From: Kevin Wolf + +For qemu-img convert, we want an operation that zeroes out the whole +image if this can be done efficiently, but that returns an error +otherwise so we don't write explicit zeroes and immediately overwrite +them with the real data, potentially doubling the amount of data to be +written. + +Signed-off-by: Kevin Wolf +Acked-by: Eric Blake + +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1648622 + +Signed-off-by: Maxim Levitsky +(Cherry picked from fe0480d6294270ff0d6fb60e66bb725a6aad2043) + +Signed-off-by: Miroslav Rezanina +--- + block/io.c | 12 +++++++++++- + include/block/block.h | 7 ++++++- + 2 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/block/io.c b/block/io.c +index 18bf3c2..26c4075 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -1029,6 +1029,7 @@ static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs, + unsigned int nb_sectors; + + assert(!(flags & ~BDRV_REQ_MASK)); ++ assert(!(flags & BDRV_REQ_NO_FALLBACK)); + + if (!drv) { + return -ENOMEDIUM; +@@ -1074,6 +1075,7 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, + int ret; + + assert(!(flags & ~BDRV_REQ_MASK)); ++ assert(!(flags & BDRV_REQ_NO_FALLBACK)); + + if (!drv) { + return -ENOMEDIUM; +@@ -1499,6 +1501,10 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, + return -ENOMEDIUM; + } + ++ if ((flags & ~bs->supported_zero_flags) & BDRV_REQ_NO_FALLBACK) { ++ return -ENOTSUP; ++ } ++ + assert(alignment % bs->bl.request_alignment == 0); + head = offset % alignment; + tail = (offset + bytes) % alignment; +@@ -1542,7 +1548,7 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, + assert(!bs->supported_zero_flags); + } + +- if (ret == -ENOTSUP) { ++ 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; + +@@ -2973,6 +2979,10 @@ static int coroutine_fn bdrv_co_copy_range_internal( + BdrvTrackedRequest req; + int ret; + ++ /* TODO We can support BDRV_REQ_NO_FALLBACK here */ ++ assert(!(read_flags & BDRV_REQ_NO_FALLBACK)); ++ assert(!(write_flags & BDRV_REQ_NO_FALLBACK)); ++ + if (!dst || !dst->bs) { + return -ENOMEDIUM; + } +diff --git a/include/block/block.h b/include/block/block.h +index 5f40140..33fb60c 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -82,8 +82,13 @@ typedef enum { + */ + 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, ++ + /* Mask of valid flags */ +- BDRV_REQ_MASK = 0xff, ++ BDRV_REQ_MASK = 0x1ff, + } BdrvRequestFlags; + + typedef struct BlockSizes { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Add-BDRV_REQ_WRITE_UNCHANGED-flag.patch b/SOURCES/kvm-block-Add-BDRV_REQ_WRITE_UNCHANGED-flag.patch new file mode 100644 index 0000000..068b6ce --- /dev/null +++ b/SOURCES/kvm-block-Add-BDRV_REQ_WRITE_UNCHANGED-flag.patch @@ -0,0 +1,70 @@ +From c757f534ae34ad858e0d706e0fb893ef5f3aeb28 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 16:12:06 +0200 +Subject: [PATCH 30/54] block: Add BDRV_REQ_WRITE_UNCHANGED flag + +RH-Author: Max Reitz +Message-id: <20180618161212.14444-5-mreitz@redhat.com> +Patchwork-id: 80764 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 04/10] block: Add BDRV_REQ_WRITE_UNCHANGED flag +Bugzilla: 1518738 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Miroslav Rezanina + +This flag signifies that a write request will not change the visible +disk content. With this flag set, it is sufficient to have the +BLK_PERM_WRITE_UNCHANGED permission instead of BLK_PERM_WRITE. + +Signed-off-by: Max Reitz +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Alberto Garcia +Message-id: 20180421132929.21610-4-mreitz@redhat.com +Reviewed-by: Kevin Wolf +Signed-off-by: Max Reitz +(cherry picked from commit c6035964f8316b504060618d05b5dd434f18595b) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/io.c | 6 +++++- + include/block/block.h | 6 +++++- + 2 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/block/io.c b/block/io.c +index bd9a19a..134b2a4 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -1501,7 +1501,11 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, + assert(!waited || !req->serialising); + assert(req->overlap_offset <= offset); + assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); +- assert(child->perm & BLK_PERM_WRITE); ++ if (flags & BDRV_REQ_WRITE_UNCHANGED) { ++ assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE)); ++ } else { ++ assert(child->perm & BLK_PERM_WRITE); ++ } + assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE); + + ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req); +diff --git a/include/block/block.h b/include/block/block.h +index 397b5e8..3894edd 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -54,8 +54,12 @@ typedef enum { + 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, ++ + /* Mask of valid flags */ +- BDRV_REQ_MASK = 0x3f, ++ BDRV_REQ_MASK = 0x7f, + } BdrvRequestFlags; + + typedef struct BlockSizes { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Add-COR-filter-driver.patch b/SOURCES/kvm-block-Add-COR-filter-driver.patch new file mode 100644 index 0000000..35eccbe --- /dev/null +++ b/SOURCES/kvm-block-Add-COR-filter-driver.patch @@ -0,0 +1,260 @@ +From b6f5671191f1ce4d7ee2fe5d794c93b316cc0a1a Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 16:12:04 +0200 +Subject: [PATCH 28/54] block: Add COR filter driver + +RH-Author: Max Reitz +Message-id: <20180618161212.14444-3-mreitz@redhat.com> +Patchwork-id: 80762 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 02/10] block: Add COR filter driver +Bugzilla: 1518738 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Miroslav Rezanina + +This adds a simple copy-on-read filter driver. It relies on the already +existing COR functionality in the central block layer code, which may be +moved here once we no longer need it there. + +Signed-off-by: Max Reitz +Message-id: 20180421132929.21610-2-mreitz@redhat.com +Reviewed-by: Alberto Garcia +Reviewed-by: Kevin Wolf +Signed-off-by: Max Reitz +(cherry picked from commit 6c6f24fd84895d03baa898bbc4324dd4ccc97071) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/Makefile.objs | 2 +- + block/copy-on-read.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++ + qapi/block-core.json | 5 +- + 3 files changed, 176 insertions(+), 2 deletions(-) + create mode 100644 block/copy-on-read.c + +diff --git a/block/Makefile.objs b/block/Makefile.objs +index c0693fc..be2cda1 100644 +--- a/block/Makefile.objs ++++ b/block/Makefile.objs +@@ -26,7 +26,7 @@ block-obj-y += accounting.o dirty-bitmap.o + block-obj-y += write-threshold.o + block-obj-y += backup.o + block-obj-$(CONFIG_REPLICATION) += replication.o +-block-obj-y += throttle.o ++block-obj-y += throttle.o copy-on-read.o + + block-obj-y += crypto.o + +diff --git a/block/copy-on-read.c b/block/copy-on-read.c +new file mode 100644 +index 0000000..823ec75 +--- /dev/null ++++ b/block/copy-on-read.c +@@ -0,0 +1,171 @@ ++/* ++ * Copy-on-read filter block driver ++ * ++ * Copyright (c) 2018 Red Hat, Inc. ++ * ++ * Author: ++ * Max Reitz ++ * ++ * 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 or ++ * (at your option) version 3 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 "block/block_int.h" ++ ++ ++static int cor_open(BlockDriverState *bs, QDict *options, int flags, ++ Error **errp) ++{ ++ bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, ++ errp); ++ if (!bs->file) { ++ return -EINVAL; ++ } ++ ++ bs->supported_write_flags = BDRV_REQ_FUA & ++ bs->file->bs->supported_write_flags; ++ ++ bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & ++ bs->file->bs->supported_zero_flags; ++ ++ return 0; ++} ++ ++ ++static void cor_close(BlockDriverState *bs) ++{ ++} ++ ++ ++#define PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \ ++ | BLK_PERM_WRITE \ ++ | BLK_PERM_RESIZE) ++#define PERM_UNCHANGED (BLK_PERM_ALL & ~PERM_PASSTHROUGH) ++ ++static void cor_child_perm(BlockDriverState *bs, BdrvChild *c, ++ const BdrvChildRole *role, ++ BlockReopenQueue *reopen_queue, ++ uint64_t perm, uint64_t shared, ++ uint64_t *nperm, uint64_t *nshared) ++{ ++ if (c == NULL) { ++ *nperm = (perm & PERM_PASSTHROUGH) | BLK_PERM_WRITE_UNCHANGED; ++ *nshared = (shared & PERM_PASSTHROUGH) | PERM_UNCHANGED; ++ return; ++ } ++ ++ *nperm = (perm & PERM_PASSTHROUGH) | ++ (c->perm & PERM_UNCHANGED); ++ *nshared = (shared & PERM_PASSTHROUGH) | ++ (c->shared_perm & PERM_UNCHANGED); ++} ++ ++ ++static int64_t cor_getlength(BlockDriverState *bs) ++{ ++ return bdrv_getlength(bs->file->bs); ++} ++ ++ ++static int cor_truncate(BlockDriverState *bs, int64_t offset, ++ PreallocMode prealloc, Error **errp) ++{ ++ return bdrv_truncate(bs->file, offset, prealloc, errp); ++} ++ ++ ++static int coroutine_fn cor_co_preadv(BlockDriverState *bs, ++ uint64_t offset, uint64_t bytes, ++ QEMUIOVector *qiov, int flags) ++{ ++ return bdrv_co_preadv(bs->file, offset, bytes, qiov, ++ flags | BDRV_REQ_COPY_ON_READ); ++} ++ ++ ++static int coroutine_fn cor_co_pwritev(BlockDriverState *bs, ++ uint64_t offset, uint64_t bytes, ++ QEMUIOVector *qiov, int flags) ++{ ++ ++ return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); ++} ++ ++ ++static int coroutine_fn cor_co_pwrite_zeroes(BlockDriverState *bs, ++ int64_t offset, int bytes, ++ BdrvRequestFlags flags) ++{ ++ return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); ++} ++ ++ ++static int coroutine_fn cor_co_pdiscard(BlockDriverState *bs, ++ int64_t offset, int bytes) ++{ ++ return bdrv_co_pdiscard(bs->file->bs, offset, bytes); ++} ++ ++ ++static void cor_eject(BlockDriverState *bs, bool eject_flag) ++{ ++ bdrv_eject(bs->file->bs, eject_flag); ++} ++ ++ ++static void cor_lock_medium(BlockDriverState *bs, bool locked) ++{ ++ bdrv_lock_medium(bs->file->bs, locked); ++} ++ ++ ++static bool cor_recurse_is_first_non_filter(BlockDriverState *bs, ++ BlockDriverState *candidate) ++{ ++ return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate); ++} ++ ++ ++BlockDriver bdrv_copy_on_read = { ++ .format_name = "copy-on-read", ++ ++ .bdrv_open = cor_open, ++ .bdrv_close = cor_close, ++ .bdrv_child_perm = cor_child_perm, ++ ++ .bdrv_getlength = cor_getlength, ++ .bdrv_truncate = cor_truncate, ++ ++ .bdrv_co_preadv = cor_co_preadv, ++ .bdrv_co_pwritev = cor_co_pwritev, ++ .bdrv_co_pwrite_zeroes = cor_co_pwrite_zeroes, ++ .bdrv_co_pdiscard = cor_co_pdiscard, ++ ++ .bdrv_eject = cor_eject, ++ .bdrv_lock_medium = cor_lock_medium, ++ ++ .bdrv_co_block_status = bdrv_co_block_status_from_file, ++ ++ .bdrv_recurse_is_first_non_filter = cor_recurse_is_first_non_filter, ++ ++ .has_variable_length = true, ++ .is_filter = true, ++}; ++ ++static void bdrv_copy_on_read_init(void) ++{ ++ bdrv_register(&bdrv_copy_on_read); ++} ++ ++block_init(bdrv_copy_on_read_init); +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 46469be..5aac0c7 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -2506,11 +2506,12 @@ + # @vxhs: Since 2.10 + # @throttle: Since 2.11 + # @nvme: Since 2.12 ++# @copy-on-read: Since 2.13 + # + # Since: 2.9 + ## + { 'enum': 'BlockdevDriver', +- 'data': [ 'blkdebug', 'blkverify', 'bochs', 'cloop', ++ 'data': [ 'blkdebug', 'blkverify', 'bochs', 'cloop', 'copy-on-read', + 'dmg', 'file', 'ftp', 'ftps', 'gluster', 'host_cdrom', + 'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs', + 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow', 'qcow2', 'qed', +@@ -3541,6 +3542,7 @@ + 'blkverify': 'BlockdevOptionsBlkverify', + 'bochs': 'BlockdevOptionsGenericFormat', + 'cloop': 'BlockdevOptionsGenericFormat', ++ 'copy-on-read':'BlockdevOptionsGenericFormat', + 'dmg': 'BlockdevOptionsGenericFormat', + 'file': 'BlockdevOptionsFile', + 'ftp': 'BlockdevOptionsCurlFtp', +@@ -4068,6 +4070,7 @@ + 'blkverify': 'BlockdevCreateNotSupported', + 'bochs': 'BlockdevCreateNotSupported', + 'cloop': 'BlockdevCreateNotSupported', ++ 'copy-on-read': 'BlockdevCreateNotSupported', + 'dmg': 'BlockdevCreateNotSupported', + 'file': 'BlockdevCreateOptionsFile', + 'ftp': 'BlockdevCreateNotSupported', +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Add-Error-parameter-to-bdrv_amend_options.patch b/SOURCES/kvm-block-Add-Error-parameter-to-bdrv_amend_options.patch new file mode 100644 index 0000000..97badc2 --- /dev/null +++ b/SOURCES/kvm-block-Add-Error-parameter-to-bdrv_amend_options.patch @@ -0,0 +1,465 @@ +From e01a2ccba2a784caad5192f8d1e4b37ae1d65bed Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 14:59:38 +0200 +Subject: [PATCH 04/89] block: Add Error parameter to bdrv_amend_options + +RH-Author: Max Reitz +Message-id: <20180618145943.4489-3-mreitz@redhat.com> +Patchwork-id: 80755 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 2/7] block: Add Error parameter to bdrv_amend_options +Bugzilla: 1537956 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Stefan Hajnoczi + +Looking at the qcow2 code that is riddled with error_report() calls, +this is really how it should have been from the start. + +Along the way, turn the target_version/current_version comparisons at +the beginning of qcow2_downgrade() into assertions (the caller has to +make sure these conditions are met), and rephrase the error message on +using compat=1.1 to get refcount widths other than 16 bits. + +Signed-off-by: Max Reitz +Message-id: 20180509210023.20283-3-mreitz@redhat.com +Reviewed-by: Eric Blake +Reviewed-by: John Snow +Signed-off-by: Max Reitz +(cherry picked from commit d1402b502691142b9cebadd5cb993dc8858e9071) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block.c | 8 ++++-- + block/qcow2.c | 72 ++++++++++++++++++++++++++-------------------- + include/block/block.h | 3 +- + include/block/block_int.h | 3 +- + qemu-img.c | 4 +-- + tests/qemu-iotests/060.out | 4 +-- + tests/qemu-iotests/061.out | 7 ----- + tests/qemu-iotests/080.out | 4 +-- + tests/qemu-iotests/112.out | 5 +--- + 9 files changed, 58 insertions(+), 52 deletions(-) + +diff --git a/block.c b/block.c +index 982d54e..d991a09 100644 +--- a/block.c ++++ b/block.c +@@ -5008,15 +5008,19 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs, + } + + int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts, +- BlockDriverAmendStatusCB *status_cb, void *cb_opaque) ++ BlockDriverAmendStatusCB *status_cb, void *cb_opaque, ++ Error **errp) + { + 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); ++ return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque, errp); + } + + /* This function will be called by the bdrv_recurse_is_first_non_filter method +diff --git a/block/qcow2.c b/block/qcow2.c +index 26a6a7f..092db81 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -4039,22 +4039,21 @@ static int qcow2_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, + * have to be removed. + */ + static int qcow2_downgrade(BlockDriverState *bs, int target_version, +- BlockDriverAmendStatusCB *status_cb, void *cb_opaque) ++ BlockDriverAmendStatusCB *status_cb, void *cb_opaque, ++ Error **errp) + { + BDRVQcow2State *s = bs->opaque; + int current_version = s->qcow_version; + int ret; + +- if (target_version == current_version) { +- return 0; +- } else if (target_version > current_version) { +- return -EINVAL; +- } else if (target_version != 2) { +- return -EINVAL; +- } ++ /* 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_report("compat=0.10 requires refcount_bits=16"); ++ error_setg(errp, "compat=0.10 requires refcount_bits=16"); + return -ENOTSUP; + } + +@@ -4062,6 +4061,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, + 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; + } + } +@@ -4071,6 +4071,8 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, + * best thing to do anyway */ + + if (s->incompatible_features) { ++ error_setg(errp, "Cannot downgrade an image with incompatible features " ++ "%#" PRIx64 " set", s->incompatible_features); + return -ENOTSUP; + } + +@@ -4084,6 +4086,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, + + 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; + } + +@@ -4091,6 +4094,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int 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; +@@ -4168,7 +4172,8 @@ static void qcow2_amend_helper_cb(BlockDriverState *bs, + + static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + BlockDriverAmendStatusCB *status_cb, +- void *cb_opaque) ++ void *cb_opaque, ++ Error **errp) + { + BDRVQcow2State *s = bs->opaque; + int old_version = s->qcow_version, new_version = old_version; +@@ -4180,7 +4185,6 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + bool encrypt; + int encformat; + int refcount_bits = s->refcount_bits; +- Error *local_err = NULL; + int ret; + QemuOptDesc *desc = opts->list->desc; + Qcow2AmendHelperCBInfo helper_cb_info; +@@ -4201,11 +4205,11 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + } else if (!strcmp(compat, "1.1")) { + new_version = 3; + } else { +- error_report("Unknown compatibility level %s", compat); ++ error_setg(errp, "Unknown compatibility level %s", compat); + return -EINVAL; + } + } else if (!strcmp(desc->name, BLOCK_OPT_PREALLOC)) { +- error_report("Cannot change preallocation mode"); ++ error_setg(errp, "Cannot change preallocation mode"); + return -ENOTSUP; + } else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) { + new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0); +@@ -4218,7 +4222,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + !!s->crypto); + + if (encrypt != !!s->crypto) { +- error_report("Changing the encryption flag is not supported"); ++ error_setg(errp, ++ "Changing the encryption flag is not supported"); + return -ENOTSUP; + } + } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT_FORMAT)) { +@@ -4226,17 +4231,19 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + qemu_opt_get(opts, BLOCK_OPT_ENCRYPT_FORMAT)); + + if (encformat != s->crypt_method_header) { +- error_report("Changing the encryption format is not supported"); ++ error_setg(errp, ++ "Changing the encryption format is not supported"); + return -ENOTSUP; + } + } else if (g_str_has_prefix(desc->name, "encrypt.")) { +- error_report("Changing the encryption parameters is not supported"); ++ error_setg(errp, ++ "Changing the encryption parameters is not supported"); + return -ENOTSUP; + } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) { + cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE, + cluster_size); + if (cluster_size != s->cluster_size) { +- error_report("Changing the cluster size is not supported"); ++ error_setg(errp, "Changing the cluster size is not supported"); + return -ENOTSUP; + } + } else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) { +@@ -4249,8 +4256,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + if (refcount_bits <= 0 || refcount_bits > 64 || + !is_power_of_2(refcount_bits)) + { +- error_report("Refcount width must be a power of two and may " +- "not exceed 64 bits"); ++ error_setg(errp, "Refcount width must be a power of two and " ++ "may not exceed 64 bits"); + return -EINVAL; + } + } else { +@@ -4275,6 +4282,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + ret = qcow2_update_header(bs); + if (ret < 0) { + s->qcow_version = old_version; ++ error_setg_errno(errp, -ret, "Failed to update the image header"); + return ret; + } + } +@@ -4283,18 +4291,17 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + int refcount_order = ctz32(refcount_bits); + + if (new_version < 3 && refcount_bits != 16) { +- error_report("Different refcount widths than 16 bits require " +- "compatibility level 1.1 or above (use compat=1.1 or " +- "greater)"); ++ 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, &local_err); ++ &helper_cb_info, errp); + if (ret < 0) { +- error_report_err(local_err); + return ret; + } + } +@@ -4304,6 +4311,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + backing_file ?: s->image_backing_file, + backing_format ?: s->image_backing_format); + if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to change the backing file"); + return ret; + } + } +@@ -4311,14 +4319,16 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + if (s->use_lazy_refcounts != lazy_refcounts) { + if (lazy_refcounts) { + if (new_version < 3) { +- error_report("Lazy refcounts only supported with compatibility " +- "level 1.1 and above (use compat=1.1 or greater)"); ++ 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; +@@ -4326,6 +4336,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + /* 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 */ +@@ -4333,6 +4344,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + 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; +@@ -4341,17 +4353,15 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + + if (new_size) { + BlockBackend *blk = blk_new(BLK_PERM_RESIZE, BLK_PERM_ALL); +- ret = blk_insert_bs(blk, bs, &local_err); ++ ret = blk_insert_bs(blk, bs, errp); + if (ret < 0) { +- error_report_err(local_err); + blk_unref(blk); + return ret; + } + +- ret = blk_truncate(blk, new_size, PREALLOC_MODE_OFF, &local_err); ++ ret = blk_truncate(blk, new_size, PREALLOC_MODE_OFF, errp); + blk_unref(blk); + if (ret < 0) { +- error_report_err(local_err); + return ret; + } + } +@@ -4360,7 +4370,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + 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); ++ &helper_cb_info, errp); + if (ret < 0) { + return ret; + } +diff --git a/include/block/block.h b/include/block/block.h +index 06cd772..2d17b09 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -343,7 +343,8 @@ int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix); + typedef void BlockDriverAmendStatusCB(BlockDriverState *bs, int64_t offset, + int64_t total_work_size, void *opaque); + int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts, +- BlockDriverAmendStatusCB *status_cb, void *cb_opaque); ++ BlockDriverAmendStatusCB *status_cb, void *cb_opaque, ++ Error **errp); + + /* external snapshots */ + bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs, +diff --git a/include/block/block_int.h b/include/block/block_int.h +index c4dd1d4..d913ed1 100644 +--- a/include/block/block_int.h ++++ b/include/block/block_int.h +@@ -324,7 +324,8 @@ struct BlockDriver { + + int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts, + BlockDriverAmendStatusCB *status_cb, +- void *cb_opaque); ++ void *cb_opaque, ++ Error **errp); + + void (*bdrv_debug_event)(BlockDriverState *bs, BlkdebugEvent event); + +diff --git a/qemu-img.c b/qemu-img.c +index 2f7c491..e40d6ff 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -3761,10 +3761,10 @@ static int img_amend(int argc, char **argv) + + /* 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); ++ ret = bdrv_amend_options(bs, opts, &amend_status_cb, NULL, &err); + qemu_progress_print(100.f, 0); + if (ret < 0) { +- error_report("Error while amending options: %s", strerror(-ret)); ++ error_report_err(err); + goto out; + } + +diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out +index 99234fb..bff023d 100644 +--- a/tests/qemu-iotests/060.out ++++ b/tests/qemu-iotests/060.out +@@ -129,7 +129,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + wrote 65536/65536 bytes at offset 0 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + qcow2: Marking image as corrupt: L2 table offset 0x42a00 unaligned (L1 index: 0); further corruption events will be suppressed +-qemu-img: Error while amending options: Input/output error ++qemu-img: Failed to turn zero into data clusters: Input/output error + + === Testing unaligned L2 entry === + +@@ -145,7 +145,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + wrote 65536/65536 bytes at offset 0 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + qcow2: Marking image as corrupt: Cluster allocation offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further corruption events will be suppressed +-qemu-img: Error while amending options: Input/output error ++qemu-img: Failed to turn zero into data clusters: Input/output error + + === Testing unaligned reftable entry === + +diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out +index e857ef9..183f7dd 100644 +--- a/tests/qemu-iotests/061.out ++++ b/tests/qemu-iotests/061.out +@@ -358,18 +358,12 @@ No errors were found on the image. + + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + qemu-img: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) +-qemu-img: Error while amending options: Invalid argument + qemu-img: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) +-qemu-img: Error while amending options: Invalid argument + qemu-img: Unknown compatibility level 0.42 +-qemu-img: Error while amending options: Invalid argument + qemu-img: Invalid parameter 'foo' + qemu-img: Changing the cluster size is not supported +-qemu-img: Error while amending options: Operation not supported + qemu-img: Changing the encryption flag is not supported +-qemu-img: Error while amending options: Operation not supported + qemu-img: Cannot change preallocation mode +-qemu-img: Error while amending options: Operation not supported + + === Testing correct handling of unset value === + +@@ -377,7 +371,6 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + Should work: + Should not work: + qemu-img: Changing the cluster size is not supported +-qemu-img: Error while amending options: Operation not supported + + === Testing zero expansion on inactive clusters === + +diff --git a/tests/qemu-iotests/080.out b/tests/qemu-iotests/080.out +index 4e0f7f7..281c7e0 100644 +--- a/tests/qemu-iotests/080.out ++++ b/tests/qemu-iotests/080.out +@@ -65,7 +65,7 @@ wrote 512/512 bytes at offset 0 + 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + qemu-img: Failed to load snapshot: Snapshot L1 table offset invalid + qemu-img: Snapshot L1 table offset invalid +-qemu-img: Error while amending options: Invalid argument ++qemu-img: Failed to turn zero into data clusters: Invalid argument + Failed to flush the refcount block cache: Invalid argument + write failed: Invalid argument + qemu-img: Snapshot L1 table offset invalid +@@ -88,7 +88,7 @@ wrote 512/512 bytes at offset 0 + 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + qemu-img: Failed to load snapshot: Snapshot L1 table too large + qemu-img: Snapshot L1 table too large +-qemu-img: Error while amending options: File too large ++qemu-img: Failed to turn zero into data clusters: File too large + Failed to flush the refcount block cache: File too large + write failed: File too large + qemu-img: Snapshot L1 table too large +diff --git a/tests/qemu-iotests/112.out b/tests/qemu-iotests/112.out +index 86f0410..ae0318c 100644 +--- a/tests/qemu-iotests/112.out ++++ b/tests/qemu-iotests/112.out +@@ -99,13 +99,11 @@ refcount bits: 64 + === Amend to compat=0.10 === + + qemu-img: compat=0.10 requires refcount_bits=16 +-qemu-img: Error while amending options: Operation not supported + refcount bits: 64 + No errors were found on the image. + refcount bits: 16 + refcount bits: 16 +-qemu-img: Different refcount widths than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater) +-qemu-img: Error while amending options: Invalid argument ++qemu-img: Refcount widths other than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater) + refcount bits: 16 + + === Amend with snapshot === +@@ -113,7 +111,6 @@ refcount bits: 16 + wrote 16777216/16777216 bytes at offset 0 + 16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + qemu-img: Cannot decrease refcount entry width to 1 bits: Cluster at offset 0x50000 has a refcount of 2 +-qemu-img: Error while amending options: Invalid argument + No errors were found on the image. + refcount bits: 16 + No errors were found on the image. +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Add-auto-read-only-option.patch b/SOURCES/kvm-block-Add-auto-read-only-option.patch new file mode 100644 index 0000000..7087ec1 --- /dev/null +++ b/SOURCES/kvm-block-Add-auto-read-only-option.patch @@ -0,0 +1,201 @@ +From c42a88a203211360e20281a8f6bd554f46062178 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 23 Nov 2018 10:41:44 +0100 +Subject: [PATCH 03/34] block: Add auto-read-only option + +RH-Author: Kevin Wolf +Message-id: <20181123104154.13541-3-kwolf@redhat.com> +Patchwork-id: 83111 +O-Subject: [RHEL-7.7/7.6.z qemu-kvm-rhev PATCH v2 02/12] block: Add auto-read-only option +Bugzilla: 1623986 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina +RH-Acked-by: John Snow + +If a management application builds the block graph node by node, the +protocol layer doesn't inherit its read-only option from the format +layer any more, so it must be set explicitly. + +Backing files should work on read-only storage, but at the same time, a +block job like commit should be able to reopen them read-write if they +are on read-write storage. However, without option inheritance, reopen +only changes the read-only option for the root node (typically the +format layer), but not the protocol layer, so reopening fails (the +format layer wants to get write permissions, but the protocol layer is +still read-only). + +A simple workaround for the problem in the management tool would be to +open the protocol layer always read-write and to make only the format +layer read-only for backing files. However, sometimes the file is +actually stored on read-only storage and we don't know whether the image +can be opened read-write (for example, for NBD it depends on the server +we're trying to connect to). This adds an option that makes QEMU try to +open the image read-write, but allows it to degrade to a read-only mode +without returning an error. + +The documentation for this option is consciously phrased in a way that +allows QEMU to switch to a better model eventually: Instead of trying +when the image is first opened, making the read-only flag dynamic and +changing it automatically whenever the first BLK_PERM_WRITE user is +attached or the last one is detached would be much more useful +behaviour. + +Unfortunately, this more useful behaviour is also a lot harder to +implement, and libvirt needs a solution now before it can switch to +-blockdev, so let's start with this easier approach for now. + +Instead of adding a new auto-read-only option, turning the existing +read-only into an enum (with a bool alternate for compatibility) was +considered, but it complicated the implementation to the point that it +didn't seem to be worth it. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +(cherry picked from commit e35bdc123a4ace9f4d3fccaaf88907014e2438cd) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 17 +++++++++++++++++ + block/vvfat.c | 1 + + blockdev.c | 2 +- + include/block/block.h | 2 ++ + qapi/block-core.json | 7 +++++++ + 5 files changed, 28 insertions(+), 1 deletion(-) + +diff --git a/block.c b/block.c +index d3ea21a..ff3ea92 100644 +--- a/block.c ++++ b/block.c +@@ -905,6 +905,7 @@ static void bdrv_inherited_options(int *child_flags, QDict *child_options, + + /* 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); + + /* Our block drivers take care to send flushes and respect unmap policy, + * so we can default to enable both on lower layers regardless of the +@@ -1028,6 +1029,7 @@ static void bdrv_backing_options(int *child_flags, QDict *child_options, + + /* backing files always opened read-only */ + qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "on"); ++ qdict_set_default_str(child_options, BDRV_OPT_AUTO_READ_ONLY, "off"); + flags &= ~BDRV_O_COPY_ON_READ; + + /* snapshot=on is handled on the top layer */ +@@ -1117,6 +1119,10 @@ static void update_flags_from_options(int *flags, QemuOpts *opts) + *flags |= BDRV_O_RDWR; + } + ++ assert(qemu_opt_find(opts, BDRV_OPT_AUTO_READ_ONLY)); ++ 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) +@@ -1131,6 +1137,10 @@ static void update_options_from_flags(QDict *options, int flags) + 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, +@@ -1304,6 +1314,11 @@ QemuOptsList bdrv_runtime_opts = { + .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)", +@@ -2490,6 +2505,8 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp) + 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, errp); +diff --git a/block/vvfat.c b/block/vvfat.c +index c7d2ed2..3efce9e 100644 +--- a/block/vvfat.c ++++ b/block/vvfat.c +@@ -3130,6 +3130,7 @@ static void vvfat_qcow_options(int *child_flags, QDict *child_options, + int parent_flags, QDict *parent_options) + { + qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "off"); ++ qdict_set_default_str(child_options, BDRV_OPT_AUTO_READ_ONLY, "off"); + qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on"); + } + +diff --git a/blockdev.c b/blockdev.c +index 6e8a1e9..3d73f05 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -2758,7 +2758,7 @@ void qmp_blockdev_change_medium(bool has_device, const char *device, + + bdrv_flags = blk_get_open_flags_from_root_state(blk); + bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | +- BDRV_O_PROTOCOL); ++ BDRV_O_PROTOCOL | BDRV_O_AUTO_RDONLY); + + if (!has_read_only) { + read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN; +diff --git a/include/block/block.h b/include/block/block.h +index 8e78daf..6ee8b2a 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -114,6 +114,7 @@ typedef struct HDGeometry { + 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_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_NO_FLUSH) + +@@ -124,6 +125,7 @@ typedef struct HDGeometry { + #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" + +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 2706012..9741555 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -3608,6 +3608,12 @@ + # either generally or in certain configurations. In this case, + # the default value does not work and the option must be + # specified explicitly. ++# @auto-read-only: if true and @read-only is false, QEMU may automatically ++# decide not to open the image read-write as requested, but ++# fall back to read-only instead (and switch between the modes ++# later), e.g. depending on whether the image file is writable ++# or whether a writing user is attached to the node ++# (default: false, since 3.1) + # @detect-zeroes: detect and optimize zero writes (Since 2.1) + # (default: off) + # @force-share: force share all permission on added nodes. +@@ -3623,6 +3629,7 @@ + '*discard': 'BlockdevDiscardOptions', + '*cache': 'BlockdevCacheOptions', + '*read-only': 'bool', ++ '*auto-read-only': 'bool', + '*force-share': 'bool', + '*detect-zeroes': 'BlockdevDetectZeroesOptions' }, + 'discriminator': 'driver', +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Add-bdrv_get_request_alignment.patch b/SOURCES/kvm-block-Add-bdrv_get_request_alignment.patch new file mode 100644 index 0000000..e5dc5fb --- /dev/null +++ b/SOURCES/kvm-block-Add-bdrv_get_request_alignment.patch @@ -0,0 +1,65 @@ +From c4d49803aeb1d6304c2728cb1475f8314ae1f8a8 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 6 May 2019 17:56:22 +0200 +Subject: [PATCH 12/53] block: Add bdrv_get_request_alignment() + +RH-Author: John Snow +Message-id: <20190506175629.11079-13-jsnow@redhat.com> +Patchwork-id: 87195 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 12/19] block: Add bdrv_get_request_alignment() +Bugzilla: 1692018 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Thomas Huth + +From: Eric Blake + +The next patch needs access to a device's minimum permitted +alignment, since NBD wants to advertise this to clients. Add +an accessor function, borrowing from blk_get_max_transfer() +for accessing a backend's block limits. + +Signed-off-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20190329042750.14704-6-eblake@redhat.com> +(cherry picked from commit 4841211e0d1628cd386b35835676d7f6f9a4fa9d) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/block-backend.c | 7 +++++++ + include/sysemu/block-backend.h | 1 + + 2 files changed, 8 insertions(+) + +diff --git a/block/block-backend.c b/block/block-backend.c +index 52eebeb..f6480f6 100644 +--- a/block/block-backend.c ++++ b/block/block-backend.c +@@ -1834,6 +1834,13 @@ int blk_get_flags(BlockBackend *blk) + } + } + ++/* Returns the minimum request alignment, in bytes; guaranteed nonzero */ ++uint32_t blk_get_request_alignment(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ return bs ? bs->bl.request_alignment : BDRV_SECTOR_SIZE; ++} ++ + /* Returns the maximum transfer length, in bytes; guaranteed nonzero */ + uint32_t blk_get_max_transfer(BlockBackend *blk) + { +diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h +index 6b6d882..815b6e5 100644 +--- a/include/sysemu/block-backend.h ++++ b/include/sysemu/block-backend.h +@@ -178,6 +178,7 @@ bool blk_is_available(BlockBackend *blk); + void blk_lock_medium(BlockBackend *blk, bool locked); + void blk_eject(BlockBackend *blk, bool eject_flag); + int blk_get_flags(BlockBackend *blk); ++uint32_t blk_get_request_alignment(BlockBackend *blk); + uint32_t blk_get_max_transfer(BlockBackend *blk); + int blk_get_max_iov(BlockBackend *blk); + void blk_set_guest_block_size(BlockBackend *blk, int align); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Add-block-specific-QDict-header.patch b/SOURCES/kvm-block-Add-block-specific-QDict-header.patch new file mode 100644 index 0000000..146ef7c --- /dev/null +++ b/SOURCES/kvm-block-Add-block-specific-QDict-header.patch @@ -0,0 +1,423 @@ +From 29e09fe238bfbf9657384e8a05087398925626ac Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:15 +0200 +Subject: [PATCH 08/54] block: Add block-specific QDict header + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-9-armbru@redhat.com> +Patchwork-id: 80736 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 08/23] block: Add block-specific QDict header +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +From: Max Reitz + +There are numerous QDict functions that have been introduced for and are +used only by the block layer. Move their declarations into an own +header file to reflect that. + +While qdict_extract_subqdict() is in fact used outside of the block +layer (in util/qemu-config.c), it is still a function related very +closely to how the block layer works with nested QDicts, namely by +sometimes flattening them. Therefore, its declaration is put into this +header as well and util/qemu-config.c includes it with a comment stating +exactly which function it needs. + +Suggested-by: Markus Armbruster +Signed-off-by: Max Reitz +Message-Id: <20180509165530.29561-7-mreitz@redhat.com> +[Copyright note tweaked, superfluous includes dropped] +Signed-off-by: Markus Armbruster +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit 609f45ea9507fc1603eaeda7f5066b99beac6721) +[Trivial conflict in block/nbd.c resolved] + +Signed-off-by: Miroslav Rezanina +--- + block.c | 1 + + block/gluster.c | 1 + + block/iscsi.c | 1 + + block/nbd.c | 1 + + block/nfs.c | 1 + + block/parallels.c | 1 + + block/qcow.c | 1 + + block/qcow2.c | 1 + + block/qed.c | 1 + + block/quorum.c | 1 + + block/rbd.c | 1 + + block/sheepdog.c | 1 + + block/snapshot.c | 1 + + block/ssh.c | 1 + + block/vhdx.c | 1 + + block/vpc.c | 1 + + block/vvfat.c | 1 + + block/vxhs.c | 1 + + blockdev.c | 1 + + include/block/qdict.h | 32 ++++++++++++++++++++++++++++++++ + include/qapi/qmp/qdict.h | 17 ----------------- + qobject/qdict.c | 1 + + tests/check-qdict.c | 1 + + tests/check-qobject.c | 1 + + tests/test-replication.c | 1 + + util/qemu-config.c | 1 + + 26 files changed, 56 insertions(+), 17 deletions(-) + create mode 100644 include/block/qdict.h + +diff --git a/block.c b/block.c +index 676e57f..3c3e8fd 100644 +--- a/block.c ++++ b/block.c +@@ -27,6 +27,7 @@ + #include "block/block_int.h" + #include "block/blockjob.h" + #include "block/nbd.h" ++#include "block/qdict.h" + #include "qemu/error-report.h" + #include "module_block.h" + #include "qemu/module.h" +diff --git a/block/gluster.c b/block/gluster.c +index 55be566..418bb73 100644 +--- a/block/gluster.c ++++ b/block/gluster.c +@@ -11,6 +11,7 @@ + #include "qemu/osdep.h" + #include + #include "block/block_int.h" ++#include "block/qdict.h" + #include "qapi/error.h" + #include "qapi/qmp/qdict.h" + #include "qapi/qmp/qerror.h" +diff --git a/block/iscsi.c b/block/iscsi.c +index 658462b..1705187 100644 +--- a/block/iscsi.c ++++ b/block/iscsi.c +@@ -33,6 +33,7 @@ + #include "qemu/bitops.h" + #include "qemu/bitmap.h" + #include "block/block_int.h" ++#include "block/qdict.h" + #include "scsi/constants.h" + #include "qemu/iov.h" + #include "qemu/option.h" +diff --git a/block/nbd.c b/block/nbd.c +index 3e1693c..f499830 100644 +--- a/block/nbd.c ++++ b/block/nbd.c +@@ -28,6 +28,7 @@ + + #include "qemu/osdep.h" + #include "block/nbd-client.h" ++#include "block/qdict.h" + #include "qapi/error.h" + #include "qemu/uri.h" + #include "block/block_int.h" +diff --git a/block/nfs.c b/block/nfs.c +index 66fddf1..5159ef0 100644 +--- a/block/nfs.c ++++ b/block/nfs.c +@@ -29,6 +29,7 @@ + #include "qemu/error-report.h" + #include "qapi/error.h" + #include "block/block_int.h" ++#include "block/qdict.h" + #include "trace.h" + #include "qemu/iov.h" + #include "qemu/option.h" +diff --git a/block/parallels.c b/block/parallels.c +index 045810d..0ee1f6a 100644 +--- a/block/parallels.c ++++ b/block/parallels.c +@@ -31,6 +31,7 @@ + #include "qemu/osdep.h" + #include "qapi/error.h" + #include "block/block_int.h" ++#include "block/qdict.h" + #include "sysemu/block-backend.h" + #include "qemu/module.h" + #include "qemu/option.h" +diff --git a/block/qcow.c b/block/qcow.c +index 4b2f7db..fb821ad 100644 +--- a/block/qcow.c ++++ b/block/qcow.c +@@ -26,6 +26,7 @@ + #include "qapi/error.h" + #include "qemu/error-report.h" + #include "block/block_int.h" ++#include "block/qdict.h" + #include "sysemu/block-backend.h" + #include "qemu/module.h" + #include "qemu/option.h" +diff --git a/block/qcow2.c b/block/qcow2.c +index 2f36e63..fa9f557 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -24,6 +24,7 @@ + + #include "qemu/osdep.h" + #include "block/block_int.h" ++#include "block/qdict.h" + #include "sysemu/block-backend.h" + #include "qemu/module.h" + #include +diff --git a/block/qed.c b/block/qed.c +index 1db8eaf..9a8997a 100644 +--- a/block/qed.c ++++ b/block/qed.c +@@ -13,6 +13,7 @@ + */ + + #include "qemu/osdep.h" ++#include "block/qdict.h" + #include "qapi/error.h" + #include "qemu/timer.h" + #include "qemu/bswap.h" +diff --git a/block/quorum.c b/block/quorum.c +index a5051da..f1f39ba 100644 +--- a/block/quorum.c ++++ b/block/quorum.c +@@ -17,6 +17,7 @@ + #include "qemu/cutils.h" + #include "qemu/option.h" + #include "block/block_int.h" ++#include "block/qdict.h" + #include "qapi/error.h" + #include "qapi/qapi-events-block.h" + #include "qapi/qmp/qdict.h" +diff --git a/block/rbd.c b/block/rbd.c +index 2842c0e..e695cf2 100644 +--- a/block/rbd.c ++++ b/block/rbd.c +@@ -18,6 +18,7 @@ + #include "qemu/error-report.h" + #include "qemu/option.h" + #include "block/block_int.h" ++#include "block/qdict.h" + #include "crypto/secret.h" + #include "qemu/cutils.h" + #include "qapi/qmp/qstring.h" +diff --git a/block/sheepdog.c b/block/sheepdog.c +index 07529f4..fd3876f 100644 +--- a/block/sheepdog.c ++++ b/block/sheepdog.c +@@ -24,6 +24,7 @@ + #include "qemu/option.h" + #include "qemu/sockets.h" + #include "block/block_int.h" ++#include "block/qdict.h" + #include "sysemu/block-backend.h" + #include "qemu/bitops.h" + #include "qemu/cutils.h" +diff --git a/block/snapshot.c b/block/snapshot.c +index 2953d96..f9903bc 100644 +--- a/block/snapshot.c ++++ b/block/snapshot.c +@@ -25,6 +25,7 @@ + #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/qerror.h" +diff --git a/block/ssh.c b/block/ssh.c +index 412a1bf..5931064 100644 +--- a/block/ssh.c ++++ b/block/ssh.c +@@ -28,6 +28,7 @@ + #include + + #include "block/block_int.h" ++#include "block/qdict.h" + #include "qapi/error.h" + #include "qemu/error-report.h" + #include "qemu/option.h" +diff --git a/block/vhdx.c b/block/vhdx.c +index c3a4220..26c05aa 100644 +--- a/block/vhdx.c ++++ b/block/vhdx.c +@@ -18,6 +18,7 @@ + #include "qemu/osdep.h" + #include "qapi/error.h" + #include "block/block_int.h" ++#include "block/qdict.h" + #include "sysemu/block-backend.h" + #include "qemu/module.h" + #include "qemu/option.h" +diff --git a/block/vpc.c b/block/vpc.c +index 0ebfcd3..41c8c98 100644 +--- a/block/vpc.c ++++ b/block/vpc.c +@@ -26,6 +26,7 @@ + #include "qemu/osdep.h" + #include "qapi/error.h" + #include "block/block_int.h" ++#include "block/qdict.h" + #include "sysemu/block-backend.h" + #include "qemu/module.h" + #include "qemu/option.h" +diff --git a/block/vvfat.c b/block/vvfat.c +index 662dca0..4595f33 100644 +--- a/block/vvfat.c ++++ b/block/vvfat.c +@@ -27,6 +27,7 @@ + #include + #include "qapi/error.h" + #include "block/block_int.h" ++#include "block/qdict.h" + #include "qemu/module.h" + #include "qemu/option.h" + #include "qemu/bswap.h" +diff --git a/block/vxhs.c b/block/vxhs.c +index 96e83d9..25fea7f 100644 +--- a/block/vxhs.c ++++ b/block/vxhs.c +@@ -13,6 +13,7 @@ + #include + #include + #include "block/block_int.h" ++#include "block/qdict.h" + #include "qapi/qmp/qerror.h" + #include "qapi/qmp/qdict.h" + #include "qapi/qmp/qstring.h" +diff --git a/blockdev.c b/blockdev.c +index 129e444..96a89cc 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -35,6 +35,7 @@ + #include "sysemu/blockdev.h" + #include "hw/block/block.h" + #include "block/blockjob.h" ++#include "block/qdict.h" + #include "block/throttle-groups.h" + #include "monitor/monitor.h" + #include "qemu/error-report.h" +diff --git a/include/block/qdict.h b/include/block/qdict.h +new file mode 100644 +index 0000000..71c037a +--- /dev/null ++++ b/include/block/qdict.h +@@ -0,0 +1,32 @@ ++/* ++ * 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" ++ ++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); ++QObject *qdict_crumple(const QDict *src, Error **errp); ++void qdict_flatten(QDict *qdict); ++ ++typedef struct QDictRenames { ++ const char *from; ++ const char *to; ++} QDictRenames; ++bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp); ++ ++#endif +diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h +index 921a28d..7f3ec10 100644 +--- a/include/qapi/qmp/qdict.h ++++ b/include/qapi/qmp/qdict.h +@@ -67,23 +67,6 @@ int64_t qdict_get_try_int(const QDict *qdict, const char *key, + 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); + +-void qdict_copy_default(QDict *dst, QDict *src, const char *key); +-void qdict_set_default_str(QDict *dst, const char *key, const char *val); +- + QDict *qdict_clone_shallow(const QDict *src); +-void qdict_flatten(QDict *qdict); +- +-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); +-QObject *qdict_crumple(const QDict *src, Error **errp); +- +-void qdict_join(QDict *dest, QDict *src, bool overwrite); +- +-typedef struct QDictRenames { +- const char *from; +- const char *to; +-} QDictRenames; +-bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp); + + #endif /* QDICT_H */ +diff --git a/qobject/qdict.c b/qobject/qdict.c +index 22800ee..0554c64 100644 +--- a/qobject/qdict.c ++++ b/qobject/qdict.c +@@ -11,6 +11,7 @@ + */ + + #include "qemu/osdep.h" ++#include "block/qdict.h" + #include "qapi/qmp/qnum.h" + #include "qapi/qmp/qdict.h" + #include "qapi/qmp/qbool.h" +diff --git a/tests/check-qdict.c b/tests/check-qdict.c +index eba5d35..93e2112 100644 +--- a/tests/check-qdict.c ++++ b/tests/check-qdict.c +@@ -11,6 +11,7 @@ + */ + + #include "qemu/osdep.h" ++#include "block/qdict.h" + #include "qapi/qmp/qdict.h" + #include "qapi/qmp/qlist.h" + #include "qapi/qmp/qnum.h" +diff --git a/tests/check-qobject.c b/tests/check-qobject.c +index 5cb08fc..16ccbde 100644 +--- a/tests/check-qobject.c ++++ b/tests/check-qobject.c +@@ -8,6 +8,7 @@ + */ + + #include "qemu/osdep.h" ++#include "block/qdict.h" + #include "qapi/qmp/qbool.h" + #include "qapi/qmp/qdict.h" + #include "qapi/qmp/qlist.h" +diff --git a/tests/test-replication.c b/tests/test-replication.c +index 68c0d04..c8165ae 100644 +--- a/tests/test-replication.c ++++ b/tests/test-replication.c +@@ -15,6 +15,7 @@ + #include "qemu/option.h" + #include "replication.h" + #include "block/block_int.h" ++#include "block/qdict.h" + #include "sysemu/block-backend.h" + + #define IMG_SIZE (64 * 1024 * 1024) +diff --git a/util/qemu-config.c b/util/qemu-config.c +index 14d8402..9d2e278 100644 +--- a/util/qemu-config.c ++++ b/util/qemu-config.c +@@ -1,4 +1,5 @@ + #include "qemu/osdep.h" ++#include "block/qdict.h" /* for qdict_extract_subqdict() */ + #include "qapi/error.h" + #include "qapi/qapi-commands-misc.h" + #include "qapi/qmp/qdict.h" +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Add-missing-locking-in-bdrv_co_drain_bh_cb.patch b/SOURCES/kvm-block-Add-missing-locking-in-bdrv_co_drain_bh_cb.patch new file mode 100644 index 0000000..d068f40 --- /dev/null +++ b/SOURCES/kvm-block-Add-missing-locking-in-bdrv_co_drain_bh_cb.patch @@ -0,0 +1,94 @@ +From e0603f9fadc9d8cabd0d47c09dc99880aafcc905 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:31 +0200 +Subject: [PATCH 40/49] block: Add missing locking in bdrv_co_drain_bh_cb() + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-34-kwolf@redhat.com> +Patchwork-id: 82185 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 33/42] block: Add missing locking in bdrv_co_drain_bh_cb() +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +bdrv_do_drained_begin/end() assume that they are called with the +AioContext lock of bs held. If we call drain functions from a coroutine +with the AioContext lock held, we yield and schedule a BH to move out of +coroutine context. This means that the lock for the home context of the +coroutine is released and must be re-acquired in the bottom half. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/io.c | 15 +++++++++++++++ + include/qemu/coroutine.h | 5 +++++ + util/qemu-coroutine.c | 5 +++++ + 3 files changed, 25 insertions(+) + +diff --git a/block/io.c b/block/io.c +index d404088..19db35e 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -286,6 +286,18 @@ static void bdrv_co_drain_bh_cb(void *opaque) + BlockDriverState *bs = data->bs; + + if (bs) { ++ AioContext *ctx = bdrv_get_aio_context(bs); ++ AioContext *co_ctx = qemu_coroutine_get_aio_context(co); ++ ++ /* ++ * When the coroutine yielded, the lock for its home context was ++ * released, so we need to re-acquire it here. If it explicitly ++ * acquired a different context, the lock is still held and we don't ++ * want to lock it a second time (or AIO_WAIT_WHILE() would hang). ++ */ ++ if (ctx == co_ctx) { ++ aio_context_acquire(ctx); ++ } + bdrv_dec_in_flight(bs); + if (data->begin) { + bdrv_do_drained_begin(bs, data->recursive, data->parent, +@@ -294,6 +306,9 @@ static void bdrv_co_drain_bh_cb(void *opaque) + bdrv_do_drained_end(bs, data->recursive, data->parent, + data->ignore_bds_parents); + } ++ if (ctx == co_ctx) { ++ aio_context_release(ctx); ++ } + } else { + assert(data->begin); + bdrv_drain_all_begin(); +diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h +index 6f8a487..9801e7f 100644 +--- a/include/qemu/coroutine.h ++++ b/include/qemu/coroutine.h +@@ -90,6 +90,11 @@ void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co); + void coroutine_fn qemu_coroutine_yield(void); + + /** ++ * Get the AioContext of the given coroutine ++ */ ++AioContext *coroutine_fn qemu_coroutine_get_aio_context(Coroutine *co); ++ ++/** + * Get the currently executing coroutine + */ + Coroutine *coroutine_fn qemu_coroutine_self(void); +diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c +index 1ba4191..2295928 100644 +--- a/util/qemu-coroutine.c ++++ b/util/qemu-coroutine.c +@@ -198,3 +198,8 @@ bool qemu_coroutine_entered(Coroutine *co) + { + return co->caller; + } ++ ++AioContext *coroutine_fn qemu_coroutine_get_aio_context(Coroutine *co) ++{ ++ return co->ctx; ++} +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Advertise-BDRV_REQ_NO_FALLBACK-in-filter-drive.patch b/SOURCES/kvm-block-Advertise-BDRV_REQ_NO_FALLBACK-in-filter-drive.patch new file mode 100644 index 0000000..8fade5d --- /dev/null +++ b/SOURCES/kvm-block-Advertise-BDRV_REQ_NO_FALLBACK-in-filter-drive.patch @@ -0,0 +1,99 @@ +From f8516c7e4c09fce49f49d065a1facd6240e9c8c3 Mon Sep 17 00:00:00 2001 +From: Maxim Levitsky +Date: Wed, 5 Jun 2019 13:56:59 +0200 +Subject: [PATCH 11/23] block: Advertise BDRV_REQ_NO_FALLBACK in filter drivers + +RH-Author: Maxim Levitsky +Message-id: <20190605135705.24526-4-mlevitsk@redhat.com> +Patchwork-id: 88559 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 3/9] block: Advertise BDRV_REQ_NO_FALLBACK in filter drivers +Bugzilla: 1648622 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow + +From: Kevin Wolf + +Filter drivers that support .bdrv_co_pwrite_zeroes can safely advertise +BDRV_REQ_NO_FALLBACK because they just forward the request flags to +their child node. + +Signed-off-by: Kevin Wolf +Acked-by: Eric Blake + +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1648622 + +Signed-off-by: Maxim Levitsky +(Cherry picked from 80f5c33ff31eb9333f5036ee278fb1483fb4ff41 with some conflicts) + +Signed-off-by: Miroslav Rezanina +--- + block/blkdebug.c | 2 +- + block/copy-on-read.c | 7 +++---- + block/mirror.c | 3 ++- + block/raw-format.c | 2 +- + 4 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/block/blkdebug.c b/block/blkdebug.c +index 526af2a..bb38e70 100644 +--- a/block/blkdebug.c ++++ b/block/blkdebug.c +@@ -401,7 +401,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, + 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_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & + bs->file->bs->supported_zero_flags); + ret = -EINVAL; + +diff --git a/block/copy-on-read.c b/block/copy-on-read.c +index 1dcdaee..dfa40a9 100644 +--- a/block/copy-on-read.c ++++ b/block/copy-on-read.c +@@ -34,12 +34,11 @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags, + } + + bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | +- (BDRV_REQ_FUA & +- bs->file->bs->supported_write_flags); ++ (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); + + bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | +- ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & +- bs->file->bs->supported_zero_flags); ++ ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & ++ bs->file->bs->supported_zero_flags); + + return 0; + } +diff --git a/block/mirror.c b/block/mirror.c +index 8658873..55dc94f 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -1175,7 +1175,8 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, + } + 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; ++ mirror_top_bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | ++ BDRV_REQ_NO_FALLBACK; + bdrv_set_aio_context(mirror_top_bs, bdrv_get_aio_context(bs)); + + /* bdrv_append takes ownership of the mirror_top_bs reference, need to keep +diff --git a/block/raw-format.c b/block/raw-format.c +index a359198..f5d26cd 100644 +--- a/block/raw-format.c ++++ b/block/raw-format.c +@@ -432,7 +432,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, + 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_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & + bs->file->bs->supported_zero_flags); + + if (bs->probed && !bdrv_is_read_only(bs)) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Allow-AIO_WAIT_WHILE-with-NULL-ctx.patch b/SOURCES/kvm-block-Allow-AIO_WAIT_WHILE-with-NULL-ctx.patch new file mode 100644 index 0000000..0b194a8 --- /dev/null +++ b/SOURCES/kvm-block-Allow-AIO_WAIT_WHILE-with-NULL-ctx.patch @@ -0,0 +1,74 @@ +From e776fb3350daea9851190d6445b4786f4b1f493f Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:14 +0200 +Subject: [PATCH 23/49] block: Allow AIO_WAIT_WHILE with NULL ctx + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-17-kwolf@redhat.com> +Patchwork-id: 82168 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 16/42] block: Allow AIO_WAIT_WHILE with NULL ctx +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +bdrv_drain_all() wants to have a single polling loop for draining the +in-flight requests of all nodes. This means that the AIO_WAIT_WHILE() +condition relies on activity in multiple AioContexts, which is polled +from the mainloop context. We must therefore call AIO_WAIT_WHILE() from +the mainloop thread and use the AioWait notification mechanism. + +Just randomly picking the AioContext of any non-mainloop thread would +work, but instead of bothering to find such a context in the caller, we +can just as well accept NULL for ctx. + +Signed-off-by: Kevin Wolf +(cherry picked from commit 4d22bbf4ef72583eefdf44db6bf9fc7683fbc4c2) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + include/block/aio-wait.h | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h +index 783d367..c85a62f 100644 +--- a/include/block/aio-wait.h ++++ b/include/block/aio-wait.h +@@ -57,7 +57,8 @@ typedef struct { + /** + * AIO_WAIT_WHILE: + * @wait: the aio wait object +- * @ctx: the aio context ++ * @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 +@@ -75,7 +76,7 @@ typedef struct { + bool waited_ = false; \ + AioWait *wait_ = (wait); \ + AioContext *ctx_ = (ctx); \ +- if (in_aio_context_home_thread(ctx_)) { \ ++ if (ctx_ && in_aio_context_home_thread(ctx_)) { \ + while ((cond)) { \ + aio_poll(ctx_, true); \ + waited_ = true; \ +@@ -86,9 +87,13 @@ typedef struct { + /* Increment wait_->num_waiters before evaluating cond. */ \ + atomic_inc(&wait_->num_waiters); \ + while ((cond)) { \ +- aio_context_release(ctx_); \ ++ if (ctx_) { \ ++ aio_context_release(ctx_); \ ++ } \ + aio_poll(qemu_get_aio_context(), true); \ +- aio_context_acquire(ctx_); \ ++ if (ctx_) { \ ++ aio_context_acquire(ctx_); \ ++ } \ + waited_ = true; \ + } \ + atomic_dec(&wait_->num_waiters); \ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Allow-graph-changes-in-bdrv_drain_all_begin-en.patch b/SOURCES/kvm-block-Allow-graph-changes-in-bdrv_drain_all_begin-en.patch new file mode 100644 index 0000000..2c8f4b6 --- /dev/null +++ b/SOURCES/kvm-block-Allow-graph-changes-in-bdrv_drain_all_begin-en.patch @@ -0,0 +1,266 @@ +From 78fde43fee8eaaa0fd1222844961614f576ebd7c Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:17 +0200 +Subject: [PATCH 26/49] block: Allow graph changes in bdrv_drain_all_begin/end + sections + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-20-kwolf@redhat.com> +Patchwork-id: 82171 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 19/42] block: Allow graph changes in bdrv_drain_all_begin/end sections +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +bdrv_drain_all_*() used bdrv_next() to iterate over all root nodes and +did a subtree drain for each of them. This works fine as long as the +graph is static, but sadly, reality looks different. + +If the graph changes so that root nodes are added or removed, we would +have to compensate for this. bdrv_next() returns each root node only +once even if it's the root node for multiple BlockBackends or for a +monitor-owned block driver tree, which would only complicate things. + +The much easier and more obviously correct way is to fundamentally +change the way the functions work: Iterate over all BlockDriverStates, +no matter who owns them, and drain them individually. Compensation is +only necessary when a new BDS is created inside a drain_all section. +Removal of a BDS doesn't require any action because it's gone afterwards +anyway. + +Signed-off-by: Kevin Wolf +(cherry picked from commit 0f12264e7a41458179ad10276a7c33c72024861a) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 34 ++++++++++++++++++++++++--- + block/io.c | 60 ++++++++++++++++++++++++++++++++++++----------- + include/block/block.h | 1 + + include/block/block_int.h | 1 + + 4 files changed, 79 insertions(+), 17 deletions(-) + +diff --git a/block.c b/block.c +index 519be6e..eea9c6f 100644 +--- a/block.c ++++ b/block.c +@@ -333,6 +333,10 @@ BlockDriverState *bdrv_new(void) + + qemu_co_queue_init(&bs->flush_queue); + ++ for (i = 0; i < bdrv_drain_all_count; i++) { ++ bdrv_drained_begin(bs); ++ } ++ + QTAILQ_INSERT_TAIL(&all_bdrv_states, bs, bs_list); + + return bs; +@@ -1170,7 +1174,7 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, + int open_flags, Error **errp) + { + Error *local_err = NULL; +- int ret; ++ int i, ret; + + bdrv_assign_node_name(bs, node_name, &local_err); + if (local_err) { +@@ -1218,6 +1222,12 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, + 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_co_drain_begin) { ++ drv->bdrv_co_drain_begin(bs); ++ } ++ } ++ + return 0; + open_failed: + bs->drv = NULL; +@@ -2039,7 +2049,12 @@ static void bdrv_replace_child_noperm(BdrvChild *child, + child->role->detach(child); + } + if (old_bs->quiesce_counter && child->role->drained_end) { +- for (i = 0; i < old_bs->quiesce_counter; i++) { ++ int num = old_bs->quiesce_counter; ++ if (child->role->parent_is_bds) { ++ num -= bdrv_drain_all_count; ++ } ++ assert(num >= 0); ++ for (i = 0; i < num; i++) { + child->role->drained_end(child); + } + } +@@ -2051,7 +2066,12 @@ static void bdrv_replace_child_noperm(BdrvChild *child, + if (new_bs) { + QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent); + if (new_bs->quiesce_counter && child->role->drained_begin) { +- for (i = 0; i < new_bs->quiesce_counter; i++) { ++ int num = new_bs->quiesce_counter; ++ if (child->role->parent_is_bds) { ++ num -= bdrv_drain_all_count; ++ } ++ assert(num >= 0); ++ for (i = 0; i < num; i++) { + child->role->drained_begin(child); + } + } +@@ -3993,6 +4013,14 @@ BlockDriverState *bdrv_next_node(BlockDriverState *bs) + return QTAILQ_NEXT(bs, node_list); + } + ++BlockDriverState *bdrv_next_all_states(BlockDriverState *bs) ++{ ++ if (!bs) { ++ return QTAILQ_FIRST(&all_bdrv_states); ++ } ++ return QTAILQ_NEXT(bs, bs_list); ++} ++ + const char *bdrv_get_node_name(const BlockDriverState *bs) + { + return bs->node_name; +diff --git a/block/io.c b/block/io.c +index 0021fefd..38ae299 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -38,6 +38,8 @@ + /* Maximum bounce buffer for copy-on-read and write zeroes, in bytes */ + #define MAX_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS) + ++static AioWait drain_all_aio_wait; ++ + static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, + int64_t offset, int bytes, BdrvRequestFlags flags); + +@@ -471,6 +473,29 @@ static void bdrv_drain_assert_idle(BlockDriverState *bs) + } + } + ++unsigned int bdrv_drain_all_count = 0; ++ ++static bool bdrv_drain_all_poll(void) ++{ ++ BlockDriverState *bs = NULL; ++ bool result = false; ++ ++ /* Execute pending BHs first (may modify the graph) and check everything ++ * else only after the BHs have executed. */ ++ while (aio_poll(qemu_get_aio_context(), false)); ++ ++ /* bdrv_drain_poll() can't make changes to the graph and we are holding the ++ * main AioContext lock, so iterating bdrv_next_all_states() is safe. */ ++ while ((bs = bdrv_next_all_states(bs))) { ++ AioContext *aio_context = bdrv_get_aio_context(bs); ++ aio_context_acquire(aio_context); ++ result |= bdrv_drain_poll(bs, false, NULL, true); ++ aio_context_release(aio_context); ++ } ++ ++ return result; ++} ++ + /* + * Wait for pending requests to complete across all BlockDriverStates + * +@@ -485,45 +510,51 @@ static void bdrv_drain_assert_idle(BlockDriverState *bs) + */ + void bdrv_drain_all_begin(void) + { +- BlockDriverState *bs; +- BdrvNextIterator it; ++ BlockDriverState *bs = NULL; + + if (qemu_in_coroutine()) { +- bdrv_co_yield_to_drain(NULL, true, false, NULL, false, true); ++ bdrv_co_yield_to_drain(NULL, true, false, NULL, true, true); + return; + } + +- /* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread +- * or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on +- * nodes in several different AioContexts, so make sure we're in the main +- * context. */ ++ /* 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++; + +- for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { ++ /* Quiesce all nodes, without polling in-flight requests yet. The graph ++ * cannot change during this loop. */ ++ while ((bs = bdrv_next_all_states(bs))) { + AioContext *aio_context = bdrv_get_aio_context(bs); + + aio_context_acquire(aio_context); +- bdrv_do_drained_begin(bs, true, NULL, false, true); ++ bdrv_do_drained_begin(bs, false, NULL, true, false); + aio_context_release(aio_context); + } + +- for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { ++ /* Now poll the in-flight requests */ ++ AIO_WAIT_WHILE(&drain_all_aio_wait, NULL, bdrv_drain_all_poll()); ++ ++ while ((bs = bdrv_next_all_states(bs))) { + bdrv_drain_assert_idle(bs); + } + } + + void bdrv_drain_all_end(void) + { +- BlockDriverState *bs; +- BdrvNextIterator it; ++ BlockDriverState *bs = NULL; + +- for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { ++ while ((bs = bdrv_next_all_states(bs))) { + AioContext *aio_context = bdrv_get_aio_context(bs); + + aio_context_acquire(aio_context); +- bdrv_do_drained_end(bs, true, NULL, false); ++ bdrv_do_drained_end(bs, false, NULL, true); + aio_context_release(aio_context); + } ++ ++ assert(bdrv_drain_all_count > 0); ++ bdrv_drain_all_count--; + } + + void bdrv_drain_all(void) +@@ -658,6 +689,7 @@ void bdrv_inc_in_flight(BlockDriverState *bs) + void bdrv_wakeup(BlockDriverState *bs) + { + aio_wait_kick(bdrv_get_aio_wait(bs)); ++ aio_wait_kick(&drain_all_aio_wait); + } + + void bdrv_dec_in_flight(BlockDriverState *bs) +diff --git a/include/block/block.h b/include/block/block.h +index 6e91803..f9079ac 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -449,6 +449,7 @@ BlockDriverState *bdrv_lookup_bs(const char *device, + Error **errp); + bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base); + BlockDriverState *bdrv_next_node(BlockDriverState *bs); ++BlockDriverState *bdrv_next_all_states(BlockDriverState *bs); + + typedef struct BdrvNextIterator { + enum { +diff --git a/include/block/block_int.h b/include/block/block_int.h +index 0ad8a76..9757d5e 100644 +--- a/include/block/block_int.h ++++ b/include/block/block_int.h +@@ -845,6 +845,7 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child, + int64_t offset, unsigned int bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags); + ++extern unsigned int bdrv_drain_all_count; + void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent); + void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent); + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Always-abort-reopen-after-prepare-succeeded.patch b/SOURCES/kvm-block-Always-abort-reopen-after-prepare-succeeded.patch new file mode 100644 index 0000000..7c107ef --- /dev/null +++ b/SOURCES/kvm-block-Always-abort-reopen-after-prepare-succeeded.patch @@ -0,0 +1,84 @@ +From 168f27ca545f530ac694ed596bdafc8fa3c26860 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 15 Mar 2019 18:10:00 +0100 +Subject: [PATCH 004/163] block: Always abort reopen after prepare succeeded + +RH-Author: Kevin Wolf +Message-id: <20190315181010.14964-5-kwolf@redhat.com> +Patchwork-id: 84881 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 04/14] block: Always abort reopen after prepare succeeded +Bugzilla: 1685989 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Max Reitz + +bdrv_reopen_multiple() does not invoke bdrv_reopen_abort() for the +element of the reopen queue for which bdrv_reopen_prepare() failed, +because it assumes that the prepare function will have rolled back all +changes already. + +However, bdrv_reopen_prepare() does not do this in every case: It may +notice an error after BlockDriver.bdrv_reopen_prepare() succeeded, and +it will not invoke BlockDriver.bdrv_reopen_abort() then; and neither +will bdrv_reopen_multiple(), as explained above. + +This is wrong because we must always call .bdrv_reopen_commit() or +.bdrv_reopen_abort() after .bdrv_reopen_prepare() has succeeded. +Otherwise, the block driver has no chance to undo what it has done in +its implementation of .bdrv_reopen_prepare(). + +To fix this, bdrv_reopen_prepare() has to call .bdrv_reopen_abort() if +it wants to return an error after .bdrv_reopen_prepare() has succeeded. + +Signed-off-by: Max Reitz +Reviewed-by: Alberto Garcia +Signed-off-by: Kevin Wolf +(cherry picked from commit 9ad08c44566bf4466c6263c71d43e9f7a354d4ba) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/block.c b/block.c +index c3148cc..2f1b4d1 100644 +--- a/block.c ++++ b/block.c +@@ -3191,6 +3191,7 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, + QemuOpts *opts; + const char *value; + bool read_only; ++ bool drv_prepared = false; + + assert(reopen_state != NULL); + assert(reopen_state->bs->drv != NULL); +@@ -3260,6 +3261,8 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, + goto error; + } + ++ drv_prepared = true; ++ + /* 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) */ +@@ -3303,6 +3306,15 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, + ret = 0; + + 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); + return ret; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Apply-auto-read-only-for-ro-whitelist-drivers.patch b/SOURCES/kvm-block-Apply-auto-read-only-for-ro-whitelist-drivers.patch new file mode 100644 index 0000000..9534937 --- /dev/null +++ b/SOURCES/kvm-block-Apply-auto-read-only-for-ro-whitelist-drivers.patch @@ -0,0 +1,66 @@ +From c13468845efde058e464deeaf8b58508f33dc948 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 19 Feb 2019 16:29:56 +0100 +Subject: [PATCH 09/23] block: Apply auto-read-only for ro-whitelist drivers + +RH-Author: Kevin Wolf +Message-id: <20190219162956.16183-2-kwolf@redhat.com> +Patchwork-id: 84539 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/1] block: Apply auto-read-only for ro-whitelist drivers +Bugzilla: 1667320 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +If QEMU was configured with a driver in --block-drv-ro-whitelist, trying +to use that driver read-write resulted in an error message even if +auto-read-only=on was set. + +Consider auto-read-only=on for the whitelist checking and use it to +automatically degrade to read-only for block drivers on the read-only +whitelist. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +Reviewed-by: Stefan Hajnoczi +(cherry picked from commit 8be25de64315ef768353eb61f2b2bf6cddc34230) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 20 +++++++++++++------- + 1 file changed, 13 insertions(+), 7 deletions(-) + +diff --git a/block.c b/block.c +index 82b16df..a69d0a2 100644 +--- a/block.c ++++ b/block.c +@@ -1418,13 +1418,19 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, + bs->read_only = !(bs->open_flags & BDRV_O_RDWR); + + if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) { +- error_setg(errp, +- !bs->read_only && bdrv_is_whitelisted(drv, true) +- ? "Driver '%s' can only be used for read-only devices" +- : "Driver '%s' is not whitelisted", +- drv->format_name); +- ret = -ENOTSUP; +- goto fail_opts; ++ if (!bs->read_only && bdrv_is_whitelisted(drv, true)) { ++ ret = bdrv_apply_auto_read_only(bs, NULL, NULL); ++ } else { ++ ret = -ENOTSUP; ++ } ++ if (ret < 0) { ++ error_setg(errp, ++ !bs->read_only && 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 */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Avoid-unnecessary-aio_poll-in-AIO_WAIT_WHILE.patch b/SOURCES/kvm-block-Avoid-unnecessary-aio_poll-in-AIO_WAIT_WHILE.patch new file mode 100644 index 0000000..d3d2560 --- /dev/null +++ b/SOURCES/kvm-block-Avoid-unnecessary-aio_poll-in-AIO_WAIT_WHILE.patch @@ -0,0 +1,112 @@ +From 1a6556bc1317af4669d058e6df70bc1c036d37a5 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:04 +0200 +Subject: [PATCH 13/49] block: Avoid unnecessary aio_poll() in AIO_WAIT_WHILE() + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-7-kwolf@redhat.com> +Patchwork-id: 82158 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 06/42] block: Avoid unnecessary aio_poll() in AIO_WAIT_WHILE() +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Commit 91af091f923 added an additional aio_poll() to BDRV_POLL_WHILE() +in order to make sure that all pending BHs are executed on drain. This +was the wrong place to make the fix, as it is useless overhead for all +other users of the macro and unnecessarily complicates the mechanism. + +This patch effectively reverts said commit (the context has changed a +bit and the code has moved to AIO_WAIT_WHILE()) and instead polls in the +loop condition for drain. + +The effect is probably hard to measure in any real-world use case +because actual I/O will dominate, but if I run only the initialisation +part of 'qemu-img convert' where it calls bdrv_block_status() for the +whole image to find out how much data there is copy, this phase actually +needs only roughly half the time after this patch. + +Signed-off-by: Kevin Wolf +Reviewed-by: Stefan Hajnoczi +(cherry picked from commit 1cc8e54ada97f7ac479554e15ca9e426c895b158) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/io.c | 11 ++++++++++- + include/block/aio-wait.h | 22 ++++++++-------------- + 2 files changed, 18 insertions(+), 15 deletions(-) + +diff --git a/block/io.c b/block/io.c +index e5fc42c..4d332c3 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -181,13 +181,22 @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) + BDRV_POLL_WHILE(bs, !data.done); + } + ++/* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */ ++static bool bdrv_drain_poll(BlockDriverState *bs) ++{ ++ /* Execute pending BHs first and check everything else only after the BHs ++ * have executed. */ ++ while (aio_poll(bs->aio_context, false)); ++ return atomic_read(&bs->in_flight); ++} ++ + static bool bdrv_drain_recurse(BlockDriverState *bs) + { + BdrvChild *child, *tmp; + bool waited; + + /* Wait for drained requests to finish */ +- waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0); ++ waited = BDRV_POLL_WHILE(bs, bdrv_drain_poll(bs)); + + QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) { + BlockDriverState *bs = child->bs; +diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h +index 8c90a2e..783d367 100644 +--- a/include/block/aio-wait.h ++++ b/include/block/aio-wait.h +@@ -73,29 +73,23 @@ typedef struct { + */ + #define AIO_WAIT_WHILE(wait, ctx, cond) ({ \ + bool waited_ = false; \ +- bool busy_ = true; \ + AioWait *wait_ = (wait); \ + AioContext *ctx_ = (ctx); \ + if (in_aio_context_home_thread(ctx_)) { \ +- while ((cond) || busy_) { \ +- busy_ = aio_poll(ctx_, (cond)); \ +- waited_ |= !!(cond) | busy_; \ ++ while ((cond)) { \ ++ aio_poll(ctx_, true); \ ++ waited_ = true; \ + } \ + } else { \ + assert(qemu_get_current_aio_context() == \ + qemu_get_aio_context()); \ + /* Increment wait_->num_waiters before evaluating cond. */ \ + atomic_inc(&wait_->num_waiters); \ +- while (busy_) { \ +- if ((cond)) { \ +- waited_ = busy_ = true; \ +- aio_context_release(ctx_); \ +- aio_poll(qemu_get_aio_context(), true); \ +- aio_context_acquire(ctx_); \ +- } else { \ +- busy_ = aio_poll(ctx_, false); \ +- waited_ |= busy_; \ +- } \ ++ while ((cond)) { \ ++ aio_context_release(ctx_); \ ++ aio_poll(qemu_get_aio_context(), true); \ ++ aio_context_acquire(ctx_); \ ++ waited_ = true; \ + } \ + atomic_dec(&wait_->num_waiters); \ + } \ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Avoid-useless-local_err.patch b/SOURCES/kvm-block-Avoid-useless-local_err.patch new file mode 100644 index 0000000..a772e0c --- /dev/null +++ b/SOURCES/kvm-block-Avoid-useless-local_err.patch @@ -0,0 +1,47 @@ +From 6e21eb2623d4103257fc9d849a639eb70ae20920 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 15 Mar 2019 18:09:58 +0100 +Subject: [PATCH 002/163] block: Avoid useless local_err + +RH-Author: Kevin Wolf +Message-id: <20190315181010.14964-3-kwolf@redhat.com> +Patchwork-id: 84879 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 02/14] block: Avoid useless local_err +Bugzilla: 1685989 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +Signed-off-by: Kevin Wolf +Reviewed-by: Alberto Garcia +Reviewed-by: Eric Blake +(cherry picked from commit a4615ab31cade201641cbb17a81dcac1b3bea624) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/block.c b/block.c +index c47e5b0..ccf9acb 100644 +--- a/block.c ++++ b/block.c +@@ -3061,14 +3061,12 @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er + { + int ret = -1; + BlockReopenQueueEntry *bs_entry, *next; +- Error *local_err = NULL; + + assert(bs_queue != NULL); + + QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) { + assert(bs_entry->state.bs->quiesce_counter > 0); +- if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) { +- error_propagate(errp, local_err); ++ if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, errp)) { + goto cleanup; + } + bs_entry->prepared = true; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-BLK_PERM_WRITE-includes-._UNCHANGED.patch b/SOURCES/kvm-block-BLK_PERM_WRITE-includes-._UNCHANGED.patch new file mode 100644 index 0000000..6f6bb02 --- /dev/null +++ b/SOURCES/kvm-block-BLK_PERM_WRITE-includes-._UNCHANGED.patch @@ -0,0 +1,50 @@ +From 68b3e533a4531486fe346f042e190fb5c426f24f Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 16:12:05 +0200 +Subject: [PATCH 29/54] block: BLK_PERM_WRITE includes ..._UNCHANGED + +RH-Author: Max Reitz +Message-id: <20180618161212.14444-4-mreitz@redhat.com> +Patchwork-id: 80763 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 03/10] block: BLK_PERM_WRITE includes ..._UNCHANGED +Bugzilla: 1518738 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Miroslav Rezanina + +Currently we never actually check whether the WRITE_UNCHANGED +permission has been taken for unchanging writes. But the one check that +is commented out checks both WRITE and WRITE_UNCHANGED; and considering +that WRITE_UNCHANGED is already documented as being weaker than WRITE, +we should probably explicitly document WRITE to include WRITE_UNCHANGED. + +Signed-off-by: Max Reitz +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Alberto Garcia +Message-id: 20180421132929.21610-3-mreitz@redhat.com +Reviewed-by: Kevin Wolf +Signed-off-by: Max Reitz +(cherry picked from commit 24b7c538fea86b598e2a335f4805a0ab50a30e98) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + include/block/block.h | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/include/block/block.h b/include/block/block.h +index cdec363..397b5e8 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -205,6 +205,9 @@ enum { + * 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, + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Cancel-job-in-bdrv_close_all-callers.patch b/SOURCES/kvm-block-Cancel-job-in-bdrv_close_all-callers.patch new file mode 100644 index 0000000..1c03077 --- /dev/null +++ b/SOURCES/kvm-block-Cancel-job-in-bdrv_close_all-callers.patch @@ -0,0 +1,85 @@ +From 7ea7ac78a4d38bc1873df660406b25949515cf07 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:24 +0200 +Subject: [PATCH 55/89] block: Cancel job in bdrv_close_all() callers + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-42-kwolf@redhat.com> +Patchwork-id: 81098 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 41/73] block: Cancel job in bdrv_close_all() callers +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Now that we cancel all jobs and not only block jobs on shutdown, doing +that in bdrv_close_all() isn't really appropriate any more. Move the +job_cancel_sync_all() call to the callers, and only assert that there +are no job running in bdrv_close_all(). + +Signed-off-by: Kevin Wolf +(cherry picked from commit b3b5299d58bce4366c647af40374e6b063f371eb) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 4 +--- + qemu-nbd.c | 8 +++++++- + vl.c | 1 + + 3 files changed, 9 insertions(+), 4 deletions(-) + +diff --git a/block.c b/block.c +index a83787f..afe30ca 100644 +--- a/block.c ++++ b/block.c +@@ -3374,9 +3374,7 @@ static void bdrv_close(BlockDriverState *bs) + + void bdrv_close_all(void) + { +- /* TODO We do want to cancel all jobs instead of just block jobs on +- * shutdown, but bdrv_close_all() isn't the right place any more. */ +- job_cancel_sync_all(); ++ assert(job_next(NULL) == NULL); + nbd_export_close_all(); + + /* Drop references from requests still in flight, such as canceled block +diff --git a/qemu-nbd.c b/qemu-nbd.c +index 0af0560..51b9d38 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -482,6 +482,12 @@ static const char *socket_activation_validate_opts(const char *device, + return NULL; + } + ++static void qemu_nbd_shutdown(void) ++{ ++ job_cancel_sync_all(); ++ bdrv_close_all(); ++} ++ + int main(int argc, char **argv) + { + BlockBackend *blk; +@@ -928,7 +934,7 @@ int main(int argc, char **argv) + exit(EXIT_FAILURE); + } + bdrv_init(); +- atexit(bdrv_close_all); ++ atexit(qemu_nbd_shutdown); + + srcpath = argv[optind]; + if (imageOpts) { +diff --git a/vl.c b/vl.c +index f3acab3..713f899 100644 +--- a/vl.c ++++ b/vl.c +@@ -4772,6 +4772,7 @@ int main(int argc, char **argv, char **envp) + /* No more vcpu or device emulation activity beyond this point */ + vm_shutdown(); + ++ job_cancel_sync_all(); + bdrv_close_all(); + + res_free(); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Clean-up-a-misuse-of-qobject_to-in-.bdrv_co_cr.patch b/SOURCES/kvm-block-Clean-up-a-misuse-of-qobject_to-in-.bdrv_co_cr.patch new file mode 100644 index 0000000..2e35356 --- /dev/null +++ b/SOURCES/kvm-block-Clean-up-a-misuse-of-qobject_to-in-.bdrv_co_cr.patch @@ -0,0 +1,241 @@ +From 48749c03c412fa59ca643bbd4defd341903c33c1 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:19 +0200 +Subject: [PATCH 12/54] block: Clean up a misuse of qobject_to() in + .bdrv_co_create_opts() + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-13-armbru@redhat.com> +Patchwork-id: 80733 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 12/23] block: Clean up a misuse of qobject_to() in .bdrv_co_create_opts() +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +The following pattern occurs in the .bdrv_co_create_opts() methods of +parallels, qcow, qcow2, qed, vhdx and vpc: + + qobj = qdict_crumple_for_keyval_qiv(qdict, errp); + qobject_unref(qdict); + qdict = qobject_to(QDict, qobj); + if (qdict == NULL) { + ret = -EINVAL; + goto done; + } + + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + [...] + ret = 0; +done: + qobject_unref(qdict); + [...] + return ret; + +If qobject_to() fails, we return failure without setting errp. That's +wrong. As far as I can tell, it cannot fail here. Clean it up +anyway, by removing the useless conversion. + +Signed-off-by: Markus Armbruster +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit 92adf9dbcd3cf9cedddae995b04a99f5c42ae08c) +Signed-off-by: Miroslav Rezanina +--- + block/parallels.c | 9 ++++----- + block/qcow.c | 9 ++++----- + block/qcow2.c | 9 ++++----- + block/qed.c | 9 ++++----- + block/vhdx.c | 9 ++++----- + block/vpc.c | 9 ++++----- + 6 files changed, 24 insertions(+), 30 deletions(-) + +diff --git a/block/parallels.c b/block/parallels.c +index aa58955..1c96c39 100644 +--- a/block/parallels.c ++++ b/block/parallels.c +@@ -614,7 +614,7 @@ static int coroutine_fn parallels_co_create_opts(const char *filename, + BlockdevCreateOptions *create_options = NULL; + Error *local_err = NULL; + BlockDriverState *bs = NULL; +- QDict *qdict = NULL; ++ QDict *qdict; + QObject *qobj; + Visitor *v; + int ret; +@@ -652,14 +652,13 @@ static int coroutine_fn parallels_co_create_opts(const char *filename, + qdict_put_str(qdict, "file", bs->node_name); + + qobj = qdict_crumple_for_keyval_qiv(qdict, errp); +- qobject_unref(qdict); +- qdict = qobject_to(QDict, qobj); +- if (qdict == NULL) { ++ if (!qobj) { + ret = -EINVAL; + goto done; + } + +- v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); ++ v = qobject_input_visitor_new_keyval(qobj); ++ qobject_unref(qobj); + visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); + visit_free(v); + +diff --git a/block/qcow.c b/block/qcow.c +index 14b7296..43a595a 100644 +--- a/block/qcow.c ++++ b/block/qcow.c +@@ -944,7 +944,7 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, + { + BlockdevCreateOptions *create_options = NULL; + BlockDriverState *bs = NULL; +- QDict *qdict = NULL; ++ QDict *qdict; + QObject *qobj; + Visitor *v; + const char *val; +@@ -996,14 +996,13 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, + qdict_put_str(qdict, "file", bs->node_name); + + qobj = qdict_crumple_for_keyval_qiv(qdict, errp); +- qobject_unref(qdict); +- qdict = qobject_to(QDict, qobj); +- if (qdict == NULL) { ++ if (!qobj) { + ret = -EINVAL; + goto fail; + } + +- v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); ++ v = qobject_input_visitor_new_keyval(qobj); ++ qobject_unref(qobj); + visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); + visit_free(v); + +diff --git a/block/qcow2.c b/block/qcow2.c +index fa06b41..ede52a8 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -3067,7 +3067,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt + Error **errp) + { + BlockdevCreateOptions *create_options = NULL; +- QDict *qdict = NULL; ++ QDict *qdict; + QObject *qobj; + Visitor *v; + BlockDriverState *bs = NULL; +@@ -3140,14 +3140,13 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt + + /* Now get the QAPI type BlockdevCreateOptions */ + qobj = qdict_crumple_for_keyval_qiv(qdict, errp); +- qobject_unref(qdict); +- qdict = qobject_to(QDict, qobj); +- if (qdict == NULL) { ++ if (!qobj) { + ret = -EINVAL; + goto finish; + } + +- v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); ++ v = qobject_input_visitor_new_keyval(qobj); ++ qobject_unref(qobj); + visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); + visit_free(v); + +diff --git a/block/qed.c b/block/qed.c +index d8810f5..3818888 100644 +--- a/block/qed.c ++++ b/block/qed.c +@@ -722,7 +722,7 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, + Error **errp) + { + BlockdevCreateOptions *create_options = NULL; +- QDict *qdict = NULL; ++ QDict *qdict; + QObject *qobj; + Visitor *v; + BlockDriverState *bs = NULL; +@@ -764,14 +764,13 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, + qdict_put_str(qdict, "file", bs->node_name); + + qobj = qdict_crumple_for_keyval_qiv(qdict, errp); +- qobject_unref(qdict); +- qdict = qobject_to(QDict, qobj); +- if (qdict == NULL) { ++ if (!qobj) { + ret = -EINVAL; + goto fail; + } + +- v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); ++ v = qobject_input_visitor_new_keyval(qobj); ++ qobject_unref(qobj); + visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); + visit_free(v); + +diff --git a/block/vhdx.c b/block/vhdx.c +index 32939c4..728d8b3 100644 +--- a/block/vhdx.c ++++ b/block/vhdx.c +@@ -1963,7 +1963,7 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, + Error **errp) + { + BlockdevCreateOptions *create_options = NULL; +- QDict *qdict = NULL; ++ QDict *qdict; + QObject *qobj; + Visitor *v; + BlockDriverState *bs = NULL; +@@ -2004,14 +2004,13 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, + qdict_put_str(qdict, "file", bs->node_name); + + qobj = qdict_crumple_for_keyval_qiv(qdict, errp); +- qobject_unref(qdict); +- qdict = qobject_to(QDict, qobj); +- if (qdict == NULL) { ++ if (!qobj) { + ret = -EINVAL; + goto fail; + } + +- v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); ++ v = qobject_input_visitor_new_keyval(qobj); ++ qobject_unref(qobj); + visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); + visit_free(v); + +diff --git a/block/vpc.c b/block/vpc.c +index 16178e5..a9bb041 100644 +--- a/block/vpc.c ++++ b/block/vpc.c +@@ -1081,7 +1081,7 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, + QemuOpts *opts, Error **errp) + { + BlockdevCreateOptions *create_options = NULL; +- QDict *qdict = NULL; ++ QDict *qdict; + QObject *qobj; + Visitor *v; + BlockDriverState *bs = NULL; +@@ -1120,14 +1120,13 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, + qdict_put_str(qdict, "file", bs->node_name); + + qobj = qdict_crumple_for_keyval_qiv(qdict, errp); +- qobject_unref(qdict); +- qdict = qobject_to(QDict, qobj); +- if (qdict == NULL) { ++ if (!qobj) { + ret = -EINVAL; + goto fail; + } + +- v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); ++ v = qobject_input_visitor_new_keyval(qobj); ++ qobject_unref(qobj); + visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); + visit_free(v); + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Convert-.bdrv_truncate-callback-to-coroutine_f.patch b/SOURCES/kvm-block-Convert-.bdrv_truncate-callback-to-coroutine_f.patch new file mode 100644 index 0000000..8860ef9 --- /dev/null +++ b/SOURCES/kvm-block-Convert-.bdrv_truncate-callback-to-coroutine_f.patch @@ -0,0 +1,865 @@ +From c557973bf44fa50d5ef2334a2179546698a9bbf0 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Thu, 12 Jul 2018 14:42:54 +0200 +Subject: [PATCH 35/89] block: Convert .bdrv_truncate callback to coroutine_fn + +RH-Author: Kevin Wolf +Message-id: <20180712144258.17303-3-kwolf@redhat.com> +Patchwork-id: 81327 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 2/6] block: Convert .bdrv_truncate callback to coroutine_fn +Bugzilla: 1595173 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow + +bdrv_truncate() is an operation that can block (even for a quite long +time, depending on the PreallocMode) in I/O paths that shouldn't block. +Convert it to a coroutine_fn so that we have the infrastructure for +drivers to make their .bdrv_co_truncate implementation asynchronous. + +This change could potentially introduce new race conditions because +bdrv_truncate() isn't necessarily executed atomically any more. Whether +this is a problem needs to be evaluated for each block driver that +supports truncate: + +* file-posix/win32, gluster, iscsi, nfs, rbd, ssh, sheepdog: The + protocol drivers are trivially safe because they don't actually yield + yet, so there is no change in behaviour. + +* copy-on-read, crypto, raw-format: Essentially just filter drivers that + pass the request to a child node, no problem. + +* qcow2: The implementation modifies metadata, so it needs to hold + s->lock to be safe with concurrent I/O requests. In order to avoid + double locking, this requires pulling the locking out into + preallocate_co() and using qcow2_write_caches() instead of + bdrv_flush(). + +* qed: Does a single header update, this is fine without locking. + +Signed-off-by: Kevin Wolf +Reviewed-by: Stefan Hajnoczi +(cherry picked from commit 061ca8a368165fae300748c17971824a089f521f) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 63 +++++++++++++++++++++++++++++++++++----- + block/copy-on-read.c | 8 ++--- + block/crypto.c | 9 +++--- + block/file-posix.c | 12 ++++---- + block/file-win32.c | 6 ++-- + block/gluster.c | 14 +++++---- + block/iscsi.c | 8 ++--- + block/nfs.c | 7 +++-- + block/qcow2.c | 74 ++++++++++++++++++++++++++++------------------- + block/qed.c | 8 +++-- + block/raw-format.c | 8 ++--- + block/rbd.c | 8 +++-- + block/sheepdog.c | 12 ++++---- + block/ssh.c | 6 ++-- + include/block/block.h | 4 +++ + include/block/block_int.h | 4 +-- + 16 files changed, 162 insertions(+), 89 deletions(-) + +diff --git a/block.c b/block.c +index 0516284..0af08ca 100644 +--- a/block.c ++++ b/block.c +@@ -3738,8 +3738,8 @@ exit: + /** + * Truncate file to 'offset' bytes (needed only for file protocols) + */ +-int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, +- Error **errp) ++int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, ++ PreallocMode prealloc, Error **errp) + { + BlockDriverState *bs = child->bs; + BlockDriver *drv = bs->drv; +@@ -3757,23 +3757,28 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, + return -EINVAL; + } + +- if (!drv->bdrv_truncate) { ++ bdrv_inc_in_flight(bs); ++ ++ if (!drv->bdrv_co_truncate) { + if (bs->file && drv->is_filter) { +- return bdrv_truncate(bs->file, offset, prealloc, errp); ++ ret = bdrv_co_truncate(bs->file, offset, prealloc, errp); ++ goto out; + } + error_setg(errp, "Image format driver does not support resize"); +- return -ENOTSUP; ++ ret = -ENOTSUP; ++ goto out; + } + if (bs->read_only) { + error_setg(errp, "Image is read-only"); +- return -EACCES; ++ ret = -EACCES; ++ goto out; + } + + assert(!(bs->open_flags & BDRV_O_INACTIVE)); + +- ret = drv->bdrv_truncate(bs, offset, prealloc, errp); ++ ret = drv->bdrv_co_truncate(bs, offset, prealloc, errp); + if (ret < 0) { +- return ret; ++ goto out; + } + ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); + if (ret < 0) { +@@ -3784,9 +3789,51 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, + bdrv_dirty_bitmap_truncate(bs, offset); + bdrv_parent_cb_resize(bs); + atomic_inc(&bs->write_gen); ++ ++out: ++ bdrv_dec_in_flight(bs); + return ret; + } + ++typedef struct TruncateCo { ++ BdrvChild *child; ++ int64_t offset; ++ PreallocMode prealloc; ++ Error **errp; ++ int ret; ++} TruncateCo; ++ ++static void coroutine_fn bdrv_truncate_co_entry(void *opaque) ++{ ++ TruncateCo *tco = opaque; ++ tco->ret = bdrv_co_truncate(tco->child, tco->offset, tco->prealloc, ++ tco->errp); ++} ++ ++int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, ++ Error **errp) ++{ ++ Coroutine *co; ++ TruncateCo tco = { ++ .child = child, ++ .offset = offset, ++ .prealloc = prealloc, ++ .errp = errp, ++ .ret = NOT_DONE, ++ }; ++ ++ if (qemu_in_coroutine()) { ++ /* Fast-path if already in coroutine context */ ++ bdrv_truncate_co_entry(&tco); ++ } else { ++ co = qemu_coroutine_create(bdrv_truncate_co_entry, &tco); ++ qemu_coroutine_enter(co); ++ BDRV_POLL_WHILE(child->bs, tco.ret == NOT_DONE); ++ } ++ ++ return tco.ret; ++} ++ + /** + * Length of a allocated file in bytes. Sparse files are counted by actual + * allocated space. Return < 0 if error or unknown. +diff --git a/block/copy-on-read.c b/block/copy-on-read.c +index 6a97208..1dcdaee 100644 +--- a/block/copy-on-read.c ++++ b/block/copy-on-read.c +@@ -80,10 +80,10 @@ static int64_t cor_getlength(BlockDriverState *bs) + } + + +-static int cor_truncate(BlockDriverState *bs, int64_t offset, +- PreallocMode prealloc, Error **errp) ++static int coroutine_fn cor_co_truncate(BlockDriverState *bs, int64_t offset, ++ PreallocMode prealloc, Error **errp) + { +- return bdrv_truncate(bs->file, offset, prealloc, errp); ++ return bdrv_co_truncate(bs->file, offset, prealloc, errp); + } + + +@@ -147,7 +147,7 @@ BlockDriver bdrv_copy_on_read = { + .bdrv_child_perm = cor_child_perm, + + .bdrv_getlength = cor_getlength, +- .bdrv_truncate = cor_truncate, ++ .bdrv_co_truncate = cor_co_truncate, + + .bdrv_co_preadv = cor_co_preadv, + .bdrv_co_pwritev = cor_co_pwritev, +diff --git a/block/crypto.c b/block/crypto.c +index f5151f4..02f04f3 100644 +--- a/block/crypto.c ++++ b/block/crypto.c +@@ -357,8 +357,9 @@ static int block_crypto_co_create_generic(BlockDriverState *bs, + return ret; + } + +-static int block_crypto_truncate(BlockDriverState *bs, int64_t offset, +- PreallocMode prealloc, Error **errp) ++static int coroutine_fn ++block_crypto_co_truncate(BlockDriverState *bs, int64_t offset, ++ PreallocMode prealloc, Error **errp) + { + BlockCrypto *crypto = bs->opaque; + uint64_t payload_offset = +@@ -371,7 +372,7 @@ static int block_crypto_truncate(BlockDriverState *bs, int64_t offset, + + offset += payload_offset; + +- return bdrv_truncate(bs->file, offset, prealloc, errp); ++ return bdrv_co_truncate(bs->file, offset, prealloc, errp); + } + + static void block_crypto_close(BlockDriverState *bs) +@@ -700,7 +701,7 @@ BlockDriver bdrv_crypto_luks = { + .bdrv_child_perm = bdrv_format_default_perms, + .bdrv_co_create = block_crypto_co_create_luks, + .bdrv_co_create_opts = block_crypto_co_create_opts_luks, +- .bdrv_truncate = block_crypto_truncate, ++ .bdrv_co_truncate = block_crypto_co_truncate, + .create_opts = &block_crypto_create_opts_luks, + + .bdrv_reopen_prepare = block_crypto_reopen_prepare, +diff --git a/block/file-posix.c b/block/file-posix.c +index cbf7c11..f8488ec 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -1832,8 +1832,8 @@ out: + return result; + } + +-static int raw_truncate(BlockDriverState *bs, int64_t offset, +- PreallocMode prealloc, Error **errp) ++static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, ++ PreallocMode prealloc, Error **errp) + { + BDRVRawState *s = bs->opaque; + struct stat st; +@@ -2474,7 +2474,7 @@ BlockDriver bdrv_file = { + .bdrv_io_plug = raw_aio_plug, + .bdrv_io_unplug = raw_aio_unplug, + +- .bdrv_truncate = raw_truncate, ++ .bdrv_co_truncate = raw_co_truncate, + .bdrv_getlength = raw_getlength, + .bdrv_get_info = raw_get_info, + .bdrv_get_allocated_file_size +@@ -2953,7 +2953,7 @@ static BlockDriver bdrv_host_device = { + .bdrv_io_plug = raw_aio_plug, + .bdrv_io_unplug = raw_aio_unplug, + +- .bdrv_truncate = raw_truncate, ++ .bdrv_co_truncate = raw_co_truncate, + .bdrv_getlength = raw_getlength, + .bdrv_get_info = raw_get_info, + .bdrv_get_allocated_file_size +@@ -3074,7 +3074,7 @@ static BlockDriver bdrv_host_cdrom = { + .bdrv_io_plug = raw_aio_plug, + .bdrv_io_unplug = raw_aio_unplug, + +- .bdrv_truncate = raw_truncate, ++ .bdrv_co_truncate = raw_co_truncate, + .bdrv_getlength = raw_getlength, + .has_variable_length = true, + .bdrv_get_allocated_file_size +@@ -3204,7 +3204,7 @@ static BlockDriver bdrv_host_cdrom = { + .bdrv_io_plug = raw_aio_plug, + .bdrv_io_unplug = raw_aio_unplug, + +- .bdrv_truncate = raw_truncate, ++ .bdrv_co_truncate = raw_co_truncate, + .bdrv_getlength = raw_getlength, + .has_variable_length = true, + .bdrv_get_allocated_file_size +diff --git a/block/file-win32.c b/block/file-win32.c +index 2e2f746..6573338 100644 +--- a/block/file-win32.c ++++ b/block/file-win32.c +@@ -463,8 +463,8 @@ static void raw_close(BlockDriverState *bs) + } + } + +-static int raw_truncate(BlockDriverState *bs, int64_t offset, +- PreallocMode prealloc, Error **errp) ++static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, ++ PreallocMode prealloc, Error **errp) + { + BDRVRawState *s = bs->opaque; + LONG low, high; +@@ -636,7 +636,7 @@ BlockDriver bdrv_file = { + .bdrv_aio_writev = raw_aio_writev, + .bdrv_aio_flush = raw_aio_flush, + +- .bdrv_truncate = raw_truncate, ++ .bdrv_co_truncate = raw_co_truncate, + .bdrv_getlength = raw_getlength, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, +diff --git a/block/gluster.c b/block/gluster.c +index 418bb73..cecfe09 100644 +--- a/block/gluster.c ++++ b/block/gluster.c +@@ -1177,8 +1177,10 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs, + return acb.ret; + } + +-static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset, +- PreallocMode prealloc, Error **errp) ++static coroutine_fn int qemu_gluster_co_truncate(BlockDriverState *bs, ++ int64_t offset, ++ PreallocMode prealloc, ++ Error **errp) + { + BDRVGlusterState *s = bs->opaque; + return qemu_gluster_do_truncate(s->fd, offset, prealloc, errp); +@@ -1497,7 +1499,7 @@ static BlockDriver bdrv_gluster = { + .bdrv_co_create_opts = qemu_gluster_co_create_opts, + .bdrv_getlength = qemu_gluster_getlength, + .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, +- .bdrv_truncate = qemu_gluster_truncate, ++ .bdrv_co_truncate = qemu_gluster_co_truncate, + .bdrv_co_readv = qemu_gluster_co_readv, + .bdrv_co_writev = qemu_gluster_co_writev, + .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, +@@ -1526,7 +1528,7 @@ static BlockDriver bdrv_gluster_tcp = { + .bdrv_co_create_opts = qemu_gluster_co_create_opts, + .bdrv_getlength = qemu_gluster_getlength, + .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, +- .bdrv_truncate = qemu_gluster_truncate, ++ .bdrv_co_truncate = qemu_gluster_co_truncate, + .bdrv_co_readv = qemu_gluster_co_readv, + .bdrv_co_writev = qemu_gluster_co_writev, + .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, +@@ -1555,7 +1557,7 @@ static BlockDriver bdrv_gluster_unix = { + .bdrv_co_create_opts = qemu_gluster_co_create_opts, + .bdrv_getlength = qemu_gluster_getlength, + .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, +- .bdrv_truncate = qemu_gluster_truncate, ++ .bdrv_co_truncate = qemu_gluster_co_truncate, + .bdrv_co_readv = qemu_gluster_co_readv, + .bdrv_co_writev = qemu_gluster_co_writev, + .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, +@@ -1590,7 +1592,7 @@ static BlockDriver bdrv_gluster_rdma = { + .bdrv_co_create_opts = qemu_gluster_co_create_opts, + .bdrv_getlength = qemu_gluster_getlength, + .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, +- .bdrv_truncate = qemu_gluster_truncate, ++ .bdrv_co_truncate = qemu_gluster_co_truncate, + .bdrv_co_readv = qemu_gluster_co_readv, + .bdrv_co_writev = qemu_gluster_co_writev, + .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, +diff --git a/block/iscsi.c b/block/iscsi.c +index 751884d..5047e83 100644 +--- a/block/iscsi.c ++++ b/block/iscsi.c +@@ -2085,8 +2085,8 @@ static void iscsi_reopen_commit(BDRVReopenState *reopen_state) + } + } + +-static int iscsi_truncate(BlockDriverState *bs, int64_t offset, +- PreallocMode prealloc, Error **errp) ++static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset, ++ PreallocMode prealloc, Error **errp) + { + IscsiLun *iscsilun = bs->opaque; + Error *local_err = NULL; +@@ -2431,7 +2431,7 @@ static BlockDriver bdrv_iscsi = { + + .bdrv_getlength = iscsi_getlength, + .bdrv_get_info = iscsi_get_info, +- .bdrv_truncate = iscsi_truncate, ++ .bdrv_co_truncate = iscsi_co_truncate, + .bdrv_refresh_limits = iscsi_refresh_limits, + + .bdrv_co_block_status = iscsi_co_block_status, +@@ -2468,7 +2468,7 @@ static BlockDriver bdrv_iser = { + + .bdrv_getlength = iscsi_getlength, + .bdrv_get_info = iscsi_get_info, +- .bdrv_truncate = iscsi_truncate, ++ .bdrv_co_truncate = iscsi_co_truncate, + .bdrv_refresh_limits = iscsi_refresh_limits, + + .bdrv_co_block_status = iscsi_co_block_status, +diff --git a/block/nfs.c b/block/nfs.c +index 743ca04..eab1a2c 100644 +--- a/block/nfs.c ++++ b/block/nfs.c +@@ -743,8 +743,9 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) + return (task.ret < 0 ? task.ret : st.st_blocks * 512); + } + +-static int nfs_file_truncate(BlockDriverState *bs, int64_t offset, +- PreallocMode prealloc, Error **errp) ++static int coroutine_fn ++nfs_file_co_truncate(BlockDriverState *bs, int64_t offset, ++ PreallocMode prealloc, Error **errp) + { + NFSClient *client = bs->opaque; + int ret; +@@ -873,7 +874,7 @@ static BlockDriver bdrv_nfs = { + + .bdrv_has_zero_init = nfs_has_zero_init, + .bdrv_get_allocated_file_size = nfs_get_allocated_file_size, +- .bdrv_truncate = nfs_file_truncate, ++ .bdrv_co_truncate = nfs_file_co_truncate, + + .bdrv_file_open = nfs_file_open, + .bdrv_close = nfs_file_close, +diff --git a/block/qcow2.c b/block/qcow2.c +index dbd448c..c5c6ae9 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -2539,15 +2539,12 @@ static void coroutine_fn preallocate_co(void *opaque) + BlockDriverState *bs = params->bs; + uint64_t offset = params->offset; + uint64_t new_length = params->new_length; +- BDRVQcow2State *s = bs->opaque; + uint64_t bytes; + uint64_t host_offset = 0; + unsigned int cur_bytes; + int ret; + QCowL2Meta *meta; + +- qemu_co_mutex_lock(&s->lock); +- + assert(offset <= new_length); + bytes = new_length - offset; + +@@ -2600,7 +2597,6 @@ static void coroutine_fn preallocate_co(void *opaque) + ret = 0; + + done: +- qemu_co_mutex_unlock(&s->lock); + params->ret = ret; + } + +@@ -3037,7 +3033,11 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) + + /* And if we're supposed to preallocate metadata, do that now */ + if (qcow2_opts->preallocation != PREALLOC_MODE_OFF) { ++ BDRVQcow2State *s = blk_bs(blk)->opaque; ++ qemu_co_mutex_lock(&s->lock); + ret = preallocate(blk_bs(blk), 0, qcow2_opts->size); ++ qemu_co_mutex_unlock(&s->lock); ++ + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not preallocate metadata"); + goto out; +@@ -3434,8 +3434,8 @@ fail: + return ret; + } + +-static int qcow2_truncate(BlockDriverState *bs, int64_t offset, +- PreallocMode prealloc, Error **errp) ++static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, ++ PreallocMode prealloc, Error **errp) + { + BDRVQcow2State *s = bs->opaque; + uint64_t old_length; +@@ -3455,17 +3455,21 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, + return -EINVAL; + } + ++ qemu_co_mutex_lock(&s->lock); ++ + /* cannot proceed if image has snapshots */ + if (s->nb_snapshots) { + error_setg(errp, "Can't resize an image which has snapshots"); +- return -ENOTSUP; ++ ret = -ENOTSUP; ++ goto fail; + } + + /* cannot proceed if image has bitmaps */ + if (s->nb_bitmaps) { + /* TODO: resize bitmaps in the image */ + error_setg(errp, "Can't resize an image which has bitmaps"); +- return -ENOTSUP; ++ ret = -ENOTSUP; ++ goto fail; + } + + old_length = bs->total_sectors * 512; +@@ -3476,7 +3480,8 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, + if (prealloc != PREALLOC_MODE_OFF) { + error_setg(errp, + "Preallocation can't be used for shrinking an image"); +- return -EINVAL; ++ ret = -EINVAL; ++ goto fail; + } + + ret = qcow2_cluster_discard(bs, ROUND_UP(offset, s->cluster_size), +@@ -3485,40 +3490,42 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, + QCOW2_DISCARD_ALWAYS, true); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to discard cropped clusters"); +- return ret; ++ 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"); +- return ret; ++ goto fail; + } + + ret = qcow2_shrink_reftable(bs); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Failed to discard unused refblocks"); +- return ret; ++ goto fail; + } + + old_file_size = bdrv_getlength(bs->file->bs); + if (old_file_size < 0) { + error_setg_errno(errp, -old_file_size, + "Failed to inquire current file length"); +- return old_file_size; ++ 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"); +- return last_cluster; ++ ret = last_cluster; ++ goto fail; + } + if ((last_cluster + 1) * s->cluster_size < old_file_size) { + Error *local_err = NULL; + +- bdrv_truncate(bs->file, (last_cluster + 1) * s->cluster_size, +- PREALLOC_MODE_OFF, &local_err); ++ bdrv_co_truncate(bs->file, (last_cluster + 1) * s->cluster_size, ++ PREALLOC_MODE_OFF, &local_err); + if (local_err) { + warn_reportf_err(local_err, + "Failed to truncate the tail of the image: "); +@@ -3528,7 +3535,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, + ret = qcow2_grow_l1_table(bs, new_l1_size, true); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to grow the L1 table"); +- return ret; ++ goto fail; + } + } + +@@ -3540,7 +3547,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, + ret = preallocate(bs, old_length, offset); + if (ret < 0) { + error_setg_errno(errp, -ret, "Preallocation failed"); +- return ret; ++ goto fail; + } + break; + +@@ -3556,7 +3563,8 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, + if (old_file_size < 0) { + error_setg_errno(errp, -old_file_size, + "Failed to inquire current file length"); +- return old_file_size; ++ ret = old_file_size; ++ goto fail; + } + old_file_size = ROUND_UP(old_file_size, s->cluster_size); + +@@ -3586,7 +3594,8 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, + if (allocation_start < 0) { + error_setg_errno(errp, -allocation_start, + "Failed to resize refcount structures"); +- return allocation_start; ++ ret = allocation_start; ++ goto fail; + } + + clusters_allocated = qcow2_alloc_clusters_at(bs, allocation_start, +@@ -3594,7 +3603,8 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, + if (clusters_allocated < 0) { + error_setg_errno(errp, -clusters_allocated, + "Failed to allocate data clusters"); +- return clusters_allocated; ++ ret = clusters_allocated; ++ goto fail; + } + + assert(clusters_allocated == nb_new_data_clusters); +@@ -3602,13 +3612,13 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, + /* Allocate the data area */ + new_file_size = allocation_start + + nb_new_data_clusters * s->cluster_size; +- ret = bdrv_truncate(bs->file, new_file_size, prealloc, errp); ++ ret = bdrv_co_truncate(bs->file, new_file_size, prealloc, 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); +- return ret; ++ goto fail; + } + + /* Create the necessary L2 entries */ +@@ -3631,7 +3641,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, + qcow2_free_clusters(bs, host_offset, + nb_new_data_clusters * s->cluster_size, + QCOW2_DISCARD_OTHER); +- return ret; ++ goto fail; + } + + guest_offset += nb_clusters * s->cluster_size; +@@ -3647,11 +3657,11 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, + + if (prealloc != PREALLOC_MODE_OFF) { + /* Flush metadata before actually changing the image size */ +- ret = bdrv_flush(bs); ++ ret = qcow2_write_caches(bs); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Failed to flush the preallocated area to disk"); +- return ret; ++ goto fail; + } + } + +@@ -3661,11 +3671,14 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, + &offset, sizeof(uint64_t)); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to update the image size"); +- return ret; ++ goto fail; + } + + s->l1_vm_state_index = new_l1_size; +- return 0; ++ ret = 0; ++fail: ++ qemu_co_mutex_unlock(&s->lock); ++ return ret; + } + + /* XXX: put compressed sectors first, then all the cluster aligned +@@ -3689,7 +3702,8 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, + if (cluster_offset < 0) { + return cluster_offset; + } +- return bdrv_truncate(bs->file, cluster_offset, PREALLOC_MODE_OFF, NULL); ++ return bdrv_co_truncate(bs->file, cluster_offset, PREALLOC_MODE_OFF, ++ NULL); + } + + if (offset_into_cluster(s, offset)) { +@@ -4694,7 +4708,7 @@ BlockDriver bdrv_qcow2 = { + .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_truncate = qcow2_truncate, ++ .bdrv_co_truncate = qcow2_co_truncate, + .bdrv_co_pwritev_compressed = qcow2_co_pwritev_compressed, + .bdrv_make_empty = qcow2_make_empty, + +diff --git a/block/qed.c b/block/qed.c +index 4c5d7e8..34cccea 100644 +--- a/block/qed.c ++++ b/block/qed.c +@@ -1466,8 +1466,10 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs, + QED_AIOCB_WRITE | QED_AIOCB_ZERO); + } + +-static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset, +- PreallocMode prealloc, Error **errp) ++static int coroutine_fn bdrv_qed_co_truncate(BlockDriverState *bs, ++ int64_t offset, ++ PreallocMode prealloc, ++ Error **errp) + { + BDRVQEDState *s = bs->opaque; + uint64_t old_image_size; +@@ -1677,7 +1679,7 @@ static BlockDriver bdrv_qed = { + .bdrv_co_readv = bdrv_qed_co_readv, + .bdrv_co_writev = bdrv_qed_co_writev, + .bdrv_co_pwrite_zeroes = bdrv_qed_co_pwrite_zeroes, +- .bdrv_truncate = bdrv_qed_truncate, ++ .bdrv_co_truncate = bdrv_qed_co_truncate, + .bdrv_getlength = bdrv_qed_getlength, + .bdrv_get_info = bdrv_qed_get_info, + .bdrv_refresh_limits = bdrv_qed_refresh_limits, +diff --git a/block/raw-format.c b/block/raw-format.c +index f2e468d..b78da56 100644 +--- a/block/raw-format.c ++++ b/block/raw-format.c +@@ -366,8 +366,8 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) + } + } + +-static int raw_truncate(BlockDriverState *bs, int64_t offset, +- PreallocMode prealloc, Error **errp) ++static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, ++ PreallocMode prealloc, Error **errp) + { + BDRVRawState *s = bs->opaque; + +@@ -383,7 +383,7 @@ static int raw_truncate(BlockDriverState *bs, int64_t offset, + + s->size = offset; + offset += s->offset; +- return bdrv_truncate(bs->file, offset, prealloc, errp); ++ return bdrv_co_truncate(bs->file, offset, prealloc, errp); + } + + static void raw_eject(BlockDriverState *bs, bool eject_flag) +@@ -545,7 +545,7 @@ BlockDriver bdrv_raw = { + .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_truncate = &raw_truncate, ++ .bdrv_co_truncate = &raw_co_truncate, + .bdrv_getlength = &raw_getlength, + .has_variable_length = true, + .bdrv_measure = &raw_measure, +diff --git a/block/rbd.c b/block/rbd.c +index 3242bcd..b93046b 100644 +--- a/block/rbd.c ++++ b/block/rbd.c +@@ -987,8 +987,10 @@ static int64_t qemu_rbd_getlength(BlockDriverState *bs) + return info.size; + } + +-static int qemu_rbd_truncate(BlockDriverState *bs, int64_t offset, +- PreallocMode prealloc, Error **errp) ++static int coroutine_fn qemu_rbd_co_truncate(BlockDriverState *bs, ++ int64_t offset, ++ PreallocMode prealloc, ++ Error **errp) + { + BDRVRBDState *s = bs->opaque; + int r; +@@ -1180,7 +1182,7 @@ static BlockDriver bdrv_rbd = { + .bdrv_get_info = qemu_rbd_getinfo, + .create_opts = &qemu_rbd_create_opts, + .bdrv_getlength = qemu_rbd_getlength, +- .bdrv_truncate = qemu_rbd_truncate, ++ .bdrv_co_truncate = qemu_rbd_co_truncate, + .protocol_name = "rbd", + + .bdrv_aio_readv = qemu_rbd_aio_readv, +diff --git a/block/sheepdog.c b/block/sheepdog.c +index 933880c..14165d3 100644 +--- a/block/sheepdog.c ++++ b/block/sheepdog.c +@@ -2294,8 +2294,8 @@ static int64_t sd_getlength(BlockDriverState *bs) + return s->inode.vdi_size; + } + +-static int sd_truncate(BlockDriverState *bs, int64_t offset, +- PreallocMode prealloc, Error **errp) ++static int coroutine_fn sd_co_truncate(BlockDriverState *bs, int64_t offset, ++ PreallocMode prealloc, Error **errp) + { + BDRVSheepdogState *s = bs->opaque; + int ret, fd; +@@ -2609,7 +2609,7 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num, + BDRVSheepdogState *s = bs->opaque; + + if (offset > s->inode.vdi_size) { +- ret = sd_truncate(bs, offset, PREALLOC_MODE_OFF, NULL); ++ ret = sd_co_truncate(bs, offset, PREALLOC_MODE_OFF, NULL); + if (ret < 0) { + return ret; + } +@@ -3229,7 +3229,7 @@ static BlockDriver bdrv_sheepdog = { + .bdrv_has_zero_init = bdrv_has_zero_init_1, + .bdrv_getlength = sd_getlength, + .bdrv_get_allocated_file_size = sd_get_allocated_file_size, +- .bdrv_truncate = sd_truncate, ++ .bdrv_co_truncate = sd_co_truncate, + + .bdrv_co_readv = sd_co_readv, + .bdrv_co_writev = sd_co_writev, +@@ -3266,7 +3266,7 @@ static BlockDriver bdrv_sheepdog_tcp = { + .bdrv_has_zero_init = bdrv_has_zero_init_1, + .bdrv_getlength = sd_getlength, + .bdrv_get_allocated_file_size = sd_get_allocated_file_size, +- .bdrv_truncate = sd_truncate, ++ .bdrv_co_truncate = sd_co_truncate, + + .bdrv_co_readv = sd_co_readv, + .bdrv_co_writev = sd_co_writev, +@@ -3303,7 +3303,7 @@ static BlockDriver bdrv_sheepdog_unix = { + .bdrv_has_zero_init = bdrv_has_zero_init_1, + .bdrv_getlength = sd_getlength, + .bdrv_get_allocated_file_size = sd_get_allocated_file_size, +- .bdrv_truncate = sd_truncate, ++ .bdrv_co_truncate = sd_co_truncate, + + .bdrv_co_readv = sd_co_readv, + .bdrv_co_writev = sd_co_writev, +diff --git a/block/ssh.c b/block/ssh.c +index aab6996..6a55d82 100644 +--- a/block/ssh.c ++++ b/block/ssh.c +@@ -1241,8 +1241,8 @@ static int64_t ssh_getlength(BlockDriverState *bs) + return length; + } + +-static int ssh_truncate(BlockDriverState *bs, int64_t offset, +- PreallocMode prealloc, Error **errp) ++static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset, ++ PreallocMode prealloc, Error **errp) + { + BDRVSSHState *s = bs->opaque; + +@@ -1277,7 +1277,7 @@ static BlockDriver bdrv_ssh = { + .bdrv_co_readv = ssh_co_readv, + .bdrv_co_writev = ssh_co_writev, + .bdrv_getlength = ssh_getlength, +- .bdrv_truncate = ssh_truncate, ++ .bdrv_co_truncate = ssh_co_truncate, + .bdrv_co_flush_to_disk = ssh_co_flush, + .create_opts = &ssh_create_opts, + }; +diff --git a/include/block/block.h b/include/block/block.h +index e677080..c3cfb40 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -300,8 +300,12 @@ int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, + BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, + const char *backing_file); + void bdrv_refresh_filename(BlockDriverState *bs); ++ ++int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, ++ PreallocMode prealloc, Error **errp); + int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, + Error **errp); ++ + int64_t bdrv_nb_sectors(BlockDriverState *bs); + int64_t bdrv_getlength(BlockDriverState *bs); + int64_t bdrv_get_allocated_file_size(BlockDriverState *bs); +diff --git a/include/block/block_int.h b/include/block/block_int.h +index 3da86a7..46b2f87 100644 +--- a/include/block/block_int.h ++++ b/include/block/block_int.h +@@ -291,8 +291,8 @@ struct BlockDriver { + * bdrv_parse_filename. + */ + const char *protocol_name; +- int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset, +- PreallocMode prealloc, Error **errp); ++ int coroutine_fn (*bdrv_co_truncate)(BlockDriverState *bs, int64_t offset, ++ PreallocMode prealloc, Error **errp); + + int64_t (*bdrv_getlength)(BlockDriverState *bs); + bool has_variable_length; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Defer-.bdrv_drain_begin-callback-to-polling-ph.patch b/SOURCES/kvm-block-Defer-.bdrv_drain_begin-callback-to-polling-ph.patch new file mode 100644 index 0000000..3d25662 --- /dev/null +++ b/SOURCES/kvm-block-Defer-.bdrv_drain_begin-callback-to-polling-ph.patch @@ -0,0 +1,83 @@ +From 2e9423c192511fd2704eea357403f6817cb9aae7 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:12 +0200 +Subject: [PATCH 21/49] block: Defer .bdrv_drain_begin callback to polling + phase + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-15-kwolf@redhat.com> +Patchwork-id: 82166 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 14/42] block: Defer .bdrv_drain_begin callback to polling phase +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +We cannot allow aio_poll() in bdrv_drain_invoke(begin=true) until we're +done with propagating the drain through the graph and are doing the +single final BDRV_POLL_WHILE(). + +Just schedule the coroutine with the callback and increase bs->in_flight +to make sure that the polling phase will wait for it. + +Signed-off-by: Kevin Wolf +(cherry picked from commit 0109e7e6f83ae5166b81bbd9a4319d60be49985a) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/io.c | 28 +++++++++++++++++++++++----- + 1 file changed, 23 insertions(+), 5 deletions(-) + +diff --git a/block/io.c b/block/io.c +index 9c41ff9..23fe069 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -181,22 +181,40 @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque) + + /* Set data->done before reading bs->wakeup. */ + atomic_mb_set(&data->done, true); +- bdrv_wakeup(bs); ++ bdrv_dec_in_flight(bs); ++ ++ if (data->begin) { ++ g_free(data); ++ } + } + + /* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */ + static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) + { +- BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin}; ++ BdrvCoDrainData *data; + + if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) || + (!begin && !bs->drv->bdrv_co_drain_end)) { + return; + } + +- data.co = qemu_coroutine_create(bdrv_drain_invoke_entry, &data); +- bdrv_coroutine_enter(bs, data.co); +- BDRV_POLL_WHILE(bs, !data.done); ++ data = g_new(BdrvCoDrainData, 1); ++ *data = (BdrvCoDrainData) { ++ .bs = bs, ++ .done = false, ++ .begin = begin ++ }; ++ ++ /* Make sure the driver callback completes during the polling phase for ++ * drain_begin. */ ++ bdrv_inc_in_flight(bs); ++ data->co = qemu_coroutine_create(bdrv_drain_invoke_entry, data); ++ aio_co_schedule(bdrv_get_aio_context(bs), data->co); ++ ++ if (!begin) { ++ BDRV_POLL_WHILE(bs, !data->done); ++ g_free(data); ++ } + } + + /* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Don-t-inactivate-children-before-parents.patch b/SOURCES/kvm-block-Don-t-inactivate-children-before-parents.patch new file mode 100644 index 0000000..61895e7 --- /dev/null +++ b/SOURCES/kvm-block-Don-t-inactivate-children-before-parents.patch @@ -0,0 +1,182 @@ +From 747fd28a2c5947940c5f17426ba7a91738f1481c Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Wed, 28 Nov 2018 09:29:46 +0100 +Subject: [PATCH 15/34] block: Don't inactivate children before parents + +RH-Author: Kevin Wolf +Message-id: <20181128092947.24543-2-kwolf@redhat.com> +Patchwork-id: 83179 +O-Subject: [RHEL-7.7/7.6.z qemu-kvm-rhev PATCH 1/2] block: Don't inactivate children before parents +Bugzilla: 1633536 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Laurent Vivier + +bdrv_child_cb_inactivate() asserts that parents are already inactive +when children get inactivated. This precondition is necessary because +parents could still issue requests in their inactivation code. + +When block nodes are created individually with -blockdev, all of them +are monitor owned and will be returned by bdrv_next() in an undefined +order (in practice, in the order of their creation, which is usually +children before parents), which obviously fails the assertion: + +qemu: block.c:899: bdrv_child_cb_inactivate: Assertion `bs->open_flags & BDRV_O_INACTIVE' failed. + +This patch fixes the ordering by skipping nodes with still active +parents in bdrv_inactivate_recurse() because we know that they will be +covered by recursion when the last active parent becomes inactive. + +With the correct parents-before-children ordering, we also got rid of +the reason why commit aad0b7a0bfb introduced two passes, so we can go +back to a single-pass recursion. This is necessary so we can rely on the +BDRV_O_INACTIVE flag to skip nodes with active parents (the flag used +to be set only in pass 2, so we would always skip non-root nodes in +pass 1 because all parents would still be considered active; setting the +flag in pass 1 would mean, that we never skip anything in pass 2 because +all parents are already considered inactive). + +Because of the change to single pass, this patch is best reviewed with +whitespace changes ignored. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit 9e37271f50ec2e95f299dc297ac08f9be0096b48) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 84 +++++++++++++++++++++++++++++++++++++++++------------------------ + 1 file changed, 53 insertions(+), 31 deletions(-) + +diff --git a/block.c b/block.c +index 037d2b0..da9b1a6 100644 +--- a/block.c ++++ b/block.c +@@ -4469,45 +4469,68 @@ void bdrv_invalidate_cache_all(Error **errp) + } + } + +-static int bdrv_inactivate_recurse(BlockDriverState *bs, +- bool setting_flag) ++static bool bdrv_has_bds_parent(BlockDriverState *bs, bool only_active) ++{ ++ BdrvChild *parent; ++ ++ QLIST_FOREACH(parent, &bs->parents, next_parent) { ++ if (parent->role->parent_is_bds) { ++ BlockDriverState *parent_bs = parent->opaque; ++ if (!only_active || !(parent_bs->open_flags & BDRV_O_INACTIVE)) { ++ return true; ++ } ++ } ++ } ++ ++ return false; ++} ++ ++static int bdrv_inactivate_recurse(BlockDriverState *bs) + { + BdrvChild *child, *parent; ++ uint64_t perm, shared_perm; + int ret; + + if (!bs->drv) { + return -ENOMEDIUM; + } + +- if (!setting_flag && bs->drv->bdrv_inactivate) { ++ /* 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; + } + } + +- if (setting_flag && !(bs->open_flags & BDRV_O_INACTIVE)) { +- uint64_t perm, shared_perm; +- +- QLIST_FOREACH(parent, &bs->parents, next_parent) { +- if (parent->role->inactivate) { +- ret = parent->role->inactivate(parent); +- if (ret < 0) { +- return ret; +- } ++ QLIST_FOREACH(parent, &bs->parents, next_parent) { ++ if (parent->role->inactivate) { ++ ret = parent->role->inactivate(parent); ++ if (ret < 0) { ++ return ret; + } + } ++ } + +- bs->open_flags |= BDRV_O_INACTIVE; ++ bs->open_flags |= BDRV_O_INACTIVE; ++ ++ /* Update permissions, they may differ for inactive nodes */ ++ bdrv_get_cumulative_perm(bs, &perm, &shared_perm); ++ bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, &error_abort); ++ bdrv_set_perm(bs, perm, shared_perm); + +- /* Update permissions, they may differ for inactive nodes */ +- bdrv_get_cumulative_perm(bs, &perm, &shared_perm); +- bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, &error_abort); +- bdrv_set_perm(bs, perm, shared_perm); +- } + ++ /* Recursively inactivate children */ + QLIST_FOREACH(child, &bs->children, next) { +- ret = bdrv_inactivate_recurse(child->bs, setting_flag); ++ ret = bdrv_inactivate_recurse(child->bs); + if (ret < 0) { + return ret; + } +@@ -4525,7 +4548,6 @@ int bdrv_inactivate_all(void) + BlockDriverState *bs = NULL; + BdrvNextIterator it; + int ret = 0; +- int pass; + GSList *aio_ctxs = NULL, *ctx; + + for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { +@@ -4537,17 +4559,17 @@ int bdrv_inactivate_all(void) + } + } + +- /* We do two passes of inactivation. The first pass calls to drivers' +- * .bdrv_inactivate callbacks recursively so all cache is flushed to disk; +- * the second pass sets the BDRV_O_INACTIVE flag so that no further write +- * is allowed. */ +- for (pass = 0; pass < 2; pass++) { +- for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { +- ret = bdrv_inactivate_recurse(bs, pass); +- if (ret < 0) { +- bdrv_next_cleanup(&it); +- goto out; +- } ++ 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); ++ goto out; + } + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Don-t-manually-poll-in-bdrv_drain_all.patch b/SOURCES/kvm-block-Don-t-manually-poll-in-bdrv_drain_all.patch new file mode 100644 index 0000000..9f07da1 --- /dev/null +++ b/SOURCES/kvm-block-Don-t-manually-poll-in-bdrv_drain_all.patch @@ -0,0 +1,113 @@ +From 9006222a2f826c5760f305bbd879f1b7ce3563b6 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:02 +0200 +Subject: [PATCH 11/49] block: Don't manually poll in bdrv_drain_all() + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-5-kwolf@redhat.com> +Patchwork-id: 82157 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 04/42] block: Don't manually poll in bdrv_drain_all() +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +All involved nodes are already idle, we called bdrv_do_drain_begin() on +them. + +The comment in the code suggested that this was not correct because the +completion of a request on one node could spawn a new request on a +different node (which might have been drained before, so we wouldn't +drain the new request). In reality, new requests to different nodes +aren't spawned out of nothing, but only in the context of a parent +request, and they aren't submitted to random nodes, but only to child +nodes. As long as we still poll for the completion of the parent request +(which we do), draining each root node separately is good enough. + +Remove the additional polling code from bdrv_drain_all_begin() and +replace it with an assertion that all nodes are already idle after we +drained them separately. + +Signed-off-by: Kevin Wolf +Reviewed-by: Stefan Hajnoczi +(cherry picked from commit c13ad59f012cbbccb866a10477458e69bc868dbb) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/io.c | 41 ++++++++++++----------------------------- + 1 file changed, 12 insertions(+), 29 deletions(-) + +diff --git a/block/io.c b/block/io.c +index aa41f1e..e5fc42c 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -376,6 +376,16 @@ void bdrv_drain(BlockDriverState *bs) + bdrv_drained_end(bs); + } + ++static void bdrv_drain_assert_idle(BlockDriverState *bs) ++{ ++ BdrvChild *child, *next; ++ ++ assert(atomic_read(&bs->in_flight) == 0); ++ QLIST_FOREACH_SAFE(child, &bs->children, next, next) { ++ bdrv_drain_assert_idle(child->bs); ++ } ++} ++ + /* + * Wait for pending requests to complete across all BlockDriverStates + * +@@ -390,11 +400,8 @@ void bdrv_drain(BlockDriverState *bs) + */ + void bdrv_drain_all_begin(void) + { +- /* Always run first iteration so any pending completion BHs run */ +- bool waited = true; + BlockDriverState *bs; + BdrvNextIterator it; +- GSList *aio_ctxs = NULL, *ctx; + + /* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread + * or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on +@@ -408,35 +415,11 @@ void bdrv_drain_all_begin(void) + aio_context_acquire(aio_context); + bdrv_do_drained_begin(bs, true, NULL); + aio_context_release(aio_context); +- +- if (!g_slist_find(aio_ctxs, aio_context)) { +- aio_ctxs = g_slist_prepend(aio_ctxs, aio_context); +- } + } + +- /* Note that completion of an asynchronous I/O operation can trigger any +- * number of other I/O operations on other devices---for example a +- * coroutine can submit an I/O request to another device in response to +- * request completion. Therefore we must keep looping until there was no +- * more activity rather than simply draining each device independently. +- */ +- while (waited) { +- waited = false; +- +- for (ctx = aio_ctxs; ctx != NULL; ctx = ctx->next) { +- AioContext *aio_context = ctx->data; +- +- aio_context_acquire(aio_context); +- for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { +- if (aio_context == bdrv_get_aio_context(bs)) { +- waited |= bdrv_drain_recurse(bs); +- } +- } +- aio_context_release(aio_context); +- } ++ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { ++ bdrv_drain_assert_idle(bs); + } +- +- g_slist_free(aio_ctxs); + } + + void bdrv_drain_all_end(void) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Don-t-poll-in-parent-drain-callbacks.patch b/SOURCES/kvm-block-Don-t-poll-in-parent-drain-callbacks.patch new file mode 100644 index 0000000..c2b4972 --- /dev/null +++ b/SOURCES/kvm-block-Don-t-poll-in-parent-drain-callbacks.patch @@ -0,0 +1,112 @@ +From 72f0fcad0938bba82537e78753e242d53f3f2583 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:10 +0200 +Subject: [PATCH 19/49] block: Don't poll in parent drain callbacks + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-13-kwolf@redhat.com> +Patchwork-id: 82164 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 12/42] block: Don't poll in parent drain callbacks +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +bdrv_do_drained_begin() is only safe if we have a single +BDRV_POLL_WHILE() after quiescing all affected nodes. We cannot allow +that parent callbacks introduce a nested polling loop that could cause +graph changes while we're traversing the graph. + +Split off bdrv_do_drained_begin_quiesce(), which only quiesces a single +node without waiting for its requests to complete. These requests will +be waited for in the BDRV_POLL_WHILE() call down the call chain. + +Signed-off-by: Kevin Wolf +(cherry picked from commit dcf94a23b1add0f856db51e9ff5ba0774e096076) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 2 +- + block/io.c | 24 ++++++++++++++++-------- + include/block/block.h | 9 +++++++++ + 3 files changed, 26 insertions(+), 9 deletions(-) + +diff --git a/block.c b/block.c +index 9afaf26..5a50de6 100644 +--- a/block.c ++++ b/block.c +@@ -818,7 +818,7 @@ static char *bdrv_child_get_parent_desc(BdrvChild *c) + static void bdrv_child_cb_drained_begin(BdrvChild *child) + { + BlockDriverState *bs = child->opaque; +- bdrv_drained_begin(bs); ++ bdrv_do_drained_begin_quiesce(bs, NULL); + } + + static bool bdrv_child_cb_drained_poll(BdrvChild *child) +diff --git a/block/io.c b/block/io.c +index 442ded1..9c41ff9 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -285,15 +285,10 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, + assert(data.done); + } + +-void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, +- BdrvChild *parent, bool poll) ++void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, ++ BdrvChild *parent) + { +- BdrvChild *child, *next; +- +- if (qemu_in_coroutine()) { +- bdrv_co_yield_to_drain(bs, true, recursive, parent, poll); +- return; +- } ++ assert(!qemu_in_coroutine()); + + /* Stop things in parent-to-child order */ + if (atomic_fetch_inc(&bs->quiesce_counter) == 0) { +@@ -302,6 +297,19 @@ void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, + + bdrv_parent_drained_begin(bs, parent); + bdrv_drain_invoke(bs, true); ++} ++ ++static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, ++ BdrvChild *parent, bool poll) ++{ ++ BdrvChild *child, *next; ++ ++ if (qemu_in_coroutine()) { ++ bdrv_co_yield_to_drain(bs, true, recursive, parent, poll); ++ return; ++ } ++ ++ bdrv_do_drained_begin_quiesce(bs, parent); + + if (recursive) { + bs->recursive_quiesce_counter++; +diff --git a/include/block/block.h b/include/block/block.h +index 2bbea7c..43f29b5 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -619,6 +619,15 @@ bool bdrv_drain_poll(BlockDriverState *bs, bool 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); ++ ++/** + * Like bdrv_drained_begin, but recursively begins a quiesced section for + * exclusive access to all child nodes as well. + */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Don-t-silently-truncate-node-names.patch b/SOURCES/kvm-block-Don-t-silently-truncate-node-names.patch new file mode 100644 index 0000000..73463fa --- /dev/null +++ b/SOURCES/kvm-block-Don-t-silently-truncate-node-names.patch @@ -0,0 +1,148 @@ +From 524287dc348c81473dd19b275a0333dd2e888878 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Thu, 5 Jul 2018 16:47:51 +0200 +Subject: [PATCH 16/89] block: Don't silently truncate node names + +RH-Author: Kevin Wolf +Message-id: <20180705164751.15271-2-kwolf@redhat.com> +Patchwork-id: 81234 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/1] block: Don't silently truncate node names +Bugzilla: 1549654 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Max Reitz +RH-Acked-by: John Snow + +If the user passes a too long node name string, we silently truncate it +to fit into BlockDriverState.node_name, i.e. to 31 characters. Apart +from surprising the user when the node has a different name than +requested, this also bypasses the check for duplicate names, so that the +same name can be assigned to multiple nodes. + +Fix this by just making too long node names an error. + +Reported-by: Peter Krempa +Signed-off-by: Kevin Wolf +(cherry picked from commit 824808dd77821ceba05357cb1ee4069a6a95bebd) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 6 ++++++ + tests/qemu-iotests/051 | 15 +++++++++++++++ + tests/qemu-iotests/051.out | 23 +++++++++++++++++++++++ + tests/qemu-iotests/051.pc.out | 23 +++++++++++++++++++++++ + 4 files changed, 67 insertions(+) + +diff --git a/block.c b/block.c +index afe30ca..0516284 100644 +--- a/block.c ++++ b/block.c +@@ -1140,6 +1140,12 @@ static void bdrv_assign_node_name(BlockDriverState *bs, + 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); +diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 +index 69d34eb..c5cc0ee 100755 +--- a/tests/qemu-iotests/051 ++++ b/tests/qemu-iotests/051 +@@ -100,6 +100,21 @@ run_qemu -drive file="$TEST_IMG",driver=raw,format=qcow2 + run_qemu -drive file="$TEST_IMG",driver=qcow2,format=qcow2 + + echo ++echo === Node names === ++echo ++ ++# Maximum length: 31 characters ++run_qemu -drive file="$TEST_IMG",node-name=x123456789012345678901234567890 ++run_qemu -drive file="$TEST_IMG",node-name=x1234567890123456789012345678901 ++ ++# First character must be alphabetic ++# Following characters alphanumeric or -._ ++run_qemu -drive file="$TEST_IMG",node-name=All-Types.of_all0wed_chars ++run_qemu -drive file="$TEST_IMG",node-name=123foo ++run_qemu -drive file="$TEST_IMG",node-name=_foo ++run_qemu -drive file="$TEST_IMG",node-name=foo#12 ++ ++echo + echo === Device without drive === + echo + +diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out +index dd9846d..b727350 100644 +--- a/tests/qemu-iotests/051.out ++++ b/tests/qemu-iotests/051.out +@@ -47,6 +47,29 @@ Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2 + QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2: Cannot specify both 'driver' and 'format' + + ++=== Node names === ++ ++Testing: -drive file=TEST_DIR/t.qcow2,node-name=x123456789012345678901234567890 ++QEMU X.Y.Z monitor - type 'help' for more information ++(qemu) quit ++ ++Testing: -drive file=TEST_DIR/t.qcow2,node-name=x1234567890123456789012345678901 ++QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=x1234567890123456789012345678901: Node name too long ++ ++Testing: -drive file=TEST_DIR/t.qcow2,node-name=All-Types.of_all0wed_chars ++QEMU X.Y.Z monitor - type 'help' for more information ++(qemu) quit ++ ++Testing: -drive file=TEST_DIR/t.qcow2,node-name=123foo ++QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=123foo: Invalid node name ++ ++Testing: -drive file=TEST_DIR/t.qcow2,node-name=_foo ++QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=_foo: Invalid node name ++ ++Testing: -drive file=TEST_DIR/t.qcow2,node-name=foo#12 ++QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=foo#12: Invalid node name ++ ++ + === Device without drive === + + Testing: -device VIRTIO_SCSI -device scsi-hd +diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out +index b01f9a9..e9257fe 100644 +--- a/tests/qemu-iotests/051.pc.out ++++ b/tests/qemu-iotests/051.pc.out +@@ -47,6 +47,29 @@ Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2 + QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2: Cannot specify both 'driver' and 'format' + + ++=== Node names === ++ ++Testing: -drive file=TEST_DIR/t.qcow2,node-name=x123456789012345678901234567890 ++QEMU X.Y.Z monitor - type 'help' for more information ++(qemu) quit ++ ++Testing: -drive file=TEST_DIR/t.qcow2,node-name=x1234567890123456789012345678901 ++QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=x1234567890123456789012345678901: Node name too long ++ ++Testing: -drive file=TEST_DIR/t.qcow2,node-name=All-Types.of_all0wed_chars ++QEMU X.Y.Z monitor - type 'help' for more information ++(qemu) quit ++ ++Testing: -drive file=TEST_DIR/t.qcow2,node-name=123foo ++QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=123foo: Invalid node name ++ ++Testing: -drive file=TEST_DIR/t.qcow2,node-name=_foo ++QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=_foo: Invalid node name ++ ++Testing: -drive file=TEST_DIR/t.qcow2,node-name=foo#12 ++QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=foo#12: Invalid node name ++ ++ + === Device without drive === + + Testing: -device VIRTIO_SCSI -device scsi-hd +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Drain-recursively-with-a-single-BDRV_POLL_WHIL.patch b/SOURCES/kvm-block-Drain-recursively-with-a-single-BDRV_POLL_WHIL.patch new file mode 100644 index 0000000..d3f6485 --- /dev/null +++ b/SOURCES/kvm-block-Drain-recursively-with-a-single-BDRV_POLL_WHIL.patch @@ -0,0 +1,242 @@ +From 3ff0f5aa671432b2c11c36818368ed1e40c95f5a Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:08 +0200 +Subject: [PATCH 17/49] block: Drain recursively with a single + BDRV_POLL_WHILE() + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-11-kwolf@redhat.com> +Patchwork-id: 82163 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 10/42] block: Drain recursively with a single BDRV_POLL_WHILE() +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Anything can happen inside BDRV_POLL_WHILE(), including graph +changes that may interfere with its callers (e.g. child list iteration +in recursive callers of bdrv_do_drained_begin). + +Switch to a single BDRV_POLL_WHILE() call for the whole subtree at the +end of bdrv_do_drained_begin() to avoid such effects. The recursion +happens now inside the loop condition. As the graph can only change +between bdrv_drain_poll() calls, but not inside of it, doing the +recursion here is safe. + +Signed-off-by: Kevin Wolf +(cherry picked from commit fe4f0614ef9e361dae12012d3c400657444836cf) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 2 +- + block/io.c | 63 ++++++++++++++++++++++++++++++++++++--------------- + include/block/block.h | 9 +++++--- + 3 files changed, 52 insertions(+), 22 deletions(-) + +diff --git a/block.c b/block.c +index 0d9698a..9afaf26 100644 +--- a/block.c ++++ b/block.c +@@ -824,7 +824,7 @@ static void bdrv_child_cb_drained_begin(BdrvChild *child) + static bool bdrv_child_cb_drained_poll(BdrvChild *child) + { + BlockDriverState *bs = child->opaque; +- return bdrv_drain_poll(bs, NULL); ++ return bdrv_drain_poll(bs, false, NULL); + } + + static void bdrv_child_cb_drained_end(BdrvChild *child) +diff --git a/block/io.c b/block/io.c +index a0e3461..442ded1 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -164,6 +164,7 @@ typedef struct { + bool done; + bool begin; + bool recursive; ++ bool poll; + BdrvChild *parent; + } BdrvCoDrainData; + +@@ -199,27 +200,42 @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) + } + + /* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */ +-bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent) ++bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, ++ BdrvChild *ignore_parent) + { ++ BdrvChild *child, *next; ++ + if (bdrv_parent_drained_poll(bs, ignore_parent)) { + return true; + } + +- return atomic_read(&bs->in_flight); ++ if (atomic_read(&bs->in_flight)) { ++ return true; ++ } ++ ++ if (recursive) { ++ QLIST_FOREACH_SAFE(child, &bs->children, next, next) { ++ if (bdrv_drain_poll(child->bs, recursive, child)) { ++ return true; ++ } ++ } ++ } ++ ++ return false; + } + +-static bool bdrv_drain_poll_top_level(BlockDriverState *bs, ++static bool bdrv_drain_poll_top_level(BlockDriverState *bs, bool recursive, + BdrvChild *ignore_parent) + { + /* Execute pending BHs first and check everything else only after the BHs + * have executed. */ + while (aio_poll(bs->aio_context, false)); + +- return bdrv_drain_poll(bs, ignore_parent); ++ return bdrv_drain_poll(bs, recursive, ignore_parent); + } + + static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, +- BdrvChild *parent); ++ BdrvChild *parent, bool poll); + static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, + BdrvChild *parent); + +@@ -231,7 +247,7 @@ static void bdrv_co_drain_bh_cb(void *opaque) + + bdrv_dec_in_flight(bs); + if (data->begin) { +- bdrv_do_drained_begin(bs, data->recursive, data->parent); ++ bdrv_do_drained_begin(bs, data->recursive, data->parent, data->poll); + } else { + bdrv_do_drained_end(bs, data->recursive, data->parent); + } +@@ -242,7 +258,7 @@ static void bdrv_co_drain_bh_cb(void *opaque) + + static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, + bool begin, bool recursive, +- BdrvChild *parent) ++ BdrvChild *parent, bool poll) + { + BdrvCoDrainData data; + +@@ -257,6 +273,7 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, + .begin = begin, + .recursive = recursive, + .parent = parent, ++ .poll = poll, + }; + bdrv_inc_in_flight(bs); + aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), +@@ -269,12 +286,12 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, + } + + void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, +- BdrvChild *parent) ++ BdrvChild *parent, bool poll) + { + BdrvChild *child, *next; + + if (qemu_in_coroutine()) { +- bdrv_co_yield_to_drain(bs, true, recursive, parent); ++ bdrv_co_yield_to_drain(bs, true, recursive, parent, poll); + return; + } + +@@ -286,25 +303,35 @@ void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, + bdrv_parent_drained_begin(bs, parent); + bdrv_drain_invoke(bs, true); + +- /* Wait for drained requests to finish */ +- BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, parent)); +- + if (recursive) { + bs->recursive_quiesce_counter++; + QLIST_FOREACH_SAFE(child, &bs->children, next, next) { +- bdrv_do_drained_begin(child->bs, true, child); ++ bdrv_do_drained_begin(child->bs, true, child, false); + } + } ++ ++ /* ++ * 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, recursive, parent)); ++ } + } + + void bdrv_drained_begin(BlockDriverState *bs) + { +- bdrv_do_drained_begin(bs, false, NULL); ++ bdrv_do_drained_begin(bs, false, NULL, true); + } + + void bdrv_subtree_drained_begin(BlockDriverState *bs) + { +- bdrv_do_drained_begin(bs, true, NULL); ++ bdrv_do_drained_begin(bs, true, NULL, true); + } + + void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, +@@ -314,7 +341,7 @@ void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, + int old_quiesce_counter; + + if (qemu_in_coroutine()) { +- bdrv_co_yield_to_drain(bs, false, recursive, parent); ++ bdrv_co_yield_to_drain(bs, false, recursive, parent, false); + return; + } + assert(bs->quiesce_counter > 0); +@@ -350,7 +377,7 @@ void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent) + int i; + + for (i = 0; i < new_parent->recursive_quiesce_counter; i++) { +- bdrv_do_drained_begin(child->bs, true, child); ++ bdrv_do_drained_begin(child->bs, true, child, true); + } + } + +@@ -420,7 +447,7 @@ void bdrv_drain_all_begin(void) + AioContext *aio_context = bdrv_get_aio_context(bs); + + aio_context_acquire(aio_context); +- bdrv_do_drained_begin(bs, true, NULL); ++ bdrv_do_drained_begin(bs, true, NULL, true); + aio_context_release(aio_context); + } + +diff --git a/include/block/block.h b/include/block/block.h +index 8c91d4c..2bbea7c 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -598,10 +598,13 @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore); + /** + * bdrv_drain_poll: + * +- * Poll for pending requests in @bs and its parents (except for +- * @ignore_parent). This is part of bdrv_drained_begin. ++ * Poll for pending requests in @bs, its parents (except for @ignore_parent), ++ * and if @recursive is true its children as well. ++ * ++ * This is part of bdrv_drained_begin. + */ +-bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent); ++bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, ++ BdrvChild *ignore_parent); + + /** + * bdrv_drained_begin: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Drain-source-node-in-bdrv_replace_node.patch b/SOURCES/kvm-block-Drain-source-node-in-bdrv_replace_node.patch new file mode 100644 index 0000000..78689de --- /dev/null +++ b/SOURCES/kvm-block-Drain-source-node-in-bdrv_replace_node.patch @@ -0,0 +1,66 @@ +From b8a78d2f99bb481e78686ef2e0beb369b5563d87 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 3 Sep 2019 15:28:28 +0200 +Subject: [PATCH 23/23] block: Drain source node in bdrv_replace_node() + +RH-Author: Kevin Wolf +Message-id: <20190903152828.15668-2-kwolf@redhat.com> +Patchwork-id: 90257 +O-Subject: [RHEL-7.7.z qemu-kvm PATCH 1/1] block: Drain source node in bdrv_replace_node() +Bugzilla: 1711643 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Pankaj Gupta + +Instead of just asserting that no requests are in flight in +bdrv_replace_node(), which is a requirement that most callers ignore, we +can just drain the source node right there. This fixes at least starting +a commit job while I/O is active on the backing chain, but probably +other callers, too. + +Having requests in flight on the target node isn't a problem because the +target just gets new parents, but the call path of running requests +isn't modified. So we can just drop this assertion without a replacement. + +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1711643 +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit f871abd60f4b67547e62c57c9bec19420052be39) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/block.c b/block.c +index 7061f9b..4986516 100644 +--- a/block.c ++++ b/block.c +@@ -3505,13 +3505,13 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, + uint64_t perm = 0, shared = BLK_PERM_ALL; + int ret; + +- assert(!atomic_read(&from->in_flight)); +- assert(!atomic_read(&to->in_flight)); +- + /* Make sure that @from doesn't go away until we have successfully attached + * all of its parents to @to. */ + bdrv_ref(from); + ++ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); ++ bdrv_drained_begin(from); ++ + /* Put all parents into @list and calculate their cumulative permissions */ + QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { + if (!should_update_child(c, to)) { +@@ -3546,6 +3546,7 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, + + out: + g_slist_free(list); ++ bdrv_drained_end(from); + bdrv_unref(from); + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Factor-out-qobject_input_visitor_new_flat_conf.patch b/SOURCES/kvm-block-Factor-out-qobject_input_visitor_new_flat_conf.patch new file mode 100644 index 0000000..975e803 --- /dev/null +++ b/SOURCES/kvm-block-Factor-out-qobject_input_visitor_new_flat_conf.patch @@ -0,0 +1,465 @@ +From 8a66fde222f0192c1d9ce215f297964ab34235c7 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:20 +0200 +Subject: [PATCH 13/54] block: Factor out + qobject_input_visitor_new_flat_confused() + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-14-armbru@redhat.com> +Patchwork-id: 80728 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 13/23] block: Factor out qobject_input_visitor_new_flat_confused() +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Signed-off-by: Markus Armbruster +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit af91062ee1408f7f5bb58389d355d29a5040c648) +Signed-off-by: Miroslav Rezanina +--- + block/nbd.c | 7 ++----- + block/nfs.c | 7 ++----- + block/parallels.c | 7 ++----- + block/qcow.c | 7 ++----- + block/qcow2.c | 7 ++----- + block/qed.c | 7 ++----- + block/rbd.c | 7 ++----- + block/sheepdog.c | 14 ++++---------- + block/ssh.c | 7 ++----- + block/vhdx.c | 7 ++----- + block/vpc.c | 7 ++----- + include/block/qdict.h | 3 ++- + qobject/block-qdict.c | 28 +++++++++++++++++++++++++++- + 13 files changed, 53 insertions(+), 62 deletions(-) + +diff --git a/block/nbd.c b/block/nbd.c +index b0be357..10912c3 100644 +--- a/block/nbd.c ++++ b/block/nbd.c +@@ -263,7 +263,6 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, + { + SocketAddress *saddr = NULL; + QDict *addr = NULL; +- QObject *crumpled_addr = NULL; + Visitor *iv = NULL; + Error *local_err = NULL; + +@@ -273,12 +272,11 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, + goto done; + } + +- crumpled_addr = qdict_crumple_for_keyval_qiv(addr, errp); +- if (!crumpled_addr) { ++ iv = qobject_input_visitor_new_flat_confused(addr, errp); ++ if (!iv) { + goto done; + } + +- iv = qobject_input_visitor_new_keyval(crumpled_addr); + visit_type_SocketAddress(iv, NULL, &saddr, &local_err); + if (local_err) { + error_propagate(errp, local_err); +@@ -287,7 +285,6 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, + + done: + qobject_unref(addr); +- qobject_unref(crumpled_addr); + visit_free(iv); + return saddr; + } +diff --git a/block/nfs.c b/block/nfs.c +index 4090d28..1e12958 100644 +--- a/block/nfs.c ++++ b/block/nfs.c +@@ -556,19 +556,16 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options, + Error **errp) + { + BlockdevOptionsNfs *opts = NULL; +- QObject *crumpled = NULL; + Visitor *v; + Error *local_err = NULL; + +- crumpled = qdict_crumple_for_keyval_qiv(options, errp); +- if (crumpled == NULL) { ++ v = qobject_input_visitor_new_flat_confused(options, errp); ++ if (!v) { + return NULL; + } + +- v = qobject_input_visitor_new_keyval(crumpled); + visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err); + visit_free(v); +- qobject_unref(crumpled); + + if (local_err) { + return NULL; +diff --git a/block/parallels.c b/block/parallels.c +index 1c96c39..b6ebe36 100644 +--- a/block/parallels.c ++++ b/block/parallels.c +@@ -615,7 +615,6 @@ static int coroutine_fn parallels_co_create_opts(const char *filename, + Error *local_err = NULL; + BlockDriverState *bs = NULL; + QDict *qdict; +- QObject *qobj; + Visitor *v; + int ret; + +@@ -651,14 +650,12 @@ static int coroutine_fn parallels_co_create_opts(const char *filename, + qdict_put_str(qdict, "driver", "parallels"); + qdict_put_str(qdict, "file", bs->node_name); + +- qobj = qdict_crumple_for_keyval_qiv(qdict, errp); +- if (!qobj) { ++ v = qobject_input_visitor_new_flat_confused(qdict, errp); ++ if (!v) { + ret = -EINVAL; + goto done; + } + +- v = qobject_input_visitor_new_keyval(qobj); +- qobject_unref(qobj); + visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); + visit_free(v); + +diff --git a/block/qcow.c b/block/qcow.c +index 43a595a..4b3bc31 100644 +--- a/block/qcow.c ++++ b/block/qcow.c +@@ -945,7 +945,6 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, + BlockdevCreateOptions *create_options = NULL; + BlockDriverState *bs = NULL; + QDict *qdict; +- QObject *qobj; + Visitor *v; + const char *val; + Error *local_err = NULL; +@@ -995,14 +994,12 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, + qdict_put_str(qdict, "driver", "qcow"); + qdict_put_str(qdict, "file", bs->node_name); + +- qobj = qdict_crumple_for_keyval_qiv(qdict, errp); +- if (!qobj) { ++ v = qobject_input_visitor_new_flat_confused(qdict, errp); ++ if (!v) { + ret = -EINVAL; + goto fail; + } + +- v = qobject_input_visitor_new_keyval(qobj); +- qobject_unref(qobj); + visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); + visit_free(v); + +diff --git a/block/qcow2.c b/block/qcow2.c +index ede52a8..35842c5 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -3068,7 +3068,6 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt + { + BlockdevCreateOptions *create_options = NULL; + QDict *qdict; +- QObject *qobj; + Visitor *v; + BlockDriverState *bs = NULL; + Error *local_err = NULL; +@@ -3139,14 +3138,12 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt + qdict_put_str(qdict, "file", bs->node_name); + + /* Now get the QAPI type BlockdevCreateOptions */ +- qobj = qdict_crumple_for_keyval_qiv(qdict, errp); +- if (!qobj) { ++ v = qobject_input_visitor_new_flat_confused(qdict, errp); ++ if (!v) { + ret = -EINVAL; + goto finish; + } + +- v = qobject_input_visitor_new_keyval(qobj); +- qobject_unref(qobj); + visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); + visit_free(v); + +diff --git a/block/qed.c b/block/qed.c +index 3818888..4c5d7e8 100644 +--- a/block/qed.c ++++ b/block/qed.c +@@ -723,7 +723,6 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, + { + BlockdevCreateOptions *create_options = NULL; + QDict *qdict; +- QObject *qobj; + Visitor *v; + BlockDriverState *bs = NULL; + Error *local_err = NULL; +@@ -763,14 +762,12 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, + qdict_put_str(qdict, "driver", "qed"); + qdict_put_str(qdict, "file", bs->node_name); + +- qobj = qdict_crumple_for_keyval_qiv(qdict, errp); +- if (!qobj) { ++ v = qobject_input_visitor_new_flat_confused(qdict, errp); ++ if (!v) { + ret = -EINVAL; + goto fail; + } + +- v = qobject_input_visitor_new_keyval(qobj); +- qobject_unref(qobj); + visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); + visit_free(v); + +diff --git a/block/rbd.c b/block/rbd.c +index 0b5455f..c834d72 100644 +--- a/block/rbd.c ++++ b/block/rbd.c +@@ -623,7 +623,6 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, + BDRVRBDState *s = bs->opaque; + BlockdevOptionsRbd *opts = NULL; + Visitor *v; +- QObject *crumpled = NULL; + const QDictEntry *e; + Error *local_err = NULL; + char *keypairs, *secretid; +@@ -640,16 +639,14 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, + } + + /* Convert the remaining options into a QAPI object */ +- crumpled = qdict_crumple_for_keyval_qiv(options, errp); +- if (crumpled == NULL) { ++ v = qobject_input_visitor_new_flat_confused(options, errp); ++ if (!v) { + r = -EINVAL; + goto out; + } + +- v = qobject_input_visitor_new_keyval(crumpled); + visit_type_BlockdevOptionsRbd(v, NULL, &opts, &local_err); + visit_free(v); +- qobject_unref(crumpled); + + if (local_err) { + error_propagate(errp, local_err); +diff --git a/block/sheepdog.c b/block/sheepdog.c +index dd582d5..d1c9bf5 100644 +--- a/block/sheepdog.c ++++ b/block/sheepdog.c +@@ -539,19 +539,17 @@ static void sd_aio_setup(SheepdogAIOCB *acb, BDRVSheepdogState *s, + static SocketAddress *sd_server_config(QDict *options, Error **errp) + { + QDict *server = NULL; +- QObject *crumpled_server = NULL; + Visitor *iv = NULL; + SocketAddress *saddr = NULL; + Error *local_err = NULL; + + qdict_extract_subqdict(options, &server, "server."); + +- crumpled_server = qdict_crumple_for_keyval_qiv(server, errp); +- if (!crumpled_server) { ++ iv = qobject_input_visitor_new_flat_confused(server, errp); ++ if (!iv) { + goto done; + } + +- iv = qobject_input_visitor_new_keyval(crumpled_server); + visit_type_SocketAddress(iv, NULL, &saddr, &local_err); + if (local_err) { + error_propagate(errp, local_err); +@@ -560,7 +558,6 @@ static SocketAddress *sd_server_config(QDict *options, Error **errp) + + done: + visit_free(iv); +- qobject_unref(crumpled_server); + qobject_unref(server); + return saddr; + } +@@ -2174,7 +2171,6 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, + { + BlockdevCreateOptions *create_options = NULL; + QDict *qdict, *location_qdict; +- QObject *crumpled; + Visitor *v; + const char *redundancy; + Error *local_err = NULL; +@@ -2210,16 +2206,14 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, + } + + /* Get the QAPI object */ +- crumpled = qdict_crumple_for_keyval_qiv(qdict, errp); +- if (crumpled == NULL) { ++ v = qobject_input_visitor_new_flat_confused(qdict, errp); ++ if (!v) { + ret = -EINVAL; + goto fail; + } + +- v = qobject_input_visitor_new_keyval(crumpled); + visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); + visit_free(v); +- qobject_unref(crumpled); + + if (local_err) { + error_propagate(errp, local_err); +diff --git a/block/ssh.c b/block/ssh.c +index 2fc7cd9..aab6996 100644 +--- a/block/ssh.c ++++ b/block/ssh.c +@@ -606,7 +606,6 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp) + BlockdevOptionsSsh *result = NULL; + QemuOpts *opts = NULL; + Error *local_err = NULL; +- QObject *crumpled; + const QDictEntry *e; + Visitor *v; + +@@ -623,15 +622,13 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp) + } + + /* Create the QAPI object */ +- crumpled = qdict_crumple_for_keyval_qiv(options, errp); +- if (crumpled == NULL) { ++ v = qobject_input_visitor_new_flat_confused(options, errp); ++ if (!v) { + goto fail; + } + +- v = qobject_input_visitor_new_keyval(crumpled); + visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err); + visit_free(v); +- qobject_unref(crumpled); + + if (local_err) { + error_propagate(errp, local_err); +diff --git a/block/vhdx.c b/block/vhdx.c +index 728d8b3..6731298 100644 +--- a/block/vhdx.c ++++ b/block/vhdx.c +@@ -1964,7 +1964,6 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, + { + BlockdevCreateOptions *create_options = NULL; + QDict *qdict; +- QObject *qobj; + Visitor *v; + BlockDriverState *bs = NULL; + Error *local_err = NULL; +@@ -2003,14 +2002,12 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, + qdict_put_str(qdict, "driver", "vhdx"); + qdict_put_str(qdict, "file", bs->node_name); + +- qobj = qdict_crumple_for_keyval_qiv(qdict, errp); +- if (!qobj) { ++ v = qobject_input_visitor_new_flat_confused(qdict, errp); ++ if (!v) { + ret = -EINVAL; + goto fail; + } + +- v = qobject_input_visitor_new_keyval(qobj); +- qobject_unref(qobj); + visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); + visit_free(v); + +diff --git a/block/vpc.c b/block/vpc.c +index a9bb041..bf294ab 100644 +--- a/block/vpc.c ++++ b/block/vpc.c +@@ -1082,7 +1082,6 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, + { + BlockdevCreateOptions *create_options = NULL; + QDict *qdict; +- QObject *qobj; + Visitor *v; + BlockDriverState *bs = NULL; + Error *local_err = NULL; +@@ -1119,14 +1118,12 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, + qdict_put_str(qdict, "driver", "vpc"); + qdict_put_str(qdict, "file", bs->node_name); + +- qobj = qdict_crumple_for_keyval_qiv(qdict, errp); +- if (!qobj) { ++ v = qobject_input_visitor_new_flat_confused(qdict, errp); ++ if (!v) { + ret = -EINVAL; + goto fail; + } + +- v = qobject_input_visitor_new_keyval(qobj); +- qobject_unref(qobj); + visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); + visit_free(v); + +diff --git a/include/block/qdict.h b/include/block/qdict.h +index 47d9638..d8cb502 100644 +--- a/include/block/qdict.h ++++ b/include/block/qdict.h +@@ -21,7 +21,6 @@ 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); + QObject *qdict_crumple(const QDict *src, Error **errp); +-QObject *qdict_crumple_for_keyval_qiv(QDict *qdict, Error **errp); + void qdict_flatten(QDict *qdict); + + typedef struct QDictRenames { +@@ -30,4 +29,6 @@ typedef struct QDictRenames { + } 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/qobject/block-qdict.c b/qobject/block-qdict.c +index aba372c..41f39ab 100644 +--- a/qobject/block-qdict.c ++++ b/qobject/block-qdict.c +@@ -13,6 +13,7 @@ + #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" + +@@ -529,7 +530,7 @@ QObject *qdict_crumple(const QDict *src, Error **errp) + * used for anything else, and it should go away once the block + * subsystem has been cleaned up. + */ +-QObject *qdict_crumple_for_keyval_qiv(QDict *src, Error **errp) ++static QObject *qdict_crumple_for_keyval_qiv(QDict *src, Error **errp) + { + QDict *tmp = NULL; + char *buf; +@@ -695,3 +696,28 @@ bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp) + } + 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; ++} +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Fix-AioContext-switch-for-bs-drv-NULL.patch b/SOURCES/kvm-block-Fix-AioContext-switch-for-bs-drv-NULL.patch new file mode 100644 index 0000000..5cd09a1 --- /dev/null +++ b/SOURCES/kvm-block-Fix-AioContext-switch-for-bs-drv-NULL.patch @@ -0,0 +1,118 @@ +From b495f8e19a84ce58d711fd1ec34b2d8234e4b542 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Thu, 9 May 2019 15:34:15 +0200 +Subject: [PATCH 12/12] block: Fix AioContext switch for bs->drv == NULL + +RH-Author: Kevin Wolf +Message-id: <20190509153415.29673-2-kwolf@redhat.com> +Patchwork-id: 87216 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/1] block: Fix AioContext switch for bs->drv == NULL +Bugzilla: 1631227 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Miroslav Rezanina + +Even for block nodes with bs->drv == NULL, we can't just ignore a +bdrv_set_aio_context() call. Leaving the node in its old context can +mean that it's still in an iothread context in bdrv_close_all() during +shutdown, resulting in an attempted unlock of the AioContext lock which +we don't hold. + +This is an example stack trace of a related crash: + + #0 0x00007ffff59da57f in raise () at /lib64/libc.so.6 + #1 0x00007ffff59c4895 in abort () at /lib64/libc.so.6 + #2 0x0000555555b97b1e in error_exit (err=, msg=msg@entry=0x555555d386d0 <__func__.19059> "qemu_mutex_unlock_impl") at util/qemu-thread-posix.c:36 + #3 0x0000555555b97f7f in qemu_mutex_unlock_impl (mutex=mutex@entry=0x5555568002f0, file=file@entry=0x555555d378df "util/async.c", line=line@entry=507) at util/qemu-thread-posix.c:97 + #4 0x0000555555b92f55 in aio_context_release (ctx=ctx@entry=0x555556800290) at util/async.c:507 + #5 0x0000555555b05cf8 in bdrv_prwv_co (child=child@entry=0x7fffc80012f0, offset=offset@entry=131072, qiov=qiov@entry=0x7fffffffd4f0, is_write=is_write@entry=true, flags=flags@entry=0) + at block/io.c:833 + #6 0x0000555555b060a9 in bdrv_pwritev (qiov=0x7fffffffd4f0, offset=131072, child=0x7fffc80012f0) at block/io.c:990 + #7 0x0000555555b060a9 in bdrv_pwrite (child=0x7fffc80012f0, offset=131072, buf=, bytes=) at block/io.c:990 + #8 0x0000555555ae172b in qcow2_cache_entry_flush (bs=bs@entry=0x555556810680, c=c@entry=0x5555568cc740, i=i@entry=0) at block/qcow2-cache.c:51 + #9 0x0000555555ae18dd in qcow2_cache_write (bs=bs@entry=0x555556810680, c=0x5555568cc740) at block/qcow2-cache.c:248 + #10 0x0000555555ae15de in qcow2_cache_flush (bs=0x555556810680, c=) at block/qcow2-cache.c:259 + #11 0x0000555555ae16b1 in qcow2_cache_flush_dependency (c=0x5555568a1700, c=0x5555568a1700, bs=0x555556810680) at block/qcow2-cache.c:194 + #12 0x0000555555ae16b1 in qcow2_cache_entry_flush (bs=bs@entry=0x555556810680, c=c@entry=0x5555568a1700, i=i@entry=0) at block/qcow2-cache.c:194 + #13 0x0000555555ae18dd in qcow2_cache_write (bs=bs@entry=0x555556810680, c=0x5555568a1700) at block/qcow2-cache.c:248 + #14 0x0000555555ae15de in qcow2_cache_flush (bs=bs@entry=0x555556810680, c=) at block/qcow2-cache.c:259 + #15 0x0000555555ad242c in qcow2_inactivate (bs=bs@entry=0x555556810680) at block/qcow2.c:2124 + #16 0x0000555555ad2590 in qcow2_close (bs=0x555556810680) at block/qcow2.c:2153 + #17 0x0000555555ab0c62 in bdrv_close (bs=0x555556810680) at block.c:3358 + #18 0x0000555555ab0c62 in bdrv_delete (bs=0x555556810680) at block.c:3542 + #19 0x0000555555ab0c62 in bdrv_unref (bs=0x555556810680) at block.c:4598 + #20 0x0000555555af4d72 in blk_remove_bs (blk=blk@entry=0x5555568103d0) at block/block-backend.c:785 + #21 0x0000555555af4dbb in blk_remove_all_bs () at block/block-backend.c:483 + #22 0x0000555555aae02f in bdrv_close_all () at block.c:3412 + #23 0x00005555557f9796 in main (argc=, argv=, envp=) at vl.c:4776 + +The reproducer I used is a qcow2 image on gluster volume, where the +virtual disk size (4 GB) is larger than the gluster volume size (64M), +so we can easily trigger an ENOSPC. This backend is assigned to a +virtio-blk device using an iothread, and then from the guest a +'dd if=/dev/zero of=/dev/vda bs=1G count=1' causes the VM to stop +because of an I/O error. qemu_gluster_co_flush_to_disk() sets +bs->drv = NULL on error, so when virtio-blk stops the dataplane, the +block nodes stay in the iothread AioContext. A 'quit' monitor command +issued from this paused state crashes the process. + +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1631227 +Cc: qemu-stable@nongnu.org +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +Reviewed-by: Max Reitz +Reviewed-by: Stefano Garzarella +(cherry picked from commit 1bffe1ae7a7b707c3a14ea2ccd00d3609d3ce4d8) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 12 ++---------- + 1 file changed, 2 insertions(+), 10 deletions(-) + +diff --git a/block.c b/block.c +index e3e0e34..7061f9b 100644 +--- a/block.c ++++ b/block.c +@@ -4946,10 +4946,6 @@ void bdrv_detach_aio_context(BlockDriverState *bs) + BdrvAioNotifier *baf, *baf_tmp; + BdrvChild *child; + +- if (!bs->drv) { +- return; +- } +- + assert(!bs->walking_aio_notifiers); + bs->walking_aio_notifiers = true; + QLIST_FOREACH_SAFE(baf, &bs->aio_notifiers, list, baf_tmp) { +@@ -4964,7 +4960,7 @@ void bdrv_detach_aio_context(BlockDriverState *bs) + */ + bs->walking_aio_notifiers = false; + +- if (bs->drv->bdrv_detach_aio_context) { ++ if (bs->drv && bs->drv->bdrv_detach_aio_context) { + bs->drv->bdrv_detach_aio_context(bs); + } + QLIST_FOREACH(child, &bs->children, next) { +@@ -4983,10 +4979,6 @@ void bdrv_attach_aio_context(BlockDriverState *bs, + BdrvAioNotifier *ban, *ban_tmp; + BdrvChild *child; + +- if (!bs->drv) { +- return; +- } +- + if (bs->quiesce_counter) { + aio_disable_external(new_context); + } +@@ -4996,7 +4988,7 @@ void bdrv_attach_aio_context(BlockDriverState *bs, + QLIST_FOREACH(child, &bs->children, next) { + bdrv_attach_aio_context(child->bs, new_context); + } +- if (bs->drv->bdrv_attach_aio_context) { ++ if (bs->drv && bs->drv->bdrv_attach_aio_context) { + bs->drv->bdrv_attach_aio_context(bs, new_context); + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Fix-AioContext-switch-for-drained-node.patch b/SOURCES/kvm-block-Fix-AioContext-switch-for-drained-node.patch new file mode 100644 index 0000000..2ecbaf6 --- /dev/null +++ b/SOURCES/kvm-block-Fix-AioContext-switch-for-drained-node.patch @@ -0,0 +1,57 @@ +From 3c3659acadf5891119a70e6dd7a2525c2706e1de Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 1 Mar 2019 14:27:45 +0100 +Subject: [PATCH 3/9] block: Fix AioContext switch for drained node + +RH-Author: Kevin Wolf +Message-id: <20190301142747.12251-4-kwolf@redhat.com> +Patchwork-id: 84763 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 3/5] block: Fix AioContext switch for drained node +Bugzilla: 1671173 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Paolo Bonzini +RH-Acked-by: John Snow + +When a drained node changes its AioContext, we need to move its +aio_disable_external() to the new context, too. + +Without this fix, drain_end will try to reenable the new context, which +has never been disabled, so an assertion failure is triggered. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +(cherry picked from commit e64f25f30b80a71bd4e409ed518c39eeb5905166) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/block.c b/block.c +index a69d0a2..7cd3651 100644 +--- a/block.c ++++ b/block.c +@@ -4954,6 +4954,9 @@ void bdrv_detach_aio_context(BlockDriverState *bs) + bdrv_detach_aio_context(child->bs); + } + ++ if (bs->quiesce_counter) { ++ aio_enable_external(bs->aio_context); ++ } + bs->aio_context = NULL; + } + +@@ -4967,6 +4970,10 @@ void bdrv_attach_aio_context(BlockDriverState *bs, + return; + } + ++ if (bs->quiesce_counter) { ++ aio_disable_external(new_context); ++ } ++ + bs->aio_context = new_context; + + QLIST_FOREACH(child, &bs->children, next) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Fix-blockdev-blockdev-add-for-empty-objects-an.patch b/SOURCES/kvm-block-Fix-blockdev-blockdev-add-for-empty-objects-an.patch new file mode 100644 index 0000000..fed55e2 --- /dev/null +++ b/SOURCES/kvm-block-Fix-blockdev-blockdev-add-for-empty-objects-an.patch @@ -0,0 +1,278 @@ +From 91f887335e57e6cdd891d7341ac070ca9359ac82 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:28 +0200 +Subject: [PATCH 21/54] block: Fix -blockdev / blockdev-add for empty objects + and arrays + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-22-armbru@redhat.com> +Patchwork-id: 80726 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 21/23] block: Fix -blockdev / blockdev-add for empty objects and arrays +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +-blockdev and blockdev-add silently ignore empty objects and arrays in +their argument. That's because qmp_blockdev_add() converts the +argument to a flat QDict, and qdict_flatten() eats empty QDict and +QList members. For instance, we ignore an empty BlockdevOptions +member @cache. No real harm, as absent means the same as empty there. + +Thus, the flaw puts an artificial restriction on the QAPI schema: we +can't have potentially empty objects and arrays within +BlockdevOptions, except when they're optional and "empty" has the same +meaning as "absent". + +Our QAPI schema satisfies this restriction (I checked), but it's a +trap for the unwary, and a temptation to employ awkward workarounds +for the wary. Let's get rid of it. + +Change qdict_flatten() and qdict_crumple() to treat empty dictionaries +and lists exactly like scalars. + +Signed-off-by: Markus Armbruster +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit 2860b2b2cb883969c8f6464bd9f8bc88742c5c73) +Signed-off-by: Miroslav Rezanina +--- + qobject/block-qdict.c | 54 +++++++++++++++++++++++++++++------------------ + tests/check-block-qdict.c | 38 ++++++++++++++++++++++++++------- + 2 files changed, 63 insertions(+), 29 deletions(-) + +diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c +index e51a3d2..df83308 100644 +--- a/qobject/block-qdict.c ++++ b/qobject/block-qdict.c +@@ -56,6 +56,8 @@ 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; + +@@ -69,16 +71,18 @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) + + 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 (qobject_type(value) == QTYPE_QDICT) { +- qdict_flatten_qdict(qobject_to(QDict, value), target, new_key); +- } else if (qobject_type(value) == QTYPE_QLIST) { +- qdict_flatten_qlist(qobject_to(QList, value), target, new_key); ++ 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)); + } +@@ -91,6 +95,8 @@ 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 *new_key; + + entry = qdict_first(qdict); +@@ -98,6 +104,8 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) + 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); + new_key = NULL; + + if (prefix) { +@@ -108,12 +116,12 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) + * Flatten non-empty QDict and QList recursively into @target, + * copy other objects to @target + */ +- if (qobject_type(value) == QTYPE_QDICT) { +- qdict_flatten_qdict(qobject_to(QDict, value), target, ++ if (dict_val && qdict_size(dict_val)) { ++ qdict_flatten_qdict(dict_val, target, + new_key ? new_key : entry->key); + qdict_del(qdict, entry->key); +- } else if (qobject_type(value) == QTYPE_QLIST) { +- qdict_flatten_qlist(qobject_to(QList, value), target, ++ } else if (list_val && !qlist_empty(list_val)) { ++ qdict_flatten_qlist(list_val, target, + new_key ? new_key : entry->key); + qdict_del(qdict, entry->key); + } else if (target != qdict) { +@@ -127,10 +135,11 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) + } + + /** +- * qdict_flatten(): For each nested 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 +- * 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). ++ * 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) +@@ -361,8 +370,8 @@ static int qdict_is_list(QDict *maybe_list, Error **errp) + * @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, and crumples it into a nested +- * structure. ++ * 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 '..' + * +@@ -399,6 +408,8 @@ 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; +@@ -409,10 +420,11 @@ QObject *qdict_crumple(const QDict *src, Error **errp) + + /* Step 1: split our totally flat dict into a two level dict */ + for (ent = qdict_first(src); ent != NULL; ent = qdict_next(src, ent)) { +- if (qobject_type(ent->value) == QTYPE_QDICT || +- qobject_type(ent->value) == QTYPE_QLIST) { +- error_setg(errp, "Value %s is not a scalar", +- ent->key); ++ 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; + } + +@@ -451,9 +463,9 @@ QObject *qdict_crumple(const QDict *src, Error **errp) + multi_level = qdict_new(); + for (ent = qdict_first(two_level); ent != NULL; + ent = qdict_next(two_level, ent)) { +- QDict *dict = qobject_to(QDict, ent->value); +- if (dict) { +- child = qdict_crumple(dict, errp); ++ dict_val = qobject_to(QDict, ent->value); ++ if (dict_val && qdict_size(dict_val)) { ++ child = qdict_crumple(dict_val, errp); + if (!child) { + goto error; + } +diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c +index 2da16f0..1d20fcc 100644 +--- a/tests/check-block-qdict.c ++++ b/tests/check-block-qdict.c +@@ -79,10 +79,10 @@ static void qdict_flatten_test(void) + * "e.1.2.b": 1, + * "f.c": 2, + * "f.d": 3, +- * "g": 4 ++ * "g": 4, ++ * "y.0": {}, ++ * "z.a": [] + * } +- * +- * Note that "y" and "z" get eaten. + */ + + qdict_put_int(e_1_2, "a", 0); +@@ -117,8 +117,10 @@ static void qdict_flatten_test(void) + g_assert(qdict_get_int(root, "f.c") == 2); + g_assert(qdict_get_int(root, "f.d") == 3); + g_assert(qdict_get_int(root, "g") == 4); ++ g_assert(!qdict_size(qdict_get_qdict(root, "y.0"))); ++ g_assert(qlist_empty(qdict_get_qlist(root, "z.a"))); + +- g_assert(qdict_size(root) == 8); ++ g_assert(qdict_size(root) == 10); + + qobject_unref(root); + } +@@ -387,7 +389,8 @@ static void qdict_join_test(void) + static void qdict_crumple_test_recursive(void) + { + QDict *src, *dst, *rule, *vnc, *acl, *listen; +- QList *rules; ++ QDict *empty, *empty_dict, *empty_list_0; ++ QList *rules, *empty_list, *empty_dict_a; + + src = qdict_new(); + qdict_put_str(src, "vnc.listen.addr", "127.0.0.1"); +@@ -399,10 +402,12 @@ static void qdict_crumple_test_recursive(void) + qdict_put_str(src, "vnc.acl.default", "deny"); + qdict_put_str(src, "vnc.acl..name", "acl0"); + qdict_put_str(src, "vnc.acl.rule..name", "acl0"); ++ qdict_put(src, "empty.dict.a", qlist_new()); ++ qdict_put(src, "empty.list.0", qdict_new()); + + dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); + g_assert(dst); +- g_assert_cmpint(qdict_size(dst), ==, 1); ++ g_assert_cmpint(qdict_size(dst), ==, 2); + + vnc = qdict_get_qdict(dst, "vnc"); + g_assert(vnc); +@@ -440,6 +445,21 @@ static void qdict_crumple_test_recursive(void) + g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name")); + g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name")); + ++ empty = qdict_get_qdict(dst, "empty"); ++ g_assert(empty); ++ g_assert_cmpint(qdict_size(empty), ==, 2); ++ empty_dict = qdict_get_qdict(empty, "dict"); ++ g_assert(empty_dict); ++ g_assert_cmpint(qdict_size(empty_dict), ==, 1); ++ empty_dict_a = qdict_get_qlist(empty_dict, "a"); ++ g_assert(empty_dict_a && qlist_empty(empty_dict_a)); ++ empty_list = qdict_get_qlist(empty, "list"); ++ g_assert(empty_list); ++ g_assert_cmpint(qlist_size(empty_list), ==, 1); ++ empty_list_0 = qobject_to(QDict, qlist_pop(empty_list)); ++ g_assert(empty_list_0); ++ g_assert_cmpint(qdict_size(empty_list_0), ==, 0); ++ + qobject_unref(src); + qobject_unref(dst); + } +@@ -587,7 +607,7 @@ static void qdict_rename_keys_test(void) + + static void qdict_crumple_test_bad_inputs(void) + { +- QDict *src; ++ QDict *src, *nested; + Error *error = NULL; + + src = qdict_new(); +@@ -614,7 +634,9 @@ static void qdict_crumple_test_bad_inputs(void) + + src = qdict_new(); + /* The input should be flat, ie no dicts or lists */ +- qdict_put(src, "rule.a", qdict_new()); ++ nested = qdict_new(); ++ qdict_put(nested, "x", qdict_new()); ++ qdict_put(src, "rule.a", nested); + qdict_put_str(src, "rule.b", "allow"); + + g_assert(qdict_crumple(src, &error) == NULL); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Fix-blockdev-for-certain-non-string-scalars.patch b/SOURCES/kvm-block-Fix-blockdev-for-certain-non-string-scalars.patch new file mode 100644 index 0000000..6c277e7 --- /dev/null +++ b/SOURCES/kvm-block-Fix-blockdev-for-certain-non-string-scalars.patch @@ -0,0 +1,302 @@ +From 26de0d4fe93a094dcf7aef4c2b139c46c1117a99 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:17 +0200 +Subject: [PATCH 10/54] block: Fix -blockdev for certain non-string scalars + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-11-armbru@redhat.com> +Patchwork-id: 80743 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 10/23] block: Fix -blockdev for certain non-string scalars +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Configuration flows through the block subsystem in a rather peculiar +way. Configuration made with -drive enters it as QemuOpts. +Configuration made with -blockdev / blockdev-add enters it as QAPI +type BlockdevOptions. The block subsystem uses QDict, QemuOpts and +QAPI types internally. The precise flow is next to impossible to +explain (I tried for this commit message, but gave up after wasting +several hours). What I can explain is a flaw in the BlockDriver +interface that leads to this bug: + + $ qemu-system-x86_64 -blockdev node-name=n1,driver=nfs,server.type=inet,server.host=localhost,path=/foo/bar,user=1234 + qemu-system-x86_64: -blockdev node-name=n1,driver=nfs,server.type=inet,server.host=localhost,path=/foo/bar,user=1234: Internal error: parameter user invalid + +QMP blockdev-add is broken the same way. + +Here's what happens. The block layer passes configuration represented +as flat QDict (with dotted keys) to BlockDriver methods +.bdrv_file_open(). The QDict's members are typed according to the +QAPI schema. + +nfs_file_open() converts it to QAPI type BlockdevOptionsNfs, with +qdict_crumple() and a qobject input visitor. + +This visitor comes in two flavors. The plain flavor requires scalars +to be typed according to the QAPI schema. That's the case here. The +keyval flavor requires string scalars. That's not the case here. +nfs_file_open() uses the latter, and promptly falls apart for members +@user, @group, @tcp-syn-count, @readahead-size, @page-cache-size, +@debug. + +Switching to the plain flavor would fix -blockdev, but break -drive, +because there the scalars arrive in nfs_file_open() as strings. + +The proper fix would be to replace the QDict by QAPI type +BlockdevOptions in the BlockDriver interface. Sadly, that's beyond my +reach right now. + +Next best would be to fix the block layer to always pass correctly +typed QDicts to the BlockDriver methods. Also beyond my reach. + +What I can do is throw another hack onto the pile: have +nfs_file_open() convert all members to string, so use of the keyval +flavor actually works, by replacing qdict_crumple() by new function +qdict_crumple_for_keyval_qiv(). + +The pattern "pass result of qdict_crumple() to +qobject_input_visitor_new_keyval()" occurs several times more: + +* qemu_rbd_open() + + Same issue as nfs_file_open(), but since BlockdevOptionsRbd has only + string members, its only a latent bug. Fix it anyway. + +* parallels_co_create_opts(), qcow_co_create_opts(), + qcow2_co_create_opts(), bdrv_qed_co_create_opts(), + sd_co_create_opts(), vhdx_co_create_opts(), vpc_co_create_opts() + + These work, because they create the QDict with + qemu_opts_to_qdict_filtered(), which creates only string scalars. + The function sports a TODO comment asking for better typing; that's + going to be fun. Use qdict_crumple_for_keyval_qiv() to be safe. + +Signed-off-by: Markus Armbruster +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit e5af0da1dcbfb1a4694150f9954554fb6df88819) +Signed-off-by: Miroslav Rezanina +--- + block/nfs.c | 2 +- + block/parallels.c | 2 +- + block/qcow.c | 2 +- + block/qcow2.c | 2 +- + block/qed.c | 2 +- + block/rbd.c | 2 +- + block/sheepdog.c | 2 +- + block/vhdx.c | 2 +- + block/vpc.c | 2 +- + include/block/qdict.h | 1 + + qobject/block-qdict.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 11 files changed, 67 insertions(+), 9 deletions(-) + +diff --git a/block/nfs.c b/block/nfs.c +index 5159ef0..4090d28 100644 +--- a/block/nfs.c ++++ b/block/nfs.c +@@ -560,7 +560,7 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options, + Visitor *v; + Error *local_err = NULL; + +- crumpled = qdict_crumple(options, errp); ++ crumpled = qdict_crumple_for_keyval_qiv(options, errp); + if (crumpled == NULL) { + return NULL; + } +diff --git a/block/parallels.c b/block/parallels.c +index 0ee1f6a..aa58955 100644 +--- a/block/parallels.c ++++ b/block/parallels.c +@@ -651,7 +651,7 @@ static int coroutine_fn parallels_co_create_opts(const char *filename, + qdict_put_str(qdict, "driver", "parallels"); + qdict_put_str(qdict, "file", bs->node_name); + +- qobj = qdict_crumple(qdict, errp); ++ qobj = qdict_crumple_for_keyval_qiv(qdict, errp); + qobject_unref(qdict); + qdict = qobject_to(QDict, qobj); + if (qdict == NULL) { +diff --git a/block/qcow.c b/block/qcow.c +index fb821ad..14b7296 100644 +--- a/block/qcow.c ++++ b/block/qcow.c +@@ -995,7 +995,7 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, + qdict_put_str(qdict, "driver", "qcow"); + qdict_put_str(qdict, "file", bs->node_name); + +- qobj = qdict_crumple(qdict, errp); ++ qobj = qdict_crumple_for_keyval_qiv(qdict, errp); + qobject_unref(qdict); + qdict = qobject_to(QDict, qobj); + if (qdict == NULL) { +diff --git a/block/qcow2.c b/block/qcow2.c +index fa9f557..fa06b41 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -3139,7 +3139,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt + qdict_put_str(qdict, "file", bs->node_name); + + /* Now get the QAPI type BlockdevCreateOptions */ +- qobj = qdict_crumple(qdict, errp); ++ qobj = qdict_crumple_for_keyval_qiv(qdict, errp); + qobject_unref(qdict); + qdict = qobject_to(QDict, qobj); + if (qdict == NULL) { +diff --git a/block/qed.c b/block/qed.c +index 9a8997a..d8810f5 100644 +--- a/block/qed.c ++++ b/block/qed.c +@@ -763,7 +763,7 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, + qdict_put_str(qdict, "driver", "qed"); + qdict_put_str(qdict, "file", bs->node_name); + +- qobj = qdict_crumple(qdict, errp); ++ qobj = qdict_crumple_for_keyval_qiv(qdict, errp); + qobject_unref(qdict); + qdict = qobject_to(QDict, qobj); + if (qdict == NULL) { +diff --git a/block/rbd.c b/block/rbd.c +index e695cf2..0b5455f 100644 +--- a/block/rbd.c ++++ b/block/rbd.c +@@ -640,7 +640,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, + } + + /* Convert the remaining options into a QAPI object */ +- crumpled = qdict_crumple(options, errp); ++ crumpled = qdict_crumple_for_keyval_qiv(options, errp); + if (crumpled == NULL) { + r = -EINVAL; + goto out; +diff --git a/block/sheepdog.c b/block/sheepdog.c +index fd3876f..821a3c4 100644 +--- a/block/sheepdog.c ++++ b/block/sheepdog.c +@@ -2218,7 +2218,7 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, + } + + /* Get the QAPI object */ +- crumpled = qdict_crumple(qdict, errp); ++ crumpled = qdict_crumple_for_keyval_qiv(qdict, errp); + if (crumpled == NULL) { + ret = -EINVAL; + goto fail; +diff --git a/block/vhdx.c b/block/vhdx.c +index 26c05aa..32939c4 100644 +--- a/block/vhdx.c ++++ b/block/vhdx.c +@@ -2003,7 +2003,7 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, + qdict_put_str(qdict, "driver", "vhdx"); + qdict_put_str(qdict, "file", bs->node_name); + +- qobj = qdict_crumple(qdict, errp); ++ qobj = qdict_crumple_for_keyval_qiv(qdict, errp); + qobject_unref(qdict); + qdict = qobject_to(QDict, qobj); + if (qdict == NULL) { +diff --git a/block/vpc.c b/block/vpc.c +index 41c8c98..16178e5 100644 +--- a/block/vpc.c ++++ b/block/vpc.c +@@ -1119,7 +1119,7 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, + qdict_put_str(qdict, "driver", "vpc"); + qdict_put_str(qdict, "file", bs->node_name); + +- qobj = qdict_crumple(qdict, errp); ++ qobj = qdict_crumple_for_keyval_qiv(qdict, errp); + qobject_unref(qdict); + qdict = qobject_to(QDict, qobj); + if (qdict == NULL) { +diff --git a/include/block/qdict.h b/include/block/qdict.h +index 71c037a..47d9638 100644 +--- a/include/block/qdict.h ++++ b/include/block/qdict.h +@@ -21,6 +21,7 @@ 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); + QObject *qdict_crumple(const QDict *src, Error **errp); ++QObject *qdict_crumple_for_keyval_qiv(QDict *qdict, Error **errp); + void qdict_flatten(QDict *qdict); + + typedef struct QDictRenames { +diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c +index fb92010..aba372c 100644 +--- a/qobject/block-qdict.c ++++ b/qobject/block-qdict.c +@@ -9,7 +9,10 @@ + + #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 "qemu/cutils.h" + #include "qapi/error.h" + +@@ -514,6 +517,60 @@ QObject *qdict_crumple(const QDict *src, Error **errp) + } + + /** ++ * 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. ++ */ ++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(tmp, ent->key, qstring_from_str(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 +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Fix-copy-on-read-crash-with-partial-final-clus.patch b/SOURCES/kvm-block-Fix-copy-on-read-crash-with-partial-final-clus.patch new file mode 100644 index 0000000..5fbb5ff --- /dev/null +++ b/SOURCES/kvm-block-Fix-copy-on-read-crash-with-partial-final-clus.patch @@ -0,0 +1,94 @@ +From aa04dc32326b3c03f0af75dd9a87530691cd53bf Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Thu, 12 Jul 2018 15:00:08 +0200 +Subject: [PATCH 40/89] block: Fix copy-on-read crash with partial final + cluster + +RH-Author: Kevin Wolf +Message-id: <20180712150008.23662-2-kwolf@redhat.com> +Patchwork-id: 81331 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/1] block: Fix copy-on-read crash with partial final cluster +Bugzilla: 1590640 +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng +RH-Acked-by: John Snow +RH-Acked-by: Richard Jones + +If the virtual disk size isn't aligned to full clusters, +bdrv_co_do_copy_on_readv() may get pnum == 0 before having the full +cluster completed, which will let it run into an assertion failure: + +qemu-io: block/io.c:1203: bdrv_co_do_copy_on_readv: Assertion `skip_bytes < pnum' failed. + +Check for EOF, assert that we read at least as much as the read request +originally wanted to have (which is true at EOF because otherwise +bdrv_check_byte_request() would already have returned an error) and +return success early even though we couldn't copy the full cluster. + +Signed-off-by: Kevin Wolf +(cherry picked from commit b0ddcbbb36a66a605eb232b905cb49b1cc72e74e) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/io.c | 6 ++++++ + tests/qemu-iotests/197 | 9 +++++++++ + tests/qemu-iotests/197.out | 8 ++++++++ + 3 files changed, 23 insertions(+) + +diff --git a/block/io.c b/block/io.c +index ad8afc0..ac36d1c 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -1095,6 +1095,12 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, + pnum = MIN(cluster_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) { +diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197 +index 3ae4975..0369aa5 100755 +--- a/tests/qemu-iotests/197 ++++ b/tests/qemu-iotests/197 +@@ -109,6 +109,15 @@ $QEMU_IO -f qcow2 -c map "$TEST_WRAP" + _check_test_img + $QEMU_IMG compare -f $IMGFMT -F qcow2 "$TEST_IMG" "$TEST_WRAP" + ++echo ++echo '=== Partial final cluster ===' ++echo ++ ++_make_test_img 1024 ++$QEMU_IO -f $IMGFMT -C -c 'read 0 1024' "$TEST_IMG" | _filter_qemu_io ++$QEMU_IO -f $IMGFMT -c map "$TEST_IMG" ++_check_test_img ++ + # success, all done + echo '*** done' + status=0 +diff --git a/tests/qemu-iotests/197.out b/tests/qemu-iotests/197.out +index 52b4137..8febda5 100644 +--- a/tests/qemu-iotests/197.out ++++ b/tests/qemu-iotests/197.out +@@ -23,4 +23,12 @@ can't open device TEST_DIR/t.wrap.qcow2: Can't use copy-on-read on read-only dev + 1023.938 MiB (0x3fff0000) bytes not allocated at offset 3 GiB (0xc0010000) + No errors were found on the image. + Images are identical. ++ ++=== Partial final cluster === ++ ++Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1024 ++read 1024/1024 bytes at offset 0 ++1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) ++No errors were found on the image. + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Fix-drive-for-certain-non-string-scalars.patch b/SOURCES/kvm-block-Fix-drive-for-certain-non-string-scalars.patch new file mode 100644 index 0000000..898f96b --- /dev/null +++ b/SOURCES/kvm-block-Fix-drive-for-certain-non-string-scalars.patch @@ -0,0 +1,123 @@ +From 0a45c26f9838054d3f97b35e82877239d4e7c920 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:18 +0200 +Subject: [PATCH 11/54] block: Fix -drive for certain non-string scalars + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-12-armbru@redhat.com> +Patchwork-id: 80737 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 11/23] block: Fix -drive for certain non-string scalars +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +The previous commit fixed -blockdev breakage due to misuse of the +qobject input visitor's keyval flavor in bdrv_file_open(). The commit +message explain why using the plain flavor would be just as wrong; it +would break -drive. Turns out we break it in three places: +nbd_open(), sd_open() and ssh_file_open(). They are even marked +FIXME. Example breakage: + + $ qemu-system-x86 -drive node-name=n1,driver=nbd,server.type=inet,server.host=localhost,server.port=1234,server.numeric=off + qemu-system-x86: -drive node-name=n1,driver=nbd,server.type=inet,server.host=localhost,server.port=1234,server.numeric=off: Invalid parameter type for 'numeric', expected: boolean + +Fix it the same way: replace qdict_crumple() by +qdict_crumple_for_keyval_qiv(), and switch from plain to the keyval +flavor. + +Signed-off-by: Markus Armbruster +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit 374c52467a38c2e811f6c0db4edc9ea7d5f34341) +Signed-off-by: Miroslav Rezanina +--- + block/nbd.c | 12 ++---------- + block/sheepdog.c | 12 ++---------- + block/ssh.c | 12 ++---------- + 3 files changed, 6 insertions(+), 30 deletions(-) + +diff --git a/block/nbd.c b/block/nbd.c +index f499830..b0be357 100644 +--- a/block/nbd.c ++++ b/block/nbd.c +@@ -273,20 +273,12 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, + goto done; + } + +- crumpled_addr = qdict_crumple(addr, errp); ++ crumpled_addr = qdict_crumple_for_keyval_qiv(addr, errp); + if (!crumpled_addr) { + goto done; + } + +- /* +- * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive +- * server.type=inet. .to doesn't matter, it's ignored anyway. +- * That's because 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. The +- * visitor expects the former. +- */ +- iv = qobject_input_visitor_new(crumpled_addr); ++ iv = qobject_input_visitor_new_keyval(crumpled_addr); + visit_type_SocketAddress(iv, NULL, &saddr, &local_err); + if (local_err) { + error_propagate(errp, local_err); +diff --git a/block/sheepdog.c b/block/sheepdog.c +index 821a3c4..dd582d5 100644 +--- a/block/sheepdog.c ++++ b/block/sheepdog.c +@@ -546,20 +546,12 @@ static SocketAddress *sd_server_config(QDict *options, Error **errp) + + qdict_extract_subqdict(options, &server, "server."); + +- crumpled_server = qdict_crumple(server, errp); ++ crumpled_server = qdict_crumple_for_keyval_qiv(server, errp); + if (!crumpled_server) { + goto done; + } + +- /* +- * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive +- * server.type=inet. .to doesn't matter, it's ignored anyway. +- * That's because 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. The +- * visitor expects the former. +- */ +- iv = qobject_input_visitor_new(crumpled_server); ++ iv = qobject_input_visitor_new_keyval(crumpled_server); + visit_type_SocketAddress(iv, NULL, &saddr, &local_err); + if (local_err) { + error_propagate(errp, local_err); +diff --git a/block/ssh.c b/block/ssh.c +index 5931064..2fc7cd9 100644 +--- a/block/ssh.c ++++ b/block/ssh.c +@@ -623,20 +623,12 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp) + } + + /* Create the QAPI object */ +- crumpled = qdict_crumple(options, errp); ++ crumpled = qdict_crumple_for_keyval_qiv(options, errp); + if (crumpled == NULL) { + goto fail; + } + +- /* +- * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive. +- * .to doesn't matter, it's ignored anyway. +- * That's because 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. The +- * visitor expects the former. +- */ +- v = qobject_input_visitor_new(crumpled); ++ v = qobject_input_visitor_new_keyval(crumpled); + visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err); + visit_free(v); + qobject_unref(crumpled); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Fix-invalidate_cache-error-path-for-parent-act.patch b/SOURCES/kvm-block-Fix-invalidate_cache-error-path-for-parent-act.patch new file mode 100644 index 0000000..36f7e6a --- /dev/null +++ b/SOURCES/kvm-block-Fix-invalidate_cache-error-path-for-parent-act.patch @@ -0,0 +1,69 @@ +From b7d8518d4d2e5d53c2f8eb6cc78d99a69dc4feff Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Mon, 4 Feb 2019 16:06:04 +0100 +Subject: [PATCH 7/8] block: Fix invalidate_cache error path for parent + activation + +RH-Author: Kevin Wolf +Message-id: <20190204160604.2723-2-kwolf@redhat.com> +Patchwork-id: 84202 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/1] block: Fix invalidate_cache error path for parent activation +Bugzilla: 1531888 +RH-Acked-by: Markus Armbruster +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz + +bdrv_co_invalidate_cache() clears the BDRV_O_INACTIVE flag before +actually activating a node so that the correct permissions etc. are +taken. In case of errors, the flag must be restored so that the next +call to bdrv_co_invalidate_cache() retries activation. + +Restoring the flag was missing in the error path for a failed +parent->role->activate() call. The consequence is that this attempt to +activate all images correctly fails because we still set errp, however +on the next attempt BDRV_O_INACTIVE is already clear, so we return +success without actually retrying the failed action. + +An example where this is observable in practice is migration to a QEMU +instance that has a raw format block node attached to a guest device +with share-rw=off (the default) while another process holds +BLK_PERM_WRITE for the same image. In this case, all activation steps +before parent->role->activate() succeed because raw can tolerate other +writers to the image. Only the parent callback (in particular +blk_root_activate()) tries to implement the share-rw=on property and +requests exclusive write permissions. This fails when the migration +completes and correctly displays an error. However, a manual 'cont' will +incorrectly resume the VM without calling blk_root_activate() again. + +This case is described in more detail in the following bug report: +https://bugzilla.redhat.com/show_bug.cgi?id=1531888 + +Fix this by correctly restoring the BDRV_O_INACTIVE flag in the error +path. + +Cc: qemu-stable@nongnu.org +Signed-off-by: Kevin Wolf +Tested-by: Markus Armbruster +Reviewed-by: Stefan Hajnoczi +(cherry picked from commit 78fc3b3a26c145eebcdee992988644974b243a74) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/block.c b/block.c +index da9b1a6..ce85c65 100644 +--- a/block.c ++++ b/block.c +@@ -4410,6 +4410,7 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, + if (parent->role->activate) { + parent->role->activate(parent, &local_err); + if (local_err) { ++ bs->open_flags |= BDRV_O_INACTIVE; + error_propagate(errp, local_err); + return; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Fix-parameter-checking-in-bdrv_co_copy_range_i.patch b/SOURCES/kvm-block-Fix-parameter-checking-in-bdrv_co_copy_range_i.patch new file mode 100644 index 0000000..d26fc5b --- /dev/null +++ b/SOURCES/kvm-block-Fix-parameter-checking-in-bdrv_co_copy_range_i.patch @@ -0,0 +1,101 @@ +From b8f1b4c9b03d2ec06ed69d0f6769499d197eccd8 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Tue, 24 Jul 2018 12:50:06 +0200 +Subject: [PATCH 65/89] block: Fix parameter checking in + bdrv_co_copy_range_internal + +RH-Author: John Snow +Message-id: <20180718225511.14878-15-jsnow@redhat.com> +Patchwork-id: 81411 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 14/35] block: Fix parameter checking in bdrv_co_copy_range_internal +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Fam Zheng + +src may be NULL if BDRV_REQ_ZERO_WRITE flag is set, in this case only +check dst and dst->bs. This bug was introduced when moving in the +request tracking code from bdrv_co_copy_range, in 37aec7d75eb. + +This especially fixes the possible segfault when initializing src_bs +with a NULL src. + +Signed-off-by: Fam Zheng +Message-id: 20180703023758.14422-2-famz@redhat.com +Reviewed-by: Jeff Cody +Signed-off-by: Jeff Cody +(cherry picked from commit d4d3e5a0d53a57282955e8a3ed7acc1ca90552d9) +Signed-off-by: John Snow +--- + block/io.c | 28 +++++++++++++++------------- + 1 file changed, 15 insertions(+), 13 deletions(-) + +diff --git a/block/io.c b/block/io.c +index 136a5d0..7981239 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -2848,17 +2848,11 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, + bool recurse_src) + { + BdrvTrackedRequest src_req, dst_req; +- BlockDriverState *src_bs = src->bs; +- BlockDriverState *dst_bs = dst->bs; + int ret; + +- if (!src || !dst || !src->bs || !dst->bs) { ++ if (!dst || !dst->bs) { + return -ENOMEDIUM; + } +- ret = bdrv_check_byte_request(src->bs, src_offset, bytes); +- if (ret) { +- return ret; +- } + + ret = bdrv_check_byte_request(dst->bs, dst_offset, bytes); + if (ret) { +@@ -2868,17 +2862,25 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, + return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, flags); + } + ++ if (!src || !src->bs) { ++ return -ENOMEDIUM; ++ } ++ ret = bdrv_check_byte_request(src->bs, src_offset, bytes); ++ 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; + } + +- bdrv_inc_in_flight(src_bs); +- bdrv_inc_in_flight(dst_bs); +- tracked_request_begin(&src_req, src_bs, src_offset, ++ bdrv_inc_in_flight(src->bs); ++ bdrv_inc_in_flight(dst->bs); ++ tracked_request_begin(&src_req, src->bs, src_offset, + bytes, BDRV_TRACKED_READ); +- tracked_request_begin(&dst_req, dst_bs, dst_offset, ++ tracked_request_begin(&dst_req, dst->bs, dst_offset, + bytes, BDRV_TRACKED_WRITE); + + wait_serialising_requests(&src_req); +@@ -2896,8 +2898,8 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, + } + tracked_request_end(&src_req); + tracked_request_end(&dst_req); +- bdrv_dec_in_flight(src_bs); +- bdrv_dec_in_flight(dst_bs); ++ bdrv_dec_in_flight(src->bs); ++ bdrv_dec_in_flight(dst->bs); + return ret; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Fix-update-of-BDRV_O_AUTO_RDONLY-in-update_fla.patch b/SOURCES/kvm-block-Fix-update-of-BDRV_O_AUTO_RDONLY-in-update_fla.patch new file mode 100644 index 0000000..6ae28c4 --- /dev/null +++ b/SOURCES/kvm-block-Fix-update-of-BDRV_O_AUTO_RDONLY-in-update_fla.patch @@ -0,0 +1,57 @@ +From e5cb59a02593db3efcb88f1dd0cc592cf5bb34b7 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 23 Nov 2018 10:41:54 +0100 +Subject: [PATCH 13/34] block: Fix update of BDRV_O_AUTO_RDONLY in + update_flags_from_options() + +RH-Author: Kevin Wolf +Message-id: <20181123104154.13541-13-kwolf@redhat.com> +Patchwork-id: 83122 +O-Subject: [RHEL-7.7/7.6.z qemu-kvm-rhev PATCH v2 12/12] block: Fix update of BDRV_O_AUTO_RDONLY in update_flags_from_options() +Bugzilla: 1623986 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina +RH-Acked-by: John Snow + +From: Alberto Garcia + +Commit e35bdc123a4ace9f4d3fcca added the auto-read-only option and the +code to update its corresponding flag in update_flags_from_options(), +but forgot to clear the flag if auto-read-only is false. + +Signed-off-by: Alberto Garcia +Reported-by: Max Reitz +Signed-off-by: Kevin Wolf +(cherry picked from commit 2a3d4331fa2d40708188b8000f98ff1f7dcd33bc) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/block.c b/block.c +index 6e3b574..037d2b0 100644 +--- a/block.c ++++ b/block.c +@@ -1112,7 +1112,7 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags) + + static void update_flags_from_options(int *flags, QemuOpts *opts) + { +- *flags &= ~BDRV_O_CACHE_MASK; ++ *flags &= ~(BDRV_O_CACHE_MASK | BDRV_O_RDWR | BDRV_O_AUTO_RDONLY); + + assert(qemu_opt_find(opts, BDRV_OPT_CACHE_NO_FLUSH)); + if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) { +@@ -1124,8 +1124,6 @@ static void update_flags_from_options(int *flags, QemuOpts *opts) + *flags |= BDRV_O_NOCACHE; + } + +- *flags &= ~BDRV_O_RDWR; +- + assert(qemu_opt_find(opts, BDRV_OPT_READ_ONLY)); + if (!qemu_opt_get_bool(opts, BDRV_OPT_READ_ONLY, false)) { + *flags |= BDRV_O_RDWR; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Fix-use-after-free-error-in-bdrv_open_inherit.patch b/SOURCES/kvm-block-Fix-use-after-free-error-in-bdrv_open_inherit.patch new file mode 100644 index 0000000..38a21ae --- /dev/null +++ b/SOURCES/kvm-block-Fix-use-after-free-error-in-bdrv_open_inherit.patch @@ -0,0 +1,50 @@ +From 9fbe06ec1dad1ffc7a2bcc138b7dbd6bf83e36cd Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 15 Mar 2019 18:10:02 +0100 +Subject: [PATCH 006/163] block: Fix use after free error in + bdrv_open_inherit() + +RH-Author: Kevin Wolf +Message-id: <20190315181010.14964-7-kwolf@redhat.com> +Patchwork-id: 84883 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 06/14] block: Fix use after free error in bdrv_open_inherit() +Bugzilla: 1685989 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Alberto Garcia + +When a block device is opened with BDRV_O_SNAPSHOT and the +bdrv_append_temp_snapshot() call fails then the error code path tries +to unref the already destroyed 'options' QDict. + +This can be reproduced easily by setting TMPDIR to a location where +the QEMU process can't write: + + $ TMPDIR=/nonexistent $QEMU -drive driver=null-co,snapshot=on + +Signed-off-by: Alberto Garcia +Signed-off-by: Kevin Wolf +(cherry picked from commit 8961be33e8ca7e809c603223803ea66ef7ea5be7) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/block.c b/block.c +index b31124c..25b3fe5 100644 +--- a/block.c ++++ b/block.c +@@ -2834,6 +2834,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, + 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. */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Honour-BDRV_REQ_NO_SERIALISING-in-copy-range.patch b/SOURCES/kvm-block-Honour-BDRV_REQ_NO_SERIALISING-in-copy-range.patch new file mode 100644 index 0000000..ba120e1 --- /dev/null +++ b/SOURCES/kvm-block-Honour-BDRV_REQ_NO_SERIALISING-in-copy-range.patch @@ -0,0 +1,72 @@ +From d1de83879c75bd2d73e9c4f24d81bfc83b992f5a Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:54:51 +0200 +Subject: [PATCH 66/89] block: Honour BDRV_REQ_NO_SERIALISING in copy range + +RH-Author: John Snow +Message-id: <20180718225511.14878-16-jsnow@redhat.com> +Patchwork-id: 81402 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 15/35] block: Honour BDRV_REQ_NO_SERIALISING in copy range +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Fam Zheng + +This semantics is needed by drive-backup so implement it before using +this API there. + +Reviewed-by: Stefan Hajnoczi +Signed-off-by: Fam Zheng +Message-id: 20180703023758.14422-3-famz@redhat.com +Signed-off-by: Jeff Cody +(cherry picked from commit dee12de89380483656072f775f5ef99f4426f966) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/io.c | 6 ++++-- + include/block/block.h | 5 +++-- + 2 files changed, 7 insertions(+), 4 deletions(-) + +diff --git a/block/io.c b/block/io.c +index 7981239..b6754f3 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -2883,8 +2883,10 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, + tracked_request_begin(&dst_req, dst->bs, dst_offset, + bytes, BDRV_TRACKED_WRITE); + +- wait_serialising_requests(&src_req); +- wait_serialising_requests(&dst_req); ++ if (!(flags & BDRV_REQ_NO_SERIALISING)) { ++ wait_serialising_requests(&src_req); ++ wait_serialising_requests(&dst_req); ++ } + if (recurse_src) { + ret = src->bs->drv->bdrv_co_copy_range_from(src->bs, + src, src_offset, +diff --git a/include/block/block.h b/include/block/block.h +index c3cfb40..e1d5e47 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -636,13 +636,14 @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host); + * @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. Must be one of: +- * 0 - actually read data from src; ++ * @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. + **/ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Introduce-API-for-copy-offloading.patch b/SOURCES/kvm-block-Introduce-API-for-copy-offloading.patch new file mode 100644 index 0000000..23ee180 --- /dev/null +++ b/SOURCES/kvm-block-Introduce-API-for-copy-offloading.patch @@ -0,0 +1,236 @@ +From 62cb216e19f41b1d1c4c5d4523df56dce865ab30 Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Fri, 29 Jun 2018 06:11:41 +0200 +Subject: [PATCH 37/57] block: Introduce API for copy offloading + +RH-Author: Fam Zheng +Message-id: <20180629061153.12687-2-famz@redhat.com> +Patchwork-id: 81153 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 01/13] block: Introduce API for copy offloading +Bugzilla: 1482537 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Kevin Wolf + +Introduce the bdrv_co_copy_range() API for copy offloading. Block +drivers implementing this API support efficient copy operations that +avoid reading each block from the source device and writing it to the +destination devices. Examples of copy offload primitives are SCSI +EXTENDED COPY and Linux copy_file_range(2). + +Signed-off-by: Fam Zheng +Reviewed-by: Stefan Hajnoczi +Message-id: 20180601092648.24614-2-famz@redhat.com +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit fcc6767836efe1b160289905dce7228d594c123c) +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + block/io.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++ + include/block/block.h | 32 ++++++++++++++++ + include/block/block_int.h | 38 +++++++++++++++++++ + 3 files changed, 167 insertions(+) + +diff --git a/block/io.c b/block/io.c +index fada4ef..5c043a4 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -2832,3 +2832,100 @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host) + bdrv_unregister_buf(child->bs, host); + } + } ++ ++static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, ++ uint64_t src_offset, ++ BdrvChild *dst, ++ uint64_t dst_offset, ++ uint64_t bytes, ++ BdrvRequestFlags flags, ++ bool recurse_src) ++{ ++ int ret; ++ ++ if (!src || !dst || !src->bs || !dst->bs) { ++ return -ENOMEDIUM; ++ } ++ ret = bdrv_check_byte_request(src->bs, src_offset, bytes); ++ if (ret) { ++ return ret; ++ } ++ ++ ret = bdrv_check_byte_request(dst->bs, dst_offset, bytes); ++ if (ret) { ++ return ret; ++ } ++ if (flags & BDRV_REQ_ZERO_WRITE) { ++ return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, flags); ++ } ++ ++ 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) { ++ return src->bs->drv->bdrv_co_copy_range_from(src->bs, ++ src, src_offset, ++ dst, dst_offset, ++ bytes, flags); ++ } else { ++ return dst->bs->drv->bdrv_co_copy_range_to(dst->bs, ++ src, src_offset, ++ dst, dst_offset, ++ bytes, flags); ++ } ++} ++ ++/* 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, uint64_t src_offset, ++ BdrvChild *dst, uint64_t dst_offset, ++ uint64_t bytes, BdrvRequestFlags flags) ++{ ++ return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, ++ bytes, 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, uint64_t src_offset, ++ BdrvChild *dst, uint64_t dst_offset, ++ uint64_t bytes, BdrvRequestFlags flags) ++{ ++ return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, ++ bytes, flags, false); ++} ++ ++int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset, ++ BdrvChild *dst, uint64_t dst_offset, ++ uint64_t bytes, BdrvRequestFlags flags) ++{ ++ BdrvTrackedRequest src_req, dst_req; ++ BlockDriverState *src_bs = src->bs; ++ BlockDriverState *dst_bs = dst->bs; ++ int ret; ++ ++ bdrv_inc_in_flight(src_bs); ++ bdrv_inc_in_flight(dst_bs); ++ tracked_request_begin(&src_req, src_bs, src_offset, ++ bytes, BDRV_TRACKED_READ); ++ tracked_request_begin(&dst_req, dst_bs, dst_offset, ++ bytes, BDRV_TRACKED_WRITE); ++ ++ wait_serialising_requests(&src_req); ++ wait_serialising_requests(&dst_req); ++ ret = bdrv_co_copy_range_from(src, src_offset, ++ dst, dst_offset, ++ bytes, flags); ++ ++ tracked_request_end(&src_req); ++ tracked_request_end(&dst_req); ++ bdrv_dec_in_flight(src_bs); ++ bdrv_dec_in_flight(dst_bs); ++ return ret; ++} +diff --git a/include/block/block.h b/include/block/block.h +index 2d17b09..e677080 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -613,4 +613,36 @@ bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, + */ + void bdrv_register_buf(BlockDriverState *bs, void *host, size_t size); + void bdrv_unregister_buf(BlockDriverState *bs, void *host); ++ ++/** ++ * ++ * 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. Must be one of: ++ * 0 - actually read data from src; ++ * 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. ++ * ++ * Returns: 0 if succeeded; negative error code if failed. ++ **/ ++int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset, ++ BdrvChild *dst, uint64_t dst_offset, ++ uint64_t bytes, BdrvRequestFlags flags); + #endif +diff --git a/include/block/block_int.h b/include/block/block_int.h +index ad2b852..3da86a7 100644 +--- a/include/block/block_int.h ++++ b/include/block/block_int.h +@@ -206,6 +206,37 @@ struct BlockDriver { + int coroutine_fn (*bdrv_co_pdiscard)(BlockDriverState *bs, + int64_t offset, int 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 (*bdrv_co_copy_range_from)(BlockDriverState *bs, ++ BdrvChild *src, ++ uint64_t offset, ++ BdrvChild *dst, ++ uint64_t dst_offset, ++ uint64_t bytes, ++ BdrvRequestFlags 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 (*bdrv_co_copy_range_to)(BlockDriverState *bs, ++ BdrvChild *src, ++ uint64_t src_offset, ++ BdrvChild *dst, ++ uint64_t dst_offset, ++ uint64_t bytes, ++ BdrvRequestFlags flags); ++ + /* + * Building block for bdrv_block_status[_above] and + * bdrv_is_allocated[_above]. The driver should answer only +@@ -1091,4 +1122,11 @@ void bdrv_dec_in_flight(BlockDriverState *bs); + + void blockdev_close_all_bdrv_states(void); + ++int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, ++ BdrvChild *dst, uint64_t dst_offset, ++ uint64_t bytes, BdrvRequestFlags flags); ++int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, ++ BdrvChild *dst, uint64_t dst_offset, ++ uint64_t bytes, BdrvRequestFlags flags); ++ + #endif /* BLOCK_INT_H */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Make-auto-read-only-on-default-for-drive.patch b/SOURCES/kvm-block-Make-auto-read-only-on-default-for-drive.patch new file mode 100644 index 0000000..6e349b2 --- /dev/null +++ b/SOURCES/kvm-block-Make-auto-read-only-on-default-for-drive.patch @@ -0,0 +1,47 @@ +From b6ffe1491ccf08594837991b5a9ead762b65ca97 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 23 Nov 2018 10:41:52 +0100 +Subject: [PATCH 11/34] block: Make auto-read-only=on default for -drive + +RH-Author: Kevin Wolf +Message-id: <20181123104154.13541-11-kwolf@redhat.com> +Patchwork-id: 83115 +O-Subject: [RHEL-7.7/7.6.z qemu-kvm-rhev PATCH v2 10/12] block: Make auto-read-only=on default for -drive +Bugzilla: 1623986 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina +RH-Acked-by: John Snow + +While we want machine interfaces like -blockdev and QMP blockdev-add to +add as little auto-detection as possible so that management tools are +explicit about their needs, -drive is a convenience option for human +users. Enabling auto-read-only=on by default there enables users to use +read-only images for read-only guest devices without having to specify +read-only=on explicitly. If they try to attach the image to a read-write +device, they will still get an error message. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +(cherry picked from commit 9384a444f6ebcd66bba0ae3c8434120d03c8dfea) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/blockdev.c b/blockdev.c +index 3d73f05..3eb1880 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -631,6 +631,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, + 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)) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Make-bdrv_is_writable-public.patch b/SOURCES/kvm-block-Make-bdrv_is_writable-public.patch new file mode 100644 index 0000000..a64c204 --- /dev/null +++ b/SOURCES/kvm-block-Make-bdrv_is_writable-public.patch @@ -0,0 +1,97 @@ +From 77e6d5c8d54be29781b15bc7dbb816dd156812a0 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 18:00:53 +0200 +Subject: [PATCH 47/54] block: Make bdrv_is_writable() public + +RH-Author: Max Reitz +Message-id: <20180618180055.22739-2-mreitz@redhat.com> +Patchwork-id: 80792 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/3] block: Make bdrv_is_writable() public +Bugzilla: 1588039 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf +RH-Acked-by: John Snow + +This is a useful function for the whole block layer, so make it public. +At the same time, users outside of block.c probably do not need to make +use of the reopen functionality, so rename the current function to +bdrv_is_writable_after_reopen() create a new bdrv_is_writable() function +that just passes NULL to it for the reopen queue. + +Cc: qemu-stable@nongnu.org +Signed-off-by: Max Reitz +Message-id: 20180606193702.7113-2-mreitz@redhat.com +Reviewed-by: John Snow +Reviewed-by: Jeff Cody +Signed-off-by: Max Reitz +(cherry picked from commit cc022140972f8b6ac3973c12ccf9dd6b1d2fd200) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block.c | 17 ++++++++++++++--- + include/block/block.h | 1 + + 2 files changed, 15 insertions(+), 3 deletions(-) + +diff --git a/block.c b/block.c +index 3c3e8fd..982d54e 100644 +--- a/block.c ++++ b/block.c +@@ -1621,13 +1621,24 @@ static int bdrv_reopen_get_flags(BlockReopenQueue *q, BlockDriverState *bs) + + /* 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(BlockDriverState *bs, BlockReopenQueue *q) ++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) ++{ ++ return bdrv_is_writable_after_reopen(bs, NULL); ++} ++ + static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, + BdrvChild *c, const BdrvChildRole *role, + BlockReopenQueue *reopen_queue, +@@ -1665,7 +1676,7 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q, + + /* Write permissions never work with read-only images */ + if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) && +- !bdrv_is_writable(bs, q)) ++ !bdrv_is_writable_after_reopen(bs, q)) + { + error_setg(errp, "Block node is read-only"); + return -EPERM; +@@ -1957,7 +1968,7 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, + &perm, &shared); + + /* Format drivers may touch metadata even if the guest doesn't write */ +- if (bdrv_is_writable(bs, reopen_queue)) { ++ if (bdrv_is_writable_after_reopen(bs, reopen_queue)) { + perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE; + } + +diff --git a/include/block/block.h b/include/block/block.h +index 3894edd..06cd772 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -407,6 +407,7 @@ bool bdrv_is_read_only(BlockDriverState *bs); + int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, + bool ignore_allow_rdw, Error **errp); + int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp); ++bool bdrv_is_writable(BlockDriverState *bs); + bool bdrv_is_sg(BlockDriverState *bs); + bool bdrv_is_inserted(BlockDriverState *bs); + void bdrv_lock_medium(BlockDriverState *bs, bool locked); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Make-permission-changes-in-reopen-less-wrong.patch b/SOURCES/kvm-block-Make-permission-changes-in-reopen-less-wrong.patch new file mode 100644 index 0000000..151b493 --- /dev/null +++ b/SOURCES/kvm-block-Make-permission-changes-in-reopen-less-wrong.patch @@ -0,0 +1,130 @@ +From f64042620963059859d0bbae60e8655ccc44736e Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 15 Mar 2019 18:10:01 +0100 +Subject: [PATCH 005/163] block: Make permission changes in reopen less wrong + +RH-Author: Kevin Wolf +Message-id: <20190315181010.14964-6-kwolf@redhat.com> +Patchwork-id: 84882 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 05/14] block: Make permission changes in reopen less wrong +Bugzilla: 1685989 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +The way that reopen interacts with permission changes has one big +problem: Both operations are recursive, and the permissions are changes +for each node in the reopen queue. + +For a simple graph that consists just of parent and child, +.bdrv_check_perm will be called twice for the child, once recursively +when adjusting the permissions of parent, and once again when the child +itself is reopened. + +Even worse, the first .bdrv_check_perm call happens before +.bdrv_reopen_prepare was called for the child and the second one is +called afterwards. + +Making sure that .bdrv_check_perm (and the other permission callbacks) +are called only once is hard. We can cope with multiple calls right now, +but as soon as file-posix gets a dynamic auto-read-only that may need to +open a new file descriptor, we get the additional requirement that all +of them are after the .bdrv_reopen_prepare call. + +So reorder things in bdrv_reopen_multiple() to first call +.bdrv_reopen_prepare for all involved nodes and only then adjust +permissions. + +Signed-off-by: Kevin Wolf +(cherry picked from commit 69b736e76567ecbc9b9e55570bc0afc840614a98) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 35 ++++++++++++++++++++++++----------- + 1 file changed, 24 insertions(+), 11 deletions(-) + +diff --git a/block.c b/block.c +index 2f1b4d1..b31124c 100644 +--- a/block.c ++++ b/block.c +@@ -1661,6 +1661,7 @@ static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared); + + typedef struct BlockReopenQueueEntry { + bool prepared; ++ bool perms_checked; + BDRVReopenState state; + QSIMPLEQ_ENTRY(BlockReopenQueueEntry) entry; + } BlockReopenQueueEntry; +@@ -3072,6 +3073,16 @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er + bs_entry->prepared = true; + } + ++ QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) { ++ BDRVReopenState *state = &bs_entry->state; ++ ret = bdrv_check_perm(state->bs, bs_queue, state->perm, ++ state->shared_perm, NULL, errp); ++ if (ret < 0) { ++ goto cleanup_perm; ++ } ++ bs_entry->perms_checked = true; ++ } ++ + /* If we reach this point, we have success and just need to apply the + * changes + */ +@@ -3080,7 +3091,20 @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er + } + + ret = 0; ++cleanup_perm: ++ QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { ++ BDRVReopenState *state = &bs_entry->state; ++ ++ if (!bs_entry->perms_checked) { ++ continue; ++ } + ++ if (ret == 0) { ++ bdrv_set_perm(state->bs, state->perm, state->shared_perm); ++ } else { ++ bdrv_abort_perm_update(state->bs); ++ } ++ } + cleanup: + QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { + if (ret) { +@@ -3297,12 +3321,6 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, + } while ((entry = qdict_next(reopen_state->options, entry))); + } + +- ret = bdrv_check_perm(reopen_state->bs, queue, reopen_state->perm, +- reopen_state->shared_perm, NULL, errp); +- if (ret < 0) { +- goto error; +- } +- + ret = 0; + + error: +@@ -3352,9 +3370,6 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state) + + bdrv_refresh_limits(bs, NULL); + +- bdrv_set_perm(reopen_state->bs, reopen_state->perm, +- reopen_state->shared_perm); +- + new_can_write = + !bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE); + if (!old_can_write && new_can_write && drv->bdrv_reopen_bitmaps_rw) { +@@ -3386,8 +3401,6 @@ void bdrv_reopen_abort(BDRVReopenState *reopen_state) + if (drv->bdrv_reopen_abort) { + drv->bdrv_reopen_abort(reopen_state); + } +- +- bdrv_abort_perm_update(reopen_state->bs); + } + + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Make-remaining-uses-of-qobject-input-visitor-m.patch b/SOURCES/kvm-block-Make-remaining-uses-of-qobject-input-visitor-m.patch new file mode 100644 index 0000000..14fc793 --- /dev/null +++ b/SOURCES/kvm-block-Make-remaining-uses-of-qobject-input-visitor-m.patch @@ -0,0 +1,121 @@ +From e5c3237d56ab85fdf98265ad9893bb532b5575ff Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:21 +0200 +Subject: [PATCH 14/54] block: Make remaining uses of qobject input visitor + more robust + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-15-armbru@redhat.com> +Patchwork-id: 80730 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 14/23] block: Make remaining uses of qobject input visitor more robust +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Remaining uses of qobject_input_visitor_new_keyval() in the block +subsystem: + +* block_crypto_open_opts_init() + Currently doesn't visit any non-string scalars, thus safe. It's + called from + - block_crypto_open_luks() + Creates the QDict with qemu_opts_to_qdict_filtered(), which + creates only string scalars, but has a TODO asking for other types. + - qcow_open() + - qcow2_open(), qcow2_co_invalidate_cache(), qcow2_reopen_prepare() + +* block_crypto_create_opts_init(), called from + - block_crypto_co_create_opts_luks() + Also creates the QDict with qemu_opts_to_qdict_filtered(). + +* vdi_co_create_opts() + Also creates the QDict with qemu_opts_to_qdict_filtered(). + +Replace these uses by qobject_input_visitor_new_flat_confused() for +robustness. This adds crumpling. Right now, that's a no-op, but if +we ever extend these things in non-flat ways, crumpling will be +needed. + +Signed-off-by: Markus Armbruster +Signed-off-by: Kevin Wolf +(cherry picked from commit f853465aacb45dbb07e4cc9815e39b55e10dc690) +Signed-off-by: Miroslav Rezanina +--- + block/crypto.c | 12 +++++++++--- + block/vdi.c | 8 ++++++-- + 2 files changed, 15 insertions(+), 5 deletions(-) + +diff --git a/block/crypto.c b/block/crypto.c +index 7e7ad2d..f5151f4 100644 +--- a/block/crypto.c ++++ b/block/crypto.c +@@ -21,11 +21,11 @@ + #include "qemu/osdep.h" + + #include "block/block_int.h" ++#include "block/qdict.h" + #include "sysemu/block-backend.h" + #include "crypto/block.h" + #include "qapi/opts-visitor.h" + #include "qapi/qapi-visit-crypto.h" +-#include "qapi/qmp/qdict.h" + #include "qapi/qobject-input-visitor.h" + #include "qapi/error.h" + #include "qemu/option.h" +@@ -159,7 +159,10 @@ block_crypto_open_opts_init(QCryptoBlockFormat format, + ret = g_new0(QCryptoBlockOpenOptions, 1); + ret->format = format; + +- v = qobject_input_visitor_new_keyval(QOBJECT(opts)); ++ v = qobject_input_visitor_new_flat_confused(opts, &local_err); ++ if (local_err) { ++ goto out; ++ } + + visit_start_struct(v, NULL, NULL, 0, &local_err); + if (local_err) { +@@ -210,7 +213,10 @@ block_crypto_create_opts_init(QCryptoBlockFormat format, + ret = g_new0(QCryptoBlockCreateOptions, 1); + ret->format = format; + +- v = qobject_input_visitor_new_keyval(QOBJECT(opts)); ++ v = qobject_input_visitor_new_flat_confused(opts, &local_err); ++ if (local_err) { ++ goto out; ++ } + + visit_start_struct(v, NULL, NULL, 0, &local_err); + if (local_err) { +diff --git a/block/vdi.c b/block/vdi.c +index 96a22b8..41859a8 100644 +--- a/block/vdi.c ++++ b/block/vdi.c +@@ -51,10 +51,10 @@ + + #include "qemu/osdep.h" + #include "qapi/error.h" +-#include "qapi/qmp/qdict.h" + #include "qapi/qobject-input-visitor.h" + #include "qapi/qapi-visit-block-core.h" + #include "block/block_int.h" ++#include "block/qdict.h" + #include "sysemu/block-backend.h" + #include "qemu/module.h" + #include "qemu/option.h" +@@ -933,7 +933,11 @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts, + } + + /* Get the QAPI object */ +- v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); ++ v = qobject_input_visitor_new_flat_confused(qdict, errp); ++ if (!v) { ++ ret = -EINVAL; ++ goto done; ++ } + visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); + visit_free(v); + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Move-bdrv_drain_all_begin-out-of-coroutine-con.patch b/SOURCES/kvm-block-Move-bdrv_drain_all_begin-out-of-coroutine-con.patch new file mode 100644 index 0000000..e18aece --- /dev/null +++ b/SOURCES/kvm-block-Move-bdrv_drain_all_begin-out-of-coroutine-con.patch @@ -0,0 +1,78 @@ +From 13f0c32e5c77278207f63ade463035d8aaa4898d Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:15 +0200 +Subject: [PATCH 24/49] block: Move bdrv_drain_all_begin() out of coroutine + context + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-18-kwolf@redhat.com> +Patchwork-id: 82169 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 17/42] block: Move bdrv_drain_all_begin() out of coroutine context +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Before we can introduce a single polling loop for all nodes in +bdrv_drain_all_begin(), we must make sure to run it outside of coroutine +context like we already do for bdrv_do_drained_begin(). + +Signed-off-by: Kevin Wolf +(cherry picked from commit c8ca33d06def97d909a8511377b82994ae4e5981) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/io.c | 22 +++++++++++++++++----- + 1 file changed, 17 insertions(+), 5 deletions(-) + +diff --git a/block/io.c b/block/io.c +index 23fe069..b58c91b 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -263,11 +263,16 @@ static void bdrv_co_drain_bh_cb(void *opaque) + Coroutine *co = data->co; + BlockDriverState *bs = data->bs; + +- bdrv_dec_in_flight(bs); +- if (data->begin) { +- bdrv_do_drained_begin(bs, data->recursive, data->parent, data->poll); ++ if (bs) { ++ bdrv_dec_in_flight(bs); ++ if (data->begin) { ++ bdrv_do_drained_begin(bs, data->recursive, data->parent, data->poll); ++ } else { ++ bdrv_do_drained_end(bs, data->recursive, data->parent); ++ } + } else { +- bdrv_do_drained_end(bs, data->recursive, data->parent); ++ assert(data->begin); ++ bdrv_drain_all_begin(); + } + + data->done = true; +@@ -293,7 +298,9 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, + .parent = parent, + .poll = poll, + }; +- bdrv_inc_in_flight(bs); ++ if (bs) { ++ bdrv_inc_in_flight(bs); ++ } + aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), + bdrv_co_drain_bh_cb, &data); + +@@ -463,6 +470,11 @@ void bdrv_drain_all_begin(void) + BlockDriverState *bs; + BdrvNextIterator it; + ++ if (qemu_in_coroutine()) { ++ bdrv_co_yield_to_drain(NULL, true, false, NULL, true); ++ return; ++ } ++ + /* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread + * or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on + * nodes in several different AioContexts, so make sure we're in the main +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Move-bdrv_truncate-implementation-to-io.c.patch b/SOURCES/kvm-block-Move-bdrv_truncate-implementation-to-io.c.patch new file mode 100644 index 0000000..4127475 --- /dev/null +++ b/SOURCES/kvm-block-Move-bdrv_truncate-implementation-to-io.c.patch @@ -0,0 +1,297 @@ +From 630e021373d8b88a5d9698c843811a518463072a Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Thu, 12 Jul 2018 14:42:56 +0200 +Subject: [PATCH 37/89] block: Move bdrv_truncate() implementation to io.c + +RH-Author: Kevin Wolf +Message-id: <20180712144258.17303-5-kwolf@redhat.com> +Patchwork-id: 81328 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 4/6] block: Move bdrv_truncate() implementation to io.c +Bugzilla: 1595173 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow + +This moves the bdrv_truncate() implementation from block.c to block/io.c +so it can have access to the tracked requests infrastructure. + +This involves making refresh_total_sectors() public (in block_int.h). + +Signed-off-by: Kevin Wolf +Reviewed-by: Stefan Hajnoczi +(cherry picked from commit 3d9f2d2af63fda5f0404fb85ea80161837a4e4e3) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 111 +--------------------------------------------- + block/io.c | 109 +++++++++++++++++++++++++++++++++++++++++++++ + include/block/block_int.h | 2 + + 3 files changed, 112 insertions(+), 110 deletions(-) + +diff --git a/block.c b/block.c +index 0af08ca..10a1ece 100644 +--- a/block.c ++++ b/block.c +@@ -721,7 +721,7 @@ static int find_image_format(BlockBackend *file, const char *filename, + * Set the current 'total_sectors' value + * Return 0 on success, -errno on error. + */ +-static int refresh_total_sectors(BlockDriverState *bs, int64_t hint) ++int refresh_total_sectors(BlockDriverState *bs, int64_t hint) + { + BlockDriver *drv = bs->drv; + +@@ -2200,16 +2200,6 @@ static void bdrv_parent_cb_change_media(BlockDriverState *bs, bool load) + } + } + +-static void bdrv_parent_cb_resize(BlockDriverState *bs) +-{ +- BdrvChild *c; +- QLIST_FOREACH(c, &bs->parents, next_parent) { +- if (c->role->resize) { +- c->role->resize(c); +- } +- } +-} +- + /* + * Sets the backing file link of a BDS. A new reference is created; callers + * which don't need their own reference any more must call bdrv_unref(). +@@ -3736,105 +3726,6 @@ exit: + } + + /** +- * Truncate file to 'offset' bytes (needed only for file protocols) +- */ +-int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, +- PreallocMode prealloc, Error **errp) +-{ +- BlockDriverState *bs = child->bs; +- BlockDriver *drv = bs->drv; +- int ret; +- +- assert(child->perm & BLK_PERM_RESIZE); +- +- /* 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; +- } +- +- bdrv_inc_in_flight(bs); +- +- if (!drv->bdrv_co_truncate) { +- if (bs->file && drv->is_filter) { +- ret = bdrv_co_truncate(bs->file, offset, prealloc, errp); +- goto out; +- } +- error_setg(errp, "Image format driver does not support resize"); +- ret = -ENOTSUP; +- goto out; +- } +- if (bs->read_only) { +- error_setg(errp, "Image is read-only"); +- ret = -EACCES; +- goto out; +- } +- +- assert(!(bs->open_flags & BDRV_O_INACTIVE)); +- +- ret = drv->bdrv_co_truncate(bs, offset, prealloc, errp); +- if (ret < 0) { +- goto out; +- } +- ret = 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; +- } +- bdrv_dirty_bitmap_truncate(bs, offset); +- bdrv_parent_cb_resize(bs); +- atomic_inc(&bs->write_gen); +- +-out: +- bdrv_dec_in_flight(bs); +- return ret; +-} +- +-typedef struct TruncateCo { +- BdrvChild *child; +- int64_t offset; +- PreallocMode prealloc; +- Error **errp; +- int ret; +-} TruncateCo; +- +-static void coroutine_fn bdrv_truncate_co_entry(void *opaque) +-{ +- TruncateCo *tco = opaque; +- tco->ret = bdrv_co_truncate(tco->child, tco->offset, tco->prealloc, +- tco->errp); +-} +- +-int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, +- Error **errp) +-{ +- Coroutine *co; +- TruncateCo tco = { +- .child = child, +- .offset = offset, +- .prealloc = prealloc, +- .errp = errp, +- .ret = NOT_DONE, +- }; +- +- if (qemu_in_coroutine()) { +- /* Fast-path if already in coroutine context */ +- bdrv_truncate_co_entry(&tco); +- } else { +- co = qemu_coroutine_create(bdrv_truncate_co_entry, &tco); +- qemu_coroutine_enter(co); +- BDRV_POLL_WHILE(child->bs, tco.ret == NOT_DONE); +- } +- +- return tco.ret; +-} +- +-/** + * Length of a allocated file in bytes. Sparse files are counted by actual + * allocated space. Return < 0 if error or unknown. + */ +diff --git a/block/io.c b/block/io.c +index 5c043a4..32a82e3 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -2929,3 +2929,112 @@ int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset, + bdrv_dec_in_flight(dst_bs); + return ret; + } ++ ++static void bdrv_parent_cb_resize(BlockDriverState *bs) ++{ ++ BdrvChild *c; ++ QLIST_FOREACH(c, &bs->parents, next_parent) { ++ if (c->role->resize) { ++ c->role->resize(c); ++ } ++ } ++} ++ ++/** ++ * Truncate file to 'offset' bytes (needed only for file protocols) ++ */ ++int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, ++ PreallocMode prealloc, Error **errp) ++{ ++ BlockDriverState *bs = child->bs; ++ BlockDriver *drv = bs->drv; ++ int ret; ++ ++ assert(child->perm & BLK_PERM_RESIZE); ++ ++ /* 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; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ ++ if (!drv->bdrv_co_truncate) { ++ if (bs->file && drv->is_filter) { ++ ret = bdrv_co_truncate(bs->file, offset, prealloc, errp); ++ goto out; ++ } ++ error_setg(errp, "Image format driver does not support resize"); ++ ret = -ENOTSUP; ++ goto out; ++ } ++ if (bs->read_only) { ++ error_setg(errp, "Image is read-only"); ++ ret = -EACCES; ++ goto out; ++ } ++ ++ assert(!(bs->open_flags & BDRV_O_INACTIVE)); ++ ++ ret = drv->bdrv_co_truncate(bs, offset, prealloc, errp); ++ if (ret < 0) { ++ goto out; ++ } ++ ret = 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; ++ } ++ bdrv_dirty_bitmap_truncate(bs, offset); ++ bdrv_parent_cb_resize(bs); ++ atomic_inc(&bs->write_gen); ++ ++out: ++ bdrv_dec_in_flight(bs); ++ return ret; ++} ++ ++typedef struct TruncateCo { ++ BdrvChild *child; ++ int64_t offset; ++ PreallocMode prealloc; ++ Error **errp; ++ int ret; ++} TruncateCo; ++ ++static void coroutine_fn bdrv_truncate_co_entry(void *opaque) ++{ ++ TruncateCo *tco = opaque; ++ tco->ret = bdrv_co_truncate(tco->child, tco->offset, tco->prealloc, ++ tco->errp); ++} ++ ++int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, ++ Error **errp) ++{ ++ Coroutine *co; ++ TruncateCo tco = { ++ .child = child, ++ .offset = offset, ++ .prealloc = prealloc, ++ .errp = errp, ++ .ret = NOT_DONE, ++ }; ++ ++ if (qemu_in_coroutine()) { ++ /* Fast-path if already in coroutine context */ ++ bdrv_truncate_co_entry(&tco); ++ } else { ++ co = qemu_coroutine_create(bdrv_truncate_co_entry, &tco); ++ qemu_coroutine_enter(co); ++ BDRV_POLL_WHILE(child->bs, tco.ret == NOT_DONE); ++ } ++ ++ return tco.ret; ++} +diff --git a/include/block/block_int.h b/include/block/block_int.h +index 46b2f87..6a844ec 100644 +--- a/include/block/block_int.h ++++ b/include/block/block_int.h +@@ -1129,4 +1129,6 @@ int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, BdrvRequestFlags flags); + ++int refresh_total_sectors(BlockDriverState *bs, int64_t hint); ++ + #endif /* BLOCK_INT_H */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Move-request-tracking-to-children-in-copy-offl.patch b/SOURCES/kvm-block-Move-request-tracking-to-children-in-copy-offl.patch new file mode 100644 index 0000000..5700f3a --- /dev/null +++ b/SOURCES/kvm-block-Move-request-tracking-to-children-in-copy-offl.patch @@ -0,0 +1,124 @@ +From fba2d77758d680a147862f26d57bb984d73f7700 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Tue, 24 Jul 2018 12:43:07 +0200 +Subject: [PATCH 64/89] block: Move request tracking to children in copy + offloading + +RH-Author: John Snow +Message-id: <20180718225511.14878-14-jsnow@redhat.com> +Patchwork-id: 81393 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 13/35] block: Move request tracking to children in copy offloading +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Fam Zheng + +in_flight and tracked requests need to be tracked in every layer during +recursion. For now the only user is qemu-img convert where overlapping +requests and IOThreads don't exist, therefore this change doesn't make +much difference form user point of view, but it is incorrect as part of +the API. Fix it. + +Reported-by: Kevin Wolf +Signed-off-by: Fam Zheng +Reviewed-by: Stefan Hajnoczi +Signed-off-by: Kevin Wolf +(cherry picked from commit 37aec7d75eb0d035a0db4f2cf9ad8b1b0c10f91b) +Signed-off-by: John Snow +--- + block/io.c | 60 +++++++++++++++++++++++++++++------------------------------- + 1 file changed, 29 insertions(+), 31 deletions(-) + +diff --git a/block/io.c b/block/io.c +index ac36d1c..136a5d0 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -2847,6 +2847,9 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, + BdrvRequestFlags flags, + bool recurse_src) + { ++ BdrvTrackedRequest src_req, dst_req; ++ BlockDriverState *src_bs = src->bs; ++ BlockDriverState *dst_bs = dst->bs; + int ret; + + if (!src || !dst || !src->bs || !dst->bs) { +@@ -2870,17 +2873,32 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, + || src->bs->encrypted || dst->bs->encrypted) { + return -ENOTSUP; + } ++ ++ bdrv_inc_in_flight(src_bs); ++ bdrv_inc_in_flight(dst_bs); ++ tracked_request_begin(&src_req, src_bs, src_offset, ++ bytes, BDRV_TRACKED_READ); ++ tracked_request_begin(&dst_req, dst_bs, dst_offset, ++ bytes, BDRV_TRACKED_WRITE); ++ ++ wait_serialising_requests(&src_req); ++ wait_serialising_requests(&dst_req); + if (recurse_src) { +- return src->bs->drv->bdrv_co_copy_range_from(src->bs, +- src, src_offset, +- dst, dst_offset, +- bytes, flags); ++ ret = src->bs->drv->bdrv_co_copy_range_from(src->bs, ++ src, src_offset, ++ dst, dst_offset, ++ bytes, flags); + } else { +- return dst->bs->drv->bdrv_co_copy_range_to(dst->bs, +- src, src_offset, +- dst, dst_offset, +- bytes, flags); ++ ret = dst->bs->drv->bdrv_co_copy_range_to(dst->bs, ++ src, src_offset, ++ dst, dst_offset, ++ bytes, flags); + } ++ tracked_request_end(&src_req); ++ tracked_request_end(&dst_req); ++ bdrv_dec_in_flight(src_bs); ++ bdrv_dec_in_flight(dst_bs); ++ return ret; + } + + /* Copy range from @src to @dst. +@@ -2911,29 +2929,9 @@ int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, BdrvRequestFlags flags) + { +- BdrvTrackedRequest src_req, dst_req; +- BlockDriverState *src_bs = src->bs; +- BlockDriverState *dst_bs = dst->bs; +- int ret; +- +- bdrv_inc_in_flight(src_bs); +- bdrv_inc_in_flight(dst_bs); +- tracked_request_begin(&src_req, src_bs, src_offset, +- bytes, BDRV_TRACKED_READ); +- tracked_request_begin(&dst_req, dst_bs, dst_offset, +- bytes, BDRV_TRACKED_WRITE); +- +- wait_serialising_requests(&src_req); +- wait_serialising_requests(&dst_req); +- ret = bdrv_co_copy_range_from(src, src_offset, +- dst, dst_offset, +- bytes, flags); +- +- tracked_request_end(&src_req); +- tracked_request_end(&dst_req); +- bdrv_dec_in_flight(src_bs); +- bdrv_dec_in_flight(dst_bs); +- return ret; ++ return bdrv_co_copy_range_from(src, src_offset, ++ dst, dst_offset, ++ bytes, flags); + } + + static void bdrv_parent_cb_resize(BlockDriverState *bs) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Poll-after-drain-on-attaching-a-node.patch b/SOURCES/kvm-block-Poll-after-drain-on-attaching-a-node.patch new file mode 100644 index 0000000..9b2d732 --- /dev/null +++ b/SOURCES/kvm-block-Poll-after-drain-on-attaching-a-node.patch @@ -0,0 +1,150 @@ +From b0c7fc7fbe64b234272db8316f81b92fe675a65a Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:19 +0200 +Subject: [PATCH 28/49] block: Poll after drain on attaching a node + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-22-kwolf@redhat.com> +Patchwork-id: 82173 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 21/42] block: Poll after drain on attaching a node +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Commit dcf94a23b1 ('block: Don't poll in parent drain callbacks') +removed polling in bdrv_child_cb_drained_begin() on the grounds that the +original bdrv_drain() already will poll and BdrvChildRole.drained_begin +calls must not cause graph changes (and therefore must not call +aio_poll() or the recursion through the graph will break. + +This reasoning is correct for calls through bdrv_do_drained_begin(). +However, BdrvChildRole.drained_begin is also called when a node that is +already in a drained section (i.e. bdrv_do_drained_begin() has already +returned and therefore can't poll any more) is attached to a new parent. +In this case, we must explicitly poll to have all requests completed +before the drained new child can be attached to the parent. + +In bdrv_replace_child_noperm(), we know that we're not inside the +recursion of bdrv_do_drained_begin() because graph changes are not +allowed there, and bdrv_replace_child_noperm() is a graph change. The +call of BdrvChildRole.drained_begin() must therefore be followed by a +BDRV_POLL_WHILE() that waits for the completion of requests. + +Reported-by: Max Reitz +Signed-off-by: Kevin Wolf +(cherry picked from commit 4be6a6d118123340f16fb8b3bf45220d0f8ed6d4) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 2 +- + block/io.c | 26 ++++++++++++++++++++------ + include/block/block.h | 8 ++++++++ + include/block/block_int.h | 3 +++ + 4 files changed, 32 insertions(+), 7 deletions(-) + +diff --git a/block.c b/block.c +index eea9c6f..e89b5e3 100644 +--- a/block.c ++++ b/block.c +@@ -2072,7 +2072,7 @@ static void bdrv_replace_child_noperm(BdrvChild *child, + } + assert(num >= 0); + for (i = 0; i < num; i++) { +- child->role->drained_begin(child); ++ bdrv_parent_drained_begin_single(child, true); + } + } + +diff --git a/block/io.c b/block/io.c +index 38ae299..d404088 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -52,9 +52,7 @@ void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore, + if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) { + continue; + } +- if (c->role->drained_begin) { +- c->role->drained_begin(c); +- } ++ bdrv_parent_drained_begin_single(c, false); + } + } + +@@ -73,6 +71,14 @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore, + } + } + ++static bool bdrv_parent_drained_poll_single(BdrvChild *c) ++{ ++ if (c->role->drained_poll) { ++ return c->role->drained_poll(c); ++ } ++ return false; ++} ++ + static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore, + bool ignore_bds_parents) + { +@@ -83,14 +89,22 @@ static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore, + if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) { + continue; + } +- if (c->role->drained_poll) { +- busy |= c->role->drained_poll(c); +- } ++ busy |= bdrv_parent_drained_poll_single(c); + } + + return busy; + } + ++void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll) ++{ ++ if (c->role->drained_begin) { ++ c->role->drained_begin(c); ++ } ++ if (poll) { ++ BDRV_POLL_WHILE(c->bs, bdrv_parent_drained_poll_single(c)); ++ } ++} ++ + static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src) + { + dst->opt_transfer = MAX(dst->opt_transfer, src->opt_transfer); +diff --git a/include/block/block.h b/include/block/block.h +index f9079ac..356712c 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -590,6 +590,14 @@ void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore, + bool ignore_bds_parents); + + /** ++ * bdrv_parent_drained_begin_single: ++ * ++ * Begin a quiesced section for the parent of @c. If @poll is true, wait for ++ * any pending activity to cease. ++ */ ++void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll); ++ ++/** + * bdrv_parent_drained_end: + * + * End a quiesced section of all users of @bs. This is part of +diff --git a/include/block/block_int.h b/include/block/block_int.h +index 9757d5e..b7806e3 100644 +--- a/include/block/block_int.h ++++ b/include/block/block_int.h +@@ -610,6 +610,9 @@ struct BdrvChildRole { + * 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. + */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Really-pause-block-jobs-on-drain.patch b/SOURCES/kvm-block-Really-pause-block-jobs-on-drain.patch new file mode 100644 index 0000000..7f0cdc3 --- /dev/null +++ b/SOURCES/kvm-block-Really-pause-block-jobs-on-drain.patch @@ -0,0 +1,360 @@ +From 70365466a45a381ebb54e49cb03579b5fd6c76ef Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:05 +0200 +Subject: [PATCH 14/49] block: Really pause block jobs on drain + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-8-kwolf@redhat.com> +Patchwork-id: 82160 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 07/42] block: Really pause block jobs on drain +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +We already requested that block jobs be paused in .bdrv_drained_begin, +but no guarantee was made that the job was actually inactive at the +point where bdrv_drained_begin() returned. + +This introduces a new callback BdrvChildRole.bdrv_drained_poll() and +uses it to make bdrv_drain_poll() consider block jobs using the node to +be drained. + +For the test case to work as expected, we have to switch from +block_job_sleep_ns() to qemu_co_sleep_ns() so that the test job is even +considered active and must be waited for when draining the node. + +Signed-off-by: Kevin Wolf +(cherry picked from commit 89bd030533e3592ca0a995450dcfc5d53e459e20) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 9 +++++++++ + block/io.c | 40 ++++++++++++++++++++++++++++++++++------ + block/mirror.c | 8 ++++++++ + blockjob.c | 23 +++++++++++++++++++++++ + include/block/block.h | 8 ++++++++ + include/block/block_int.h | 7 +++++++ + include/block/blockjob_int.h | 8 ++++++++ + tests/test-bdrv-drain.c | 18 ++++++++++-------- + 8 files changed, 107 insertions(+), 14 deletions(-) + +diff --git a/block.c b/block.c +index 10a1ece..0d9698a 100644 +--- a/block.c ++++ b/block.c +@@ -821,6 +821,12 @@ static void bdrv_child_cb_drained_begin(BdrvChild *child) + bdrv_drained_begin(bs); + } + ++static bool bdrv_child_cb_drained_poll(BdrvChild *child) ++{ ++ BlockDriverState *bs = child->opaque; ++ return bdrv_drain_poll(bs, NULL); ++} ++ + static void bdrv_child_cb_drained_end(BdrvChild *child) + { + BlockDriverState *bs = child->opaque; +@@ -905,6 +911,7 @@ const BdrvChildRole child_file = { + .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, +@@ -929,6 +936,7 @@ const BdrvChildRole child_format = { + .get_parent_desc = bdrv_child_get_parent_desc, + .inherit_options = bdrv_inherited_fmt_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, +@@ -1048,6 +1056,7 @@ const BdrvChildRole child_backing = { + .detach = bdrv_backing_detach, + .inherit_options = bdrv_backing_options, + .drained_begin = bdrv_child_cb_drained_begin, ++ .drained_poll = bdrv_child_cb_drained_poll, + .drained_end = bdrv_child_cb_drained_end, + .inactivate = bdrv_child_cb_inactivate, + .update_filename = bdrv_backing_update_filename, +diff --git a/block/io.c b/block/io.c +index 4d332c3..e260394 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -69,6 +69,23 @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore) + } + } + ++static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore) ++{ ++ BdrvChild *c, *next; ++ bool busy = false; ++ ++ QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { ++ if (c == ignore) { ++ continue; ++ } ++ if (c->role->drained_poll) { ++ busy |= c->role->drained_poll(c); ++ } ++ } ++ ++ return busy; ++} ++ + static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src) + { + dst->opt_transfer = MAX(dst->opt_transfer, src->opt_transfer); +@@ -182,21 +199,32 @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) + } + + /* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */ +-static bool bdrv_drain_poll(BlockDriverState *bs) ++bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent) ++{ ++ if (bdrv_parent_drained_poll(bs, ignore_parent)) { ++ return true; ++ } ++ ++ return atomic_read(&bs->in_flight); ++} ++ ++static bool bdrv_drain_poll_top_level(BlockDriverState *bs, ++ BdrvChild *ignore_parent) + { + /* Execute pending BHs first and check everything else only after the BHs + * have executed. */ + while (aio_poll(bs->aio_context, false)); +- return atomic_read(&bs->in_flight); ++ ++ return bdrv_drain_poll(bs, ignore_parent); + } + +-static bool bdrv_drain_recurse(BlockDriverState *bs) ++static bool bdrv_drain_recurse(BlockDriverState *bs, BdrvChild *parent) + { + BdrvChild *child, *tmp; + bool waited; + + /* Wait for drained requests to finish */ +- waited = BDRV_POLL_WHILE(bs, bdrv_drain_poll(bs)); ++ waited = BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, parent)); + + QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) { + BlockDriverState *bs = child->bs; +@@ -213,7 +241,7 @@ static bool bdrv_drain_recurse(BlockDriverState *bs) + */ + bdrv_ref(bs); + } +- waited |= bdrv_drain_recurse(bs); ++ waited |= bdrv_drain_recurse(bs, child); + if (in_main_loop) { + bdrv_unref(bs); + } +@@ -289,7 +317,7 @@ void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, + + bdrv_parent_drained_begin(bs, parent); + bdrv_drain_invoke(bs, true); +- bdrv_drain_recurse(bs); ++ bdrv_drain_recurse(bs, parent); + + if (recursive) { + bs->recursive_quiesce_counter++; +diff --git a/block/mirror.c b/block/mirror.c +index 313e6e9..4b27f71 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -976,6 +976,12 @@ static void mirror_pause(Job *job) + mirror_wait_for_all_io(s); + } + ++static bool mirror_drained_poll(BlockJob *job) ++{ ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); ++ return !!s->in_flight; ++} ++ + static void mirror_attached_aio_context(BlockJob *job, AioContext *new_context) + { + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); +@@ -1011,6 +1017,7 @@ static const BlockJobDriver mirror_job_driver = { + .pause = mirror_pause, + .complete = mirror_complete, + }, ++ .drained_poll = mirror_drained_poll, + .attached_aio_context = mirror_attached_aio_context, + .drain = mirror_drain, + }; +@@ -1028,6 +1035,7 @@ static const BlockJobDriver commit_active_job_driver = { + .pause = mirror_pause, + .complete = mirror_complete, + }, ++ .drained_poll = mirror_drained_poll, + .attached_aio_context = mirror_attached_aio_context, + .drain = mirror_drain, + }; +diff --git a/blockjob.c b/blockjob.c +index 0306533..be5903a 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -155,6 +155,28 @@ static void child_job_drained_begin(BdrvChild *c) + 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. */ ++ if (!job->busy || job_is_completed(job) || job->deferred_to_main_loop) { ++ 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; +@@ -164,6 +186,7 @@ static void child_job_drained_end(BdrvChild *c) + static const BdrvChildRole 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, + .stay_at_node = true, + }; +diff --git a/include/block/block.h b/include/block/block.h +index 8f87eea..8c91d4c 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -596,6 +596,14 @@ void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore); + void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore); + + /** ++ * bdrv_drain_poll: ++ * ++ * Poll for pending requests in @bs and its parents (except for ++ * @ignore_parent). This is part of bdrv_drained_begin. ++ */ ++bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent); ++ ++/** + * bdrv_drained_begin: + * + * Begin a quiesced section for exclusive access to the BDS, by disabling +diff --git a/include/block/block_int.h b/include/block/block_int.h +index 341cbe8..beeacde 100644 +--- a/include/block/block_int.h ++++ b/include/block/block_int.h +@@ -610,6 +610,13 @@ struct BdrvChildRole { + void (*drained_begin)(BdrvChild *child); + void (*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 (*drained_poll)(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. */ +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index 5cd50c6..e4a318d 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -39,6 +39,14 @@ struct BlockJobDriver { + JobDriver job_driver; + + /* ++ * 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); ++ ++ /* + * 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. +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index f5d85c9..49786ea 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -681,7 +681,11 @@ static int coroutine_fn test_job_run(Job *job, Error **errp) + + job_transition_to_ready(&s->common.job); + while (!s->should_complete) { +- job_sleep_ns(&s->common.job, 100000); ++ /* Avoid block_job_sleep_ns() because it marks the job as !busy. We ++ * want to emulate some actual activity (probably some I/O) here so ++ * that drain has to wait for this acitivity to stop. */ ++ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000); ++ job_pause_point(&s->common.job); + } + + return 0; +@@ -728,7 +732,7 @@ static void test_blockjob_common(enum drain_type drain_type) + + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); +- g_assert_false(job->job.busy); /* We're in job_sleep_ns() */ ++ g_assert_true(job->job.busy); /* We're in job_sleep_ns() */ + + do_drain_begin(drain_type, src); + +@@ -738,15 +742,14 @@ static void test_blockjob_common(enum drain_type drain_type) + } else { + g_assert_cmpint(job->job.pause_count, ==, 1); + } +- /* XXX We don't wait until the job is actually paused. Is this okay? */ +- /* g_assert_true(job->job.paused); */ ++ g_assert_true(job->job.paused); + g_assert_false(job->job.busy); /* The job is paused */ + + do_drain_end(drain_type, src); + + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); +- g_assert_false(job->job.busy); /* We're in job_sleep_ns() */ ++ g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ + + do_drain_begin(drain_type, target); + +@@ -756,15 +759,14 @@ static void test_blockjob_common(enum drain_type drain_type) + } else { + g_assert_cmpint(job->job.pause_count, ==, 1); + } +- /* XXX We don't wait until the job is actually paused. Is this okay? */ +- /* g_assert_true(job->job.paused); */ ++ g_assert_true(job->job.paused); + g_assert_false(job->job.busy); /* The job is paused */ + + do_drain_end(drain_type, target); + + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); +- g_assert_false(job->job.busy); /* We're in job_sleep_ns() */ ++ g_assert_true(job->job.busy); /* We're in job_sleep_ns() */ + + ret = job_complete_sync(&job->job, &error_abort); + g_assert_cmpint(ret, ==, 0); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Remove-aio_poll-in-bdrv_drain_poll-variants.patch b/SOURCES/kvm-block-Remove-aio_poll-in-bdrv_drain_poll-variants.patch new file mode 100644 index 0000000..83dc6a5 --- /dev/null +++ b/SOURCES/kvm-block-Remove-aio_poll-in-bdrv_drain_poll-variants.patch @@ -0,0 +1,62 @@ +From 78a927e7c583a7556604e55b5d27d4c4c082fb64 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:37 +0200 +Subject: [PATCH 46/49] block: Remove aio_poll() in bdrv_drain_poll variants + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-40-kwolf@redhat.com> +Patchwork-id: 82191 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 39/42] block: Remove aio_poll() in bdrv_drain_poll variants +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +bdrv_drain_poll_top_level() was buggy because it didn't release the +AioContext lock of the node to be drained before calling aio_poll(). +This way, callbacks called by aio_poll() would possibly take the lock a +second time and run into a deadlock with a nested AIO_WAIT_WHILE() call. + +However, it turns out that the aio_poll() call isn't actually needed any +more. It was introduced in commit 91af091f923, which is effectively +reverted by this patch. The cases it was supposed to fix are now covered +by bdrv_drain_poll(), which waits for block jobs to reach a quiescent +state. + +Signed-off-by: Kevin Wolf +Reviewed-by: Fam Zheng +Reviewed-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/io.c | 8 -------- + 1 file changed, 8 deletions(-) + +diff --git a/block/io.c b/block/io.c +index 19db35e..3313958 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -266,10 +266,6 @@ bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, + static bool bdrv_drain_poll_top_level(BlockDriverState *bs, bool recursive, + BdrvChild *ignore_parent) + { +- /* Execute pending BHs first and check everything else only after the BHs +- * have executed. */ +- while (aio_poll(bs->aio_context, false)); +- + return bdrv_drain_poll(bs, recursive, ignore_parent, false); + } + +@@ -509,10 +505,6 @@ static bool bdrv_drain_all_poll(void) + BlockDriverState *bs = NULL; + bool result = false; + +- /* Execute pending BHs first (may modify the graph) and check everything +- * else only after the BHs have executed. */ +- while (aio_poll(qemu_get_aio_context(), false)); +- + /* bdrv_drain_poll() can't make changes to the graph and we are holding the + * main AioContext lock, so iterating bdrv_next_all_states() is safe. */ + while ((bs = bdrv_next_all_states(bs))) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Remove-bdrv_drain_recurse.patch b/SOURCES/kvm-block-Remove-bdrv_drain_recurse.patch new file mode 100644 index 0000000..89901b1 --- /dev/null +++ b/SOURCES/kvm-block-Remove-bdrv_drain_recurse.patch @@ -0,0 +1,91 @@ +From 6d4f47108b6e121924f7bf12c97cbabb674dee1b Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:06 +0200 +Subject: [PATCH 15/49] block: Remove bdrv_drain_recurse() + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-9-kwolf@redhat.com> +Patchwork-id: 82161 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 08/42] block: Remove bdrv_drain_recurse() +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +For bdrv_drain(), recursively waiting for child node requests is +pointless because we didn't quiesce their parents, so new requests could +come in anyway. Letting the function work only on a single node makes it +more consistent. + +For subtree drains and drain_all, we already have the recursion in +bdrv_do_drained_begin(), so the extra recursion doesn't add anything +either. + +Remove the useless code. + +Signed-off-by: Kevin Wolf +Reviewed-by: Stefan Hajnoczi +(cherry picked from commit d30b8e64b7b282da785307504ada59efa8096fb1) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/io.c | 36 +++--------------------------------- + 1 file changed, 3 insertions(+), 33 deletions(-) + +diff --git a/block/io.c b/block/io.c +index e260394..a0e3461 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -218,38 +218,6 @@ static bool bdrv_drain_poll_top_level(BlockDriverState *bs, + return bdrv_drain_poll(bs, ignore_parent); + } + +-static bool bdrv_drain_recurse(BlockDriverState *bs, BdrvChild *parent) +-{ +- BdrvChild *child, *tmp; +- bool waited; +- +- /* Wait for drained requests to finish */ +- waited = BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, parent)); +- +- QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) { +- BlockDriverState *bs = child->bs; +- bool in_main_loop = +- qemu_get_current_aio_context() == qemu_get_aio_context(); +- assert(bs->refcnt > 0); +- if (in_main_loop) { +- /* In case the recursive bdrv_drain_recurse processes a +- * block_job_defer_to_main_loop BH and modifies the graph, +- * let's hold a reference to bs until we are done. +- * +- * IOThread doesn't have such a BH, and it is not safe to call +- * bdrv_unref without BQL, so skip doing it there. +- */ +- bdrv_ref(bs); +- } +- waited |= bdrv_drain_recurse(bs, child); +- if (in_main_loop) { +- bdrv_unref(bs); +- } +- } +- +- return waited; +-} +- + static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, + BdrvChild *parent); + static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, +@@ -317,7 +285,9 @@ void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, + + bdrv_parent_drained_begin(bs, parent); + bdrv_drain_invoke(bs, true); +- bdrv_drain_recurse(bs, parent); ++ ++ /* Wait for drained requests to finish */ ++ BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, parent)); + + if (recursive) { + bs->recursive_quiesce_counter++; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Remove-error-messages-in-bdrv_make_zero.patch b/SOURCES/kvm-block-Remove-error-messages-in-bdrv_make_zero.patch new file mode 100644 index 0000000..424b8f8 --- /dev/null +++ b/SOURCES/kvm-block-Remove-error-messages-in-bdrv_make_zero.patch @@ -0,0 +1,60 @@ +From 8403396f05aa1e54198433409a6db7924eb53969 Mon Sep 17 00:00:00 2001 +From: Maxim Levitsky +Date: Wed, 5 Jun 2019 13:56:57 +0200 +Subject: [PATCH 09/23] block: Remove error messages in bdrv_make_zero() + +RH-Author: Maxim Levitsky +Message-id: <20190605135705.24526-2-mlevitsk@redhat.com> +Patchwork-id: 88564 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/9] block: Remove error messages in bdrv_make_zero() +Bugzilla: 1648622 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow + +From: Kevin Wolf + +There is only a single caller of bdrv_make_zero(), which is qemu-img +convert. If the function fails, we just fall back to a different method +of zeroing out blocks on the target image. There is no good reason to +print error messages on stderr when the higher level operation will +actually succeed. + +Signed-off-by: Kevin Wolf +Acked-by: Eric Blake + +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1648622 + +Signed-off-by: Maxim Levitsky +(Cherry picked from 48ce986096bb70354b12f0becb253a06bcf9c434) + +Signed-off-by: Miroslav Rezanina +--- + block/io.c | 4 ---- + 1 file changed, 4 deletions(-) + +diff --git a/block/io.c b/block/io.c +index 7a99f7b..18bf3c2 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -913,8 +913,6 @@ int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags) + } + ret = bdrv_block_status(bs, offset, bytes, &bytes, NULL, NULL); + if (ret < 0) { +- error_report("error getting block status at offset %" PRId64 ": %s", +- offset, strerror(-ret)); + return ret; + } + if (ret & BDRV_BLOCK_ZERO) { +@@ -923,8 +921,6 @@ int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags) + } + ret = bdrv_pwrite_zeroes(child, offset, bytes, flags); + if (ret < 0) { +- error_report("error writing zeroes at offset %" PRId64 ": %s", +- offset, strerror(-ret)); + return ret; + } + offset += bytes; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Remove-recursive-parameter-from-bdrv_drain_inv.patch b/SOURCES/kvm-block-Remove-recursive-parameter-from-bdrv_drain_inv.patch new file mode 100644 index 0000000..8da965b --- /dev/null +++ b/SOURCES/kvm-block-Remove-recursive-parameter-from-bdrv_drain_inv.patch @@ -0,0 +1,75 @@ +From 6ba43fc7c323ae3f0ee66262db96e22c261c1bbe Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:01 +0200 +Subject: [PATCH 10/49] block: Remove 'recursive' parameter from + bdrv_drain_invoke() + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-4-kwolf@redhat.com> +Patchwork-id: 82155 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 03/42] block: Remove 'recursive' parameter from bdrv_drain_invoke() +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +All callers pass false for the 'recursive' parameter now. Remove it. + +Signed-off-by: Kevin Wolf +Reviewed-by: Stefan Hajnoczi +(cherry picked from commit 7d40d9ef9dfb4948a857bfc6ec8408eed1d1d9e7) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/io.c | 13 +++---------- + 1 file changed, 3 insertions(+), 10 deletions(-) + +diff --git a/block/io.c b/block/io.c +index 230b551..aa41f1e 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -167,9 +167,8 @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque) + } + + /* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */ +-static void bdrv_drain_invoke(BlockDriverState *bs, bool begin, bool recursive) ++static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) + { +- BdrvChild *child, *tmp; + BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin}; + + if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) || +@@ -180,12 +179,6 @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin, bool recursive) + data.co = qemu_coroutine_create(bdrv_drain_invoke_entry, &data); + bdrv_coroutine_enter(bs, data.co); + BDRV_POLL_WHILE(bs, !data.done); +- +- if (recursive) { +- QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) { +- bdrv_drain_invoke(child->bs, begin, true); +- } +- } + } + + static bool bdrv_drain_recurse(BlockDriverState *bs) +@@ -286,7 +279,7 @@ void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, + } + + bdrv_parent_drained_begin(bs, parent); +- bdrv_drain_invoke(bs, true, false); ++ bdrv_drain_invoke(bs, true); + bdrv_drain_recurse(bs); + + if (recursive) { +@@ -321,7 +314,7 @@ void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, + old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter); + + /* Re-enable things in child-to-parent order */ +- bdrv_drain_invoke(bs, false, false); ++ bdrv_drain_invoke(bs, false); + bdrv_parent_drained_end(bs, parent); + if (old_quiesce_counter == 1) { + aio_enable_external(bdrv_get_aio_context(bs)); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Require-auto-read-only-for-existing-fallbacks.patch b/SOURCES/kvm-block-Require-auto-read-only-for-existing-fallbacks.patch new file mode 100644 index 0000000..0ffe4b9 --- /dev/null +++ b/SOURCES/kvm-block-Require-auto-read-only-for-existing-fallbacks.patch @@ -0,0 +1,267 @@ +From f258c5ce8e80d9e21fbca9cc359c06f3ad02bab7 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 23 Nov 2018 10:41:46 +0100 +Subject: [PATCH 05/34] block: Require auto-read-only for existing fallbacks + +RH-Author: Kevin Wolf +Message-id: <20181123104154.13541-5-kwolf@redhat.com> +Patchwork-id: 83114 +O-Subject: [RHEL-7.7/7.6.z qemu-kvm-rhev PATCH v2 04/12] block: Require auto-read-only for existing fallbacks +Bugzilla: 1623986 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina +RH-Acked-by: John Snow + +Some block drivers have traditionally changed their node to read-only +mode without asking the user. This behaviour has been marked deprecated +since 2.11, expecting users to provide an explicit read-only=on option. + +Now that we have auto-read-only=on, enable these drivers to make use of +the option. + +This is the only use of bdrv_set_read_only(), so we can make it a bit +more specific and turn it into a bdrv_apply_auto_read_only() that is +more convenient for drivers to use. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +(cherry picked from commit eaa2410f1ea864609090c0a5fda9e0ce9499da79) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 42 +++++++++++++++++++++++++++--------------- + block/bochs.c | 17 ++++++----------- + block/cloop.c | 16 +++++----------- + block/dmg.c | 16 +++++----------- + block/rbd.c | 15 ++++----------- + block/vvfat.c | 10 ++-------- + include/block/block.h | 3 ++- + 7 files changed, 51 insertions(+), 68 deletions(-) + +diff --git a/block.c b/block.c +index ff3ea92..6e3b574 100644 +--- a/block.c ++++ b/block.c +@@ -266,29 +266,41 @@ int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, + return 0; + } + +-/* TODO Remove (deprecated since 2.11) +- * Block drivers are not supposed to automatically change bs->read_only. +- * Instead, they should just check whether they can provide what the user +- * explicitly requested and error out if read-write is requested, but they can +- * only provide read-only access. */ +-int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp) ++/* ++ * 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; + +- ret = bdrv_can_set_read_only(bs, read_only, false, errp); +- if (ret < 0) { +- return ret; ++ if (!(bs->open_flags & BDRV_O_RDWR)) { ++ return 0; ++ } ++ if (!(bs->open_flags & BDRV_O_AUTO_RDONLY)) { ++ goto fail; + } + +- bs->read_only = read_only; +- +- if (read_only) { +- bs->open_flags &= ~BDRV_O_RDWR; +- } else { +- bs->open_flags |= BDRV_O_RDWR; ++ ret = bdrv_can_set_read_only(bs, true, false, NULL); ++ if (ret < 0) { ++ goto fail; + } + ++ bs->read_only = true; ++ bs->open_flags &= ~BDRV_O_RDWR; ++ + return 0; ++ ++fail: ++ error_setg(errp, "%s", errmsg ?: "Image is read-only"); ++ return -EACCES; + } + + void bdrv_get_full_backing_filename_from_filename(const char *backed, +diff --git a/block/bochs.c b/block/bochs.c +index 50c6300..22e7d44 100644 +--- a/block/bochs.c ++++ b/block/bochs.c +@@ -105,23 +105,18 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, + struct bochs_header bochs; + int ret; + ++ /* No write support yet */ ++ ret = bdrv_apply_auto_read_only(bs, NULL, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, + false, errp); + if (!bs->file) { + return -EINVAL; + } + +- if (!bdrv_is_read_only(bs)) { +- error_report("Opening bochs images without an explicit read-only=on " +- "option is deprecated. Future versions will refuse to " +- "open the image instead of automatically marking the " +- "image read-only."); +- ret = bdrv_set_read_only(bs, true, errp); /* no write support yet */ +- if (ret < 0) { +- return ret; +- } +- } +- + ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); + if (ret < 0) { + return ret; +diff --git a/block/cloop.c b/block/cloop.c +index 2be6898..df2b85f 100644 +--- a/block/cloop.c ++++ b/block/cloop.c +@@ -67,23 +67,17 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, + uint32_t offsets_size, max_compressed_block_size = 1, i; + int ret; + ++ ret = bdrv_apply_auto_read_only(bs, NULL, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, + false, errp); + if (!bs->file) { + return -EINVAL; + } + +- if (!bdrv_is_read_only(bs)) { +- error_report("Opening cloop images without an explicit read-only=on " +- "option is deprecated. Future versions will refuse to " +- "open the image instead of automatically marking the " +- "image read-only."); +- ret = bdrv_set_read_only(bs, true, errp); +- if (ret < 0) { +- return ret; +- } +- } +- + /* read header */ + ret = bdrv_pread(bs->file, 128, &s->block_size, 4); + if (ret < 0) { +diff --git a/block/dmg.c b/block/dmg.c +index c9b3c51..1d9283b 100644 +--- a/block/dmg.c ++++ b/block/dmg.c +@@ -413,23 +413,17 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, + int64_t offset; + int ret; + ++ ret = bdrv_apply_auto_read_only(bs, NULL, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, + false, errp); + if (!bs->file) { + return -EINVAL; + } + +- if (!bdrv_is_read_only(bs)) { +- error_report("Opening dmg images without an explicit read-only=on " +- "option is deprecated. Future versions will refuse to " +- "open the image instead of automatically marking the " +- "image read-only."); +- ret = bdrv_set_read_only(bs, true, errp); +- if (ret < 0) { +- return ret; +- } +- } +- + block_module_load_one("dmg-bz2"); + + s->n_chunks = 0; +diff --git a/block/rbd.c b/block/rbd.c +index 8ce68c8..91ebb8b 100644 +--- a/block/rbd.c ++++ b/block/rbd.c +@@ -772,17 +772,10 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, + /* If we are using an rbd snapshot, we must be r/o, otherwise + * leave as-is */ + if (s->snap != NULL) { +- if (!bdrv_is_read_only(bs)) { +- error_report("Opening rbd snapshots without an explicit " +- "read-only=on option is deprecated. Future versions " +- "will refuse to open the image instead of " +- "automatically marking the image read-only."); +- r = bdrv_set_read_only(bs, true, &local_err); +- if (r < 0) { +- rbd_close(s->image); +- error_propagate(errp, local_err); +- goto failed_open; +- } ++ r = bdrv_apply_auto_read_only(bs, "rbd snapshots are read-only", errp); ++ if (r < 0) { ++ rbd_close(s->image); ++ goto failed_open; + } + } + +diff --git a/block/vvfat.c b/block/vvfat.c +index 3efce9e..a5a3fb9 100644 +--- a/block/vvfat.c ++++ b/block/vvfat.c +@@ -1262,15 +1262,9 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, + "Unable to set VVFAT to 'rw' when drive is read-only"); + goto fail; + } +- } else if (!bdrv_is_read_only(bs)) { +- error_report("Opening non-rw vvfat images without an explicit " +- "read-only=on option is deprecated. Future versions " +- "will refuse to open the image instead of " +- "automatically marking the image read-only."); +- /* read only is the default for safety */ +- ret = bdrv_set_read_only(bs, true, &local_err); ++ } else { ++ ret = bdrv_apply_auto_read_only(bs, NULL, errp); + if (ret < 0) { +- error_propagate(errp, local_err); + goto fail; + } + } +diff --git a/include/block/block.h b/include/block/block.h +index 6ee8b2a..36a702c 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -433,7 +433,8 @@ int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, + bool bdrv_is_read_only(BlockDriverState *bs); + int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, + bool ignore_allow_rdw, Error **errp); +-int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp); ++int bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg, ++ Error **errp); + bool bdrv_is_writable(BlockDriverState *bs); + bool bdrv_is_sg(BlockDriverState *bs); + bool bdrv_is_inserted(BlockDriverState *bs); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Set-BDRV_REQ_WRITE_UNCHANGED-for-COR-writes.patch b/SOURCES/kvm-block-Set-BDRV_REQ_WRITE_UNCHANGED-for-COR-writes.patch new file mode 100644 index 0000000..6585adb --- /dev/null +++ b/SOURCES/kvm-block-Set-BDRV_REQ_WRITE_UNCHANGED-for-COR-writes.patch @@ -0,0 +1,52 @@ +From 5e44d51d7631be589579e5e3c4dece9f651257ab Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 16:12:07 +0200 +Subject: [PATCH 31/54] block: Set BDRV_REQ_WRITE_UNCHANGED for COR writes + +RH-Author: Max Reitz +Message-id: <20180618161212.14444-6-mreitz@redhat.com> +Patchwork-id: 80766 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 05/10] block: Set BDRV_REQ_WRITE_UNCHANGED for COR writes +Bugzilla: 1518738 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Miroslav Rezanina + +Signed-off-by: Max Reitz +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Alberto Garcia +Message-id: 20180421132929.21610-5-mreitz@redhat.com +Reviewed-by: Kevin Wolf +Signed-off-by: Max Reitz +(cherry picked from commit 7adcf59fecf3c8ce9330430187350b53f9e50cf7) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/io.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/block/io.c b/block/io.c +index 134b2a4..fada4ef 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -1115,13 +1115,15 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, + /* 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, cluster_offset, pnum, 0); ++ ret = bdrv_co_do_pwrite_zeroes(bs, cluster_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, cluster_offset, pnum, +- &local_qiov, 0); ++ &local_qiov, ++ BDRV_REQ_WRITE_UNCHANGED); + } + + if (ret < 0) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Simplify-bdrv_reopen_abort.patch b/SOURCES/kvm-block-Simplify-bdrv_reopen_abort.patch new file mode 100644 index 0000000..7d178c3 --- /dev/null +++ b/SOURCES/kvm-block-Simplify-bdrv_reopen_abort.patch @@ -0,0 +1,62 @@ +From e790236fc17d7ceb73571bdd5f62c15693e7a071 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 15 Mar 2019 18:09:59 +0100 +Subject: [PATCH 003/163] block: Simplify bdrv_reopen_abort() + +RH-Author: Kevin Wolf +Message-id: <20190315181010.14964-4-kwolf@redhat.com> +Patchwork-id: 84880 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 03/14] block: Simplify bdrv_reopen_abort() +Bugzilla: 1685989 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Alberto Garcia + +If a bdrv_reopen_multiple() call fails, then the explicit_options +QDict has to be deleted for every entry in the reopen queue. This must +happen regardless of whether that entry's bdrv_reopen_prepare() call +succeeded or not. + +This patch simplifies the cleanup code a bit. + +Signed-off-by: Alberto Garcia +Signed-off-by: Kevin Wolf +(cherry picked from commit 1bab38e7bd29347aca642c55a1de91ec6680efce) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +diff --git a/block.c b/block.c +index ccf9acb..c3148cc 100644 +--- a/block.c ++++ b/block.c +@@ -3083,9 +3083,10 @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er + + cleanup: + QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { +- if (ret && bs_entry->prepared) { +- bdrv_reopen_abort(&bs_entry->state); +- } else if (ret) { ++ if (ret) { ++ if (bs_entry->prepared) { ++ bdrv_reopen_abort(&bs_entry->state); ++ } + qobject_unref(bs_entry->state.explicit_options); + } + qobject_unref(bs_entry->state.options); +@@ -3374,8 +3375,6 @@ void bdrv_reopen_abort(BDRVReopenState *reopen_state) + drv->bdrv_reopen_abort(reopen_state); + } + +- qobject_unref(reopen_state->explicit_options); +- + bdrv_abort_perm_update(reopen_state->bs); + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Support-BDRV_REQ_WRITE_UNCHANGED-in-filters.patch b/SOURCES/kvm-block-Support-BDRV_REQ_WRITE_UNCHANGED-in-filters.patch new file mode 100644 index 0000000..691760a --- /dev/null +++ b/SOURCES/kvm-block-Support-BDRV_REQ_WRITE_UNCHANGED-in-filters.patch @@ -0,0 +1,173 @@ +From faab0229fc583ca730c337b1d37d66872f4198ec Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 16:12:09 +0200 +Subject: [PATCH 33/54] block: Support BDRV_REQ_WRITE_UNCHANGED in filters + +RH-Author: Max Reitz +Message-id: <20180618161212.14444-8-mreitz@redhat.com> +Patchwork-id: 80767 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 07/10] block: Support BDRV_REQ_WRITE_UNCHANGED in filters +Bugzilla: 1518738 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Miroslav Rezanina + +Update the rest of the filter drivers to support +BDRV_REQ_WRITE_UNCHANGED. They already forward write request flags to +their children, so we just have to announce support for it. + +This patch does not cover the replication driver because that currently +does not support flags at all, and because it just grabs the WRITE +permission for its children when it can, so we should be fine just +submitting the incoming WRITE_UNCHANGED requests as normal writes. + +It also does not cover format drivers for similar reasons. They all use +bdrv_format_default_perms() as their .bdrv_child_perm() implementation +so they just always grab the WRITE permission for their file children +whenever possible. In addition, it often would be difficult to +ascertain whether incoming unchanging writes end up as unchanging writes +in their files. So we just leave them as normal potentially changing +writes. + +Signed-off-by: Max Reitz +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Alberto Garcia +Message-id: 20180421132929.21610-7-mreitz@redhat.com +Reviewed-by: Kevin Wolf +Signed-off-by: Max Reitz +(cherry picked from commit 228345bf5db8bc97d1c64f062e138d389065d1ab) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/blkdebug.c | 9 +++++---- + block/blkreplay.c | 3 +++ + block/blkverify.c | 3 +++ + block/copy-on-read.c | 10 ++++++---- + block/mirror.c | 2 ++ + block/raw-format.c | 9 +++++---- + block/throttle.c | 6 ++++-- + 7 files changed, 28 insertions(+), 14 deletions(-) + +diff --git a/block/blkdebug.c b/block/blkdebug.c +index 053372c..526af2a 100644 +--- a/block/blkdebug.c ++++ b/block/blkdebug.c +@@ -398,10 +398,11 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, + goto out; + } + +- bs->supported_write_flags = BDRV_REQ_FUA & +- bs->file->bs->supported_write_flags; +- bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & +- bs->file->bs->supported_zero_flags; ++ 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) & ++ bs->file->bs->supported_zero_flags); + ret = -EINVAL; + + /* Set alignment overrides */ +diff --git a/block/blkreplay.c b/block/blkreplay.c +index fe5a9b4..b016dbe 100755 +--- a/block/blkreplay.c ++++ b/block/blkreplay.c +@@ -35,6 +35,9 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags, + goto fail; + } + ++ bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; ++ bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; ++ + ret = 0; + fail: + return ret; +diff --git a/block/blkverify.c b/block/blkverify.c +index 754cc9e..da97ee5 100644 +--- a/block/blkverify.c ++++ b/block/blkverify.c +@@ -141,6 +141,9 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, + goto fail; + } + ++ bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; ++ bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; ++ + ret = 0; + fail: + qemu_opts_del(opts); +diff --git a/block/copy-on-read.c b/block/copy-on-read.c +index 823ec75..6a97208 100644 +--- a/block/copy-on-read.c ++++ b/block/copy-on-read.c +@@ -33,11 +33,13 @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags, + return -EINVAL; + } + +- bs->supported_write_flags = BDRV_REQ_FUA & +- bs->file->bs->supported_write_flags; ++ bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | ++ (BDRV_REQ_FUA & ++ bs->file->bs->supported_write_flags); + +- bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & +- bs->file->bs->supported_zero_flags; ++ bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | ++ ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & ++ bs->file->bs->supported_zero_flags); + + return 0; + } +diff --git a/block/mirror.c b/block/mirror.c +index 99da9c0..003f957 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -1152,6 +1152,8 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, + mirror_top_bs->implicit = 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_set_aio_context(mirror_top_bs, bdrv_get_aio_context(bs)); + + /* bdrv_append takes ownership of the mirror_top_bs reference, need to keep +diff --git a/block/raw-format.c b/block/raw-format.c +index a378547..fe33693 100644 +--- a/block/raw-format.c ++++ b/block/raw-format.c +@@ -415,10 +415,11 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, + } + + bs->sg = bs->file->bs->sg; +- bs->supported_write_flags = BDRV_REQ_FUA & +- bs->file->bs->supported_write_flags; +- bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & +- bs->file->bs->supported_zero_flags; ++ 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) & ++ bs->file->bs->supported_zero_flags); + + if (bs->probed && !bdrv_is_read_only(bs)) { + fprintf(stderr, +diff --git a/block/throttle.c b/block/throttle.c +index 95ed06a..e298827 100644 +--- a/block/throttle.c ++++ b/block/throttle.c +@@ -81,8 +81,10 @@ static int throttle_open(BlockDriverState *bs, QDict *options, + if (!bs->file) { + return -EINVAL; + } +- bs->supported_write_flags = bs->file->bs->supported_write_flags; +- bs->supported_zero_flags = bs->file->bs->supported_zero_flags; ++ bs->supported_write_flags = bs->file->bs->supported_write_flags | ++ BDRV_REQ_WRITE_UNCHANGED; ++ bs->supported_zero_flags = bs->file->bs->supported_zero_flags | ++ BDRV_REQ_WRITE_UNCHANGED; + + return throttle_configure_tgm(bs, tgm, options, errp); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Update-flags-in-bdrv_set_read_only.patch b/SOURCES/kvm-block-Update-flags-in-bdrv_set_read_only.patch new file mode 100644 index 0000000..b238404 --- /dev/null +++ b/SOURCES/kvm-block-Update-flags-in-bdrv_set_read_only.patch @@ -0,0 +1,49 @@ +From 0e48a34df1ebd61f5fd0e914d638de2646b09635 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 23 Nov 2018 10:41:43 +0100 +Subject: [PATCH 02/34] block: Update flags in bdrv_set_read_only() + +RH-Author: Kevin Wolf +Message-id: <20181123104154.13541-2-kwolf@redhat.com> +Patchwork-id: 83110 +O-Subject: [RHEL-7.7/7.6.z qemu-kvm-rhev PATCH v2 01/12] block: Update flags in bdrv_set_read_only() +Bugzilla: 1623986 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina +RH-Acked-by: John Snow + +To fully change the read-only state of a node, we must not only change +bs->read_only, but also update bs->open_flags. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +Reviewed-by: Alberto Garcia +(cherry picked from commit eeae6a596b0efc092f5101c67683053e245e6250) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/block.c b/block.c +index fbd569c..d3ea21a 100644 +--- a/block.c ++++ b/block.c +@@ -281,6 +281,13 @@ int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp) + } + + bs->read_only = read_only; ++ ++ if (read_only) { ++ bs->open_flags &= ~BDRV_O_RDWR; ++ } else { ++ bs->open_flags |= BDRV_O_RDWR; ++ } ++ + return 0; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Use-a-single-global-AioWait.patch b/SOURCES/kvm-block-Use-a-single-global-AioWait.patch new file mode 100644 index 0000000..16cf302 --- /dev/null +++ b/SOURCES/kvm-block-Use-a-single-global-AioWait.patch @@ -0,0 +1,335 @@ +From 54d30086f066f1094871c4886f3a6dee51263d76 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 21 Sep 2018 12:46:29 +0200 +Subject: [PATCH 2/3] block: Use a single global AioWait + +RH-Author: Kevin Wolf +Message-id: <20180921124630.29036-3-kwolf@redhat.com> +Patchwork-id: 82231 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 2/3] block: Use a single global AioWait +Bugzilla: 1618584 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Paolo Bonzini +RH-Acked-by: John Snow +RH-Acked-by: Eric Blake + +When draining a block node, we recurse to its parent and for subtree +drains also to its children. A single AIO_WAIT_WHILE() is then used to +wait for bdrv_drain_poll() to become true, which depends on all of the +nodes we recursed to. However, if the respective child or parent becomes +quiescent and calls bdrv_wakeup(), only the AioWait of the child/parent +is checked, while AIO_WAIT_WHILE() depends on the AioWait of the +original node. + +Fix this by using a single AioWait for all callers of AIO_WAIT_WHILE(). + +This may mean that the draining thread gets a few more unnecessary +wakeups because an unrelated operation got completed, but we already +wake it up when something _could_ have changed rather than only if it +has certainly changed. + +Apart from that, drain is a slow path anyway. In theory it would be +possible to use wakeups more selectively and still correctly, but the +gains are likely not worth the additional complexity. In fact, this +patch is a nice simplification for some places in the code. + +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 5 ----- + block/block-backend.c | 11 ++++------- + block/io.c | 7 ++----- + blockjob.c | 13 +------------ + include/block/aio-wait.h | 11 +++++------ + include/block/block.h | 6 +----- + include/block/block_int.h | 3 --- + include/block/blockjob.h | 10 ---------- + job.c | 3 +-- + util/aio-wait.c | 11 ++++++----- + 10 files changed, 20 insertions(+), 60 deletions(-) + +diff --git a/block.c b/block.c +index e89b5e3..fbd569c 100644 +--- a/block.c ++++ b/block.c +@@ -4847,11 +4847,6 @@ AioContext *bdrv_get_aio_context(BlockDriverState *bs) + return bs ? bs->aio_context : qemu_get_aio_context(); + } + +-AioWait *bdrv_get_aio_wait(BlockDriverState *bs) +-{ +- return bs ? &bs->wait : NULL; +-} +- + void bdrv_coroutine_enter(BlockDriverState *bs, Coroutine *co) + { + aio_co_enter(bdrv_get_aio_context(bs), co); +diff --git a/block/block-backend.c b/block/block-backend.c +index 466bc27..36922d1 100644 +--- a/block/block-backend.c ++++ b/block/block-backend.c +@@ -88,7 +88,6 @@ struct BlockBackend { + * Accessed with atomic ops. + */ + unsigned int in_flight; +- AioWait wait; + }; + + typedef struct BlockBackendAIOCB { +@@ -1300,7 +1299,7 @@ static void blk_inc_in_flight(BlockBackend *blk) + static void blk_dec_in_flight(BlockBackend *blk) + { + atomic_dec(&blk->in_flight); +- aio_wait_kick(&blk->wait); ++ aio_wait_kick(); + } + + static void error_callback_bh(void *opaque) +@@ -1609,9 +1608,8 @@ void blk_drain(BlockBackend *blk) + } + + /* We may have -ENOMEDIUM completions in flight */ +- AIO_WAIT_WHILE(&blk->wait, +- blk_get_aio_context(blk), +- atomic_mb_read(&blk->in_flight) > 0); ++ AIO_WAIT_WHILE(blk_get_aio_context(blk), ++ atomic_mb_read(&blk->in_flight) > 0); + + if (bs) { + bdrv_drained_end(bs); +@@ -1630,8 +1628,7 @@ void blk_drain_all(void) + aio_context_acquire(ctx); + + /* We may have -ENOMEDIUM completions in flight */ +- AIO_WAIT_WHILE(&blk->wait, ctx, +- atomic_mb_read(&blk->in_flight) > 0); ++ AIO_WAIT_WHILE(ctx, atomic_mb_read(&blk->in_flight) > 0); + + aio_context_release(ctx); + } +diff --git a/block/io.c b/block/io.c +index 3313958..7a99f7b 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -38,8 +38,6 @@ + /* Maximum bounce buffer for copy-on-read and write zeroes, in bytes */ + #define MAX_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS) + +-static AioWait drain_all_aio_wait; +- + static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, + int64_t offset, int bytes, BdrvRequestFlags flags); + +@@ -555,7 +553,7 @@ void bdrv_drain_all_begin(void) + } + + /* Now poll the in-flight requests */ +- AIO_WAIT_WHILE(&drain_all_aio_wait, NULL, bdrv_drain_all_poll()); ++ AIO_WAIT_WHILE(NULL, bdrv_drain_all_poll()); + + while ((bs = bdrv_next_all_states(bs))) { + bdrv_drain_assert_idle(bs); +@@ -709,8 +707,7 @@ void bdrv_inc_in_flight(BlockDriverState *bs) + + void bdrv_wakeup(BlockDriverState *bs) + { +- aio_wait_kick(bdrv_get_aio_wait(bs)); +- aio_wait_kick(&drain_all_aio_wait); ++ aio_wait_kick(); + } + + void bdrv_dec_in_flight(BlockDriverState *bs) +diff --git a/blockjob.c b/blockjob.c +index 617d86f..06f2429 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -221,20 +221,9 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, + return 0; + } + +-void block_job_wakeup_all_bdrv(BlockJob *job) +-{ +- GSList *l; +- +- for (l = job->nodes; l; l = l->next) { +- BdrvChild *c = l->data; +- bdrv_wakeup(c->bs); +- } +-} +- + static void block_job_on_idle(Notifier *n, void *opaque) + { +- BlockJob *job = opaque; +- block_job_wakeup_all_bdrv(job); ++ aio_wait_kick(); + } + + bool block_job_is_internal(BlockJob *job) +diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h +index 600fad1..46f86f9 100644 +--- a/include/block/aio-wait.h ++++ b/include/block/aio-wait.h +@@ -54,9 +54,10 @@ typedef struct { + unsigned num_waiters; + } AioWait; + ++extern AioWait global_aio_wait; ++ + /** + * AIO_WAIT_WHILE: +- * @wait: the aio wait object + * @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 +@@ -72,9 +73,9 @@ typedef struct { + * wait on conditions between two IOThreads since that could lead to deadlock, + * go via the main loop instead. + */ +-#define AIO_WAIT_WHILE(wait, ctx, cond) ({ \ ++#define AIO_WAIT_WHILE(ctx, cond) ({ \ + bool waited_ = false; \ +- AioWait *wait_ = (wait); \ ++ AioWait *wait_ = &global_aio_wait; \ + AioContext *ctx_ = (ctx); \ + /* Increment wait_->num_waiters before evaluating cond. */ \ + atomic_inc(&wait_->num_waiters); \ +@@ -102,14 +103,12 @@ typedef struct { + + /** + * aio_wait_kick: +- * @wait: the aio wait object that should re-evaluate its condition +- * + * 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(AioWait *wait); ++void aio_wait_kick(void); + + /** + * aio_wait_bh_oneshot: +diff --git a/include/block/block.h b/include/block/block.h +index 356712c..8e78daf 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -406,13 +406,9 @@ void bdrv_drain_all_begin(void); + void bdrv_drain_all_end(void); + void bdrv_drain_all(void); + +-/* Returns NULL when bs == NULL */ +-AioWait *bdrv_get_aio_wait(BlockDriverState *bs); +- + #define BDRV_POLL_WHILE(bs, cond) ({ \ + BlockDriverState *bs_ = (bs); \ +- AIO_WAIT_WHILE(bdrv_get_aio_wait(bs_), \ +- bdrv_get_aio_context(bs_), \ ++ AIO_WAIT_WHILE(bdrv_get_aio_context(bs_), \ + cond); }) + + int bdrv_pdiscard(BlockDriverState *bs, int64_t offset, int bytes); +diff --git a/include/block/block_int.h b/include/block/block_int.h +index b7806e3..ff923b7 100644 +--- a/include/block/block_int.h ++++ b/include/block/block_int.h +@@ -782,9 +782,6 @@ struct BlockDriverState { + unsigned int in_flight; + unsigned int serialising_in_flight; + +- /* Kicked to signal main loop when a request completes. */ +- AioWait wait; +- + /* counter for nested bdrv_io_plug. + * Accessed with atomic ops. + */ +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 2290bbb..ede0bd8 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -122,16 +122,6 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, + void block_job_remove_all_bdrv(BlockJob *job); + + /** +- * block_job_wakeup_all_bdrv: +- * @job: The block job +- * +- * Calls bdrv_wakeup() for all BlockDriverStates that have been added to the +- * job. This function is to be called whenever child_job_drained_poll() would +- * go from true to false to notify waiting drain requests. +- */ +-void block_job_wakeup_all_bdrv(BlockJob *job); +- +-/** + * block_job_set_speed: + * @job: The job to set the speed for. + * @speed: The new value +diff --git a/job.c b/job.c +index 5b53e43..3a7db59 100644 +--- a/job.c ++++ b/job.c +@@ -973,7 +973,6 @@ void job_complete(Job *job, Error **errp) + int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) + { + Error *local_err = NULL; +- AioWait dummy_wait = {}; + int ret; + + job_ref(job); +@@ -987,7 +986,7 @@ int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) + return -EBUSY; + } + +- AIO_WAIT_WHILE(&dummy_wait, job->aio_context, ++ AIO_WAIT_WHILE(job->aio_context, + (job_drain(job), !job_is_completed(job))); + + ret = (job_is_cancelled(job) && job->ret == 0) ? -ECANCELED : job->ret; +diff --git a/util/aio-wait.c b/util/aio-wait.c +index b8a8f86..b487749 100644 +--- a/util/aio-wait.c ++++ b/util/aio-wait.c +@@ -26,21 +26,22 @@ + #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(AioWait *wait) ++void aio_wait_kick(void) + { + /* The barrier (or an atomic op) is in the caller. */ +- if (atomic_read(&wait->num_waiters)) { ++ if (atomic_read(&global_aio_wait.num_waiters)) { + aio_bh_schedule_oneshot(qemu_get_aio_context(), dummy_bh_cb, NULL); + } + } + + typedef struct { +- AioWait wait; + bool done; + QEMUBHFunc *cb; + void *opaque; +@@ -54,7 +55,7 @@ static void aio_wait_bh(void *opaque) + data->cb(data->opaque); + + data->done = true; +- aio_wait_kick(&data->wait); ++ aio_wait_kick(); + } + + void aio_wait_bh_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque) +@@ -67,5 +68,5 @@ void aio_wait_bh_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque) + assert(qemu_get_current_aio_context() == qemu_get_aio_context()); + + aio_bh_schedule_oneshot(ctx, aio_wait_bh, &data); +- AIO_WAIT_WHILE(&data.wait, ctx, !data.done); ++ AIO_WAIT_WHILE(ctx, !data.done); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Use-bdrv_do_drain_begin-end-in-bdrv_drain_all.patch b/SOURCES/kvm-block-Use-bdrv_do_drain_begin-end-in-bdrv_drain_all.patch new file mode 100644 index 0000000..ac2c3b6 --- /dev/null +++ b/SOURCES/kvm-block-Use-bdrv_do_drain_begin-end-in-bdrv_drain_all.patch @@ -0,0 +1,113 @@ +From 21362b70047568949d14388b254e4b87674a2730 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:00 +0200 +Subject: [PATCH 09/49] block: Use bdrv_do_drain_begin/end in bdrv_drain_all() + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-3-kwolf@redhat.com> +Patchwork-id: 82154 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 02/42] block: Use bdrv_do_drain_begin/end in bdrv_drain_all() +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +bdrv_do_drain_begin/end() implement already everything that +bdrv_drain_all_begin/end() need and currently still do manually: Disable +external events, call parent drain callbacks, call block driver +callbacks. + +It also does two more things: + +The first is incrementing bs->quiesce_counter. bdrv_drain_all() already +stood out in the test case by behaving different from the other drain +variants. Adding this is not only safe, but in fact a bug fix. + +The second is calling bdrv_drain_recurse(). We already do that later in +the same function in a loop, so basically doing an early first iteration +doesn't hurt. + +Signed-off-by: Kevin Wolf +Reviewed-by: Stefan Hajnoczi +(cherry picked from commit 79ab8b21dc19c08adc407504e456ff64b9dacb66) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/io.c | 10 ++-------- + tests/test-bdrv-drain.c | 14 ++++---------- + 2 files changed, 6 insertions(+), 18 deletions(-) + +diff --git a/block/io.c b/block/io.c +index 7e0a169..230b551 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -412,11 +412,8 @@ void bdrv_drain_all_begin(void) + for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { + AioContext *aio_context = bdrv_get_aio_context(bs); + +- /* Stop things in parent-to-child order */ + aio_context_acquire(aio_context); +- aio_disable_external(aio_context); +- bdrv_parent_drained_begin(bs, NULL); +- bdrv_drain_invoke(bs, true, true); ++ bdrv_do_drained_begin(bs, true, NULL); + aio_context_release(aio_context); + + if (!g_slist_find(aio_ctxs, aio_context)) { +@@ -457,11 +454,8 @@ void bdrv_drain_all_end(void) + for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { + AioContext *aio_context = bdrv_get_aio_context(bs); + +- /* Re-enable things in child-to-parent order */ + aio_context_acquire(aio_context); +- bdrv_drain_invoke(bs, false, true); +- bdrv_parent_drained_end(bs, NULL); +- aio_enable_external(aio_context); ++ bdrv_do_drained_end(bs, true, NULL); + aio_context_release(aio_context); + } + } +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index dee0a10..f1276a1 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -276,8 +276,7 @@ static void test_quiesce_common(enum drain_type drain_type, bool recursive) + + static void test_quiesce_drain_all(void) + { +- // XXX drain_all doesn't quiesce +- //test_quiesce_common(BDRV_DRAIN_ALL, true); ++ test_quiesce_common(BDRV_DRAIN_ALL, true); + } + + static void test_quiesce_drain(void) +@@ -319,12 +318,7 @@ static void test_nested(void) + + for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) { + for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) { +- /* XXX bdrv_drain_all() doesn't increase the quiesce_counter */ +- int bs_quiesce = (outer != BDRV_DRAIN_ALL) + +- (inner != BDRV_DRAIN_ALL); +- int backing_quiesce = (outer == BDRV_SUBTREE_DRAIN) + +- (inner == BDRV_SUBTREE_DRAIN); +- int backing_cb_cnt = (outer != BDRV_DRAIN) + ++ int backing_quiesce = (outer != BDRV_DRAIN) + + (inner != BDRV_DRAIN); + + g_assert_cmpint(bs->quiesce_counter, ==, 0); +@@ -335,10 +329,10 @@ static void test_nested(void) + do_drain_begin(outer, bs); + do_drain_begin(inner, bs); + +- g_assert_cmpint(bs->quiesce_counter, ==, bs_quiesce); ++ g_assert_cmpint(bs->quiesce_counter, ==, 2); + g_assert_cmpint(backing->quiesce_counter, ==, backing_quiesce); + g_assert_cmpint(s->drain_count, ==, 2); +- g_assert_cmpint(backing_s->drain_count, ==, backing_cb_cnt); ++ g_assert_cmpint(backing_s->drain_count, ==, backing_quiesce); + + do_drain_end(inner, bs); + do_drain_end(outer, bs); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Use-normal-drain-for-bdrv_set_aio_context.patch b/SOURCES/kvm-block-Use-normal-drain-for-bdrv_set_aio_context.patch new file mode 100644 index 0000000..d3ac85f --- /dev/null +++ b/SOURCES/kvm-block-Use-normal-drain-for-bdrv_set_aio_context.patch @@ -0,0 +1,76 @@ +From 1d47b2827170416abf387add121a3871194ddc40 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 1 Mar 2019 14:27:47 +0100 +Subject: [PATCH 5/9] block: Use normal drain for bdrv_set_aio_context() + +RH-Author: Kevin Wolf +Message-id: <20190301142747.12251-6-kwolf@redhat.com> +Patchwork-id: 84765 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 5/5] block: Use normal drain for bdrv_set_aio_context() +Bugzilla: 1671173 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Paolo Bonzini +RH-Acked-by: John Snow + +Now that bdrv_set_aio_context() works inside drained sections, it can +also use the real drain function instead of open coding something +similar. + +Signed-off-by: Kevin Wolf +(cherry picked from commit d70d595429ecd9ac4917e53453dd8979db8e5ffd) + +RHEL: This conflicts because we didn't backport the removal of the +polling loop. The conflict is resolved so that the polling loop moves to +above the drain and any requests a BH would spawn would still be +correctly drained afterwards. The changed order alone would have +compensated for the virtio-blk bug and it potentially compensates for +other bugs, too (we know of bugs in the NBD client at least), so leaving +the polling loop in, with the new ordering, feels like the safe way for +a downstream backport. + +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 11 +++++------ + 1 file changed, 5 insertions(+), 6 deletions(-) + +diff --git a/block.c b/block.c +index 7cd3651..c47e5b0 100644 +--- a/block.c ++++ b/block.c +@@ -4995,18 +4995,18 @@ void bdrv_attach_aio_context(BlockDriverState *bs, + bs->walking_aio_notifiers = false; + } + ++/* The caller must own the AioContext lock for the old AioContext of bs, but it ++ * must not own the AioContext lock for new_context (unless new_context is ++ * the same as the current context of bs). */ + void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context) + { + AioContext *ctx = bdrv_get_aio_context(bs); + +- aio_disable_external(ctx); +- bdrv_parent_drained_begin(bs, NULL, false); +- bdrv_drain(bs); /* ensure there are no in-flight requests */ +- + while (aio_poll(ctx, false)) { + /* wait for all bottom halves to execute */ + } + ++ bdrv_drained_begin(bs); + bdrv_detach_aio_context(bs); + + /* This function executes in the old AioContext so acquire the new one in +@@ -5014,8 +5014,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context) + */ + aio_context_acquire(new_context); + bdrv_attach_aio_context(bs, new_context); +- bdrv_parent_drained_end(bs, NULL, false); +- aio_enable_external(ctx); ++ bdrv_drained_end(bs); + aio_context_release(new_context); + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-Use-tracked-request-for-truncate.patch b/SOURCES/kvm-block-Use-tracked-request-for-truncate.patch new file mode 100644 index 0000000..f9023e0 --- /dev/null +++ b/SOURCES/kvm-block-Use-tracked-request-for-truncate.patch @@ -0,0 +1,104 @@ +From 309929687960d52f77df69a745bd1c351023febb Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Thu, 12 Jul 2018 14:42:57 +0200 +Subject: [PATCH 38/89] block: Use tracked request for truncate + +RH-Author: Kevin Wolf +Message-id: <20180712144258.17303-6-kwolf@redhat.com> +Patchwork-id: 81326 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 5/6] block: Use tracked request for truncate +Bugzilla: 1595173 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow + +When growing an image, block drivers (especially protocol drivers) may +initialise the newly added area. I/O requests to the same area need to +wait for this initialisation to be completed so that data writes don't +get overwritten and reads don't read uninitialised data. + +To avoid overhead in the fast I/O path by adding new locking in the +protocol drivers and to restrict the impact to requests that actually +touch the new area, reuse the existing tracked request infrastructure in +block/io.c and mark all discard requests as serialising. + +With this change, it is safe for protocol drivers to make +.bdrv_co_truncate actually asynchronous. + +Signed-off-by: Kevin Wolf +Reviewed-by: Stefan Hajnoczi +(cherry picked from commit 1bc5f09f2e1b2be8f6f737b8d5352b438fc41492) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/io.c | 25 +++++++++++++++++++++++++ + include/block/block_int.h | 1 + + 2 files changed, 26 insertions(+) + +diff --git a/block/io.c b/block/io.c +index 32a82e3..ad8afc0 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -2948,6 +2948,8 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, + { + BlockDriverState *bs = child->bs; + BlockDriver *drv = bs->drv; ++ BdrvTrackedRequest req; ++ int64_t old_size, new_bytes; + int ret; + + assert(child->perm & BLK_PERM_RESIZE); +@@ -2962,7 +2964,28 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, + return -EINVAL; + } + ++ old_size = bdrv_getlength(bs); ++ if (old_size < 0) { ++ error_setg_errno(errp, -old_size, "Failed to get old image size"); ++ return old_size; ++ } ++ ++ 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, 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) { ++ mark_request_serialising(&req, 1); ++ wait_serialising_requests(&req); ++ } + + if (!drv->bdrv_co_truncate) { + if (bs->file && drv->is_filter) { +@@ -2996,7 +3019,9 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, + atomic_inc(&bs->write_gen); + + out: ++ tracked_request_end(&req); + bdrv_dec_in_flight(bs); ++ + return ret; + } + +diff --git a/include/block/block_int.h b/include/block/block_int.h +index 6a844ec..27e168f 100644 +--- a/include/block/block_int.h ++++ b/include/block/block_int.h +@@ -63,6 +63,7 @@ enum BdrvTrackedRequestType { + BDRV_TRACKED_READ, + BDRV_TRACKED_WRITE, + BDRV_TRACKED_DISCARD, ++ BDRV_TRACKED_TRUNCATE, + }; + + typedef struct BdrvTrackedRequest { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-add-BDRV_REQ_SERIALISING-flag.patch b/SOURCES/kvm-block-add-BDRV_REQ_SERIALISING-flag.patch new file mode 100644 index 0000000..3ebed8a --- /dev/null +++ b/SOURCES/kvm-block-add-BDRV_REQ_SERIALISING-flag.patch @@ -0,0 +1,137 @@ +From 0ea414a81a3e1372257a54c8756a643b91ae773a Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:54:57 +0200 +Subject: [PATCH 72/89] block: add BDRV_REQ_SERIALISING flag + +RH-Author: John Snow +Message-id: <20180718225511.14878-22-jsnow@redhat.com> +Patchwork-id: 81421 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 21/35] block: add BDRV_REQ_SERIALISING flag +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +Serialized writes should be used in copy-on-write of backup(sync=none) +for image fleecing scheme. + +We need to change an assert in bdrv_aligned_pwritev, added in +28de2dcd88de. The assert may fail now, because call to +wait_serialising_requests here may become first call to it for this +request with serializing flag set. It occurs if the request is aligned +(otherwise, we should already set serializing flag before calling +bdrv_aligned_pwritev and correspondingly waited for all intersecting +requests). However, for aligned requests, we should not care about +outdating of previously read data, as there no such data. Therefore, +let's just update an assert to not care about aligned requests. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: Fam Zheng +Signed-off-by: Kevin Wolf +(cherry picked from commit 09d2f948462f4979d18f573a0734d1daae8e67a9) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/io.c | 28 +++++++++++++++++++++++++++- + include/block/block.h | 14 +++++++++++++- + 2 files changed, 40 insertions(+), 2 deletions(-) + +diff --git a/block/io.c b/block/io.c +index 2d04289..bb617de 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -535,6 +535,18 @@ static void mark_request_serialising(BdrvTrackedRequest *req, uint64_t align) + req->overlap_bytes = MAX(req->overlap_bytes, overlap_bytes); + } + ++static bool is_request_serialising_and_aligned(BdrvTrackedRequest *req) ++{ ++ /* ++ * If the request is serialising, overlap_offset and overlap_bytes are set, ++ * so we can check if the request is aligned. Otherwise, don't care and ++ * return false. ++ */ ++ ++ return req->serialising && (req->offset == req->overlap_offset) && ++ (req->bytes == req->overlap_bytes); ++} ++ + /** + * Round a region to cluster boundaries + */ +@@ -1206,6 +1218,9 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child, + mark_request_serialising(req, bdrv_get_cluster_size(bs)); + } + ++ /* BDRV_REQ_SERIALISING is only for write operation */ ++ assert(!(flags & BDRV_REQ_SERIALISING)); ++ + if (!(flags & BDRV_REQ_NO_SERIALISING)) { + wait_serialising_requests(req); + } +@@ -1507,8 +1522,14 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, + + /* BDRV_REQ_NO_SERIALISING is only for read operation */ + assert(!(flags & BDRV_REQ_NO_SERIALISING)); ++ ++ if (flags & BDRV_REQ_SERIALISING) { ++ mark_request_serialising(req, bdrv_get_cluster_size(bs)); ++ } ++ + waited = wait_serialising_requests(req); +- assert(!waited || !req->serialising); ++ assert(!waited || !req->serialising || ++ is_request_serialising_and_aligned(req)); + assert(req->overlap_offset <= offset); + assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); + if (flags & BDRV_REQ_WRITE_UNCHANGED) { +@@ -2881,6 +2902,8 @@ static int coroutine_fn bdrv_co_copy_range_internal( + 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)); + if (!(read_flags & BDRV_REQ_NO_SERIALISING)) { + wait_serialising_requests(&req); + } +@@ -2900,6 +2923,9 @@ static int coroutine_fn bdrv_co_copy_range_internal( + + /* BDRV_REQ_NO_SERIALISING is only for read operation */ + assert(!(write_flags & BDRV_REQ_NO_SERIALISING)); ++ if (write_flags & BDRV_REQ_SERIALISING) { ++ mark_request_serialising(&req, bdrv_get_cluster_size(dst->bs)); ++ } + wait_serialising_requests(&req); + + ret = dst->bs->drv->bdrv_co_copy_range_to(dst->bs, +diff --git a/include/block/block.h b/include/block/block.h +index 409db21..8f87eea 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -70,8 +70,20 @@ typedef enum { + * content. */ + BDRV_REQ_WRITE_UNCHANGED = 0x40, + ++ /* ++ * BDRV_REQ_SERIALISING forces request serialisation for writes. ++ * It is used to ensure that writes to the backing file of a backup process ++ * target cannot race with a read of the backup target that defers to the ++ * backing file. ++ * ++ * Note, that BDRV_REQ_SERIALISING is _not_ opposite in meaning to ++ * BDRV_REQ_NO_SERIALISING. A more descriptive name for the latter might be ++ * _DO_NOT_WAIT_FOR_SERIALISING, except that is too long. ++ */ ++ BDRV_REQ_SERIALISING = 0x80, ++ + /* Mask of valid flags */ +- BDRV_REQ_MASK = 0x7f, ++ BDRV_REQ_MASK = 0xff, + } BdrvRequestFlags; + + typedef struct BlockSizes { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-backend-Add-.drained_poll-callback.patch b/SOURCES/kvm-block-backend-Add-.drained_poll-callback.patch new file mode 100644 index 0000000..99aa032 --- /dev/null +++ b/SOURCES/kvm-block-backend-Add-.drained_poll-callback.patch @@ -0,0 +1,64 @@ +From 79543d4ff66ab2c8f7050e5a5749ea11d1f0c2c0 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:32 +0200 +Subject: [PATCH 41/49] block-backend: Add .drained_poll callback + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-35-kwolf@redhat.com> +Patchwork-id: 82190 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 34/42] block-backend: Add .drained_poll callback +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +A bdrv_drain operation must ensure that all parents are quiesced, this +includes BlockBackends. Otherwise, callbacks called by requests that are +completed on the BDS layer, but not quite yet on the BlockBackend layer +could still create new requests. + +Signed-off-by: Kevin Wolf +Reviewed-by: Fam Zheng +Reviewed-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/block-backend.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/block/block-backend.c b/block/block-backend.c +index 2262506..ea85770 100644 +--- a/block/block-backend.c ++++ b/block/block-backend.c +@@ -121,6 +121,7 @@ static void blk_root_inherit_options(int *child_flags, QDict *child_options, + 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); +@@ -294,6 +295,7 @@ static const BdrvChildRole child_root = { + .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, +@@ -2217,6 +2219,13 @@ static void blk_root_drained_begin(BdrvChild *child) + } + } + ++static bool blk_root_drained_poll(BdrvChild *child) ++{ ++ BlockBackend *blk = child->opaque; ++ assert(blk->quiesce_counter); ++ return !!blk->in_flight; ++} ++ + static void blk_root_drained_end(BdrvChild *child) + { + BlockBackend *blk = child->opaque; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-backend-Add-blk_co_copy_range.patch b/SOURCES/kvm-block-backend-Add-blk_co_copy_range.patch new file mode 100644 index 0000000..97bfced --- /dev/null +++ b/SOURCES/kvm-block-backend-Add-blk_co_copy_range.patch @@ -0,0 +1,70 @@ +From 6f8e77a19dc072d428624f521e35a82a80893d25 Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Fri, 29 Jun 2018 06:11:49 +0200 +Subject: [PATCH 45/57] block-backend: Add blk_co_copy_range + +RH-Author: Fam Zheng +Message-id: <20180629061153.12687-10-famz@redhat.com> +Patchwork-id: 81161 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 09/13] block-backend: Add blk_co_copy_range +Bugzilla: 1482537 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Kevin Wolf + +It's a BlockBackend wrapper of the BDS interface. + +Signed-off-by: Fam Zheng +Reviewed-by: Stefan Hajnoczi +Message-id: 20180601092648.24614-10-famz@redhat.com +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit b5679fa49c9a70efa7bf01f6efad1a65e2349a0b) +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + block/block-backend.c | 18 ++++++++++++++++++ + include/sysemu/block-backend.h | 4 ++++ + 2 files changed, 22 insertions(+) + +diff --git a/block/block-backend.c b/block/block-backend.c +index fd342db..56ae535 100644 +--- a/block/block-backend.c ++++ b/block/block-backend.c +@@ -2236,3 +2236,21 @@ void blk_unregister_buf(BlockBackend *blk, void *host) + { + bdrv_unregister_buf(blk_bs(blk), host); + } ++ ++int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, ++ BlockBackend *blk_out, int64_t off_out, ++ int bytes, BdrvRequestFlags flags) ++{ ++ int r; ++ 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, flags); ++} +diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h +index 92ab624..8d03d49 100644 +--- a/include/sysemu/block-backend.h ++++ b/include/sysemu/block-backend.h +@@ -232,4 +232,8 @@ void blk_set_force_allow_inactivate(BlockBackend *blk); + void blk_register_buf(BlockBackend *blk, void *host, size_t size); + void blk_unregister_buf(BlockBackend *blk, void *host); + ++int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, ++ BlockBackend *blk_out, int64_t off_out, ++ int bytes, BdrvRequestFlags flags); ++ + #endif +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-backend-Decrease-in_flight-only-after-callback.patch b/SOURCES/kvm-block-backend-Decrease-in_flight-only-after-callback.patch new file mode 100644 index 0000000..6bc4bc9 --- /dev/null +++ b/SOURCES/kvm-block-backend-Decrease-in_flight-only-after-callback.patch @@ -0,0 +1,53 @@ +From 68265b080cec07c5a6ba6b1c6fbd867f4d9cf9ba Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:34 +0200 +Subject: [PATCH 43/49] block-backend: Decrease in_flight only after callback + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-37-kwolf@redhat.com> +Patchwork-id: 82187 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 36/42] block-backend: Decrease in_flight only after callback +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Request callbacks can do pretty much anything, including operations that +will yield from the coroutine (such as draining the backend). In that +case, a decreased in_flight would be visible to other code and could +lead to a drain completing while the callback hasn't actually completed +yet. + +Signed-off-by: Kevin Wolf +Reviewed-by: Fam Zheng +Reviewed-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/block-backend.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/block/block-backend.c b/block/block-backend.c +index 1adf76b..466bc27 100644 +--- a/block/block-backend.c ++++ b/block/block-backend.c +@@ -1341,8 +1341,16 @@ static const AIOCBInfo blk_aio_em_aiocb_info = { + static void blk_aio_complete(BlkAioEmAIOCB *acb) + { + if (acb->has_returned) { +- blk_dec_in_flight(acb->rwco.blk); ++ if (qemu_get_current_aio_context() == qemu_get_aio_context()) { ++ /* If we are in the main thread, the callback is allowed to unref ++ * the BlockBackend, so we have to hold an additional reference */ ++ blk_ref(acb->rwco.blk); ++ } + acb->common.cb(acb->common.opaque, acb->rwco.ret); ++ blk_dec_in_flight(acb->rwco.blk); ++ if (qemu_get_current_aio_context() == qemu_get_aio_context()) { ++ blk_unref(acb->rwco.blk); ++ } + qemu_aio_unref(acb); + } + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-backend-Fix-potential-double-blk_delete.patch b/SOURCES/kvm-block-backend-Fix-potential-double-blk_delete.patch new file mode 100644 index 0000000..effd0f8 --- /dev/null +++ b/SOURCES/kvm-block-backend-Fix-potential-double-blk_delete.patch @@ -0,0 +1,65 @@ +From f006159bba058c873bf8c1a3684ec72f338f2a2c Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:33 +0200 +Subject: [PATCH 42/49] block-backend: Fix potential double blk_delete() + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-36-kwolf@redhat.com> +Patchwork-id: 82186 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 35/42] block-backend: Fix potential double blk_delete() +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +blk_unref() first decreases the refcount of the BlockBackend and calls +blk_delete() if the refcount reaches zero. Requests can still be in +flight at this point, they are only drained during blk_delete(): + +At this point, arbitrary callbacks can run. If any callback takes a +temporary BlockBackend reference, it will first increase the refcount to +1 and then decrease it to 0 again, triggering another blk_delete(). This +will cause a use-after-free crash in the outer blk_delete(). + +Fix it by draining the BlockBackend before decreasing to refcount to 0. +Assert in blk_ref() that it never takes the first refcount (which would +mean that the BlockBackend is already being deleted). + +Signed-off-by: Kevin Wolf +Reviewed-by: Fam Zheng +Reviewed-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/block-backend.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/block/block-backend.c b/block/block-backend.c +index ea85770..1adf76b 100644 +--- a/block/block-backend.c ++++ b/block/block-backend.c +@@ -436,6 +436,7 @@ int blk_get_refcnt(BlockBackend *blk) + */ + void blk_ref(BlockBackend *blk) + { ++ assert(blk->refcnt > 0); + blk->refcnt++; + } + +@@ -448,7 +449,13 @@ void blk_unref(BlockBackend *blk) + { + if (blk) { + assert(blk->refcnt > 0); +- if (!--blk->refcnt) { ++ 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); + } + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-backend-Make-blk_inc-dec_in_flight-public.patch b/SOURCES/kvm-block-backend-Make-blk_inc-dec_in_flight-public.patch new file mode 100644 index 0000000..c7c5800 --- /dev/null +++ b/SOURCES/kvm-block-backend-Make-blk_inc-dec_in_flight-public.patch @@ -0,0 +1,62 @@ +From fe68ddea8e2283fad78a3596ee0682d49294987f Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 1 Mar 2019 14:27:43 +0100 +Subject: [PATCH 1/9] block-backend: Make blk_inc/dec_in_flight public + +RH-Author: Kevin Wolf +Message-id: <20190301142747.12251-2-kwolf@redhat.com> +Patchwork-id: 84764 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/5] block-backend: Make blk_inc/dec_in_flight public +Bugzilla: 1671173 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Paolo Bonzini +RH-Acked-by: John Snow + +For some users of BlockBackends, just increasing the in_flight counter +is easier than implementing separate handlers in BlockDevOps. Make the +helper functions for this public. + +Signed-off-by: Kevin Wolf +(cherry picked from commit c90e2a9cfd94bd02d92c53b97f04fd595001de7e) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/block-backend.c | 4 ++-- + include/sysemu/block-backend.h | 2 ++ + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/block/block-backend.c b/block/block-backend.c +index ddec869..52eebeb 100644 +--- a/block/block-backend.c ++++ b/block/block-backend.c +@@ -1294,12 +1294,12 @@ int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags) + return bdrv_make_zero(blk->root, flags); + } + +-static void blk_inc_in_flight(BlockBackend *blk) ++void blk_inc_in_flight(BlockBackend *blk) + { + atomic_inc(&blk->in_flight); + } + +-static void blk_dec_in_flight(BlockBackend *blk) ++void blk_dec_in_flight(BlockBackend *blk) + { + atomic_dec(&blk->in_flight); + aio_wait_kick(); +diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h +index 830d873..6b6d882 100644 +--- a/include/sysemu/block-backend.h ++++ b/include/sysemu/block-backend.h +@@ -157,6 +157,8 @@ int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes); + int blk_co_flush(BlockBackend *blk); + int blk_flush(BlockBackend *blk); + int blk_commit_all(void); ++void blk_inc_in_flight(BlockBackend *blk); ++void blk_dec_in_flight(BlockBackend *blk); + void blk_drain(BlockBackend *blk); + void blk_drain_all(void); + void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-backend-Set-werror-rerror-defaults-in-blk_new.patch b/SOURCES/kvm-block-backend-Set-werror-rerror-defaults-in-blk_new.patch new file mode 100644 index 0000000..7654783 --- /dev/null +++ b/SOURCES/kvm-block-backend-Set-werror-rerror-defaults-in-blk_new.patch @@ -0,0 +1,69 @@ +From db87a7100f44bbaeef9860cf3c313642586bf030 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 19 Feb 2019 14:35:27 +0100 +Subject: [PATCH 08/23] block-backend: Set werror/rerror defaults in blk_new() + +RH-Author: Kevin Wolf +Message-id: <20190219143527.26763-2-kwolf@redhat.com> +Patchwork-id: 84537 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/1] block-backend: Set werror/rerror defaults in blk_new() +Bugzilla: 1631615 +RH-Acked-by: Markus Armbruster +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz + +Currently, the default values for werror and rerror have to be set +explicitly with blk_set_on_error() by the callers of blk_new(). The only +caller actually doing this is blockdev_init(), which is called for +BlockBackends created using -drive. + +In particular, anonymous BlockBackends created with +-device ...,drive= didn't get the correct default set and +instead defaulted to the integer value 0 (= BLOCKDEV_ON_ERROR_REPORT). +This is the intended default for rerror anyway, but the default for +werror should be BLOCKDEV_ON_ERROR_ENOSPC. + +Set the defaults in blk_new() instead so that they apply no matter what +way the BlockBackend was created. + +Cc: qemu-stable@nongnu.org +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +Reviewed-by: Fam Zheng +(cherry picked from commit cb53460b708db3617ab73248374d071d5552c263) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/block-backend.c | 3 +++ + tests/qemu-iotests/067.out | 1 + + 2 files changed, 4 insertions(+) + +diff --git a/block/block-backend.c b/block/block-backend.c +index 36922d1..ddec869 100644 +--- a/block/block-backend.c ++++ b/block/block-backend.c +@@ -325,6 +325,9 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_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); + + notifier_list_init(&blk->remove_bs_notifiers); +diff --git a/tests/qemu-iotests/067.out b/tests/qemu-iotests/067.out +index 2e71cff..b10c71d 100644 +--- a/tests/qemu-iotests/067.out ++++ b/tests/qemu-iotests/067.out +@@ -385,6 +385,7 @@ Testing: -device virtio-scsi -device scsi-cd,id=cd0 + { + "return": [ + { ++ "io-status": "ok", + "device": "", + "locked": false, + "removable": true, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-backup-disable-copy-offloading-for-backup.patch b/SOURCES/kvm-block-backup-disable-copy-offloading-for-backup.patch new file mode 100644 index 0000000..64a5fa2 --- /dev/null +++ b/SOURCES/kvm-block-backup-disable-copy-offloading-for-backup.patch @@ -0,0 +1,43 @@ +From 0dff4725df2174ed2715d05793b40663a981633d Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:54:53 +0200 +Subject: [PATCH 68/89] block/backup: disable copy offloading for backup + +RH-Author: John Snow +Message-id: <20180718225511.14878-18-jsnow@redhat.com> +Patchwork-id: 81417 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 17/35] block/backup: disable copy offloading for backup +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +Downstream only for 2.12.0; there are several upstream fixes that fix +the copy_range support for block/backup that are in 3.0 that we're not +prepared to backport to 2.12.0. + +For now, then, hardcode block/backup to use the old bounce buffer method +that works without additional caveats. + +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/block/backup.c b/block/backup.c +index d26eeb5..adb3cbd 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -727,7 +727,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, + } else { + job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size); + } +- job->use_copy_range = true; ++ job->use_copy_range = false; + job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk), + blk_get_max_transfer(job->target)); + job->copy_range_size = MAX(job->cluster_size, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-backup-fix-fleecing-scheme-use-serialized-writ.patch b/SOURCES/kvm-block-backup-fix-fleecing-scheme-use-serialized-writ.patch new file mode 100644 index 0000000..c4392ca --- /dev/null +++ b/SOURCES/kvm-block-backup-fix-fleecing-scheme-use-serialized-writ.patch @@ -0,0 +1,141 @@ +From 5627c9fb0b86809d42914f1beef9b68226141d4b Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:54:58 +0200 +Subject: [PATCH 73/89] block/backup: fix fleecing scheme: use serialized + writes + +RH-Author: John Snow +Message-id: <20180718225511.14878-23-jsnow@redhat.com> +Patchwork-id: 81396 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 22/35] block/backup: fix fleecing scheme: use serialized writes +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +Fleecing scheme works as follows: we want a kind of temporary snapshot +of active drive A. We create temporary image B, with B->backing = A. +Then we start backup(sync=none) from A to B. From this point, B reads +as point-in-time snapshot of A (A continues to be active drive, +accepting guest IO). + +This scheme needs some additional synchronization between reads from B +and backup COW operations, otherwise, the following situation is +theoretically possible: + +(assume B is qcow2, client is NBD client, reading from B) + +1. client starts reading and take qcow2 mutex in qcow2_co_preadv, and + goes up to l2 table loading (assume cache miss) + +2) guest write => backup COW => qcow2 write => + try to take qcow2 mutex => waiting + +3. l2 table loaded, we see that cluster is UNALLOCATED, go to + "case QCOW2_CLUSTER_UNALLOCATED" and unlock mutex before + bdrv_co_preadv(bs->backing, ...) + +4) aha, mutex unlocked, backup COW continues, and we finally finish + guest write and change cluster in our active disk A + +5. actually, do bdrv_co_preadv(bs->backing, ...) and read + _new updated_ data. + +To avoid this, let's make backup writes serializing, to not intersect +with reads from B. + +Note: we expand range of handled cases from (sync=none and +B->backing = A) to just (A in backing chain of B), to finally allow +safe reading from B during backup for all cases when A in backing chain +of B, i.e. B formally looks like point-in-time snapshot of A. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: Fam Zheng +Signed-off-by: Kevin Wolf +(cherry picked from commit f8d59dfb40bbc6f5aeea57c8aac1e68c1d2454ee) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 20 ++++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index 369155a..4ba1a6a 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -47,6 +47,8 @@ typedef struct BackupBlockJob { + HBitmap *copy_bitmap; + bool use_copy_range; + int64_t copy_range_size; ++ ++ bool serialize_target_writes; + } BackupBlockJob; + + static const BlockJobDriver backup_job_driver; +@@ -102,6 +104,8 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, + QEMUIOVector qiov; + BlockBackend *blk = job->common.blk; + int nbytes; ++ int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0; ++ int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0; + + hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1); + nbytes = MIN(job->cluster_size, job->len - start); +@@ -112,8 +116,7 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, + iov.iov_len = nbytes; + qemu_iovec_init_external(&qiov, &iov, 1); + +- ret = blk_co_preadv(blk, start, qiov.size, &qiov, +- is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0); ++ ret = blk_co_preadv(blk, start, qiov.size, &qiov, read_flags); + if (ret < 0) { + trace_backup_do_cow_read_fail(job, start, ret); + if (error_is_read) { +@@ -124,11 +127,11 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, + + if (qemu_iovec_is_zero(&qiov)) { + ret = blk_co_pwrite_zeroes(job->target, start, +- qiov.size, BDRV_REQ_MAY_UNMAP); ++ qiov.size, write_flags | BDRV_REQ_MAY_UNMAP); + } else { + ret = blk_co_pwritev(job->target, start, +- qiov.size, &qiov, +- job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0); ++ qiov.size, &qiov, write_flags | ++ (job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0)); + } + if (ret < 0) { + trace_backup_do_cow_write_fail(job, start, ret); +@@ -156,6 +159,8 @@ static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job, + int nr_clusters; + BlockBackend *blk = job->common.blk; + int nbytes; ++ int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0; ++ int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0; + + assert(QEMU_IS_ALIGNED(job->copy_range_size, job->cluster_size)); + nbytes = MIN(job->copy_range_size, end - start); +@@ -163,7 +168,7 @@ static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job, + hbitmap_reset(job->copy_bitmap, start / job->cluster_size, + nr_clusters); + ret = blk_co_copy_range(blk, start, job->target, start, nbytes, +- is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0, 0); ++ read_flags, write_flags); + if (ret < 0) { + trace_backup_do_cow_copy_range_fail(job, start, ret); + hbitmap_set(job->copy_bitmap, start / job->cluster_size, +@@ -701,6 +706,9 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, + sync_bitmap : NULL; + job->compress = compress; + ++ /* Detect image-fleecing (and similar) schemes */ ++ job->serialize_target_writes = bdrv_chain_contains(target, bs); ++ + /* If there is no backing file on the target, we cannot rely on COW if our + * backup cluster size is smaller than the target cluster size. Even for + * targets with a backing file, try to avoid COW if possible. */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-backup-make-function-variables-consistently-na.patch b/SOURCES/kvm-block-backup-make-function-variables-consistently-na.patch new file mode 100644 index 0000000..cfbfc2b --- /dev/null +++ b/SOURCES/kvm-block-backup-make-function-variables-consistently-na.patch @@ -0,0 +1,166 @@ +From d829a781a2832a72264ade11062dad0e5ef578da Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:45 +0200 +Subject: [PATCH 07/25] block/backup: make function variables consistently + named + +RH-Author: John Snow +Message-id: <20180910181803.11781-8-jsnow@redhat.com> +Patchwork-id: 82086 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 07/25] block/backup: make function variables consistently named +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Rename opaque_job to job to be consistent with other job implementations. +Rename 'job', the BackupBlockJob object, to 's' to also be consistent. + +Suggested-by: Eric Blake +Signed-off-by: John Snow +Reviewed-by: Max Reitz +Message-id: 20180830015734.19765-8-jsnow@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit 6870277535493fea31761d8d11ec23add2de0fb0) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 62 +++++++++++++++++++++++++++++----------------------------- + 1 file changed, 31 insertions(+), 31 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index 08a5b74..524e0ff 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -468,59 +468,59 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) + bdrv_dirty_iter_free(dbi); + } + +-static int coroutine_fn backup_run(Job *opaque_job, Error **errp) ++static int coroutine_fn backup_run(Job *job, Error **errp) + { +- BackupBlockJob *job = container_of(opaque_job, BackupBlockJob, common.job); +- BlockDriverState *bs = blk_bs(job->common.blk); ++ BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); ++ BlockDriverState *bs = blk_bs(s->common.blk); + int64_t offset, nb_clusters; + int ret = 0; + +- QLIST_INIT(&job->inflight_reqs); +- qemu_co_rwlock_init(&job->flush_rwlock); ++ QLIST_INIT(&s->inflight_reqs); ++ qemu_co_rwlock_init(&s->flush_rwlock); + +- nb_clusters = DIV_ROUND_UP(job->len, job->cluster_size); +- job_progress_set_remaining(&job->common.job, job->len); ++ nb_clusters = DIV_ROUND_UP(s->len, s->cluster_size); ++ job_progress_set_remaining(job, s->len); + +- job->copy_bitmap = hbitmap_alloc(nb_clusters, 0); +- if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { +- backup_incremental_init_copy_bitmap(job); ++ s->copy_bitmap = hbitmap_alloc(nb_clusters, 0); ++ if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { ++ backup_incremental_init_copy_bitmap(s); + } else { +- hbitmap_set(job->copy_bitmap, 0, nb_clusters); ++ hbitmap_set(s->copy_bitmap, 0, nb_clusters); + } + + +- job->before_write.notify = backup_before_write_notify; +- bdrv_add_before_write_notifier(bs, &job->before_write); ++ s->before_write.notify = backup_before_write_notify; ++ bdrv_add_before_write_notifier(bs, &s->before_write); + +- if (job->sync_mode == MIRROR_SYNC_MODE_NONE) { ++ if (s->sync_mode == MIRROR_SYNC_MODE_NONE) { + /* All bits are set in copy_bitmap to allow any cluster to be copied. + * This does not actually require them to be copied. */ +- while (!job_is_cancelled(&job->common.job)) { ++ while (!job_is_cancelled(job)) { + /* Yield until the job is cancelled. We just let our before_write + * notify callback service CoW requests. */ +- job_yield(&job->common.job); ++ job_yield(job); + } +- } else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { +- ret = backup_run_incremental(job); ++ } else if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { ++ ret = backup_run_incremental(s); + } else { + /* Both FULL and TOP SYNC_MODE's require copying.. */ +- for (offset = 0; offset < job->len; +- offset += job->cluster_size) { ++ for (offset = 0; offset < s->len; ++ offset += s->cluster_size) { + bool error_is_read; + int alloced = 0; + +- if (yield_and_check(job)) { ++ if (yield_and_check(s)) { + break; + } + +- if (job->sync_mode == MIRROR_SYNC_MODE_TOP) { ++ if (s->sync_mode == MIRROR_SYNC_MODE_TOP) { + int i; + int64_t n; + + /* Check to see if these blocks are already in the + * backing file. */ + +- for (i = 0; i < job->cluster_size;) { ++ for (i = 0; i < s->cluster_size;) { + /* bdrv_is_allocated() only returns true/false based + * on the first set of sectors it comes across that + * are are all in the same state. +@@ -529,7 +529,7 @@ static int coroutine_fn backup_run(Job *opaque_job, Error **errp) + * needed but at some point that is always the case. */ + alloced = + bdrv_is_allocated(bs, offset + i, +- job->cluster_size - i, &n); ++ s->cluster_size - i, &n); + i += n; + + if (alloced || n == 0) { +@@ -547,29 +547,29 @@ static int coroutine_fn backup_run(Job *opaque_job, Error **errp) + if (alloced < 0) { + ret = alloced; + } else { +- ret = backup_do_cow(job, offset, job->cluster_size, ++ ret = backup_do_cow(s, offset, s->cluster_size, + &error_is_read, false); + } + if (ret < 0) { + /* Depending on error action, fail now or retry cluster */ + BlockErrorAction action = +- backup_error_action(job, error_is_read, -ret); ++ backup_error_action(s, error_is_read, -ret); + if (action == BLOCK_ERROR_ACTION_REPORT) { + break; + } else { +- offset -= job->cluster_size; ++ offset -= s->cluster_size; + continue; + } + } + } + } + +- notifier_with_return_remove(&job->before_write); ++ notifier_with_return_remove(&s->before_write); + + /* wait until pending backup_do_cow() calls have completed */ +- qemu_co_rwlock_wrlock(&job->flush_rwlock); +- qemu_co_rwlock_unlock(&job->flush_rwlock); +- hbitmap_free(job->copy_bitmap); ++ qemu_co_rwlock_wrlock(&s->flush_rwlock); ++ qemu_co_rwlock_unlock(&s->flush_rwlock); ++ hbitmap_free(s->copy_bitmap); + + return ret; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-backup-prohibit-backup-from-using-in-use-bitma.patch b/SOURCES/kvm-block-backup-prohibit-backup-from-using-in-use-bitma.patch new file mode 100644 index 0000000..c6c5e9b --- /dev/null +++ b/SOURCES/kvm-block-backup-prohibit-backup-from-using-in-use-bitma.patch @@ -0,0 +1,62 @@ +From 2b82b7986e02dfe32ed23724d7a6dfaef2c79031 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:35 +0100 +Subject: [PATCH 25/33] block/backup: prohibit backup from using in use bitmaps + +RH-Author: John Snow +Message-id: <20190206221243.7407-16-jsnow@redhat.com> +Patchwork-id: 84268 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 15/23] block/backup: prohibit backup from using in use bitmaps +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +If the bitmap is frozen, we shouldn't touch it. + +Signed-off-by: John Snow +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20181002230218.13949-6-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit b27a6b8b329a8dcbab9dc1af45586f7585f3d47b) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/blockdev.c b/blockdev.c +index 9921de6..c9bda43 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3669,10 +3669,10 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, + bdrv_unref(target_bs); + goto out; + } +- if (bdrv_dirty_bitmap_qmp_locked(bmap)) { ++ if (bdrv_dirty_bitmap_user_locked(bmap)) { + error_setg(errp, +- "Bitmap '%s' is currently locked and cannot be used for " +- "backup", backup->bitmap); ++ "Bitmap '%s' is currently in use by another operation" ++ " and cannot be used for backup", backup->bitmap); + goto out; + } + } +@@ -3776,10 +3776,10 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, + error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap); + goto out; + } +- if (bdrv_dirty_bitmap_qmp_locked(bmap)) { ++ if (bdrv_dirty_bitmap_user_locked(bmap)) { + error_setg(errp, +- "Bitmap '%s' is currently locked and cannot be used for " +- "backup", backup->bitmap); ++ "Bitmap '%s' is currently in use by another operation" ++ " and cannot be used for backup", backup->bitmap); + goto out; + } + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-backup-qapi-documentation-fixup.patch b/SOURCES/kvm-block-backup-qapi-documentation-fixup.patch new file mode 100644 index 0000000..8600365 --- /dev/null +++ b/SOURCES/kvm-block-backup-qapi-documentation-fixup.patch @@ -0,0 +1,73 @@ +From 74396004eb3705a30023e5206cc46e6b414830be Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:18:02 +0200 +Subject: [PATCH 24/25] block/backup: qapi documentation fixup + +RH-Author: John Snow +Message-id: <20180910181803.11781-25-jsnow@redhat.com> +Patchwork-id: 82094 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 24/25] block/backup: qapi documentation fixup +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Fix documentation to match the other jobs amended for 3.1. + +Signed-off-by: John Snow +Reviewed-by: Max Reitz +Message-id: 20180906130225.5118-16-jsnow@redhat.com +Reviewed-by: Jeff Cody +Signed-off-by: Max Reitz +(cherry picked from commit 487e31ee250d0f05a26506107fd17191bbce614a) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + qapi/block-core.json | 18 ++++++++++-------- + 1 file changed, 10 insertions(+), 8 deletions(-) + +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 5526cbc..56937db 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -1255,13 +1255,14 @@ + # a different block device than @device). + # + # @auto-finalize: When false, this job will wait in a PENDING state after it has +-# finished its work, waiting for @block-job-finalize. +-# When true, this job will automatically perform its abort or +-# commit actions. ++# finished its work, waiting for @block-job-finalize before ++# making any block graph changes. ++# When true, this job will automatically ++# perform its abort or commit actions. + # Defaults to true. (Since 2.12) + # + # @auto-dismiss: When false, this job will wait in a CONCLUDED state after it +-# has completed ceased all work, and wait for @block-job-dismiss. ++# has completely ceased all work, and awaits @block-job-dismiss. + # When true, this job will automatically disappear from the query + # list without user intervention. + # Defaults to true. (Since 2.12) +@@ -1310,13 +1311,14 @@ + # a different block device than @device). + # + # @auto-finalize: When false, this job will wait in a PENDING state after it has +-# finished its work, waiting for @block-job-finalize. +-# When true, this job will automatically perform its abort or +-# commit actions. ++# finished its work, waiting for @block-job-finalize before ++# making any block graph changes. ++# When true, this job will automatically ++# perform its abort or commit actions. + # Defaults to true. (Since 2.12) + # + # @auto-dismiss: When false, this job will wait in a CONCLUDED state after it +-# has completed ceased all work, and wait for @block-job-dismiss. ++# has completely ceased all work, and awaits @block-job-dismiss. + # When true, this job will automatically disappear from the query + # list without user intervention. + # Defaults to true. (Since 2.12) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-commit-add-block-job-creation-flags.patch b/SOURCES/kvm-block-commit-add-block-job-creation-flags.patch new file mode 100644 index 0000000..e7fba00 --- /dev/null +++ b/SOURCES/kvm-block-commit-add-block-job-creation-flags.patch @@ -0,0 +1,110 @@ +From a846fdf85ff06e60d1d4f2da8ee26a111011e1f9 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:48 +0200 +Subject: [PATCH 10/25] block/commit: add block job creation flags + +RH-Author: John Snow +Message-id: <20180910181803.11781-11-jsnow@redhat.com> +Patchwork-id: 82109 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 10/25] block/commit: add block job creation flags +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Add support for taking and passing forward job creation flags. + +Signed-off-by: John Snow +Reviewed-by: Max Reitz +Reviewed-by: Jeff Cody +Message-id: 20180906130225.5118-2-jsnow@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit cbb05aee29ba2c2dbbe8ac913a05bd6e1eff4e8a) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/commit.c | 5 +++-- + blockdev.c | 7 ++++--- + include/block/block_int.h | 5 ++++- + 3 files changed, 11 insertions(+), 6 deletions(-) + +diff --git a/block/commit.c b/block/commit.c +index 25b3cb8..c737664 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -254,7 +254,8 @@ static BlockDriver bdrv_commit_top = { + }; + + void commit_start(const char *job_id, BlockDriverState *bs, +- BlockDriverState *base, BlockDriverState *top, int64_t speed, ++ BlockDriverState *base, BlockDriverState *top, ++ int creation_flags, int64_t speed, + BlockdevOnError on_error, const char *backing_file_str, + const char *filter_node_name, Error **errp) + { +@@ -272,7 +273,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, + } + + s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL, +- speed, JOB_DEFAULT, NULL, NULL, errp); ++ speed, creation_flags, NULL, NULL, errp); + if (!s) { + return; + } +diff --git a/blockdev.c b/blockdev.c +index baa7e18..20dbcf5 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3371,6 +3371,7 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, + * BlockdevOnError change for blkmirror makes it in + */ + BlockdevOnError on_error = BLOCKDEV_ON_ERROR_REPORT; ++ int job_flags = JOB_DEFAULT; + + if (!has_speed) { + speed = 0; +@@ -3452,15 +3453,15 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, + goto out; + } + commit_active_start(has_job_id ? job_id : NULL, bs, base_bs, +- JOB_DEFAULT, speed, on_error, ++ 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)) { + goto out; + } +- commit_start(has_job_id ? job_id : NULL, bs, base_bs, top_bs, speed, +- on_error, has_backing_file ? backing_file : NULL, ++ commit_start(has_job_id ? job_id : NULL, bs, base_bs, top_bs, job_flags, ++ speed, on_error, has_backing_file ? backing_file : NULL, + filter_node_name, &local_err); + } + if (local_err != NULL) { +diff --git a/include/block/block_int.h b/include/block/block_int.h +index b05cf11..25ad363 100644 +--- a/include/block/block_int.h ++++ b/include/block/block_int.h +@@ -951,6 +951,8 @@ void stream_start(const char *job_id, BlockDriverState *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 +@@ -961,7 +963,8 @@ void stream_start(const char *job_id, BlockDriverState *bs, + * + */ + void commit_start(const char *job_id, BlockDriverState *bs, +- BlockDriverState *base, BlockDriverState *top, int64_t speed, ++ BlockDriverState *base, BlockDriverState *top, ++ int creation_flags, int64_t speed, + BlockdevOnError on_error, const char *backing_file_str, + const char *filter_node_name, Error **errp); + /** +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-commit-refactor-commit-to-use-job-callbacks.patch b/SOURCES/kvm-block-commit-refactor-commit-to-use-job-callbacks.patch new file mode 100644 index 0000000..8a1894f --- /dev/null +++ b/SOURCES/kvm-block-commit-refactor-commit-to-use-job-callbacks.patch @@ -0,0 +1,180 @@ +From c720832a9aa72b4b9a326ea95c46ed1e2cbd5cad Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:51 +0200 +Subject: [PATCH 13/25] block/commit: refactor commit to use job callbacks + +RH-Author: John Snow +Message-id: <20180910181803.11781-14-jsnow@redhat.com> +Patchwork-id: 82092 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 13/25] block/commit: refactor commit to use job callbacks +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Use the component callbacks; prepare, abort, and clean. + +NB: prepare is only called when the job has not yet failed; +and abort can be called after prepare. + +complete -> prepare -> abort -> clean +complete -> abort -> clean + +During refactor, a potential problem with bdrv_drop_intermediate +was identified, the patched behavior is no worse than the pre-patch +behavior, so leave a FIXME for now to be fixed in a future patch. + +Signed-off-by: John Snow +Reviewed-by: Max Reitz +Message-id: 20180906130225.5118-5-jsnow@redhat.com +Reviewed-by: Jeff Cody +Signed-off-by: Max Reitz +(cherry picked from commit 38ff0656c24ad88fb08af343ad610c9fc4741c58) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/commit.c | 92 ++++++++++++++++++++++++++++++++-------------------------- + 1 file changed, 51 insertions(+), 41 deletions(-) + +diff --git a/block/commit.c b/block/commit.c +index c737664..b387765 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -36,6 +36,7 @@ typedef struct CommitBlockJob { + BlockDriverState *commit_top_bs; + BlockBackend *top; + BlockBackend *base; ++ BlockDriverState *base_bs; + BlockdevOnError on_error; + int base_flags; + char *backing_file_str; +@@ -68,61 +69,67 @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base, + return 0; + } + +-static void commit_exit(Job *job) ++static int commit_prepare(Job *job) + { + CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); +- BlockJob *bjob = &s->common; +- BlockDriverState *top = blk_bs(s->top); +- BlockDriverState *base = blk_bs(s->base); +- BlockDriverState *commit_top_bs = s->commit_top_bs; +- bool remove_commit_top_bs = false; +- +- /* Make sure commit_top_bs and top stay around until bdrv_replace_node() */ +- bdrv_ref(top); +- bdrv_ref(commit_top_bs); + + /* 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); ++} + +- if (!job_is_cancelled(job) && job->ret == 0) { +- /* success */ +- job->ret = bdrv_drop_intermediate(s->commit_top_bs, base, +- s->backing_file_str); +- } else { +- /* 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. */ +- remove_commit_top_bs = true; ++static void commit_abort(Job *job) ++{ ++ CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); ++ BlockDriverState *top_bs = blk_bs(s->top); ++ ++ /* 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_child_try_set_perm(s->commit_top_bs->backing, 0, BLK_PERM_ALL, ++ &error_abort); ++ bdrv_replace_node(s->commit_top_bs, backing_bs(s->commit_top_bs), ++ &error_abort); ++ ++ 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_flags != bdrv_get_flags(base)) { +- bdrv_reopen(base, s->base_flags, NULL); ++ if (s->base_flags != bdrv_get_flags(s->base_bs)) { ++ bdrv_reopen(s->base_bs, s->base_flags, NULL); + } ++ + g_free(s->backing_file_str); + blk_unref(s->top); +- +- /* If there is more than one reference to the job (e.g. if called from +- * job_finish_sync()), job_completed() won't free it and therefore the +- * blockers on the intermediate nodes remain. This would cause +- * bdrv_set_backing_hd() to fail. */ +- block_job_remove_all_bdrv(bjob); +- +- /* If bdrv_drop_intermediate() didn't already do that, remove the commit +- * filter driver from the backing chain. Do this as the final step so that +- * the 'consistent read' permission can be granted. */ +- if (remove_commit_top_bs) { +- bdrv_child_try_set_perm(commit_top_bs->backing, 0, BLK_PERM_ALL, +- &error_abort); +- bdrv_replace_node(commit_top_bs, backing_bs(commit_top_bs), +- &error_abort); +- } +- +- bdrv_unref(commit_top_bs); +- bdrv_unref(top); + } + + static int coroutine_fn commit_run(Job *job, Error **errp) +@@ -211,7 +218,9 @@ static const BlockJobDriver commit_job_driver = { + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .run = commit_run, +- .exit = commit_exit, ++ .prepare = commit_prepare, ++ .abort = commit_abort, ++ .clean = commit_clean + }, + }; + +@@ -350,6 +359,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, + if (ret < 0) { + goto fail; + } ++ s->base_bs = base; + + /* Required permissions are already taken with block_job_add_bdrv() */ + s->top = blk_new(0, BLK_PERM_ALL); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-commit-utilize-job_exit-shim.patch b/SOURCES/kvm-block-commit-utilize-job_exit-shim.patch new file mode 100644 index 0000000..c426005 --- /dev/null +++ b/SOURCES/kvm-block-commit-utilize-job_exit-shim.patch @@ -0,0 +1,115 @@ +From a0b03467a363f0e6804042925ea1df8ef56f8a46 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:42 +0200 +Subject: [PATCH 04/25] block/commit: utilize job_exit shim + +RH-Author: John Snow +Message-id: <20180910181803.11781-5-jsnow@redhat.com> +Patchwork-id: 82110 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 04/25] block/commit: utilize job_exit shim +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Change the manual deferment to commit_complete into the implicit +callback to job_exit, renaming commit_complete to commit_exit. + +This conversion does change the timing of when job_completed is +called to after the bdrv_replace_node and bdrv_unref calls, which +could have implications for bjob->blk which will now be put down +after this cleanup. + +Kevin highlights that we did not take any permissions for that backend +at job creation time, so it is safe to reorder these operations. + +Signed-off-by: John Snow +Reviewed-by: Max Reitz +Message-id: 20180830015734.19765-5-jsnow@redhat.com +Reviewed-by: Jeff Cody +Signed-off-by: Max Reitz +(cherry picked from commit f369b48dc4095861223f9bc4329935599e03b1c5) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/commit.c | 22 +++++----------------- + 1 file changed, 5 insertions(+), 17 deletions(-) + +diff --git a/block/commit.c b/block/commit.c +index af7579d..25b3cb8 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -68,19 +68,13 @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base, + return 0; + } + +-typedef struct { +- int ret; +-} CommitCompleteData; +- +-static void commit_complete(Job *job, void *opaque) ++static void commit_exit(Job *job) + { + CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); + BlockJob *bjob = &s->common; +- CommitCompleteData *data = opaque; + BlockDriverState *top = blk_bs(s->top); + BlockDriverState *base = blk_bs(s->base); + BlockDriverState *commit_top_bs = s->commit_top_bs; +- int ret = data->ret; + bool remove_commit_top_bs = false; + + /* Make sure commit_top_bs and top stay around until bdrv_replace_node() */ +@@ -91,10 +85,10 @@ static void commit_complete(Job *job, void *opaque) + * the normal backing chain can be restored. */ + blk_unref(s->base); + +- if (!job_is_cancelled(job) && ret == 0) { ++ if (!job_is_cancelled(job) && job->ret == 0) { + /* success */ +- ret = bdrv_drop_intermediate(s->commit_top_bs, base, +- s->backing_file_str); ++ job->ret = bdrv_drop_intermediate(s->commit_top_bs, base, ++ s->backing_file_str); + } else { + /* XXX Can (or should) we somehow keep 'consistent read' blocked even + * after the failed/cancelled commit job is gone? If we already wrote +@@ -117,9 +111,6 @@ static void commit_complete(Job *job, void *opaque) + * bdrv_set_backing_hd() to fail. */ + block_job_remove_all_bdrv(bjob); + +- job_completed(job, ret); +- g_free(data); +- + /* If bdrv_drop_intermediate() didn't already do that, remove the commit + * filter driver from the backing chain. Do this as the final step so that + * the 'consistent read' permission can be granted. */ +@@ -137,7 +128,6 @@ static void commit_complete(Job *job, void *opaque) + static int coroutine_fn commit_run(Job *job, Error **errp) + { + CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); +- CommitCompleteData *data; + int64_t offset; + uint64_t delay_ns = 0; + int ret = 0; +@@ -210,9 +200,6 @@ static int coroutine_fn commit_run(Job *job, Error **errp) + out: + qemu_vfree(buf); + +- data = g_malloc(sizeof(*data)); +- data->ret = ret; +- job_defer_to_main_loop(&s->common.job, commit_complete, data); + return ret; + } + +@@ -224,6 +211,7 @@ static const BlockJobDriver commit_job_driver = { + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .run = commit_run, ++ .exit = commit_exit, + }, + }; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-create-Do-not-abort-if-a-block-driver-is-not-a.patch b/SOURCES/kvm-block-create-Do-not-abort-if-a-block-driver-is-not-a.patch new file mode 100644 index 0000000..b6330b8 --- /dev/null +++ b/SOURCES/kvm-block-create-Do-not-abort-if-a-block-driver-is-not-a.patch @@ -0,0 +1,107 @@ +From a87f7f01dbdf6d87d6cd0f1a2e48341d4b2269ba Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= +Date: Fri, 13 Sep 2019 14:12:25 +0200 +Subject: [PATCH 2/4] block/create: Do not abort if a block driver is not + available +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Philippe Mathieu-Daudé +Message-id: <20190913141225.12022-2-philmd@redhat.com> +Patchwork-id: 90451 +O-Subject: [RHEL-7.7 qemu-kvm-rhev + RHEL-AV-8.1.0 qemu-kvm PATCH v2 1/1] block/create: Do not abort if a block driver is not available +Bugzilla: 1746224 +RH-Acked-by: Kevin Wolf +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi + +The 'blockdev-create' QMP command was introduced as experimental +feature in commit b0292b851b8, using the assert() debug call. +It got promoted to 'stable' command in 3fb588a0f2c, but the +assert call was not removed. + +Some block drivers are optional, and bdrv_find_format() might +return a NULL value, triggering the assertion. + +Stable code is not expected to abort, so return an error instead. + +This is easily reproducible when libnfs is not installed: + + ./configure + [...] + module support no + Block whitelist (rw) + Block whitelist (ro) + libiscsi support yes + libnfs support no + [...] + +Start QEMU: + + $ qemu-system-x86_64 -S -qmp unix:/tmp/qemu.qmp,server,nowait + +Send the 'blockdev-create' with the 'nfs' driver: + + $ ( cat << 'EOF' + {'execute': 'qmp_capabilities'} + {'execute': 'blockdev-create', 'arguments': {'job-id': 'x', 'options': {'size': 0, 'driver': 'nfs', 'location': {'path': '/', 'server': {'host': '::1', 'type': 'inet'}}}}, 'id': 'x'} + EOF + ) | socat STDIO UNIX:/tmp/qemu.qmp + {"QMP": {"version": {"qemu": {"micro": 50, "minor": 1, "major": 4}, "package": "v4.1.0-733-g89ea03a7dc"}, "capabilities": ["oob"]}} + {"return": {}} + +QEMU crashes: + + $ gdb qemu-system-x86_64 core + Program received signal SIGSEGV, Segmentation fault. + (gdb) bt + #0 0x00007ffff510957f in raise () at /lib64/libc.so.6 + #1 0x00007ffff50f3895 in abort () at /lib64/libc.so.6 + #2 0x00007ffff50f3769 in _nl_load_domain.cold.0 () at /lib64/libc.so.6 + #3 0x00007ffff5101a26 in .annobin_assert.c_end () at /lib64/libc.so.6 + #4 0x0000555555d7e1f1 in qmp_blockdev_create (job_id=0x555556baee40 "x", options=0x555557666610, errp=0x7fffffffc770) at block/create.c:69 + #5 0x0000555555c96b52 in qmp_marshal_blockdev_create (args=0x7fffdc003830, ret=0x7fffffffc7f8, errp=0x7fffffffc7f0) at qapi/qapi-commands-block-core.c:1314 + #6 0x0000555555deb0a0 in do_qmp_dispatch (cmds=0x55555645de70 , request=0x7fffdc005c70, allow_oob=false, errp=0x7fffffffc898) at qapi/qmp-dispatch.c:131 + #7 0x0000555555deb2a1 in qmp_dispatch (cmds=0x55555645de70 , request=0x7fffdc005c70, allow_oob=false) at qapi/qmp-dispatch.c:174 + +With this patch applied, QEMU returns a QMP error: + + {'execute': 'blockdev-create', 'arguments': {'job-id': 'x', 'options': {'size': 0, 'driver': 'nfs', 'location': {'path': '/', 'server': {'host': '::1', 'type': 'inet'}}}}, 'id': 'x'} + {"id": "x", "error": {"class": "GenericError", "desc": "Block driver 'nfs' not found or not supported"}} + +Cc: qemu-stable@nongnu.org +Reported-by: Xu Tian +Signed-off-by: Philippe Mathieu-Daudé +Reviewed-by: Eric Blake +Reviewed-by: John Snow +Signed-off-by: Kevin Wolf +(cherry picked from commit d90d5cae2b10efc0e8d0b3cc91ff16201853d3ba) +Signed-off-by: Philippe Mathieu-Daudé +Signed-off-by: Miroslav Rezanina +--- + block/create.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/block/create.c b/block/create.c +index 9534121..de5e97b 100644 +--- a/block/create.c ++++ b/block/create.c +@@ -63,9 +63,13 @@ void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options, + const char *fmt = BlockdevDriver_str(options->driver); + BlockDriver *drv = bdrv_find_format(fmt); + ++ if (!drv) { ++ error_setg(errp, "Block driver '%s' not found or not supported", fmt); ++ return; ++ } ++ + /* If the driver is in the schema, we know that it exists. But it may not + * be whitelisted. */ +- assert(drv); + if (bdrv_uses_whitelist() && !bdrv_is_whitelisted(drv, false)) { + error_setg(errp, "Driver is not whitelisted"); + return; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-create-Make-x-blockdev-create-a-job.patch b/SOURCES/kvm-block-create-Make-x-blockdev-create-a-job.patch new file mode 100644 index 0000000..7946e86 --- /dev/null +++ b/SOURCES/kvm-block-create-Make-x-blockdev-create-a-job.patch @@ -0,0 +1,217 @@ +From 15606cc179623a45cc36fd3a769897678083e804 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:40 +0200 +Subject: [PATCH 71/89] block/create: Make x-blockdev-create a job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-58-kwolf@redhat.com> +Patchwork-id: 81066 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 57/73] block/create: Make x-blockdev-create a job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This changes the x-blockdev-create QMP command so that it doesn't block +the monitor and the main loop any more, but starts a background job that +performs the image creation. + +The basic job as implemented here is all that is necessary to make image +creation asynchronous and to provide a QMP interface that can be marked +stable, but it still lacks a few features that jobs usually provide: The +job will ignore pause commands and it doesn't publish more than very +basic progress yet (total-progress is 1 and current-progress advances +from 0 to 1 when the driver callbacks returns). These features can be +added later without breaking compatibility. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: Jeff Cody +(cherry picked from commit e5ab4347f9f53495e31fcef5e232c7c6be4a0567) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/create.c | 67 +++++++++++++++++++++++++++++++++--------------- + qapi/block-core.json | 14 ++++++---- + qapi/job.json | 4 ++- + tests/qemu-iotests/group | 14 +++++----- + 4 files changed, 66 insertions(+), 33 deletions(-) + +diff --git a/block/create.c b/block/create.c +index 8bd8a03..1a263e4 100644 +--- a/block/create.c ++++ b/block/create.c +@@ -24,28 +24,51 @@ + + #include "qemu/osdep.h" + #include "block/block_int.h" ++#include "qemu/job.h" + #include "qapi/qapi-commands-block-core.h" ++#include "qapi/qapi-visit-block-core.h" ++#include "qapi/clone-visitor.h" + #include "qapi/error.h" + +-typedef struct BlockdevCreateCo { ++typedef struct BlockdevCreateJob { ++ Job common; + BlockDriver *drv; + BlockdevCreateOptions *opts; + int ret; +- Error **errp; +-} BlockdevCreateCo; ++ Error *err; ++} BlockdevCreateJob; + +-static void coroutine_fn bdrv_co_create_co_entry(void *opaque) ++static void blockdev_create_complete(Job *job, void *opaque) + { +- BlockdevCreateCo *cco = opaque; +- cco->ret = cco->drv->bdrv_co_create(cco->opts, cco->errp); ++ BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); ++ ++ job_completed(job, s->ret, s->err); + } + +-void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp) ++static void coroutine_fn blockdev_create_run(void *opaque) + { ++ BlockdevCreateJob *s = opaque; ++ ++ job_progress_set_remaining(&s->common, 1); ++ s->ret = s->drv->bdrv_co_create(s->opts, &s->err); ++ job_progress_update(&s->common, 1); ++ ++ qapi_free_BlockdevCreateOptions(s->opts); ++ job_defer_to_main_loop(&s->common, blockdev_create_complete, NULL); ++} ++ ++static const JobDriver blockdev_create_job_driver = { ++ .instance_size = sizeof(BlockdevCreateJob), ++ .job_type = JOB_TYPE_CREATE, ++ .start = blockdev_create_run, ++}; ++ ++void qmp_x_blockdev_create(const char *job_id, BlockdevCreateOptions *options, ++ Error **errp) ++{ ++ BlockdevCreateJob *s; + const char *fmt = BlockdevDriver_str(options->driver); + BlockDriver *drv = bdrv_find_format(fmt); +- Coroutine *co; +- BlockdevCreateCo cco; + + /* If the driver is in the schema, we know that it exists. But it may not + * be whitelisted. */ +@@ -55,22 +78,24 @@ void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp) + return; + } + +- /* Call callback if it exists */ ++ /* Error out if the driver doesn't support .bdrv_co_create */ + if (!drv->bdrv_co_create) { + error_setg(errp, "Driver does not support blockdev-create"); + return; + } + +- cco = (BlockdevCreateCo) { +- .drv = drv, +- .opts = options, +- .ret = -EINPROGRESS, +- .errp = errp, +- }; +- +- co = qemu_coroutine_create(bdrv_co_create_co_entry, &cco); +- qemu_coroutine_enter(co); +- while (cco.ret == -EINPROGRESS) { +- aio_poll(qemu_get_aio_context(), true); ++ /* Create the block job */ ++ /* TODO Running in the main context. Block drivers need to error out or add ++ * locking when they use a BDS in a different AioContext. */ ++ s = job_create(job_id, &blockdev_create_job_driver, NULL, ++ qemu_get_aio_context(), JOB_DEFAULT | JOB_MANUAL_DISMISS, ++ NULL, NULL, errp); ++ if (!s) { ++ return; + } ++ ++ s->drv = drv, ++ s->opts = QAPI_CLONE(BlockdevCreateOptions, options), ++ ++ job_start(&s->common); + } +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 67e613a..d22d1db 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -4027,14 +4027,18 @@ + ## + # @x-blockdev-create: + # +-# Create an image format on a given node. +-# TODO Replace with something asynchronous (block job?) ++# Starts a job to create an image format on a given node. The job is ++# automatically finalized, but a manual job-dismiss is required. + # +-# Since: 2.12 ++# @job-id: Identifier for the newly created job. ++# ++# @options: Options for the image creation. ++# ++# Since: 3.0 + ## + { 'command': 'x-blockdev-create', +- 'data': 'BlockdevCreateOptions', +- 'boxed': true } ++ 'data': { 'job-id': 'str', ++ 'options': 'BlockdevCreateOptions' } } + + ## + # @blockdev-open-tray: +diff --git a/qapi/job.json b/qapi/job.json +index 970124d..69c1970 100644 +--- a/qapi/job.json ++++ b/qapi/job.json +@@ -17,10 +17,12 @@ + # + # @backup: drive backup job type, see "drive-backup" + # ++# @create: image creation job type, see "x-blockdev-create" (since 3.0) ++# + # Since: 1.7 + ## + { 'enum': 'JobType', +- 'data': ['commit', 'stream', 'mirror', 'backup'] } ++ 'data': ['commit', 'stream', 'mirror', 'backup', 'create'] } + + ## + # @JobStatus: +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index 5c55adc..37ec5f1 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -204,14 +204,16 @@ + 203 rw auto + 204 rw auto quick + 205 rw auto quick +-206 rw auto +-207 rw auto ++# TODO The following commented out tests need to be reworked to work ++# with the x-blockdev-create job ++#206 rw auto ++#207 rw auto + 208 rw auto quick + 209 rw auto quick +-210 rw auto +-211 rw auto quick +-212 rw auto quick +-213 rw auto quick ++#210 rw auto ++#211 rw auto quick ++#212 rw auto quick ++#213 rw auto quick + 214 rw auto + 215 rw auto quick + 216 rw auto quick +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-create-Mark-blockdev-create-stable.patch b/SOURCES/kvm-block-create-Mark-blockdev-create-stable.patch new file mode 100644 index 0000000..4fa9fa3 --- /dev/null +++ b/SOURCES/kvm-block-create-Mark-blockdev-create-stable.patch @@ -0,0 +1,969 @@ +From 6d13aa547ffb24311a0cd6f2268d35fd0a272182 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:52 +0200 +Subject: [PATCH 83/89] block/create: Mark blockdev-create stable + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-70-kwolf@redhat.com> +Patchwork-id: 81104 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 69/73] block/create: Mark blockdev-create stable +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +We're ready to declare the blockdev-create job stable. This renames the +corresponding QMP command from x-blockdev-create to blockdev-create. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: Jeff Cody +(cherry picked from commit 3fb588a0f2c006122c34e1960a15c87ae2b927eb) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/create.c | 4 ++-- + qapi/block-core.json | 4 ++-- + qapi/job.json | 2 +- + tests/qemu-iotests/206 | 2 +- + tests/qemu-iotests/206.out | 54 +++++++++++++++++++++++----------------------- + tests/qemu-iotests/207 | 2 +- + tests/qemu-iotests/207.out | 18 ++++++++-------- + tests/qemu-iotests/210 | 2 +- + tests/qemu-iotests/210.out | 18 ++++++++-------- + tests/qemu-iotests/211 | 2 +- + tests/qemu-iotests/211.out | 24 ++++++++++----------- + tests/qemu-iotests/212 | 2 +- + tests/qemu-iotests/212.out | 42 ++++++++++++++++++------------------ + tests/qemu-iotests/213 | 2 +- + tests/qemu-iotests/213.out | 44 ++++++++++++++++++------------------- + 15 files changed, 111 insertions(+), 111 deletions(-) + +diff --git a/block/create.c b/block/create.c +index 1a263e4..915cd41 100644 +--- a/block/create.c ++++ b/block/create.c +@@ -63,8 +63,8 @@ static const JobDriver blockdev_create_job_driver = { + .start = blockdev_create_run, + }; + +-void qmp_x_blockdev_create(const char *job_id, BlockdevCreateOptions *options, +- Error **errp) ++void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options, ++ Error **errp) + { + BlockdevCreateJob *s; + const char *fmt = BlockdevDriver_str(options->driver); +diff --git a/qapi/block-core.json b/qapi/block-core.json +index d22d1db..0b07e41 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -4025,7 +4025,7 @@ + } } + + ## +-# @x-blockdev-create: ++# @blockdev-create: + # + # Starts a job to create an image format on a given node. The job is + # automatically finalized, but a manual job-dismiss is required. +@@ -4036,7 +4036,7 @@ + # + # Since: 3.0 + ## +-{ 'command': 'x-blockdev-create', ++{ 'command': 'blockdev-create', + 'data': { 'job-id': 'str', + 'options': 'BlockdevCreateOptions' } } + +diff --git a/qapi/job.json b/qapi/job.json +index 69c1970..17d1003 100644 +--- a/qapi/job.json ++++ b/qapi/job.json +@@ -17,7 +17,7 @@ + # + # @backup: drive backup job type, see "drive-backup" + # +-# @create: image creation job type, see "x-blockdev-create" (since 3.0) ++# @create: image creation job type, see "blockdev-create" (since 3.0) + # + # Since: 1.7 + ## +diff --git a/tests/qemu-iotests/206 b/tests/qemu-iotests/206 +index b8cf2e7..128c334 100755 +--- a/tests/qemu-iotests/206 ++++ b/tests/qemu-iotests/206 +@@ -26,7 +26,7 @@ from iotests import imgfmt + iotests.verify_image_format(supported_fmts=['qcow2']) + + def blockdev_create(vm, options): +- result = vm.qmp_log('x-blockdev-create', job_id='job0', options=options) ++ result = vm.qmp_log('blockdev-create', job_id='job0', options=options) + + if 'return' in result: + assert result['return'] == {} +diff --git a/tests/qemu-iotests/206.out b/tests/qemu-iotests/206.out +index 34451a3..789eebe 100644 +--- a/tests/qemu-iotests/206.out ++++ b/tests/qemu-iotests/206.out +@@ -1,13 +1,13 @@ + === Successful image creation (defaults) === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + + {'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}} + {u'return': {}} +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'imgfile', 'size': 134217728}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'imgfile', 'size': 134217728}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -24,12 +24,12 @@ Format specific information: + + === Successful image creation (inline blockdev-add, explicit defaults) === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'nocow': False, 'preallocation': 'off', 'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'nocow': False, 'preallocation': 'off', 'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 65536, 'refcount-bits': 16, 'version': 'v3', 'preallocation': 'off', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'lazy-refcounts': False, 'driver': 'qcow2', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 65536, 'refcount-bits': 16, 'version': 'v3', 'preallocation': 'off', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'lazy-refcounts': False, 'driver': 'qcow2', 'size': 67108864}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -46,12 +46,12 @@ Format specific information: + + === Successful image creation (v3 non-default options) === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'nocow': True, 'preallocation': 'falloc', 'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'nocow': True, 'preallocation': 'falloc', 'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 2097152, 'refcount-bits': 1, 'version': 'v3', 'preallocation': 'metadata', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'lazy-refcounts': True, 'driver': 'qcow2', 'size': 33554432}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 2097152, 'refcount-bits': 1, 'version': 'v3', 'preallocation': 'metadata', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'lazy-refcounts': True, 'driver': 'qcow2', 'size': 33554432}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -68,12 +68,12 @@ Format specific information: + + === Successful image creation (v2 non-default options) === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'backing-fmt': 'qcow2', 'driver': 'qcow2', 'version': 'v2', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'backing-file': 'TEST_DIR/PID-t.qcow2.base', 'size': 33554432}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'backing-fmt': 'qcow2', 'driver': 'qcow2', 'version': 'v2', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'backing-file': 'TEST_DIR/PID-t.qcow2.base', 'size': 33554432}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -90,7 +90,7 @@ Format specific information: + + === Successful image creation (encrypted) === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'encrypt': {'key-secret': 'keysec0', 'iter-time': 10, 'cipher-mode': 'ctr', 'ivgen-hash-alg': 'md5', 'cipher-alg': 'twofish-128', 'format': 'luks', 'ivgen-alg': 'plain64', 'hash-alg': 'sha1'}, 'driver': 'qcow2', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'size': 33554432}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'encrypt': {'key-secret': 'keysec0', 'iter-time': 10, 'cipher-mode': 'ctr', 'ivgen-hash-alg': 'md5', 'cipher-alg': 'twofish-128', 'format': 'luks', 'ivgen-alg': 'plain64', 'hash-alg': 'sha1'}, 'driver': 'qcow2', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'size': 33554432}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -144,111 +144,111 @@ Format specific information: + + === Invalid BlockdevRef === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': "this doesn't exist", 'size': 33554432}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': "this doesn't exist", 'size': 33554432}}} + {u'return': {}} + Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + + === Invalid sizes === +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 1234}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 1234}}} + {u'return': {}} + Job failed: Image size must be a multiple of 512 bytes + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 18446744073709551104L}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 18446744073709551104L}}} + {u'return': {}} + Job failed: Could not resize image: Image size cannot be negative + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 9223372036854775808L}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 9223372036854775808L}}} + {u'return': {}} + Job failed: Could not resize image: Image size cannot be negative + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 9223372036854775296}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 9223372036854775296}}} + {u'return': {}} + Job failed: Could not resize image: Failed to grow the L1 table: File too large + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + + === Invalid version === +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'version': 'v1', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'version': 'v1', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} + {u'error': {u'class': u'GenericError', u'desc': u"Invalid parameter 'v1'"}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'lazy-refcounts': True, 'version': 'v2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'lazy-refcounts': True, 'version': 'v2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater) + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 8, 'version': 'v2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 8, 'version': 'v2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater) + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + + === Invalid backing file options === +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'full', 'driver': 'qcow2', 'backing-file': '/dev/null', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'full', 'driver': 'qcow2', 'backing-file': '/dev/null', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Backing file and preallocation cannot be used at the same time + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'backing-fmt': 'qcow2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'backing-fmt': 'qcow2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Backing format cannot be used without backing file + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + + === Invalid cluster size === +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1234, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1234, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Cluster size must be a power of two between 512 and 2048k + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 128, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 128, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Cluster size must be a power of two between 512 and 2048k + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 4194304, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 4194304, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Cluster size must be a power of two between 512 and 2048k + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 0, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 0, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Cluster size must be a power of two between 512 and 2048k + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'driver': 'qcow2', 'file': 'node0', 'size': 281474976710656}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'driver': 'qcow2', 'file': 'node0', 'size': 281474976710656}}} + {u'return': {}} + Job failed: Could not resize image: Failed to grow the L1 table: File too large + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + + === Invalid refcount width === +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 128, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 128, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Refcount width must be a power of two and may not exceed 64 bits + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 0, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 0, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Refcount width must be a power of two and may not exceed 64 bits + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 7, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 7, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Refcount width must be a power of two and may not exceed 64 bits + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207 +index b595c92..444ae23 100755 +--- a/tests/qemu-iotests/207 ++++ b/tests/qemu-iotests/207 +@@ -31,7 +31,7 @@ def filter_hash(msg): + return re.sub("'hash': '[0-9a-f]+'", "'hash': HASH", msg) + + def blockdev_create(vm, options): +- result = vm.qmp_log('x-blockdev-create', job_id='job0', options=options, ++ result = vm.qmp_log('blockdev-create', job_id='job0', options=options, + filters=[iotests.filter_testfiles, filter_hash]) + + if 'return' in result: +diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out +index 5eee17b..078b7e6 100644 +--- a/tests/qemu-iotests/207.out ++++ b/tests/qemu-iotests/207.out +@@ -1,6 +1,6 @@ + === Successful image creation (defaults) === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -16,7 +16,7 @@ virtual size: 4.0M (4194304 bytes) + + === Test host-key-check options === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'none'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 8388608}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'none'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 8388608}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -25,7 +25,7 @@ image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.po + file format: IMGFMT + virtual size: 8.0M (8388608 bytes) + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'known_hosts'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'known_hosts'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -34,13 +34,13 @@ image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.po + file format: IMGFMT + virtual size: 4.0M (4194304 bytes) + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': 'wrong', 'type': 'md5', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 2097152}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': 'wrong', 'type': 'md5', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 2097152}}} + {u'return': {}} + Job failed: remote host key does not match host_key_check 'wrong' + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': HASH, 'type': 'md5', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 8388608}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': HASH, 'type': 'md5', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 8388608}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -49,13 +49,13 @@ image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.po + file format: IMGFMT + virtual size: 8.0M (8388608 bytes) + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': 'wrong', 'type': 'sha1', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 2097152}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': 'wrong', 'type': 'sha1', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 2097152}}} + {u'return': {}} + Job failed: remote host key does not match host_key_check 'wrong' + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': HASH, 'type': 'sha1', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': HASH, 'type': 'sha1', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -66,13 +66,13 @@ virtual size: 4.0M (4194304 bytes) + + === Invalid path and user === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': '/this/is/not/an/existing/path', 'host-key-check': {'mode': 'none'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': '/this/is/not/an/existing/path', 'host-key-check': {'mode': 'none'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} + {u'return': {}} + Job failed: failed to open remote file '/this/is/not/an/existing/path': Failed opening remote file (libssh2 error code: -31) + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'none'}, 'user': 'invalid user', 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'none'}, 'user': 'invalid user', 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} + {u'return': {}} + Job failed: failed to authenticate using publickey authentication and the identities held by your ssh-agent + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +diff --git a/tests/qemu-iotests/210 b/tests/qemu-iotests/210 +index ff4fdde..d142841 100755 +--- a/tests/qemu-iotests/210 ++++ b/tests/qemu-iotests/210 +@@ -27,7 +27,7 @@ iotests.verify_image_format(supported_fmts=['luks']) + iotests.verify_protocol(supported=['file']) + + def blockdev_create(vm, options): +- result = vm.qmp_log('x-blockdev-create', job_id='job0', options=options) ++ result = vm.qmp_log('blockdev-create', job_id='job0', options=options) + + if 'return' in result: + assert result['return'] == {} +diff --git a/tests/qemu-iotests/210.out b/tests/qemu-iotests/210.out +index 0e6e5c0..078ba54 100644 +--- a/tests/qemu-iotests/210.out ++++ b/tests/qemu-iotests/210.out +@@ -1,13 +1,13 @@ + === Successful image creation (defaults) === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + + {'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}} + {u'return': {}} +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'iter-time': 10, 'driver': 'luks', 'file': 'imgfile', 'size': 134217728}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'iter-time': 10, 'driver': 'luks', 'file': 'imgfile', 'size': 134217728}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -54,12 +54,12 @@ Format specific information: + + === Successful image creation (with non-default options) === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'hash-alg': 'sha1', 'cipher-mode': 'ctr', 'cipher-alg': 'twofish-128', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}, 'iter-time': 10, 'ivgen-alg': 'plain64', 'ivgen-hash-alg': 'md5', 'driver': 'luks', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'hash-alg': 'sha1', 'cipher-mode': 'ctr', 'cipher-alg': 'twofish-128', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}, 'iter-time': 10, 'ivgen-alg': 'plain64', 'ivgen-hash-alg': 'md5', 'driver': 'luks', 'size': 67108864}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -106,7 +106,7 @@ Format specific information: + + === Invalid BlockdevRef === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'luks', 'file': "this doesn't exist", 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'luks', 'file': "this doesn't exist", 'size': 67108864}}} + {u'return': {}} + Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +@@ -114,7 +114,7 @@ Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exi + + === Zero size === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'iter-time': 10, 'driver': 'luks', 'file': 'node0', 'size': 0}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'iter-time': 10, 'driver': 'luks', 'file': 'node0', 'size': 0}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -161,19 +161,19 @@ Format specific information: + + === Invalid sizes === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 18446744073709551104L}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 18446744073709551104L}}} + {u'return': {}} + Job failed: The requested file size is too large + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 9223372036854775808L}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 9223372036854775808L}}} + {u'return': {}} + Job failed: The requested file size is too large + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 9223372036854775296}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 9223372036854775296}}} + {u'return': {}} + Job failed: The requested file size is too large + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +diff --git a/tests/qemu-iotests/211 b/tests/qemu-iotests/211 +index b45f886..7b7985d 100755 +--- a/tests/qemu-iotests/211 ++++ b/tests/qemu-iotests/211 +@@ -27,7 +27,7 @@ iotests.verify_image_format(supported_fmts=['vdi']) + iotests.verify_protocol(supported=['file']) + + def blockdev_create(vm, options): +- result = vm.qmp_log('x-blockdev-create', job_id='job0', options=options) ++ result = vm.qmp_log('blockdev-create', job_id='job0', options=options) + + if 'return' in result: + assert result['return'] == {} +diff --git a/tests/qemu-iotests/211.out b/tests/qemu-iotests/211.out +index 2bf1c4a..6feaea3 100644 +--- a/tests/qemu-iotests/211.out ++++ b/tests/qemu-iotests/211.out +@@ -1,13 +1,13 @@ + === Successful image creation (defaults) === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + + {'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}} + {u'return': {}} +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'imgfile', 'size': 134217728}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'imgfile', 'size': 134217728}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -21,12 +21,12 @@ cluster_size: 1048576 + + === Successful image creation (explicit defaults) === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'off', 'driver': 'vdi', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}, 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'off', 'driver': 'vdi', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}, 'size': 67108864}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -40,12 +40,12 @@ cluster_size: 1048576 + + === Successful image creation (with non-default options) === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'metadata', 'driver': 'vdi', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}, 'size': 33554432}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'metadata', 'driver': 'vdi', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}, 'size': 33554432}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -60,7 +60,7 @@ cluster_size: 1048576 + + === Invalid BlockdevRef === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': "this doesn't exist", 'size': 33554432}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': "this doesn't exist", 'size': 33554432}}} + {u'return': {}} + Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +@@ -68,7 +68,7 @@ Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exi + + === Zero size === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 0}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 0}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -80,7 +80,7 @@ cluster_size: 1048576 + + === Maximum size === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 562949819203584}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 562949819203584}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -92,19 +92,19 @@ cluster_size: 1048576 + + === Invalid sizes === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 18446744073709551104L}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 18446744073709551104L}}} + {u'return': {}} + Job failed: Unsupported VDI image size (size is 0xfffffffffffffe00, max supported is 0x1fffff8000000) + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 9223372036854775808L}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 9223372036854775808L}}} + {u'return': {}} + Job failed: Unsupported VDI image size (size is 0x8000000000000000, max supported is 0x1fffff8000000) + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 562949819203585}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 562949819203585}}} + {u'return': {}} + Job failed: Unsupported VDI image size (size is 0x1fffff8000001, max supported is 0x1fffff8000000) + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +diff --git a/tests/qemu-iotests/212 b/tests/qemu-iotests/212 +index 03cf41d..95c8810 100755 +--- a/tests/qemu-iotests/212 ++++ b/tests/qemu-iotests/212 +@@ -27,7 +27,7 @@ iotests.verify_image_format(supported_fmts=['parallels']) + iotests.verify_protocol(supported=['file']) + + def blockdev_create(vm, options): +- result = vm.qmp_log('x-blockdev-create', job_id='job0', options=options) ++ result = vm.qmp_log('blockdev-create', job_id='job0', options=options) + + if 'return' in result: + assert result['return'] == {} +diff --git a/tests/qemu-iotests/212.out b/tests/qemu-iotests/212.out +index 780bc30..9150da7 100644 +--- a/tests/qemu-iotests/212.out ++++ b/tests/qemu-iotests/212.out +@@ -1,13 +1,13 @@ + === Successful image creation (defaults) === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + + {'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}} + {u'return': {}} +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'imgfile', 'size': 134217728}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'imgfile', 'size': 134217728}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -18,12 +18,12 @@ virtual size: 128M (134217728 bytes) + + === Successful image creation (explicit defaults) === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1048576, 'driver': 'parallels', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}, 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1048576, 'driver': 'parallels', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}, 'size': 67108864}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -34,12 +34,12 @@ virtual size: 64M (67108864 bytes) + + === Successful image creation (with non-default options) === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 65536, 'driver': 'parallels', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}, 'size': 33554432}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 65536, 'driver': 'parallels', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}, 'size': 33554432}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -50,7 +50,7 @@ virtual size: 32M (33554432 bytes) + + === Invalid BlockdevRef === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': "this doesn't exist", 'size': 33554432}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': "this doesn't exist", 'size': 33554432}}} + {u'return': {}} + Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +@@ -58,7 +58,7 @@ Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exi + + === Zero size === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 0}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 0}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -69,7 +69,7 @@ virtual size: 0 (0 bytes) + + === Maximum size === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 4503599627369984}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 4503599627369984}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -80,31 +80,31 @@ virtual size: 4096T (4503599627369984 bytes) + + === Invalid sizes === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 1234}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 1234}}} + {u'return': {}} + Job failed: Image size must be a multiple of 512 bytes + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 18446744073709551104L}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 18446744073709551104L}}} + {u'return': {}} + Job failed: Image size is too large for this cluster size + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 9223372036854775808L}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 9223372036854775808L}}} + {u'return': {}} + Job failed: Image size is too large for this cluster size + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 9223372036854775296}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 9223372036854775296}}} + {u'return': {}} + Job failed: Image size is too large for this cluster size + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 4503599627370497}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 4503599627370497}}} + {u'return': {}} + Job failed: Image size is too large for this cluster size + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +@@ -112,43 +112,43 @@ Job failed: Image size is too large for this cluster size + + === Invalid cluster size === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1234, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1234, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Cluster size must be a multiple of 512 bytes + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 128, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 128, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Cluster size must be a multiple of 512 bytes + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 4294967296, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 4294967296, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Cluster size is too large + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 9223372036854775808L, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 9223372036854775808L, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Cluster size is too large + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 18446744073709551104L, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 18446744073709551104L, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Cluster size is too large + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 0, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 0, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Image size is too large for this cluster size + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'driver': 'parallels', 'file': 'node0', 'size': 281474976710656}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'driver': 'parallels', 'file': 'node0', 'size': 281474976710656}}} + {u'return': {}} + Job failed: Image size is too large for this cluster size + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +diff --git a/tests/qemu-iotests/213 b/tests/qemu-iotests/213 +index 29d25bc..4054439 100755 +--- a/tests/qemu-iotests/213 ++++ b/tests/qemu-iotests/213 +@@ -27,7 +27,7 @@ iotests.verify_image_format(supported_fmts=['vhdx']) + iotests.verify_protocol(supported=['file']) + + def blockdev_create(vm, options): +- result = vm.qmp_log('x-blockdev-create', job_id='job0', options=options) ++ result = vm.qmp_log('blockdev-create', job_id='job0', options=options) + + if 'return' in result: + assert result['return'] == {} +diff --git a/tests/qemu-iotests/213.out b/tests/qemu-iotests/213.out +index f18a39a..e1dcd47 100644 +--- a/tests/qemu-iotests/213.out ++++ b/tests/qemu-iotests/213.out +@@ -1,13 +1,13 @@ + === Successful image creation (defaults) === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + + {'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}} + {u'return': {}} +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'imgfile', 'size': 134217728}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'imgfile', 'size': 134217728}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -19,12 +19,12 @@ cluster_size: 8388608 + + === Successful image creation (explicit defaults) === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'block-size': 8388608, 'driver': 'vhdx', 'subformat': 'dynamic', 'log-size': 1048576, 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}, 'block-state-zero': True, 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'block-size': 8388608, 'driver': 'vhdx', 'subformat': 'dynamic', 'log-size': 1048576, 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}, 'block-state-zero': True, 'size': 67108864}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -36,12 +36,12 @@ cluster_size: 8388608 + + === Successful image creation (with non-default options) === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'block-size': 268435456, 'driver': 'vhdx', 'subformat': 'fixed', 'log-size': 8388608, 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}, 'block-state-zero': False, 'size': 33554432}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'block-size': 268435456, 'driver': 'vhdx', 'subformat': 'fixed', 'log-size': 8388608, 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}, 'block-state-zero': False, 'size': 33554432}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -53,7 +53,7 @@ cluster_size: 268435456 + + === Invalid BlockdevRef === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': "this doesn't exist", 'size': 33554432}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': "this doesn't exist", 'size': 33554432}}} + {u'return': {}} + Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +@@ -61,7 +61,7 @@ Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exi + + === Zero size === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 0}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 0}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -73,7 +73,7 @@ cluster_size: 8388608 + + === Maximum size === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 70368744177664}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 70368744177664}}} + {u'return': {}} + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} +@@ -85,25 +85,25 @@ cluster_size: 67108864 + + === Invalid sizes === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 18446744073709551104L}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 18446744073709551104L}}} + {u'return': {}} + Job failed: Image size too large; max of 64TB + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 9223372036854775808L}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 9223372036854775808L}}} + {u'return': {}} + Job failed: Image size too large; max of 64TB + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 9223372036854775296}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 9223372036854775296}}} + {u'return': {}} + Job failed: Image size too large; max of 64TB + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 70368744177665}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 70368744177665}}} + {u'return': {}} + Job failed: Image size too large; max of 64TB + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +@@ -111,31 +111,31 @@ Job failed: Image size too large; max of 64TB + + === Invalid block size === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 1234567, 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 1234567, 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Block size must be a multiple of 1 MB + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 128, 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 128, 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Block size must be a multiple of 1 MB + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 3145728, 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 3145728, 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Block size must be a power of two + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 536870912, 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 536870912, 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Block size must not exceed 268435456 + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 0, 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 0, 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Block size must be a multiple of 1 MB + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +@@ -143,25 +143,25 @@ Job failed: Block size must be a multiple of 1 MB + + === Invalid log size === + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 1234567, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 1234567, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Log size must be a multiple of 1 MB + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 128, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 128, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Log size must be a multiple of 1 MB + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 4294967296, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 4294967296, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Log size must be smaller than 4 GB + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} + {u'return': {}} + +-{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 0, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} ++{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 0, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} + {u'return': {}} + Job failed: Log size must be a multiple of 1 MB + {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmap-Add-bdrv_dirty_iter_next_area.patch b/SOURCES/kvm-block-dirty-bitmap-Add-bdrv_dirty_iter_next_area.patch new file mode 100644 index 0000000..52e18d8 --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmap-Add-bdrv_dirty_iter_next_area.patch @@ -0,0 +1,114 @@ +From 7e539b23b6911cb38bf70dcbf818acbc10790816 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:25 +0100 +Subject: [PATCH 15/33] block/dirty-bitmap: Add bdrv_dirty_iter_next_area + +RH-Author: John Snow +Message-id: <20190206221243.7407-6-jsnow@redhat.com> +Patchwork-id: 84265 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 05/23] block/dirty-bitmap: Add bdrv_dirty_iter_next_area +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +From: Max Reitz + +This new function allows to look for a consecutively dirty area in a +dirty bitmap. + +Signed-off-by: Max Reitz +Reviewed-by: Fam Zheng +Reviewed-by: John Snow +Message-id: 20180613181823.13618-10-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit 72d10a94213a954ad569095cb4491f2ae0853c40) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 55 ++++++++++++++++++++++++++++++++++++++++++++ + include/block/dirty-bitmap.h | 2 ++ + 2 files changed, 57 insertions(+) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index f4a4cb7..59027d4 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -528,6 +528,61 @@ int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) + return hbitmap_iter_next(&iter->hbi, true); + } + ++/** ++ * Return the next consecutively dirty area in the dirty bitmap ++ * belonging to the given iterator @iter. ++ * ++ * @max_offset: Maximum value that may be returned for ++ * *offset + *bytes ++ * @offset: Will contain the start offset of the next dirty area ++ * @bytes: Will contain the length of the next dirty area ++ * ++ * Returns: True if a dirty area could be found before max_offset ++ * (which means that *offset and *bytes then contain valid ++ * values), false otherwise. ++ * ++ * Note that @iter is never advanced if false is returned. If an area ++ * is found (which means that true is returned), it will be advanced ++ * past that area. ++ */ ++bool bdrv_dirty_iter_next_area(BdrvDirtyBitmapIter *iter, uint64_t max_offset, ++ uint64_t *offset, int *bytes) ++{ ++ uint32_t granularity = bdrv_dirty_bitmap_granularity(iter->bitmap); ++ uint64_t gran_max_offset; ++ int64_t ret; ++ int size; ++ ++ if (max_offset == iter->bitmap->size) { ++ /* If max_offset points to the image end, round it up by the ++ * bitmap granularity */ ++ gran_max_offset = ROUND_UP(max_offset, granularity); ++ } else { ++ gran_max_offset = max_offset; ++ } ++ ++ ret = hbitmap_iter_next(&iter->hbi, false); ++ if (ret < 0 || ret + granularity > gran_max_offset) { ++ return false; ++ } ++ ++ *offset = ret; ++ size = 0; ++ ++ assert(granularity <= INT_MAX); ++ ++ do { ++ /* Advance iterator */ ++ ret = hbitmap_iter_next(&iter->hbi, true); ++ size += granularity; ++ } while (ret + granularity <= gran_max_offset && ++ hbitmap_iter_next(&iter->hbi, false) == ret + granularity && ++ size <= INT_MAX - granularity); ++ ++ *bytes = MIN(size, max_offset - *offset); ++ return true; ++} ++ + /* Called within bdrv_dirty_bitmap_lock..unlock */ + void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, + int64_t offset, int64_t bytes) +diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h +index bf68dd7..259bd27 100644 +--- a/include/block/dirty-bitmap.h ++++ b/include/block/dirty-bitmap.h +@@ -83,6 +83,8 @@ void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, + void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, + int64_t offset, int64_t bytes); + int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter); ++bool bdrv_dirty_iter_next_area(BdrvDirtyBitmapIter *iter, uint64_t max_offset, ++ uint64_t *offset, int *bytes); + void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *hbi, int64_t offset); + int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap); + int64_t bdrv_get_meta_dirty_count(BdrvDirtyBitmap *bitmap); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmap-Documentation-and-Comment-fixups.patch b/SOURCES/kvm-block-dirty-bitmap-Documentation-and-Comment-fixups.patch new file mode 100644 index 0000000..652f47f --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmap-Documentation-and-Comment-fixups.patch @@ -0,0 +1,131 @@ +From de38b4dad0dfce73080295923dd3e2d863f6f855 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 18:18:38 +0200 +Subject: [PATCH 133/163] block/dirty-bitmap: Documentation and Comment fixups + +RH-Author: John Snow +Message-id: <20190403181857.9693-3-jsnow@redhat.com> +Patchwork-id: 85416 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 02/21] block/dirty-bitmap: Documentation and Comment fixups +Bugzilla: 1677073 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Sergio Lopez Pascual + +The meaning of the states has changed subtly over time, +this should bring the understanding more in-line with the +current, actual usages. + +Reported-by: Eric Blake +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20190202011048.12343-1-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 73ab5d601c8b4311090a70e525f5970f70b8b2a0) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 20 ++++++++++++++------ + qapi/block-core.json | 34 +++++++++++++++++++++++++--------- + 2 files changed, 39 insertions(+), 15 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index e46f72b..c6d4ace 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -29,12 +29,20 @@ + #include "block/blockjob.h" + + /** +- * A BdrvDirtyBitmap can be in three possible states: +- * (1) successor is NULL and disabled is false: full r/w mode +- * (2) successor is NULL and disabled is true: read only mode ("disabled") +- * (3) successor is set: frozen mode. +- * A frozen bitmap cannot be renamed, deleted, anonymized, cleared, set, +- * or enabled. A frozen bitmap can only abdicate() or reclaim(). ++ * A BdrvDirtyBitmap can be in four possible user-visible states: ++ * (1) Active: successor is NULL, and disabled is false: full r/w mode ++ * (2) Disabled: successor is NULL, and disabled is true: qualified r/w mode, ++ * guest writes are dropped, but monitor writes are possible, ++ * through commands like merge and clear. ++ * (3) Frozen: successor is not NULL. ++ * A frozen bitmap cannot be renamed, deleted, cleared, set, ++ * enabled, merged to, etc. A frozen bitmap can only abdicate() ++ * or reclaim(). ++ * In this state, the anonymous successor bitmap may be either ++ * Active and recording writes from the guest (e.g. backup jobs), ++ * but it can be Disabled and not recording writes. ++ * (4) Locked: Whether Active or Disabled, the user cannot modify this bitmap ++ * in any way from the monitor. + */ + struct BdrvDirtyBitmap { + QemuMutex *mutex; +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 23c9462..5fe7897 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -421,17 +421,27 @@ + # + # An enumeration of possible states that a dirty bitmap can report to the user. + # +-# @frozen: The bitmap is currently in-use by a backup operation or block job, +-# and is immutable. +-# +-# @disabled: The bitmap is currently in-use by an internal operation and is +-# read-only. It can still be deleted. ++# @frozen: The bitmap is currently in-use by some operation and is immutable. ++# If the bitmap was @active prior to the operation, new writes by the ++# guest are being recorded in a temporary buffer, and will not be lost. ++# Generally, bitmaps are cleared on successful use in an operation and ++# the temporary buffer is committed into the bitmap. On failure, the ++# temporary buffer is merged back into the bitmap without first ++# clearing it. ++# Please refer to the documentation for each bitmap-using operation, ++# See also @blockdev-backup, @drive-backup. ++# ++# @disabled: The bitmap is not currently recording new writes by the guest. ++# This is requested explicitly via @block-dirty-bitmap-disable. ++# It can still be cleared, deleted, or used for backup operations. + # + # @active: The bitmap is actively monitoring for new writes, and can be cleared, + # deleted, or used for backup operations. + # +-# @locked: The bitmap is currently in-use by some operation and can not be +-# cleared, deleted, or used for backup operations. (Since 2.12) ++# @locked: The bitmap is currently in-use by some operation and is immutable. ++# If the bitmap was @active prior to the operation, it is still ++# recording new writes. If the bitmap was @disabled, it is not ++# recording new writes. (Since 2.12) + # + # Since: 2.4 + ## +@@ -1966,9 +1976,15 @@ + # @block-dirty-bitmap-merge: + # + # Merge dirty bitmaps listed in @bitmaps to the @target dirty bitmap. +-# The @bitmaps dirty bitmaps are unchanged. ++# Dirty bitmaps in @bitmaps will be unchanged, except if it also appears ++# as the @target bitmap. Any bits already set in @target will still be ++# set after the merge, i.e., this operation does not clear the target. + # On error, @target is unchanged. + # ++# The resulting bitmap will count as dirty any clusters that were dirty in any ++# of the source bitmaps. This can be used to achieve backup checkpoints, or in ++# simpler usages, to copy bitmaps. ++# + # Returns: nothing on success + # If @node is not a valid block device, DeviceNotFound + # If any bitmap in @bitmaps or @target is not found, GenericError +@@ -2003,7 +2019,7 @@ + ## + # @x-debug-block-dirty-bitmap-sha256: + # +-# Get bitmap SHA256 ++# Get bitmap SHA256. + # + # Returns: BlockDirtyBitmapSha256 on success + # If @node is not a valid block device, DeviceNotFound +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmap-add-bdrv_enable_dirty_bitmap_lock.patch b/SOURCES/kvm-block-dirty-bitmap-add-bdrv_enable_dirty_bitmap_lock.patch new file mode 100644 index 0000000..bc52072 --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmap-add-bdrv_enable_dirty_bitmap_lock.patch @@ -0,0 +1,72 @@ +From b5c5693d22cda0db33d01c6350d790b59a98c92c Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:54:42 +0200 +Subject: [PATCH 57/89] block/dirty-bitmap: add bdrv_enable_dirty_bitmap_locked + +RH-Author: John Snow +Message-id: <20180718225511.14878-7-jsnow@redhat.com> +Patchwork-id: 81405 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 06/35] block/dirty-bitmap: add bdrv_enable_dirty_bitmap_locked +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +Add _locked version of bdrv_enable_dirty_bitmap, to fix dirty bitmap +migration in the following patch. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-id: 20180625165745.25259-2-vsementsov@virtuozzo.com +Signed-off-by: John Snow +(cherry picked from commit 92bcea40d3aac62853e60426bd109b748d4d1cd2) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 9 +++++++-- + include/block/dirty-bitmap.h | 1 + + 2 files changed, 8 insertions(+), 2 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index 4159d39..4d6ae8b 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -250,6 +250,12 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, + return 0; + } + ++void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) ++{ ++ assert(!bdrv_dirty_bitmap_frozen(bitmap)); ++ bitmap->disabled = false; ++} ++ + /* Called with BQL taken. */ + void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap) + { +@@ -453,8 +459,7 @@ void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) + void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap) + { + bdrv_dirty_bitmap_lock(bitmap); +- assert(!bdrv_dirty_bitmap_frozen(bitmap)); +- bitmap->disabled = false; ++ bdrv_enable_dirty_bitmap_locked(bitmap); + bdrv_dirty_bitmap_unlock(bitmap); + } + +diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h +index 1e14743..af9ba3c 100644 +--- a/include/block/dirty-bitmap.h ++++ b/include/block/dirty-bitmap.h +@@ -33,6 +33,7 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, + 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); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmap-add-inconsistent-status.patch b/SOURCES/kvm-block-dirty-bitmap-add-inconsistent-status.patch new file mode 100644 index 0000000..618f7dd --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmap-add-inconsistent-status.patch @@ -0,0 +1,76 @@ +From 615fbb1d8b28619f0a6a00cdaaa57d223670bf92 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 18:18:50 +0200 +Subject: [PATCH 145/163] block/dirty-bitmap: add inconsistent status + +RH-Author: John Snow +Message-id: <20190403181857.9693-15-jsnow@redhat.com> +Patchwork-id: 85411 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 14/21] block/dirty-bitmap: add inconsistent status +Bugzilla: 1677073 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Sergio Lopez Pascual + +Even though the status field is deprecated, we still have to support +it for a few more releases. Since this is a very new kind of bitmap +state, it makes sense for it to have its own status field. + +Reviewed-by: Eric Blake +Signed-off-by: John Snow +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20190301191545.8728-3-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 0064cfefa4e90c11e394befb7abe47602f2f30d7) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 7 ++++++- + qapi/block-core.json | 7 ++++++- + 2 files changed, 12 insertions(+), 2 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index 096c1b7..4a2349d 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -210,10 +210,15 @@ bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap) + * or it can be Disabled and not recording writes. + * (4) Locked: Whether Active or Disabled, the user cannot modify this bitmap + * in any way from the monitor. ++ * (5) Inconsistent: This is a persistent bitmap whose "in use" bit is set, and ++ * is unusable by QEMU. It can be deleted to remove it from ++ * the qcow2. + */ + DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap) + { +- if (bdrv_dirty_bitmap_has_successor(bitmap)) { ++ if (bdrv_dirty_bitmap_inconsistent(bitmap)) { ++ return DIRTY_BITMAP_STATUS_INCONSISTENT; ++ } else if (bdrv_dirty_bitmap_has_successor(bitmap)) { + return DIRTY_BITMAP_STATUS_FROZEN; + } else if (bdrv_dirty_bitmap_busy(bitmap)) { + return DIRTY_BITMAP_STATUS_LOCKED; +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 92a42ef..c84144d 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -443,10 +443,15 @@ + # recording new writes. If the bitmap was @disabled, it is not + # recording new writes. (Since 2.12) + # ++# @inconsistent: This is a persistent dirty bitmap that was marked in-use on ++# disk, and is unusable by QEMU. It can only be deleted. ++# Please rely on the inconsistent field in @BlockDirtyInfo ++# instead, as the status field is deprecated. (Since 4.0) ++# + # Since: 2.4 + ## + { 'enum': 'DirtyBitmapStatus', +- 'data': ['active', 'disabled', 'frozen', 'locked'] } ++ 'data': ['active', 'disabled', 'frozen', 'locked', 'inconsistent'] } + + ## + # @BlockDirtyInfo: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmap-add-lock-to-bdrv_enable-disable_d.patch b/SOURCES/kvm-block-dirty-bitmap-add-lock-to-bdrv_enable-disable_d.patch new file mode 100644 index 0000000..71aca6a --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmap-add-lock-to-bdrv_enable-disable_d.patch @@ -0,0 +1,62 @@ +From d47b51e706c36542cf32e754fe9c539aed237aef Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:54:37 +0200 +Subject: [PATCH 52/89] block/dirty-bitmap: add lock to + bdrv_enable/disable_dirty_bitmap + +RH-Author: John Snow +Message-id: <20180718225511.14878-2-jsnow@redhat.com> +Patchwork-id: 81426 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 01/35] block/dirty-bitmap: add lock to bdrv_enable/disable_dirty_bitmap +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +Add locks and remove comments about BQL accordingly to +dirty_bitmap_mutex definition in block_int.h. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Signed-off-by: John Snow +Reviewed-by: Jeff Cody +Message-id: 20180606182449.1607-2-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 8b1402ce80d74dc02802f101a0f6c340462380d1) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index 9671594..5623425 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -442,18 +442,20 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, + } + } + +-/* Called with BQL taken. */ + void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) + { ++ bdrv_dirty_bitmap_lock(bitmap); + assert(!bdrv_dirty_bitmap_frozen(bitmap)); + bitmap->disabled = true; ++ bdrv_dirty_bitmap_unlock(bitmap); + } + +-/* Called with BQL taken. */ + void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap) + { ++ bdrv_dirty_bitmap_lock(bitmap); + assert(!bdrv_dirty_bitmap_frozen(bitmap)); + bitmap->disabled = false; ++ bdrv_dirty_bitmap_unlock(bitmap); + } + + BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmap-add-recording-and-busy-properties.patch b/SOURCES/kvm-block-dirty-bitmap-add-recording-and-busy-properties.patch new file mode 100644 index 0000000..49656b4 --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmap-add-recording-and-busy-properties.patch @@ -0,0 +1,268 @@ +From baab9fd178c51f5712acef9a54cd2848903f90e9 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 18:18:39 +0200 +Subject: [PATCH 134/163] block/dirty-bitmap: add recording and busy properties + +RH-Author: John Snow +Message-id: <20190403181857.9693-4-jsnow@redhat.com> +Patchwork-id: 85412 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 03/21] block/dirty-bitmap: add recording and busy properties +Bugzilla: 1677073 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Sergio Lopez Pascual + +The current API allows us to report a single status, which we've defined as: + +Frozen: has a successor, treated as qmp_locked, may or may not be enabled. +Locked: no successor, qmp_locked. may or may not be enabled. +Disabled: Not frozen or locked, disabled. +Active: Not frozen, locked, or disabled. + +The problem is that both "Frozen" and "Locked" mean nearly the same thing, +and that both of them do not intuit whether they are recording guest writes +or not. + +This patch deprecates that status field and introduces two orthogonal +properties instead to replace it. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20190223000614.13894-2-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 4db6ceb0b594e179fcbd46a351b8cebaa840bf0d) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 9 +++++++++ + qapi/block-core.json | 10 +++++++++- + qemu-doc.texi | 6 ++++++ + tests/qemu-iotests/236.out | 28 ++++++++++++++++++++++++++++ + 4 files changed, 52 insertions(+), 1 deletion(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index c6d4ace..101383b 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -226,6 +226,13 @@ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap) + } + } + ++/* Called with BQL taken. */ ++static bool bdrv_dirty_bitmap_recording(BdrvDirtyBitmap *bitmap) ++{ ++ return !bitmap->disabled || (bitmap->successor && ++ !bitmap->successor->disabled); ++} ++ + /** + * Create a successor bitmap destined to replace this bitmap after an operation. + * Requires that the bitmap is not frozen and has no successor. +@@ -448,6 +455,8 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) + info->has_name = !!bm->name; + info->name = g_strdup(bm->name); + info->status = bdrv_dirty_bitmap_status(bm); ++ info->recording = bdrv_dirty_bitmap_recording(bm); ++ info->busy = bdrv_dirty_bitmap_user_locked(bm); + info->persistent = bm->persistent; + entry->value = info; + *plist = entry; +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 5fe7897..98bd3a8 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -459,7 +459,14 @@ + # + # @granularity: granularity of the dirty bitmap in bytes (since 1.4) + # +-# @status: current status of the dirty bitmap (since 2.4) ++# @status: Deprecated in favor of @recording and @locked. (since 2.4) ++# ++# @recording: true if the bitmap is recording new writes from the guest. ++# Replaces `active` and `disabled` statuses. (since 4.0) ++# ++# @busy: true if the bitmap is in-use by some operation (NBD or jobs) ++# and cannot be modified via QMP or used by another operation. ++# Replaces `locked` and `frozen` statuses. (since 4.0) + # + # @persistent: true if the bitmap will eventually be flushed to persistent + # storage (since 4.0) +@@ -468,6 +475,7 @@ + ## + { 'struct': 'BlockDirtyInfo', + 'data': {'*name': 'str', 'count': 'int', 'granularity': 'uint32', ++ 'recording': 'bool', 'busy': 'bool', + 'status': 'DirtyBitmapStatus', 'persistent': 'bool' } } + + ## +diff --git a/qemu-doc.texi b/qemu-doc.texi +index 2acbec5..d5060dd 100644 +--- a/qemu-doc.texi ++++ b/qemu-doc.texi +@@ -2951,6 +2951,12 @@ for these file types is 'host_cdrom' or 'host_device' as appropriate. + "autoload" parameter is now ignored. All bitmaps are automatically loaded + from qcow2 images. + ++@subsection query-block result field dirty-bitmaps[i].status (since 4.0) ++ ++The ``status'' field of the ``BlockDirtyInfo'' structure, returned by ++the query-block command is deprecated. Two new boolean fields, ++``recording'' and ``busy'' effectively replace it. ++ + @subsection query-cpus (since 2.12.0) + + The ``query-cpus'' command is replaced by the ``query-cpus-fast'' command. +diff --git a/tests/qemu-iotests/236.out b/tests/qemu-iotests/236.out +index 5006f7b..815cd05 100644 +--- a/tests/qemu-iotests/236.out ++++ b/tests/qemu-iotests/236.out +@@ -22,17 +22,21 @@ write -P0xcd 0x3ff0000 64k + "bitmaps": { + "drive0": [ + { ++ "busy": false, + "count": 262144, + "granularity": 65536, + "name": "bitmapB", + "persistent": false, ++ "recording": true, + "status": "active" + }, + { ++ "busy": false, + "count": 262144, + "granularity": 65536, + "name": "bitmapA", + "persistent": false, ++ "recording": true, + "status": "active" + } + ] +@@ -84,17 +88,21 @@ write -P0xcd 0x3ff0000 64k + "bitmaps": { + "drive0": [ + { ++ "busy": false, + "count": 262144, + "granularity": 65536, + "name": "bitmapB", + "persistent": false, ++ "recording": true, + "status": "active" + }, + { ++ "busy": false, + "count": 262144, + "granularity": 65536, + "name": "bitmapA", + "persistent": false, ++ "recording": true, + "status": "active" + } + ] +@@ -184,24 +192,30 @@ write -P0xea 0x3fe0000 64k + "bitmaps": { + "drive0": [ + { ++ "busy": false, + "count": 393216, + "granularity": 65536, + "name": "bitmapC", + "persistent": false, ++ "recording": false, + "status": "disabled" + }, + { ++ "busy": false, + "count": 262144, + "granularity": 65536, + "name": "bitmapB", + "persistent": false, ++ "recording": false, + "status": "disabled" + }, + { ++ "busy": false, + "count": 458752, + "granularity": 65536, + "name": "bitmapA", + "persistent": false, ++ "recording": false, + "status": "disabled" + } + ] +@@ -251,24 +265,30 @@ write -P0xea 0x3fe0000 64k + "bitmaps": { + "drive0": [ + { ++ "busy": false, + "count": 393216, + "granularity": 65536, + "name": "bitmapC", + "persistent": false, ++ "recording": false, + "status": "disabled" + }, + { ++ "busy": false, + "count": 262144, + "granularity": 65536, + "name": "bitmapB", + "persistent": false, ++ "recording": false, + "status": "disabled" + }, + { ++ "busy": false, + "count": 458752, + "granularity": 65536, + "name": "bitmapA", + "persistent": false, ++ "recording": false, + "status": "disabled" + } + ] +@@ -311,31 +331,39 @@ write -P0xea 0x3fe0000 64k + "bitmaps": { + "drive0": [ + { ++ "busy": false, + "count": 458752, + "granularity": 65536, + "name": "bitmapD", + "persistent": false, ++ "recording": false, + "status": "disabled" + }, + { ++ "busy": false, + "count": 393216, + "granularity": 65536, + "name": "bitmapC", + "persistent": false, ++ "recording": false, + "status": "disabled" + }, + { ++ "busy": false, + "count": 262144, + "granularity": 65536, + "name": "bitmapB", + "persistent": false, ++ "recording": false, + "status": "disabled" + }, + { ++ "busy": false, + "count": 458752, + "granularity": 65536, + "name": "bitmapA", + "persistent": false, ++ "recording": false, + "status": "disabled" + } + ] +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmap-change-semantics-of-enabled-predi.patch b/SOURCES/kvm-block-dirty-bitmap-change-semantics-of-enabled-predi.patch new file mode 100644 index 0000000..7ba1c31 --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmap-change-semantics-of-enabled-predi.patch @@ -0,0 +1,114 @@ +From 7bcc4bb06fb8455874578fe3ad722600081e8d1c Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 18:18:42 +0200 +Subject: [PATCH 137/163] block/dirty-bitmap: change semantics of enabled + predicate + +RH-Author: John Snow +Message-id: <20190403181857.9693-7-jsnow@redhat.com> +Patchwork-id: 85427 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 06/21] block/dirty-bitmap: change semantics of enabled predicate +Bugzilla: 1677073 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Sergio Lopez Pascual + +Currently, the enabled predicate means something like: +"the QAPI status of the bitmap is ACTIVE." +After this patch, it should mean exclusively: +"This bitmap is recording guest writes, and is allowed to do so." + +In many places, this is how this predicate was already used. +Internal usages of the bitmap QPI can call user_locked to find out if +the bitmap is in use by an operation. + +To accommodate this, modify the create_successor routine to now +explicitly disable the parent bitmap at creation time. + +Justifications: + +1. bdrv_dirty_bitmap_status suffers no change from the lack of + 1:1 parity with the new predicates because of the order in which + the predicates are checked. This is now only for compatibility. + +2. bdrv_set_dirty() is unchanged: pre-patch, it was skipping bitmaps that were + disabled or had a successor, while post-patch it is only skipping bitmaps + that are disabled. To accommodate this, create_successor now ensures that + any bitmap with a successor is explicitly disabled. + +3. qcow2_store_persistent_dirty_bitmaps: No functional change. This function + cares only about the literal enabled bit, and makes no effort to check if + the bitmap is in-use or not. After this patch there are still no ways to + produce an enabled bitmap with a successor. + +4. block_dirty_bitmap_enable_prepare + block_dirty_bitmap_disable_prepare + init_dirty_bitmap_migration + nbd_export_new + + These functions care about the literal enabled bit, + and already check user_locked separately. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20190223000614.13894-5-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 8b2e20f64f25a5bf9a7cd45b4babdf2d7416f7ad) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index 7dc5b55..fa411f9 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -209,7 +209,7 @@ bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap) + /* Called with BQL taken. */ + bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap) + { +- return !(bitmap->disabled || bitmap->successor); ++ return !bitmap->disabled; + } + + /* Called with BQL taken. */ +@@ -236,6 +236,7 @@ static bool bdrv_dirty_bitmap_recording(BdrvDirtyBitmap *bitmap) + /** + * Create a successor bitmap destined to replace this bitmap after an operation. + * Requires that the bitmap is not user_locked and has no successor. ++ * The successor will be enabled if the parent bitmap was. + * Called with BQL taken. + */ + int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, +@@ -264,6 +265,7 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, + + /* Successor will be on or off based on our current state. */ + child->disabled = bitmap->disabled; ++ bitmap->disabled = true; + + /* Install the successor and freeze the parent */ + bitmap->successor = child; +@@ -329,7 +331,8 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, + /** + * 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 not be user_locked, nor explicitly re-enabled. ++ * The merged parent will not be user_locked. ++ * 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(BlockDriverState *bs, +@@ -347,6 +350,8 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, + error_setg(errp, "Merging of parent and successor bitmap failed"); + return NULL; + } ++ ++ parent->disabled = successor->disabled; + bdrv_release_dirty_bitmap_locked(successor); + parent->successor = NULL; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmap-explicitly-lock-bitmaps-with-succ.patch b/SOURCES/kvm-block-dirty-bitmap-explicitly-lock-bitmaps-with-succ.patch new file mode 100644 index 0000000..2e7672e --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmap-explicitly-lock-bitmaps-with-succ.patch @@ -0,0 +1,86 @@ +From dec8a19c9a45f060a9f20556672edeb61ecb6685 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 18:18:44 +0200 +Subject: [PATCH 139/163] block/dirty-bitmap: explicitly lock bitmaps with + successors + +RH-Author: John Snow +Message-id: <20190403181857.9693-9-jsnow@redhat.com> +Patchwork-id: 85410 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 08/21] block/dirty-bitmap: explicitly lock bitmaps with successors +Bugzilla: 1677073 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Sergio Lopez Pascual + +Instead of implying a user_locked/busy status, make it explicit. +Now, bitmaps in use by migration, NBD or backup operations +are all treated the same way with the same code paths. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20190223000614.13894-7-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 21d2376f264bcaa1692bd71ab1f99b9b0ff5afbf) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index fa411f9..a495178 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -50,7 +50,7 @@ struct BdrvDirtyBitmap { + HBitmap *meta; /* Meta dirty bitmap */ + bool qmp_locked; /* Bitmap is locked, it can't be modified + through QMP */ +- BdrvDirtyBitmap *successor; /* Anonymous child; implies user_locked state */ ++ 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 +@@ -188,10 +188,8 @@ bool bdrv_dirty_bitmap_has_successor(BdrvDirtyBitmap *bitmap) + return bitmap->successor; + } + +-/* Both conditions disallow user-modification via QMP. */ + bool bdrv_dirty_bitmap_user_locked(BdrvDirtyBitmap *bitmap) { +- return bdrv_dirty_bitmap_has_successor(bitmap) || +- bdrv_dirty_bitmap_qmp_locked(bitmap); ++ return bdrv_dirty_bitmap_qmp_locked(bitmap); + } + + void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked) +@@ -267,8 +265,9 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, + child->disabled = bitmap->disabled; + bitmap->disabled = true; + +- /* Install the successor and freeze the parent */ ++ /* Install the successor and lock the parent */ + bitmap->successor = child; ++ bitmap->qmp_locked = true; + return 0; + } + +@@ -323,6 +322,7 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, + bitmap->successor = NULL; + successor->persistent = bitmap->persistent; + bitmap->persistent = false; ++ bitmap->qmp_locked = false; + bdrv_release_dirty_bitmap(bs, bitmap); + + return successor; +@@ -352,6 +352,7 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, + } + + parent->disabled = successor->disabled; ++ parent->qmp_locked = false; + bdrv_release_dirty_bitmap_locked(successor); + parent->successor = NULL; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmap-remove-assertion-from-restore.patch b/SOURCES/kvm-block-dirty-bitmap-remove-assertion-from-restore.patch new file mode 100644 index 0000000..237ea7f --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmap-remove-assertion-from-restore.patch @@ -0,0 +1,49 @@ +From 03544f6a149a35f62d97106fd5320f8b53cbc085 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Thu, 24 Jan 2019 00:55:11 +0100 +Subject: [PATCH 6/8] block/dirty-bitmap: remove assertion from restore + +RH-Author: John Snow +Message-id: <20190124005511.27662-3-jsnow@redhat.com> +Patchwork-id: 84104 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 2/2] block/dirty-bitmap: remove assertion from restore +Bugzilla: 1658426 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Eric Blake + +When making a backup of a dirty bitmap (for transactions), we want to +restore that backup whether or not the bitmap is enabled. + +It is perfectly valid to write into bitmaps that are disabled. It is +only illegitimate for the guest to have done so. + +Remove this assertion. + +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Signed-off-by: John Snow +Message-Id: <20181221093529.23855-3-jsnow@redhat.com> +Signed-off-by: Eric Blake +(cherry picked from commit 07d5a8df6a0018d831baef6d50f53f31a06b5a60) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index f580c1a..bc662d3 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -610,7 +610,6 @@ void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out) + void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in) + { + HBitmap *tmp = bitmap->bitmap; +- assert(bdrv_dirty_bitmap_enabled(bitmap)); + assert(!bdrv_dirty_bitmap_readonly(bitmap)); + bitmap->bitmap = in; + hbitmap_free(tmp); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmap-remove-set-reset-assertions-again.patch b/SOURCES/kvm-block-dirty-bitmap-remove-set-reset-assertions-again.patch new file mode 100644 index 0000000..cc3994a --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmap-remove-set-reset-assertions-again.patch @@ -0,0 +1,60 @@ +From f969846f7376f6e33b3af86c498a51cb29ca0cca Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 18:18:41 +0200 +Subject: [PATCH 136/163] block/dirty-bitmap: remove set/reset assertions + against enabled bit + +RH-Author: John Snow +Message-id: <20190403181857.9693-6-jsnow@redhat.com> +Patchwork-id: 85426 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 05/21] block/dirty-bitmap: remove set/reset assertions against enabled bit +Bugzilla: 1677073 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Sergio Lopez Pascual + +bdrv_set_dirty_bitmap and bdrv_reset_dirty_bitmap are only used as an +internal API by the mirror and migration areas of our code. These +calls modify the bitmap, but do so at the behest of QEMU and not the +guest. + +Presently, these bitmaps are always "enabled" anyway, but there's no +reason they have to be. + +Modify these internal APIs to drop this assertion. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20190223000614.13894-4-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit c28ddbb07ef39d79a81941d97faa1a1bb1ce2249) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index f8984b8..7dc5b55 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -544,7 +544,6 @@ int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) + void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, + int64_t offset, int64_t bytes) + { +- assert(bdrv_dirty_bitmap_enabled(bitmap)); + assert(!bdrv_dirty_bitmap_readonly(bitmap)); + hbitmap_set(bitmap->bitmap, offset, bytes); + } +@@ -561,7 +560,6 @@ void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap, + void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, + int64_t offset, int64_t bytes) + { +- assert(bdrv_dirty_bitmap_enabled(bitmap)); + assert(!bdrv_dirty_bitmap_readonly(bitmap)); + hbitmap_reset(bitmap->bitmap, offset, bytes); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmaps-add-block_dirty_bitmap_check-fun.patch b/SOURCES/kvm-block-dirty-bitmaps-add-block_dirty_bitmap_check-fun.patch new file mode 100644 index 0000000..647b3d1 --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmaps-add-block_dirty_bitmap_check-fun.patch @@ -0,0 +1,324 @@ +From ac4ba4bb4b2118dab2e7f0b7c7e3daec3698d060 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 18:18:51 +0200 +Subject: [PATCH 146/163] block/dirty-bitmaps: add block_dirty_bitmap_check + function + +RH-Author: John Snow +Message-id: <20190403181857.9693-16-jsnow@redhat.com> +Patchwork-id: 85429 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 15/21] block/dirty-bitmaps: add block_dirty_bitmap_check function +Bugzilla: 1677073 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Sergio Lopez Pascual + +Instead of checking against busy, inconsistent, or read only directly, +use a check function with permissions bits that let us streamline the +checks without reproducing them in many places. + +Included in this patch are permissions changes that simply add the +inconsistent check to existing permissions call spots, without +addressing existing bugs. + +In general, this means that busy+readonly checks become BDRV_BITMAP_DEFAULT, +which checks against all three conditions. busy-only checks become +BDRV_BITMAP_ALLOW_RO. + +Notably, remove allows inconsistent bitmaps, so it doesn't follow the pattern. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20190301191545.8728-4-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 3ae96d66840f72ef54902d012dbdf87ef4e9fe0c) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 42 +++++++++++++++++++++++++----------- + blockdev.c | 49 ++++++++---------------------------------- + include/block/dirty-bitmap.h | 13 ++++++++++- + migration/block-dirty-bitmap.c | 13 ++++------- + nbd/server.c | 3 +-- + 5 files changed, 56 insertions(+), 64 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index 4a2349d..6170f3a 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -174,7 +174,7 @@ bool bdrv_dirty_bitmap_has_successor(BdrvDirtyBitmap *bitmap) + return bitmap->successor; + } + +-bool bdrv_dirty_bitmap_busy(BdrvDirtyBitmap *bitmap) ++static bool bdrv_dirty_bitmap_busy(const BdrvDirtyBitmap *bitmap) + { + return bitmap->busy; + } +@@ -236,6 +236,33 @@ static bool bdrv_dirty_bitmap_recording(BdrvDirtyBitmap *bitmap) + !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"); ++ 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. +@@ -248,9 +275,7 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, + uint64_t granularity; + BdrvDirtyBitmap *child; + +- if (bdrv_dirty_bitmap_busy(bitmap)) { +- error_setg(errp, "Cannot create a successor for a bitmap that is " +- "in-use by an operation"); ++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) { + return -1; + } + if (bdrv_dirty_bitmap_has_successor(bitmap)) { +@@ -796,17 +821,10 @@ void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, + + qemu_mutex_lock(dest->mutex); + +- if (bdrv_dirty_bitmap_busy(dest)) { +- error_setg(errp, "Bitmap '%s' is currently in use by another" +- " operation and cannot be modified", dest->name); ++ if (bdrv_dirty_bitmap_check(dest, BDRV_BITMAP_DEFAULT, errp)) { + goto out; + } + +- if (bdrv_dirty_bitmap_readonly(dest)) { +- error_setg(errp, "Bitmap '%s' is readonly and cannot be modified", +- dest->name); +- goto out; +- } + + if (!hbitmap_can_merge(dest->bitmap, src->bitmap)) { + error_setg(errp, "Bitmaps are incompatible and can't be merged"); +diff --git a/blockdev.c b/blockdev.c +index a9e2e1d..860cea6 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -2160,11 +2160,7 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common, + return; + } + +- if (bdrv_dirty_bitmap_busy(state->bitmap)) { +- error_setg(errp, "Cannot modify a bitmap in use by another operation"); +- return; +- } else if (bdrv_dirty_bitmap_readonly(state->bitmap)) { +- error_setg(errp, "Cannot clear a readonly bitmap"); ++ if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_DEFAULT, errp)) { + return; + } + +@@ -2209,10 +2205,7 @@ static void block_dirty_bitmap_enable_prepare(BlkActionState *common, + return; + } + +- if (bdrv_dirty_bitmap_busy(state->bitmap)) { +- error_setg(errp, +- "Bitmap '%s' is currently in use by another operation" +- " and cannot be enabled", action->name); ++ if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { + return; + } + +@@ -2250,10 +2243,7 @@ static void block_dirty_bitmap_disable_prepare(BlkActionState *common, + return; + } + +- if (bdrv_dirty_bitmap_busy(state->bitmap)) { +- error_setg(errp, +- "Bitmap '%s' is currently in use by another operation" +- " and cannot be disabled", action->name); ++ if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { + return; + } + +@@ -3044,10 +3034,7 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name, + return; + } + +- if (bdrv_dirty_bitmap_busy(bitmap)) { +- error_setg(errp, +- "Bitmap '%s' is currently in use by another operation and" +- " cannot be removed", name); ++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) { + return; + } + +@@ -3083,13 +3070,7 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name, + return; + } + +- if (bdrv_dirty_bitmap_busy(bitmap)) { +- error_setg(errp, +- "Bitmap '%s' is currently in use by another operation" +- " and cannot be cleared", name); +- return; +- } else if (bdrv_dirty_bitmap_readonly(bitmap)) { +- error_setg(errp, "Bitmap '%s' is readonly and cannot be cleared", name); ++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) { + return; + } + +@@ -3107,10 +3088,7 @@ void qmp_block_dirty_bitmap_enable(const char *node, const char *name, + return; + } + +- if (bdrv_dirty_bitmap_busy(bitmap)) { +- error_setg(errp, +- "Bitmap '%s' is currently in use by another operation" +- " and cannot be enabled", name); ++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { + return; + } + +@@ -3128,10 +3106,7 @@ void qmp_block_dirty_bitmap_disable(const char *node, const char *name, + return; + } + +- if (bdrv_dirty_bitmap_busy(bitmap)) { +- error_setg(errp, +- "Bitmap '%s' is currently in use by another operation" +- " and cannot be disabled", name); ++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { + return; + } + +@@ -3709,10 +3684,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, + bdrv_unref(target_bs); + goto out; + } +- if (bdrv_dirty_bitmap_busy(bmap)) { +- error_setg(errp, +- "Bitmap '%s' is currently in use by another operation" +- " and cannot be used for backup", backup->bitmap); ++ if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_ALLOW_RO, errp)) { + goto out; + } + } +@@ -3816,10 +3788,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, + error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap); + goto out; + } +- if (bdrv_dirty_bitmap_busy(bmap)) { +- error_setg(errp, +- "Bitmap '%s' is currently in use by another operation" +- " and cannot be used for backup", backup->bitmap); ++ if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_ALLOW_RO, errp)) { + goto out; + } + } +diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h +index bd1b647..2a78243 100644 +--- a/include/block/dirty-bitmap.h ++++ b/include/block/dirty-bitmap.h +@@ -5,6 +5,16 @@ + #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) ++ + BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, + uint32_t granularity, + const char *name, +@@ -24,6 +34,8 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, + 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(BlockDriverState *bs, BdrvDirtyBitmap *bitmap); + void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs); + void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, +@@ -93,7 +105,6 @@ bool bdrv_has_readonly_bitmaps(BlockDriverState *bs); + bool bdrv_dirty_bitmap_get_autoload(const BdrvDirtyBitmap *bitmap); + bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap); + bool bdrv_dirty_bitmap_inconsistent(const BdrvDirtyBitmap *bitmap); +-bool bdrv_dirty_bitmap_busy(BdrvDirtyBitmap *bitmap); + bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs); + BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap); +diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c +index 62f2806..06ab58d 100644 +--- a/migration/block-dirty-bitmap.c ++++ b/migration/block-dirty-bitmap.c +@@ -274,6 +274,7 @@ static int init_dirty_bitmap_migration(void) + BdrvDirtyBitmap *bitmap; + DirtyBitmapMigBitmapState *dbms; + BdrvNextIterator it; ++ Error *local_err = NULL; + + dirty_bitmap_mig_state.bulk_completed = false; + dirty_bitmap_mig_state.prev_bs = NULL; +@@ -301,15 +302,9 @@ static int init_dirty_bitmap_migration(void) + goto fail; + } + +- if (bdrv_dirty_bitmap_busy(bitmap)) { +- error_report("Can't migrate a bitmap that is in use by another operation: '%s'", +- bdrv_dirty_bitmap_name(bitmap)); +- goto fail; +- } +- +- if (bdrv_dirty_bitmap_readonly(bitmap)) { +- error_report("Can't migrate read-only dirty bitmap: '%s", +- bdrv_dirty_bitmap_name(bitmap)); ++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, ++ &local_err)) { ++ error_report_err(local_err); + goto fail; + } + +diff --git a/nbd/server.c b/nbd/server.c +index 02773e2..9b87c7f 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -1510,8 +1510,7 @@ NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset, + goto fail; + } + +- if (bdrv_dirty_bitmap_busy(bm)) { +- error_setg(errp, "Bitmap '%s' is in use", bitmap); ++ if (bdrv_dirty_bitmap_check(bm, BDRV_BITMAP_ALLOW_RO, errp)) { + goto fail; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmaps-add-inconsistent-bit.patch b/SOURCES/kvm-block-dirty-bitmaps-add-inconsistent-bit.patch new file mode 100644 index 0000000..1eababd --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmaps-add-inconsistent-bit.patch @@ -0,0 +1,136 @@ +From a955ea9fa72ac1a0e21b66d0958e582fbbc3f716 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 18:18:49 +0200 +Subject: [PATCH 144/163] block/dirty-bitmaps: add inconsistent bit + +RH-Author: John Snow +Message-id: <20190403181857.9693-14-jsnow@redhat.com> +Patchwork-id: 85419 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 13/21] block/dirty-bitmaps: add inconsistent bit +Bugzilla: 1677073 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Sergio Lopez Pascual + +Add an inconsistent bit to dirty-bitmaps that allows us to report a bitmap as +persistent but potentially inconsistent, i.e. if we find bitmaps on a qcow2 +that have been marked as "in use". + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20190301191545.8728-2-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit b0f455599d0f092abc11aa3daba693be1453d29a) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 20 ++++++++++++++++++++ + include/block/dirty-bitmap.h | 2 ++ + qapi/block-core.json | 13 +++++++++---- + 3 files changed, 31 insertions(+), 4 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index e090237..096c1b7 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -46,6 +46,9 @@ struct BdrvDirtyBitmap { + 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 migration; /* Bitmap is selected for migration, it should + not be stored on the next inactivation + (persistent flag doesn't matter until next +@@ -465,6 +468,8 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) + 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; + entry->value = info; + *plist = entry; + plist = &entry->next; +@@ -713,6 +718,16 @@ void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent) + } + + /* Called with BQL taken. */ ++void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap) ++{ ++ qemu_mutex_lock(bitmap->mutex); ++ assert(bitmap->persistent == true); ++ bitmap->inconsistent = true; ++ bitmap->disabled = true; ++ qemu_mutex_unlock(bitmap->mutex); ++} ++ ++/* Called with BQL taken. */ + void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration) + { + qemu_mutex_lock(bitmap->mutex); +@@ -725,6 +740,11 @@ bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap) + return bitmap->persistent && !bitmap->migration; + } + ++bool bdrv_dirty_bitmap_inconsistent(const BdrvDirtyBitmap *bitmap) ++{ ++ return bitmap->inconsistent; ++} ++ + bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs) + { + BdrvDirtyBitmap *bm; +diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h +index ba8477b..bd1b647 100644 +--- a/include/block/dirty-bitmap.h ++++ b/include/block/dirty-bitmap.h +@@ -68,6 +68,7 @@ void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap); + void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value); + void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, + bool persistent); ++void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap); + void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy); + void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, + HBitmap **backup, Error **errp); +@@ -91,6 +92,7 @@ bool bdrv_dirty_bitmap_readonly(const BdrvDirtyBitmap *bitmap); + bool bdrv_has_readonly_bitmaps(BlockDriverState *bs); + bool bdrv_dirty_bitmap_get_autoload(const BdrvDirtyBitmap *bitmap); + bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap); ++bool bdrv_dirty_bitmap_inconsistent(const BdrvDirtyBitmap *bitmap); + bool bdrv_dirty_bitmap_busy(BdrvDirtyBitmap *bitmap); + bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs); + BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs, +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 98bd3a8..92a42ef 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -468,15 +468,20 @@ + # and cannot be modified via QMP or used by another operation. + # Replaces `locked` and `frozen` statuses. (since 4.0) + # +-# @persistent: true if the bitmap will eventually be flushed to persistent +-# storage (since 4.0) ++# @persistent: true if the bitmap was stored on disk, is scheduled to be stored ++# on disk, or both. (since 4.0) ++# ++# @inconsistent: true if this is a persistent bitmap that was improperly ++# stored. Implies @persistent to be true; @recording and ++# @busy to be false. This bitmap cannot be used. To remove ++# it, use @block-dirty-bitmap-remove. (Since 4.0) + # + # Since: 1.3 + ## + { 'struct': 'BlockDirtyInfo', + 'data': {'*name': 'str', 'count': 'int', 'granularity': 'uint32', +- 'recording': 'bool', 'busy': 'bool', +- 'status': 'DirtyBitmapStatus', 'persistent': 'bool' } } ++ 'recording': 'bool', 'busy': 'bool', 'status': 'DirtyBitmapStatus', ++ 'persistent': 'bool', '*inconsistent': 'bool' } } + + ## + # @Qcow2BitmapInfoFlags: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmaps-add-user_locked-status-checker.patch b/SOURCES/kvm-block-dirty-bitmaps-add-user_locked-status-checker.patch new file mode 100644 index 0000000..f9a9a4b --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmaps-add-user_locked-status-checker.patch @@ -0,0 +1,148 @@ +From 489118db0f6952e55e05792d9ee53ecd80ef08ea Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:31 +0100 +Subject: [PATCH 21/33] block/dirty-bitmaps: add user_locked status checker + +RH-Author: John Snow +Message-id: <20190206221243.7407-12-jsnow@redhat.com> +Patchwork-id: 84272 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 11/23] block/dirty-bitmaps: add user_locked status checker +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +Instead of both frozen and qmp_locked checks, wrap it into one check. +frozen implies the bitmap is split in two (for backup), and shouldn't +be modified. qmp_locked implies it's being used by another operation, +like being exported over NBD. In both cases it means we shouldn't allow +the user to modify it in any meaningful way. + +Replace any usages where we check both frozen and qmp_locked with the +new check. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Message-id: 20181002230218.13949-2-jsnow@redhat.com +[w/edits Suggested-By: Vladimir Sementsov-Ogievskiy ] +Signed-off-by: John Snow +(cherry picked from commit 993edc0ce0c6f44deb8272a7a857e419417f5f84) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 6 ++++++ + blockdev.c | 29 ++++++++--------------------- + include/block/dirty-bitmap.h | 1 + + migration/block-dirty-bitmap.c | 10 ++-------- + 4 files changed, 17 insertions(+), 29 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index a9421cd..8cc7b71 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -176,6 +176,12 @@ bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap) + return bitmap->successor; + } + ++/* Both conditions disallow user-modification via QMP. */ ++bool bdrv_dirty_bitmap_user_locked(BdrvDirtyBitmap *bitmap) { ++ return bdrv_dirty_bitmap_frozen(bitmap) || ++ bdrv_dirty_bitmap_qmp_locked(bitmap); ++} ++ + void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked) + { + qemu_mutex_lock(bitmap->mutex); +diff --git a/blockdev.c b/blockdev.c +index df0cbe2..6cf7654 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -2161,11 +2161,8 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common, + return; + } + +- if (bdrv_dirty_bitmap_frozen(state->bitmap)) { +- error_setg(errp, "Cannot modify a frozen bitmap"); +- return; +- } else if (bdrv_dirty_bitmap_qmp_locked(state->bitmap)) { +- error_setg(errp, "Cannot modify a locked bitmap"); ++ if (bdrv_dirty_bitmap_user_locked(state->bitmap)) { ++ error_setg(errp, "Cannot modify a bitmap in use by another operation"); + return; + } else if (!bdrv_dirty_bitmap_enabled(state->bitmap)) { + error_setg(errp, "Cannot clear a disabled bitmap"); +@@ -3034,15 +3031,10 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name, + return; + } + +- if (bdrv_dirty_bitmap_frozen(bitmap)) { +- error_setg(errp, +- "Bitmap '%s' is currently frozen and cannot be removed", +- name); +- return; +- } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) { ++ if (bdrv_dirty_bitmap_user_locked(bitmap)) { + error_setg(errp, +- "Bitmap '%s' is currently locked and cannot be removed", +- name); ++ "Bitmap '%s' is currently in use by another operation and" ++ " cannot be removed", name); + return; + } + +@@ -3072,15 +3064,10 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name, + return; + } + +- if (bdrv_dirty_bitmap_frozen(bitmap)) { ++ if (bdrv_dirty_bitmap_user_locked(bitmap)) { + error_setg(errp, +- "Bitmap '%s' is currently frozen and cannot be modified", +- name); +- return; +- } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) { +- error_setg(errp, +- "Bitmap '%s' is currently locked and cannot be modified", +- name); ++ "Bitmap '%s' is currently in use by another operation" ++ " and cannot be cleared", name); + return; + } else if (!bdrv_dirty_bitmap_enabled(bitmap)) { + error_setg(errp, +diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h +index 201ff7f..1463943 100644 +--- a/include/block/dirty-bitmap.h ++++ b/include/block/dirty-bitmap.h +@@ -94,6 +94,7 @@ bool bdrv_has_readonly_bitmaps(BlockDriverState *bs); + bool bdrv_dirty_bitmap_get_autoload(const BdrvDirtyBitmap *bitmap); + bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap); + bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap); ++bool bdrv_dirty_bitmap_user_locked(BdrvDirtyBitmap *bitmap); + bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs); + BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap); +diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c +index fefbc6a..47251af 100644 +--- a/migration/block-dirty-bitmap.c ++++ b/migration/block-dirty-bitmap.c +@@ -301,14 +301,8 @@ static int init_dirty_bitmap_migration(void) + goto fail; + } + +- if (bdrv_dirty_bitmap_frozen(bitmap)) { +- error_report("Can't migrate frozen dirty bitmap: '%s", +- bdrv_dirty_bitmap_name(bitmap)); +- goto fail; +- } +- +- if (bdrv_dirty_bitmap_qmp_locked(bitmap)) { +- error_report("Can't migrate locked dirty bitmap: '%s", ++ if (bdrv_dirty_bitmap_user_locked(bitmap)) { ++ error_report("Can't migrate a bitmap that is in use by another operation: '%s'", + bdrv_dirty_bitmap_name(bitmap)); + goto fail; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmaps-allow-clear-on-disabled-bitmaps.patch b/SOURCES/kvm-block-dirty-bitmaps-allow-clear-on-disabled-bitmaps.patch new file mode 100644 index 0000000..9c196c8 --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmaps-allow-clear-on-disabled-bitmaps.patch @@ -0,0 +1,72 @@ +From 2f1acc04e2744651329f4c9bf2de480923f63a41 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:33 +0100 +Subject: [PATCH 23/33] block/dirty-bitmaps: allow clear on disabled bitmaps + +RH-Author: John Snow +Message-id: <20190206221243.7407-14-jsnow@redhat.com> +Patchwork-id: 84266 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 13/23] block/dirty-bitmaps: allow clear on disabled bitmaps +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +Similarly to merge, it's OK to allow clear operations on disabled +bitmaps, as this condition only means that they are not recording +new writes. We are free to clear it if the user requests it. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20181002230218.13949-4-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 0be37c9e19f541643ef407bdafe0282b667ec23c) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 1 - + blockdev.c | 8 -------- + 2 files changed, 9 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index 8c7dc60..db8021a 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -625,7 +625,6 @@ void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap, + + void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out) + { +- assert(bdrv_dirty_bitmap_enabled(bitmap)); + assert(!bdrv_dirty_bitmap_readonly(bitmap)); + bdrv_dirty_bitmap_lock(bitmap); + if (!out) { +diff --git a/blockdev.c b/blockdev.c +index 6cf7654..7300f01 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -2164,9 +2164,6 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common, + if (bdrv_dirty_bitmap_user_locked(state->bitmap)) { + error_setg(errp, "Cannot modify a bitmap in use by another operation"); + return; +- } else if (!bdrv_dirty_bitmap_enabled(state->bitmap)) { +- error_setg(errp, "Cannot clear a disabled bitmap"); +- return; + } else if (bdrv_dirty_bitmap_readonly(state->bitmap)) { + error_setg(errp, "Cannot clear a readonly bitmap"); + return; +@@ -3069,11 +3066,6 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name, + "Bitmap '%s' is currently in use by another operation" + " and cannot be cleared", name); + return; +- } else if (!bdrv_dirty_bitmap_enabled(bitmap)) { +- error_setg(errp, +- "Bitmap '%s' is currently disabled and cannot be cleared", +- name); +- return; + } else if (bdrv_dirty_bitmap_readonly(bitmap)) { + error_setg(errp, "Bitmap '%s' is readonly and cannot be cleared", name); + return; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmaps-disallow-busy-bitmaps-as-merge-s.patch b/SOURCES/kvm-block-dirty-bitmaps-disallow-busy-bitmaps-as-merge-s.patch new file mode 100644 index 0000000..7ff29a9 --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmaps-disallow-busy-bitmaps-as-merge-s.patch @@ -0,0 +1,49 @@ +From 971a623b5bb048b3ecf079f4a761d423388ea59c Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 18:18:54 +0200 +Subject: [PATCH 149/163] block/dirty-bitmaps: disallow busy bitmaps as merge + source + +RH-Author: John Snow +Message-id: <20190403181857.9693-19-jsnow@redhat.com> +Patchwork-id: 85422 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 18/21] block/dirty-bitmaps: disallow busy bitmaps as merge source +Bugzilla: 1677073 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Sergio Lopez Pascual + +We didn't do any state checking on source bitmaps at all, +so this adds inconsistent and busy checks. readonly is +allowed, so you can still copy a readonly bitmap to a new +destination to use it for operations like drive-backup. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20190301191545.8728-7-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit cb8e58e3de1a0f39c60de272faa0133b98b02cb5) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index 6170f3a..2139354 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -825,6 +825,9 @@ void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, + goto out; + } + ++ if (bdrv_dirty_bitmap_check(src, BDRV_BITMAP_ALLOW_RO, errp)) { ++ goto out; ++ } + + if (!hbitmap_can_merge(dest->bitmap, src->bitmap)) { + error_setg(errp, "Bitmaps are incompatible and can't be merged"); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmaps-fix-merge-permissions.patch b/SOURCES/kvm-block-dirty-bitmaps-fix-merge-permissions.patch new file mode 100644 index 0000000..f8a04e7 --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmaps-fix-merge-permissions.patch @@ -0,0 +1,53 @@ +From c215981d4abf69a7aaac79dbeea3e5c1ad2ac115 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:32 +0100 +Subject: [PATCH 22/33] block/dirty-bitmaps: fix merge permissions + +RH-Author: John Snow +Message-id: <20190206221243.7407-13-jsnow@redhat.com> +Patchwork-id: 84275 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 12/23] block/dirty-bitmaps: fix merge permissions +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +In prior commits that made merge transactionable, we removed the +assertion that merge cannot operate on disabled bitmaps. In addition, +we want to make sure that we are prohibiting merges to "locked" bitmaps. + +Use the new user_locked function to check. + +Reported-by: Eric Blake +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20181002230218.13949-3-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 283d7a04f2addcc51468635300208b60c19a0db3) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index 8cc7b71..8c7dc60 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -805,9 +805,9 @@ void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, + + qemu_mutex_lock(dest->mutex); + +- if (bdrv_dirty_bitmap_frozen(dest)) { +- error_setg(errp, "Bitmap '%s' is frozen and cannot be modified", +- dest->name); ++ if (bdrv_dirty_bitmap_user_locked(dest)) { ++ error_setg(errp, "Bitmap '%s' is currently in use by another" ++ " operation and cannot be modified", dest->name); + goto out; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmaps-implement-inconsistent-bit.patch b/SOURCES/kvm-block-dirty-bitmaps-implement-inconsistent-bit.patch new file mode 100644 index 0000000..b49b265 --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmaps-implement-inconsistent-bit.patch @@ -0,0 +1,196 @@ +From 9f251abbe1e79a790aaf3a3ca48de60deb0c2a10 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 18:18:55 +0200 +Subject: [PATCH 150/163] block/dirty-bitmaps: implement inconsistent bit + +RH-Author: John Snow +Message-id: <20190403181857.9693-20-jsnow@redhat.com> +Patchwork-id: 85428 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 19/21] block/dirty-bitmaps: implement inconsistent bit +Bugzilla: 1677073 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Sergio Lopez Pascual + +Set the inconsistent bit on load instead of rejecting such bitmaps. +There is no way to un-set it; the only option is to delete the bitmap. + +Obvervations: +- bitmap loading does not need to update the header for in_use bitmaps. +- inconsistent bitmaps don't need to have their data loaded; they're + glorified corruption sentinels. +- bitmap saving does not need to save inconsistent bitmaps back to disk. +- bitmap reopening DOES need to drop the readonly flag from inconsistent + bitmaps to allow reopening of qcow2 files with non-qemu-owned bitmaps + being eventually flushed back to disk. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Message-id: 20190301191545.8728-8-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 74da6b943565c451d275de1b0253546c5e729d20) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/qcow2-bitmap.c | 99 +++++++++++++++++++++++++++------------------------- + 1 file changed, 51 insertions(+), 48 deletions(-) + +diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c +index 4899719..cbab0e5 100644 +--- a/block/qcow2-bitmap.c ++++ b/block/qcow2-bitmap.c +@@ -345,11 +345,17 @@ static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs, + uint32_t granularity; + BdrvDirtyBitmap *bitmap = NULL; + +- if (bm->flags & BME_FLAG_IN_USE) { +- error_setg(errp, "Bitmap '%s' is in use", bm->name); ++ 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, +@@ -358,12 +364,6 @@ static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs, + goto fail; + } + +- granularity = 1U << bm->granularity_bits; +- bitmap = bdrv_create_dirty_bitmap(bs, granularity, bm->name, errp); +- if (bitmap == NULL) { +- 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", +@@ -951,6 +951,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) + Qcow2Bitmap *bm; + GSList *created_dirty_bitmaps = NULL; + bool header_updated = false; ++ bool needs_update = false; + + if (s->nb_bitmaps == 0) { + /* No bitmaps - nothing to do */ +@@ -964,35 +965,39 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) + } + + QSIMPLEQ_FOREACH(bm, bm_list, entry) { +- if (!(bm->flags & BME_FLAG_IN_USE)) { +- BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp); +- if (bitmap == NULL) { +- goto fail; +- } ++ BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp); ++ if (bitmap == NULL) { ++ goto fail; ++ } + +- if (!(bm->flags & BME_FLAG_AUTO)) { +- bdrv_disable_dirty_bitmap(bitmap); +- } +- bdrv_dirty_bitmap_set_persistance(bitmap, true); ++ bdrv_dirty_bitmap_set_persistance(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; +- created_dirty_bitmaps = +- g_slist_append(created_dirty_bitmaps, bitmap); ++ 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 (created_dirty_bitmaps != NULL) { +- if (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; +- } +- header_updated = true; +- } else { +- g_slist_foreach(created_dirty_bitmaps, set_readonly_helper, +- (gpointer)true); ++ 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; + } ++ header_updated = true; ++ } ++ ++ if (!can_write(bs)) { ++ g_slist_foreach(created_dirty_bitmaps, set_readonly_helper, ++ (gpointer)true); + } + + g_slist_free(created_dirty_bitmaps); +@@ -1114,23 +1119,21 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated, + } + + QSIMPLEQ_FOREACH(bm, bm_list, entry) { +- if (!(bm->flags & BME_FLAG_IN_USE)) { +- BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name); +- if (bitmap == NULL) { +- continue; +- } +- +- if (!bdrv_dirty_bitmap_readonly(bitmap)) { +- error_setg(errp, "Bitmap %s is not readonly but not marked" +- "'IN_USE' in the image. Something went wrong," +- "all the bitmaps may be corrupted", bm->name); +- ret = -EINVAL; +- goto out; +- } ++ BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name); ++ if (bitmap == NULL) { ++ continue; ++ } + +- bm->flags |= BME_FLAG_IN_USE; +- ro_dirty_bitmaps = g_slist_append(ro_dirty_bitmaps, bitmap); ++ if (!bdrv_dirty_bitmap_readonly(bitmap)) { ++ error_setg(errp, "Bitmap %s was loaded prior to rw-reopen, but was " ++ "not marked as readonly. This is a bug, something went " ++ "wrong. All of the bitmaps may be corrupted", bm->name); ++ ret = -EINVAL; ++ goto out; + } ++ ++ bm->flags |= BME_FLAG_IN_USE; ++ ro_dirty_bitmaps = g_slist_append(ro_dirty_bitmaps, bitmap); + } + + if (ro_dirty_bitmaps != NULL) { +@@ -1426,8 +1429,8 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp) + Qcow2Bitmap *bm; + + if (!bdrv_dirty_bitmap_get_persistance(bitmap) || +- bdrv_dirty_bitmap_readonly(bitmap)) +- { ++ bdrv_dirty_bitmap_readonly(bitmap) || ++ bdrv_dirty_bitmap_inconsistent(bitmap)) { + continue; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmaps-move-comment-block.patch b/SOURCES/kvm-block-dirty-bitmaps-move-comment-block.patch new file mode 100644 index 0000000..1077438 --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmaps-move-comment-block.patch @@ -0,0 +1,87 @@ +From 4123b978727d577491f84dde507f841c23b6caa4 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 18:18:46 +0200 +Subject: [PATCH 141/163] block/dirty-bitmaps: move comment block + +RH-Author: John Snow +Message-id: <20190403181857.9693-11-jsnow@redhat.com> +Patchwork-id: 85415 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 10/21] block/dirty-bitmaps: move comment block +Bugzilla: 1677073 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Sergio Lopez Pascual + +Simply move the big status enum comment block to above the status +function, and document it as being deprecated. The whole confusing +block can get deleted in three releases time. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20190223000614.13894-9-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 1e6fddcd6f2d3b6b8c7584004763b376334a8457) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 36 +++++++++++++++++++----------------- + 1 file changed, 19 insertions(+), 17 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index 89c9665..e090237 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -28,22 +28,6 @@ + #include "block/block_int.h" + #include "block/blockjob.h" + +-/** +- * A BdrvDirtyBitmap can be in four possible user-visible states: +- * (1) Active: successor is NULL, and disabled is false: full r/w mode +- * (2) Disabled: successor is NULL, and disabled is true: qualified r/w mode, +- * guest writes are dropped, but monitor writes are possible, +- * through commands like merge and clear. +- * (3) Frozen: successor is not NULL. +- * A frozen bitmap cannot be renamed, deleted, cleared, set, +- * enabled, merged to, etc. A frozen bitmap can only abdicate() +- * or reclaim(). +- * In this state, the anonymous successor bitmap may be either +- * Active and recording writes from the guest (e.g. backup jobs), +- * but it can be Disabled and not recording writes. +- * (4) Locked: Whether Active or Disabled, the user cannot modify this bitmap +- * in any way from the monitor. +- */ + struct BdrvDirtyBitmap { + QemuMutex *mutex; + HBitmap *bitmap; /* Dirty bitmap implementation */ +@@ -205,7 +189,25 @@ bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap) + return !bitmap->disabled; + } + +-/* Called with BQL taken. */ ++/** ++ * bdrv_dirty_bitmap_status: This API is now deprecated. ++ * Called with BQL taken. ++ * ++ * A BdrvDirtyBitmap can be in four possible user-visible states: ++ * (1) Active: successor is NULL, and disabled is false: full r/w mode ++ * (2) Disabled: successor is NULL, and disabled is true: qualified r/w mode, ++ * guest writes are dropped, but monitor writes are possible, ++ * through commands like merge and clear. ++ * (3) Frozen: successor is not NULL. ++ * A frozen bitmap cannot be renamed, deleted, cleared, set, ++ * enabled, merged to, etc. A frozen bitmap can only abdicate() ++ * or reclaim(). ++ * In this state, the anonymous successor bitmap may be either ++ * Active and recording writes from the guest (e.g. backup jobs), ++ * or it can be Disabled and not recording writes. ++ * (4) Locked: Whether Active or Disabled, the user cannot modify this bitmap ++ * in any way from the monitor. ++ */ + DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap) + { + if (bdrv_dirty_bitmap_has_successor(bitmap)) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmaps-prohibit-enable-disable-on-locke.patch b/SOURCES/kvm-block-dirty-bitmaps-prohibit-enable-disable-on-locke.patch new file mode 100644 index 0000000..767aa65 --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmaps-prohibit-enable-disable-on-locke.patch @@ -0,0 +1,93 @@ +From 6f65d2a9ee0258869d7420b2cf1b3890c7fcfef0 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:34 +0100 +Subject: [PATCH 24/33] block/dirty-bitmaps: prohibit enable/disable on + locked/frozen bitmaps + +RH-Author: John Snow +Message-id: <20190206221243.7407-15-jsnow@redhat.com> +Patchwork-id: 84264 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 14/23] block/dirty-bitmaps: prohibit enable/disable on locked/frozen bitmaps +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +We're not being consistent about this. If it's in use by an operation, +the user should not be able to change the behavior of that bitmap. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20181002230218.13949-5-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit b053bb55738f35832f3d6472b12277a75c32a038) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 26 ++++++++++++++++++++------ + 1 file changed, 20 insertions(+), 6 deletions(-) + +diff --git a/blockdev.c b/blockdev.c +index 7300f01..9921de6 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -2210,6 +2210,13 @@ static void block_dirty_bitmap_enable_prepare(BlkActionState *common, + return; + } + ++ if (bdrv_dirty_bitmap_user_locked(state->bitmap)) { ++ error_setg(errp, ++ "Bitmap '%s' is currently in use by another operation" ++ " and cannot be enabled", action->name); ++ return; ++ } ++ + state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap); + bdrv_enable_dirty_bitmap(state->bitmap); + } +@@ -2244,6 +2251,13 @@ static void block_dirty_bitmap_disable_prepare(BlkActionState *common, + return; + } + ++ if (bdrv_dirty_bitmap_user_locked(state->bitmap)) { ++ error_setg(errp, ++ "Bitmap '%s' is currently in use by another operation" ++ " and cannot be disabled", action->name); ++ return; ++ } ++ + state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap); + bdrv_disable_dirty_bitmap(state->bitmap); + } +@@ -3085,10 +3099,10 @@ void qmp_x_block_dirty_bitmap_enable(const char *node, const char *name, + return; + } + +- if (bdrv_dirty_bitmap_frozen(bitmap)) { ++ if (bdrv_dirty_bitmap_user_locked(bitmap)) { + error_setg(errp, +- "Bitmap '%s' is currently frozen and cannot be enabled", +- name); ++ "Bitmap '%s' is currently in use by another operation" ++ " and cannot be enabled", name); + return; + } + +@@ -3106,10 +3120,10 @@ void qmp_x_block_dirty_bitmap_disable(const char *node, const char *name, + return; + } + +- if (bdrv_dirty_bitmap_frozen(bitmap)) { ++ if (bdrv_dirty_bitmap_user_locked(bitmap)) { + error_setg(errp, +- "Bitmap '%s' is currently frozen and cannot be disabled", +- name); ++ "Bitmap '%s' is currently in use by another operation" ++ " and cannot be disabled", name); + return; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmaps-prohibit-readonly-bitmaps-for-ba.patch b/SOURCES/kvm-block-dirty-bitmaps-prohibit-readonly-bitmaps-for-ba.patch new file mode 100644 index 0000000..b0d7ff2 --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmaps-prohibit-readonly-bitmaps-for-ba.patch @@ -0,0 +1,57 @@ +From 259fa803d7b12cda535e1c115e3757bc58131acf Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 18:18:52 +0200 +Subject: [PATCH 147/163] block/dirty-bitmaps: prohibit readonly bitmaps for + backups + +RH-Author: John Snow +Message-id: <20190403181857.9693-17-jsnow@redhat.com> +Patchwork-id: 85418 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 16/21] block/dirty-bitmaps: prohibit readonly bitmaps for backups +Bugzilla: 1677073 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Sergio Lopez Pascual + +drive and blockdev backup cannot use readonly bitmaps, because the +sync=incremental mechanism actually edits the bitmaps on success. + +If you really want to do this operation, use a copied bitmap. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20190301191545.8728-5-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit a54a0c113b333aee49e484758fab7f1f1c593dd3) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/blockdev.c b/blockdev.c +index 860cea6..de8a2bf 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3684,7 +3684,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, + bdrv_unref(target_bs); + goto out; + } +- if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_ALLOW_RO, errp)) { ++ if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_DEFAULT, errp)) { + goto out; + } + } +@@ -3788,7 +3788,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, + error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap); + goto out; + } +- if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_ALLOW_RO, errp)) { ++ if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_DEFAULT, errp)) { + goto out; + } + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmaps-prohibit-removing-readonly-bitma.patch b/SOURCES/kvm-block-dirty-bitmaps-prohibit-removing-readonly-bitma.patch new file mode 100644 index 0000000..87b3abd --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmaps-prohibit-removing-readonly-bitma.patch @@ -0,0 +1,47 @@ +From 87a49b51dcddd2f779fbc387d83f182870fdcaa8 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 18:18:53 +0200 +Subject: [PATCH 148/163] block/dirty-bitmaps: prohibit removing readonly + bitmaps + +RH-Author: John Snow +Message-id: <20190403181857.9693-18-jsnow@redhat.com> +Patchwork-id: 85413 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 17/21] block/dirty-bitmaps: prohibit removing readonly bitmaps +Bugzilla: 1677073 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Sergio Lopez Pascual + +Remove is an inherently RW operation, so this will fail anyway, but +we can fail it very quickly instead of trying and failing, so do so. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20190301191545.8728-6-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit c3edf13cd1efdb5a59e0ae4f15e63080ffb35525) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/blockdev.c b/blockdev.c +index de8a2bf..e497939 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3034,7 +3034,8 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name, + return; + } + +- if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) { ++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY | BDRV_BITMAP_RO, ++ errp)) { + return; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmaps-rename-frozen-predicate-helper.patch b/SOURCES/kvm-block-dirty-bitmaps-rename-frozen-predicate-helper.patch new file mode 100644 index 0000000..f37ea88 --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmaps-rename-frozen-predicate-helper.patch @@ -0,0 +1,196 @@ +From 1e53f224150ad795131e8ea93605979de0e3c40c Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 18:18:40 +0200 +Subject: [PATCH 135/163] block/dirty-bitmaps: rename frozen predicate helper + +RH-Author: John Snow +Message-id: <20190403181857.9693-5-jsnow@redhat.com> +Patchwork-id: 85421 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 04/21] block/dirty-bitmaps: rename frozen predicate helper +Bugzilla: 1677073 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Sergio Lopez Pascual + +"Frozen" was a good description a long time ago, but it isn't adequate now. +Rename the frozen predicate to has_successor to make the semantics of the +predicate more clear to outside callers. + +In the process, remove some calls to frozen() that no longer semantically +make sense. For bdrv_enable_dirty_bitmap_locked and +bdrv_disable_dirty_bitmap_locked, it doesn't make sense to prohibit QEMU +internals from performing this action when we only wished to prohibit QMP +users from issuing these commands. All of the QMP API commands for bitmap +manipulation already check against user_locked() to prohibit these actions. + +Several other assertions really want to check that the bitmap isn't in-use +by another operation -- use the bitmap_user_locked function for this instead, +which presently also checks for has_successor. This leaves some redundant +checks of has_successor through different helpers that are addressed in +forthcoming patches. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20190223000614.13894-3-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 50a47257f8f1368ba08e4789bb63ca84c4306dde) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 32 ++++++++++++++++++-------------- + include/block/dirty-bitmap.h | 2 +- + migration/block-dirty-bitmap.c | 2 +- + 3 files changed, 20 insertions(+), 16 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index 101383b..f8984b8 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -50,7 +50,7 @@ struct BdrvDirtyBitmap { + HBitmap *meta; /* Meta dirty bitmap */ + bool qmp_locked; /* Bitmap is locked, it can't be modified + through QMP */ +- BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */ ++ BdrvDirtyBitmap *successor; /* Anonymous child; implies user_locked state */ + 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 +@@ -183,14 +183,14 @@ const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap) + } + + /* Called with BQL taken. */ +-bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap) ++bool bdrv_dirty_bitmap_has_successor(BdrvDirtyBitmap *bitmap) + { + return bitmap->successor; + } + + /* Both conditions disallow user-modification via QMP. */ + bool bdrv_dirty_bitmap_user_locked(BdrvDirtyBitmap *bitmap) { +- return bdrv_dirty_bitmap_frozen(bitmap) || ++ return bdrv_dirty_bitmap_has_successor(bitmap) || + bdrv_dirty_bitmap_qmp_locked(bitmap); + } + +@@ -215,7 +215,7 @@ bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap) + /* Called with BQL taken. */ + DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap) + { +- if (bdrv_dirty_bitmap_frozen(bitmap)) { ++ if (bdrv_dirty_bitmap_has_successor(bitmap)) { + return DIRTY_BITMAP_STATUS_FROZEN; + } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) { + return DIRTY_BITMAP_STATUS_LOCKED; +@@ -235,7 +235,7 @@ static bool bdrv_dirty_bitmap_recording(BdrvDirtyBitmap *bitmap) + + /** + * Create a successor bitmap destined to replace this bitmap after an operation. +- * Requires that the bitmap is not frozen and has no successor. ++ * Requires that the bitmap is not user_locked and has no successor. + * Called with BQL taken. + */ + int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, +@@ -244,12 +244,16 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, + uint64_t granularity; + BdrvDirtyBitmap *child; + +- if (bdrv_dirty_bitmap_frozen(bitmap)) { ++ if (bdrv_dirty_bitmap_user_locked(bitmap)) { + error_setg(errp, "Cannot create a successor for a bitmap that is " +- "currently frozen"); ++ "in-use by an operation"); ++ 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; + } +- assert(!bitmap->successor); + + /* Create an anonymous successor */ + granularity = bdrv_dirty_bitmap_granularity(bitmap); +@@ -268,7 +272,6 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, + + void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) + { +- assert(!bdrv_dirty_bitmap_frozen(bitmap)); + bitmap->disabled = false; + } + +@@ -285,7 +288,8 @@ void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap) + static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) + { + assert(!bitmap->active_iterators); +- assert(!bdrv_dirty_bitmap_frozen(bitmap)); ++ assert(!bdrv_dirty_bitmap_user_locked(bitmap)); ++ assert(!bdrv_dirty_bitmap_has_successor(bitmap)); + assert(!bitmap->meta); + QLIST_REMOVE(bitmap, list); + hbitmap_free(bitmap->bitmap); +@@ -325,7 +329,7 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, + /** + * 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 un-frozen, but not explicitly re-enabled. ++ * The merged parent will not be user_locked, nor explicitly re-enabled. + * Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken. + */ + BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, +@@ -373,7 +377,8 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes) + + bdrv_dirty_bitmaps_lock(bs); + QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { +- assert(!bdrv_dirty_bitmap_frozen(bitmap)); ++ assert(!bdrv_dirty_bitmap_user_locked(bitmap)); ++ assert(!bdrv_dirty_bitmap_has_successor(bitmap)); + assert(!bitmap->active_iterators); + hbitmap_truncate(bitmap->bitmap, bytes); + bitmap->size = bytes; +@@ -391,7 +396,7 @@ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) + + /** + * Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()). +- * There must not be any frozen bitmaps attached. ++ * There must not be any user_locked bitmaps attached. + * This function does not remove persistent bitmaps from the storage. + * Called with BQL taken. + */ +@@ -428,7 +433,6 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, + void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) + { + bdrv_dirty_bitmap_lock(bitmap); +- assert(!bdrv_dirty_bitmap_frozen(bitmap)); + bitmap->disabled = true; + bdrv_dirty_bitmap_unlock(bitmap); + } +diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h +index 04a117f..cdbb4df 100644 +--- a/include/block/dirty-bitmap.h ++++ b/include/block/dirty-bitmap.h +@@ -36,7 +36,7 @@ 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_frozen(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); + DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap); +diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c +index ffe7aca..89b2a2f 100644 +--- a/migration/block-dirty-bitmap.c ++++ b/migration/block-dirty-bitmap.c +@@ -542,7 +542,7 @@ static void dirty_bitmap_load_complete(QEMUFile *f, DirtyBitmapLoadState *s) + } + } + +- if (bdrv_dirty_bitmap_frozen(s->bitmap)) { ++ if (bdrv_dirty_bitmap_has_successor(s->bitmap)) { + bdrv_dirty_bitmap_lock(s->bitmap); + if (enabled_bitmaps == NULL) { + /* in postcopy */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-dirty-bitmaps-unify-qmp_locked-and-user_locked.patch b/SOURCES/kvm-block-dirty-bitmaps-unify-qmp_locked-and-user_locked.patch new file mode 100644 index 0000000..4191e41 --- /dev/null +++ b/SOURCES/kvm-block-dirty-bitmaps-unify-qmp_locked-and-user_locked.patch @@ -0,0 +1,362 @@ +From dc1a43268c8f0859cce105a0025c3b7affc6d52c Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 18:18:45 +0200 +Subject: [PATCH 140/163] block/dirty-bitmaps: unify qmp_locked and user_locked + calls + +RH-Author: John Snow +Message-id: <20190403181857.9693-10-jsnow@redhat.com> +Patchwork-id: 85417 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 09/21] block/dirty-bitmaps: unify qmp_locked and user_locked calls +Bugzilla: 1677073 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Sergio Lopez Pascual + +These mean the same thing now. Unify them and rename the merged call +bdrv_dirty_bitmap_busy to indicate semantically what we are describing, +as well as help disambiguate from the various _locked and _unlocked +versions of bitmap helpers that refer to mutex locks. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20190223000614.13894-8-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 27a1b301a448a0426a96716484fc034b73c10c51) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 43 +++++++++++++++++++----------------------- + blockdev.c | 18 +++++++++--------- + include/block/dirty-bitmap.h | 5 ++--- + migration/block-dirty-bitmap.c | 6 +++--- + nbd/server.c | 6 +++--- + 5 files changed, 36 insertions(+), 42 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index a495178..89c9665 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -48,8 +48,7 @@ struct BdrvDirtyBitmap { + QemuMutex *mutex; + HBitmap *bitmap; /* Dirty bitmap implementation */ + HBitmap *meta; /* Meta dirty bitmap */ +- bool qmp_locked; /* Bitmap is locked, it can't be modified +- through QMP */ ++ 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 */ +@@ -188,22 +187,18 @@ bool bdrv_dirty_bitmap_has_successor(BdrvDirtyBitmap *bitmap) + return bitmap->successor; + } + +-bool bdrv_dirty_bitmap_user_locked(BdrvDirtyBitmap *bitmap) { +- return bdrv_dirty_bitmap_qmp_locked(bitmap); ++bool bdrv_dirty_bitmap_busy(BdrvDirtyBitmap *bitmap) ++{ ++ return bitmap->busy; + } + +-void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked) ++void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy) + { + qemu_mutex_lock(bitmap->mutex); +- bitmap->qmp_locked = qmp_locked; ++ bitmap->busy = busy; + qemu_mutex_unlock(bitmap->mutex); + } + +-bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap) +-{ +- return bitmap->qmp_locked; +-} +- + /* Called with BQL taken. */ + bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap) + { +@@ -215,7 +210,7 @@ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap) + { + if (bdrv_dirty_bitmap_has_successor(bitmap)) { + return DIRTY_BITMAP_STATUS_FROZEN; +- } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) { ++ } else if (bdrv_dirty_bitmap_busy(bitmap)) { + return DIRTY_BITMAP_STATUS_LOCKED; + } else if (!bdrv_dirty_bitmap_enabled(bitmap)) { + return DIRTY_BITMAP_STATUS_DISABLED; +@@ -233,7 +228,7 @@ static bool bdrv_dirty_bitmap_recording(BdrvDirtyBitmap *bitmap) + + /** + * Create a successor bitmap destined to replace this bitmap after an operation. +- * Requires that the bitmap is not user_locked and has no successor. ++ * 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. + */ +@@ -243,7 +238,7 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, + uint64_t granularity; + BdrvDirtyBitmap *child; + +- if (bdrv_dirty_bitmap_user_locked(bitmap)) { ++ if (bdrv_dirty_bitmap_busy(bitmap)) { + error_setg(errp, "Cannot create a successor for a bitmap that is " + "in-use by an operation"); + return -1; +@@ -265,9 +260,9 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, + child->disabled = bitmap->disabled; + bitmap->disabled = true; + +- /* Install the successor and lock the parent */ ++ /* Install the successor and mark the parent as busy */ + bitmap->successor = child; +- bitmap->qmp_locked = true; ++ bitmap->busy = true; + return 0; + } + +@@ -289,7 +284,7 @@ void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap) + static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) + { + assert(!bitmap->active_iterators); +- assert(!bdrv_dirty_bitmap_user_locked(bitmap)); ++ assert(!bdrv_dirty_bitmap_busy(bitmap)); + assert(!bdrv_dirty_bitmap_has_successor(bitmap)); + assert(!bitmap->meta); + QLIST_REMOVE(bitmap, list); +@@ -322,7 +317,7 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, + bitmap->successor = NULL; + successor->persistent = bitmap->persistent; + bitmap->persistent = false; +- bitmap->qmp_locked = false; ++ bitmap->busy = false; + bdrv_release_dirty_bitmap(bs, bitmap); + + return successor; +@@ -331,7 +326,7 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, + /** + * 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 not be user_locked. ++ * 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. + */ +@@ -352,7 +347,7 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, + } + + parent->disabled = successor->disabled; +- parent->qmp_locked = false; ++ parent->busy = false; + bdrv_release_dirty_bitmap_locked(successor); + parent->successor = NULL; + +@@ -383,7 +378,7 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes) + + bdrv_dirty_bitmaps_lock(bs); + QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { +- assert(!bdrv_dirty_bitmap_user_locked(bitmap)); ++ assert(!bdrv_dirty_bitmap_busy(bitmap)); + assert(!bdrv_dirty_bitmap_has_successor(bitmap)); + assert(!bitmap->active_iterators); + hbitmap_truncate(bitmap->bitmap, bytes); +@@ -402,7 +397,7 @@ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) + + /** + * Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()). +- * There must not be any user_locked bitmaps attached. ++ * There must not be any busy bitmaps attached. + * This function does not remove persistent bitmaps from the storage. + * Called with BQL taken. + */ +@@ -466,7 +461,7 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) + info->name = g_strdup(bm->name); + info->status = bdrv_dirty_bitmap_status(bm); + info->recording = bdrv_dirty_bitmap_recording(bm); +- info->busy = bdrv_dirty_bitmap_user_locked(bm); ++ info->busy = bdrv_dirty_bitmap_busy(bm); + info->persistent = bm->persistent; + entry->value = info; + *plist = entry; +@@ -774,7 +769,7 @@ void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, + + qemu_mutex_lock(dest->mutex); + +- if (bdrv_dirty_bitmap_user_locked(dest)) { ++ if (bdrv_dirty_bitmap_busy(dest)) { + error_setg(errp, "Bitmap '%s' is currently in use by another" + " operation and cannot be modified", dest->name); + goto out; +diff --git a/blockdev.c b/blockdev.c +index f437896..c9ade12 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -2161,7 +2161,7 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common, + return; + } + +- if (bdrv_dirty_bitmap_user_locked(state->bitmap)) { ++ if (bdrv_dirty_bitmap_busy(state->bitmap)) { + error_setg(errp, "Cannot modify a bitmap in use by another operation"); + return; + } else if (bdrv_dirty_bitmap_readonly(state->bitmap)) { +@@ -2210,7 +2210,7 @@ static void block_dirty_bitmap_enable_prepare(BlkActionState *common, + return; + } + +- if (bdrv_dirty_bitmap_user_locked(state->bitmap)) { ++ if (bdrv_dirty_bitmap_busy(state->bitmap)) { + error_setg(errp, + "Bitmap '%s' is currently in use by another operation" + " and cannot be enabled", action->name); +@@ -2251,7 +2251,7 @@ static void block_dirty_bitmap_disable_prepare(BlkActionState *common, + return; + } + +- if (bdrv_dirty_bitmap_user_locked(state->bitmap)) { ++ if (bdrv_dirty_bitmap_busy(state->bitmap)) { + error_setg(errp, + "Bitmap '%s' is currently in use by another operation" + " and cannot be disabled", action->name); +@@ -3045,7 +3045,7 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name, + return; + } + +- if (bdrv_dirty_bitmap_user_locked(bitmap)) { ++ if (bdrv_dirty_bitmap_busy(bitmap)) { + error_setg(errp, + "Bitmap '%s' is currently in use by another operation and" + " cannot be removed", name); +@@ -3084,7 +3084,7 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name, + return; + } + +- if (bdrv_dirty_bitmap_user_locked(bitmap)) { ++ if (bdrv_dirty_bitmap_busy(bitmap)) { + error_setg(errp, + "Bitmap '%s' is currently in use by another operation" + " and cannot be cleared", name); +@@ -3108,7 +3108,7 @@ void qmp_block_dirty_bitmap_enable(const char *node, const char *name, + return; + } + +- if (bdrv_dirty_bitmap_user_locked(bitmap)) { ++ if (bdrv_dirty_bitmap_busy(bitmap)) { + error_setg(errp, + "Bitmap '%s' is currently in use by another operation" + " and cannot be enabled", name); +@@ -3129,7 +3129,7 @@ void qmp_block_dirty_bitmap_disable(const char *node, const char *name, + return; + } + +- if (bdrv_dirty_bitmap_user_locked(bitmap)) { ++ if (bdrv_dirty_bitmap_busy(bitmap)) { + error_setg(errp, + "Bitmap '%s' is currently in use by another operation" + " and cannot be disabled", name); +@@ -3710,7 +3710,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, + bdrv_unref(target_bs); + goto out; + } +- if (bdrv_dirty_bitmap_user_locked(bmap)) { ++ if (bdrv_dirty_bitmap_busy(bmap)) { + error_setg(errp, + "Bitmap '%s' is currently in use by another operation" + " and cannot be used for backup", backup->bitmap); +@@ -3817,7 +3817,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, + error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap); + goto out; + } +- if (bdrv_dirty_bitmap_user_locked(bmap)) { ++ if (bdrv_dirty_bitmap_busy(bmap)) { + error_setg(errp, + "Bitmap '%s' is currently in use by another operation" + " and cannot be used for backup", backup->bitmap); +diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h +index cdbb4df..ba8477b 100644 +--- a/include/block/dirty-bitmap.h ++++ b/include/block/dirty-bitmap.h +@@ -68,7 +68,7 @@ void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap); + void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value); + void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, + bool persistent); +-void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked); ++void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy); + void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, + HBitmap **backup, Error **errp); + void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration); +@@ -91,8 +91,7 @@ bool bdrv_dirty_bitmap_readonly(const BdrvDirtyBitmap *bitmap); + bool bdrv_has_readonly_bitmaps(BlockDriverState *bs); + bool bdrv_dirty_bitmap_get_autoload(const BdrvDirtyBitmap *bitmap); + bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap); +-bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap); +-bool bdrv_dirty_bitmap_user_locked(BdrvDirtyBitmap *bitmap); ++bool bdrv_dirty_bitmap_busy(BdrvDirtyBitmap *bitmap); + bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs); + BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap); +diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c +index 89b2a2f..62f2806 100644 +--- a/migration/block-dirty-bitmap.c ++++ b/migration/block-dirty-bitmap.c +@@ -261,7 +261,7 @@ static void dirty_bitmap_mig_cleanup(void) + + while ((dbms = QSIMPLEQ_FIRST(&dirty_bitmap_mig_state.dbms_list)) != NULL) { + QSIMPLEQ_REMOVE_HEAD(&dirty_bitmap_mig_state.dbms_list, entry); +- bdrv_dirty_bitmap_set_qmp_locked(dbms->bitmap, false); ++ bdrv_dirty_bitmap_set_busy(dbms->bitmap, false); + bdrv_unref(dbms->bs); + g_free(dbms); + } +@@ -301,7 +301,7 @@ static int init_dirty_bitmap_migration(void) + goto fail; + } + +- if (bdrv_dirty_bitmap_user_locked(bitmap)) { ++ if (bdrv_dirty_bitmap_busy(bitmap)) { + error_report("Can't migrate a bitmap that is in use by another operation: '%s'", + bdrv_dirty_bitmap_name(bitmap)); + goto fail; +@@ -314,7 +314,7 @@ static int init_dirty_bitmap_migration(void) + } + + bdrv_ref(bs); +- bdrv_dirty_bitmap_set_qmp_locked(bitmap, true); ++ bdrv_dirty_bitmap_set_busy(bitmap, true); + + dbms = g_new0(DirtyBitmapMigBitmapState, 1); + dbms->bs = bs; +diff --git a/nbd/server.c b/nbd/server.c +index de21c64..02773e2 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -1510,7 +1510,7 @@ NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset, + goto fail; + } + +- if (bdrv_dirty_bitmap_user_locked(bm)) { ++ if (bdrv_dirty_bitmap_busy(bm)) { + error_setg(errp, "Bitmap '%s' is in use", bitmap); + goto fail; + } +@@ -1523,7 +1523,7 @@ NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset, + goto fail; + } + +- bdrv_dirty_bitmap_set_qmp_locked(bm, true); ++ bdrv_dirty_bitmap_set_busy(bm, true); + exp->export_bitmap = bm; + exp->export_bitmap_context = g_strdup_printf("qemu:dirty-bitmap:%s", + bitmap); +@@ -1641,7 +1641,7 @@ void nbd_export_put(NBDExport *exp) + } + + if (exp->export_bitmap) { +- bdrv_dirty_bitmap_set_qmp_locked(exp->export_bitmap, false); ++ bdrv_dirty_bitmap_set_busy(exp->export_bitmap, false); + g_free(exp->export_bitmap_context); + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-file-posix-File-locking-during-creation.patch b/SOURCES/kvm-block-file-posix-File-locking-during-creation.patch new file mode 100644 index 0000000..e5f9543 --- /dev/null +++ b/SOURCES/kvm-block-file-posix-File-locking-during-creation.patch @@ -0,0 +1,102 @@ +From 8a3d5f5ed54868329ab665e6f2ebe7eb2fcb2055 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 14:53:36 +0200 +Subject: [PATCH 07/57] block/file-posix: File locking during creation + +RH-Author: Max Reitz +Message-id: <20180618145337.633-3-mreitz@redhat.com> +Patchwork-id: 80749 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 2/3] block/file-posix: File locking during creation +Bugzilla: 1519144 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng +RH-Acked-by: Miroslav Rezanina + +When creating a file, we should take the WRITE and RESIZE permissions. +We do not need either for the creation itself, but we do need them for +clearing and resizing it. So we can take the proper permissions by +replacing O_TRUNC with an explicit truncation to 0, and by taking the +appropriate file locks between those two steps. + +Signed-off-by: Max Reitz +Message-id: 20180509215336.31304-3-mreitz@redhat.com +Reviewed-by: Fam Zheng +Signed-off-by: Max Reitz +(cherry picked from commit b8cf1913a989b9ea6f248aaa233330047a62962e) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 37 +++++++++++++++++++++++++++++++++++-- + 1 file changed, 35 insertions(+), 2 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index c98a4a1..370a483 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -1992,6 +1992,7 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp) + { + BlockdevCreateOptionsFile *file_opts; + int fd; ++ int perm, shared; + int result = 0; + + /* Validate options and set default values */ +@@ -2006,14 +2007,44 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp) + } + + /* Create file */ +- fd = qemu_open(file_opts->filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, +- 0644); ++ fd = qemu_open(file_opts->filename, O_RDWR | O_CREAT | O_BINARY, 0644); + if (fd < 0) { + result = -errno; + error_setg_errno(errp, -result, "Could not create file"); + 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(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) { ++ goto out_close; ++ } ++ ++ /* Clear the file by truncating it to 0 */ ++ result = raw_regular_truncate(fd, 0, PREALLOC_MODE_OFF, errp); ++ if (result < 0) { ++ goto out_close; ++ } ++ + if (file_opts->nocow) { + #ifdef __linux__ + /* Set NOCOW flag to solve performance issue on fs like btrfs. +@@ -2029,6 +2060,8 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp) + #endif + } + ++ /* Resize and potentially preallocate the file to the desired ++ * final size */ + result = raw_regular_truncate(fd, file_opts->size, file_opts->preallocation, + errp); + if (result < 0) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-file-posix-Pass-FD-to-locking-helpers.patch b/SOURCES/kvm-block-file-posix-Pass-FD-to-locking-helpers.patch new file mode 100644 index 0000000..cff8930 --- /dev/null +++ b/SOURCES/kvm-block-file-posix-Pass-FD-to-locking-helpers.patch @@ -0,0 +1,141 @@ +From 4667112e87999632688621951ce8c2ec8a0ebb8d Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 14:53:35 +0200 +Subject: [PATCH 06/57] block/file-posix: Pass FD to locking helpers + +RH-Author: Max Reitz +Message-id: <20180618145337.633-2-mreitz@redhat.com> +Patchwork-id: 80751 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/3] block/file-posix: Pass FD to locking helpers +Bugzilla: 1519144 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng +RH-Acked-by: Miroslav Rezanina + +raw_apply_lock_bytes() and raw_check_lock_bytes() currently take a +BDRVRawState *, but they only use the lock_fd field. During image +creation, we do not have a BDRVRawState, but we do have an FD; so if we +want to reuse the functions there, we should modify them to receive only +the FD. + +Signed-off-by: Max Reitz +Reviewed-by: Fam Zheng +Message-id: 20180509215336.31304-2-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit d0a96155de099d388f5e1f46277a54bdcfbb0bb2) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 27 ++++++++++++++------------- + 1 file changed, 14 insertions(+), 13 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index 3794c00..c98a4a1 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -630,7 +630,7 @@ typedef enum { + * 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, ++static int raw_apply_lock_bytes(int fd, + uint64_t perm_lock_bits, + uint64_t shared_perm_lock_bits, + bool unlock, Error **errp) +@@ -641,13 +641,13 @@ static int raw_apply_lock_bytes(BDRVRawState *s, + PERM_FOREACH(i) { + int off = RAW_LOCK_PERM_BASE + i; + if (perm_lock_bits & (1ULL << i)) { +- ret = qemu_lock_fd(s->lock_fd, off, 1, false); ++ ret = qemu_lock_fd(fd, off, 1, false); + if (ret) { + error_setg(errp, "Failed to lock byte %d", off); + return ret; + } + } else if (unlock) { +- ret = qemu_unlock_fd(s->lock_fd, off, 1); ++ ret = qemu_unlock_fd(fd, off, 1); + if (ret) { + error_setg(errp, "Failed to unlock byte %d", off); + return ret; +@@ -657,13 +657,13 @@ static int raw_apply_lock_bytes(BDRVRawState *s, + PERM_FOREACH(i) { + int off = RAW_LOCK_SHARED_BASE + i; + if (shared_perm_lock_bits & (1ULL << i)) { +- ret = qemu_lock_fd(s->lock_fd, off, 1, false); ++ ret = qemu_lock_fd(fd, off, 1, false); + if (ret) { + error_setg(errp, "Failed to lock byte %d", off); + return ret; + } + } else if (unlock) { +- ret = qemu_unlock_fd(s->lock_fd, off, 1); ++ ret = qemu_unlock_fd(fd, off, 1); + if (ret) { + error_setg(errp, "Failed to unlock byte %d", off); + return ret; +@@ -674,8 +674,7 @@ static int raw_apply_lock_bytes(BDRVRawState *s, + } + + /* Check "unshared" bytes implied by @perm and ~@shared_perm in the file. */ +-static int raw_check_lock_bytes(BDRVRawState *s, +- uint64_t perm, uint64_t shared_perm, ++static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm, + Error **errp) + { + int ret; +@@ -685,7 +684,7 @@ static int raw_check_lock_bytes(BDRVRawState *s, + int off = RAW_LOCK_SHARED_BASE + i; + uint64_t p = 1ULL << i; + if (perm & p) { +- ret = qemu_lock_fd_test(s->lock_fd, off, 1, true); ++ ret = qemu_lock_fd_test(fd, off, 1, true); + if (ret) { + char *perm_name = bdrv_perm_names(p); + error_setg(errp, +@@ -702,7 +701,7 @@ static int raw_check_lock_bytes(BDRVRawState *s, + int off = RAW_LOCK_PERM_BASE + i; + uint64_t p = 1ULL << i; + if (!(shared_perm & p)) { +- ret = qemu_lock_fd_test(s->lock_fd, off, 1, true); ++ ret = qemu_lock_fd_test(fd, off, 1, true); + if (ret) { + char *perm_name = bdrv_perm_names(p); + error_setg(errp, +@@ -739,11 +738,11 @@ static int raw_handle_perm_lock(BlockDriverState *bs, + + switch (op) { + case RAW_PL_PREPARE: +- ret = raw_apply_lock_bytes(s, s->perm | new_perm, ++ ret = raw_apply_lock_bytes(s->lock_fd, s->perm | new_perm, + ~s->shared_perm | ~new_shared, + false, errp); + if (!ret) { +- ret = raw_check_lock_bytes(s, new_perm, new_shared, errp); ++ ret = raw_check_lock_bytes(s->lock_fd, new_perm, new_shared, errp); + if (!ret) { + return 0; + } +@@ -751,7 +750,8 @@ static int raw_handle_perm_lock(BlockDriverState *bs, + op = RAW_PL_ABORT; + /* fall through to unlock bytes. */ + case RAW_PL_ABORT: +- raw_apply_lock_bytes(s, s->perm, ~s->shared_perm, true, &local_err); ++ raw_apply_lock_bytes(s->lock_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. +@@ -760,7 +760,8 @@ static int raw_handle_perm_lock(BlockDriverState *bs, + } + break; + case RAW_PL_COMMIT: +- raw_apply_lock_bytes(s, new_perm, ~new_shared, true, &local_err); ++ raw_apply_lock_bytes(s->lock_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. +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-file-posix-do-not-fail-on-unlock-bytes.patch b/SOURCES/kvm-block-file-posix-do-not-fail-on-unlock-bytes.patch new file mode 100644 index 0000000..11e181f --- /dev/null +++ b/SOURCES/kvm-block-file-posix-do-not-fail-on-unlock-bytes.patch @@ -0,0 +1,58 @@ +From 82d4a912b8b197d9d2f56ffc54ca89c535a0f47c Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Wed, 3 Apr 2019 18:43:30 +0200 +Subject: [PATCH 153/163] block/file-posix: do not fail on unlock bytes + +RH-Author: Max Reitz +Message-id: <20190403184330.1210-2-mreitz@redhat.com> +Patchwork-id: 85432 +O-Subject: [RHV-7.7 qemu-kvm-rhev PATCH 1/1] block/file-posix: do not fail on unlock bytes +Bugzilla: 1603104 +RH-Acked-by: John Snow +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Stefan Hajnoczi + +From: Vladimir Sementsov-Ogievskiy + +bdrv_replace_child() calls bdrv_check_perm() with error_abort on +loosening permissions. However file-locking operations may fail even +in this case, for example on NFS. And this leads to Qemu crash. + +Let's avoid such errors. Note, that we ignore such things anyway on +permission update commit and abort. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Signed-off-by: Kevin Wolf +(cherry picked from commit 696aaaed579ac5bf5fa336216909b46d3d8f07a8) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/block/file-posix.c b/block/file-posix.c +index 0cf7261..518f16b 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -795,6 +795,18 @@ static int raw_handle_perm_lock(BlockDriverState *bs, + + 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); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-fix-QEMU-crash-with-scsi-hd-and-drive_del.patch b/SOURCES/kvm-block-fix-QEMU-crash-with-scsi-hd-and-drive_del.patch new file mode 100644 index 0000000..88bc661 --- /dev/null +++ b/SOURCES/kvm-block-fix-QEMU-crash-with-scsi-hd-and-drive_del.patch @@ -0,0 +1,90 @@ +From 08e7b4d1dc3f71818fec2ebd5f189142011969a9 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Thu, 12 Jul 2018 16:06:19 +0200 +Subject: [PATCH 41/89] block: fix QEMU crash with scsi-hd and drive_del + +RH-Author: Kevin Wolf +Message-id: <20180712160619.30712-2-kwolf@redhat.com> +Patchwork-id: 81334 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/1] block: fix QEMU crash with scsi-hd and drive_del +Bugzilla: 1599515 +RH-Acked-by: Fam Zheng +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi + +From: Greg Kurz + +Removing a drive with drive_del while it is being used to run an I/O +intensive workload can cause QEMU to crash. + +An AIO flush can yield at some point: + +blk_aio_flush_entry() + blk_co_flush(blk) + bdrv_co_flush(blk->root->bs) + ... + qemu_coroutine_yield() + +and let the HMP command to run, free blk->root and give control +back to the AIO flush: + + hmp_drive_del() + blk_remove_bs() + bdrv_root_unref_child(blk->root) + child_bs = blk->root->bs + bdrv_detach_child(blk->root) + bdrv_replace_child(blk->root, NULL) + blk->root->bs = NULL + g_free(blk->root) <============== blk->root becomes stale + bdrv_unref(child_bs) + bdrv_delete(child_bs) + bdrv_close() + bdrv_drained_begin() + bdrv_do_drained_begin() + bdrv_drain_recurse() + aio_poll() + ... + qemu_coroutine_switch() + +and the AIO flush completion ends up dereferencing blk->root: + + blk_aio_complete() + scsi_aio_complete() + blk_get_aio_context(blk) + bs = blk_bs(blk) + ie, bs = blk->root ? blk->root->bs : NULL + ^^^^^ + stale + +The problem is that we should avoid making block driver graph +changes while we have in-flight requests. Let's drain all I/O +for this BB before calling bdrv_root_unref_child(). + +Signed-off-by: Greg Kurz +Signed-off-by: Kevin Wolf +(cherry picked from commit f45280cbf66d8e58224f6a253d0ae2aa72cc6280) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/block-backend.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/block/block-backend.c b/block/block-backend.c +index 56ae535..a469fc6 100644 +--- a/block/block-backend.c ++++ b/block/block-backend.c +@@ -768,6 +768,11 @@ void blk_remove_bs(BlockBackend *blk) + + 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); + bdrv_root_unref_child(blk->root); + blk->root = NULL; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-for-jobs-do-not-clear-user_paused-until-after-.patch b/SOURCES/kvm-block-for-jobs-do-not-clear-user_paused-until-after-.patch new file mode 100644 index 0000000..60ac8bb --- /dev/null +++ b/SOURCES/kvm-block-for-jobs-do-not-clear-user_paused-until-after-.patch @@ -0,0 +1,58 @@ +From 9382be6ea4b3785bcc3a4868e1f69348157ba5b1 Mon Sep 17 00:00:00 2001 +From: Jeffrey Cody +Date: Tue, 28 Aug 2018 21:08:16 +0200 +Subject: [PATCH 03/29] block: for jobs, do not clear user_paused until after + the resume + +RH-Author: Jeffrey Cody +Message-id: +Patchwork-id: 81957 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/3] block: for jobs, do not clear user_paused until after the resume +Bugzilla: 1605026 +RH-Acked-by: Max Reitz +RH-Acked-by: John Snow +RH-Acked-by: Miroslav Rezanina + +The function job_cancel_async() will always cause an assert for blockjob +user resume. We set job->user_paused to false, and then call +job->driver->user_resume(). In the case of blockjobs, this is the +block_job_user_resume() function. + +In that function, we assert that job.user_paused is set to true. +Unfortunately, right before calling this function, it has explicitly +been set to false. + +The fix is pretty simple: set job->user_paused to false only after the +job user_resume() function has been called. + +Reviewed-by: John Snow +Reviewed-by: Eric Blake +Signed-off-by: Jeff Cody +Message-id: bb183b77d8f2dd6bd67b8da559a90ac1e74b2052.1534868459.git.jcody@redhat.com +Signed-off-by: Jeff Cody +(cherry picked from commit e321c0597c7590499bacab239d7f86e257f96bcd) +Signed-off-by: Jeff Cody +Signed-off-by: Miroslav Rezanina +--- + job.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/job.c b/job.c +index 84e1402..95dc998 100644 +--- a/job.c ++++ b/job.c +@@ -727,10 +727,10 @@ static void job_cancel_async(Job *job, bool force) + { + if (job->user_paused) { + /* Do not call job_enter here, the caller will handle it. */ +- job->user_paused = false; + if (job->driver->user_resume) { + job->driver->user_resume(job); + } ++ job->user_paused = false; + assert(job->pause_count > 0); + job->pause_count--; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-ignore_bds_parents-parameter-for-drain-functio.patch b/SOURCES/kvm-block-ignore_bds_parents-parameter-for-drain-functio.patch new file mode 100644 index 0000000..2e3eea4 --- /dev/null +++ b/SOURCES/kvm-block-ignore_bds_parents-parameter-for-drain-functio.patch @@ -0,0 +1,481 @@ +From 83fe250c63d9a291267155bae9b86d983062c9d7 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:16 +0200 +Subject: [PATCH 25/49] block: ignore_bds_parents parameter for drain functions +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-19-kwolf@redhat.com> +Patchwork-id: 82170 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 18/42] block: ignore_bds_parents parameter for drain functions +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +In the future, bdrv_drained_all_begin/end() will drain all invidiual +nodes separately rather than whole subtrees. This means that we don't +want to propagate the drain to all parents any more: If the parent is a +BDS, it will already be drained separately. Recursing to all parents is +unnecessary work and would make it an O(n²) operation. + +Prepare the drain function for the changed drain_all by adding an +ignore_bds_parents parameter to the internal implementation that +prevents the propagation of the drain to BDS parents. We still (have to) +propagate it to non-BDS parents like BlockBackends or Jobs because those +are not drained separately. + +Signed-off-by: Kevin Wolf +(cherry picked from commit 6cd5c9d7b2df93ef54144f170d4c908934a4767f) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 11 +++--- + block/io.c | 88 ++++++++++++++++++++++++++++------------------- + block/vvfat.c | 1 + + include/block/block.h | 16 ++++++--- + include/block/block_int.h | 6 ++++ + 5 files changed, 78 insertions(+), 44 deletions(-) + +diff --git a/block.c b/block.c +index 5a50de6..519be6e 100644 +--- a/block.c ++++ b/block.c +@@ -818,13 +818,13 @@ static char *bdrv_child_get_parent_desc(BdrvChild *c) + static void bdrv_child_cb_drained_begin(BdrvChild *child) + { + BlockDriverState *bs = child->opaque; +- bdrv_do_drained_begin_quiesce(bs, NULL); ++ bdrv_do_drained_begin_quiesce(bs, NULL, false); + } + + static bool bdrv_child_cb_drained_poll(BdrvChild *child) + { + BlockDriverState *bs = child->opaque; +- return bdrv_drain_poll(bs, false, NULL); ++ return bdrv_drain_poll(bs, false, NULL, false); + } + + static void bdrv_child_cb_drained_end(BdrvChild *child) +@@ -908,6 +908,7 @@ static void bdrv_inherited_options(int *child_flags, QDict *child_options, + } + + const BdrvChildRole child_file = { ++ .parent_is_bds = true, + .get_parent_desc = bdrv_child_get_parent_desc, + .inherit_options = bdrv_inherited_options, + .drained_begin = bdrv_child_cb_drained_begin, +@@ -933,6 +934,7 @@ static void bdrv_inherited_fmt_options(int *child_flags, QDict *child_options, + } + + const BdrvChildRole child_format = { ++ .parent_is_bds = true, + .get_parent_desc = bdrv_child_get_parent_desc, + .inherit_options = bdrv_inherited_fmt_options, + .drained_begin = bdrv_child_cb_drained_begin, +@@ -1051,6 +1053,7 @@ static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base, + } + + const BdrvChildRole child_backing = { ++ .parent_is_bds = true, + .get_parent_desc = bdrv_child_get_parent_desc, + .attach = bdrv_backing_attach, + .detach = bdrv_backing_detach, +@@ -4901,7 +4904,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context) + AioContext *ctx = bdrv_get_aio_context(bs); + + aio_disable_external(ctx); +- bdrv_parent_drained_begin(bs, NULL); ++ bdrv_parent_drained_begin(bs, NULL, false); + bdrv_drain(bs); /* ensure there are no in-flight requests */ + + while (aio_poll(ctx, false)) { +@@ -4915,7 +4918,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context) + */ + aio_context_acquire(new_context); + bdrv_attach_aio_context(bs, new_context); +- bdrv_parent_drained_end(bs, NULL); ++ bdrv_parent_drained_end(bs, NULL, false); + aio_enable_external(ctx); + aio_context_release(new_context); + } +diff --git a/block/io.c b/block/io.c +index b58c91b..0021fefd 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -41,12 +41,13 @@ + static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, + int64_t offset, int bytes, BdrvRequestFlags flags); + +-void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore) ++void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore, ++ bool ignore_bds_parents) + { + BdrvChild *c, *next; + + QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { +- if (c == ignore) { ++ if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) { + continue; + } + if (c->role->drained_begin) { +@@ -55,12 +56,13 @@ void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore) + } + } + +-void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore) ++void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore, ++ bool ignore_bds_parents) + { + BdrvChild *c, *next; + + QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { +- if (c == ignore) { ++ if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) { + continue; + } + if (c->role->drained_end) { +@@ -69,13 +71,14 @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore) + } + } + +-static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore) ++static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore, ++ bool ignore_bds_parents) + { + BdrvChild *c, *next; + bool busy = false; + + QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { +- if (c == ignore) { ++ if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) { + continue; + } + if (c->role->drained_poll) { +@@ -166,6 +169,7 @@ typedef struct { + bool recursive; + bool poll; + BdrvChild *parent; ++ bool ignore_bds_parents; + } BdrvCoDrainData; + + static void coroutine_fn bdrv_drain_invoke_entry(void *opaque) +@@ -219,11 +223,11 @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) + + /* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */ + bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, +- BdrvChild *ignore_parent) ++ BdrvChild *ignore_parent, bool ignore_bds_parents) + { + BdrvChild *child, *next; + +- if (bdrv_parent_drained_poll(bs, ignore_parent)) { ++ if (bdrv_parent_drained_poll(bs, ignore_parent, ignore_bds_parents)) { + return true; + } + +@@ -232,8 +236,9 @@ bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, + } + + if (recursive) { ++ assert(!ignore_bds_parents); + QLIST_FOREACH_SAFE(child, &bs->children, next, next) { +- if (bdrv_drain_poll(child->bs, recursive, child)) { ++ if (bdrv_drain_poll(child->bs, recursive, child, false)) { + return true; + } + } +@@ -249,13 +254,14 @@ static bool bdrv_drain_poll_top_level(BlockDriverState *bs, bool recursive, + * have executed. */ + while (aio_poll(bs->aio_context, false)); + +- return bdrv_drain_poll(bs, recursive, ignore_parent); ++ return bdrv_drain_poll(bs, recursive, ignore_parent, false); + } + + static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, +- BdrvChild *parent, bool poll); ++ BdrvChild *parent, bool ignore_bds_parents, ++ bool poll); + static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, +- BdrvChild *parent); ++ BdrvChild *parent, bool ignore_bds_parents); + + static void bdrv_co_drain_bh_cb(void *opaque) + { +@@ -266,9 +272,11 @@ static void bdrv_co_drain_bh_cb(void *opaque) + if (bs) { + bdrv_dec_in_flight(bs); + if (data->begin) { +- bdrv_do_drained_begin(bs, data->recursive, data->parent, data->poll); ++ bdrv_do_drained_begin(bs, data->recursive, data->parent, ++ data->ignore_bds_parents, data->poll); + } else { +- bdrv_do_drained_end(bs, data->recursive, data->parent); ++ bdrv_do_drained_end(bs, data->recursive, data->parent, ++ data->ignore_bds_parents); + } + } else { + assert(data->begin); +@@ -281,7 +289,9 @@ static void bdrv_co_drain_bh_cb(void *opaque) + + static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, + bool begin, bool recursive, +- BdrvChild *parent, bool poll) ++ BdrvChild *parent, ++ bool ignore_bds_parents, ++ bool poll) + { + BdrvCoDrainData data; + +@@ -296,6 +306,7 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, + .begin = begin, + .recursive = recursive, + .parent = parent, ++ .ignore_bds_parents = ignore_bds_parents, + .poll = poll, + }; + if (bs) { +@@ -311,7 +322,7 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, + } + + void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, +- BdrvChild *parent) ++ BdrvChild *parent, bool ignore_bds_parents) + { + assert(!qemu_in_coroutine()); + +@@ -320,26 +331,30 @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, + aio_disable_external(bdrv_get_aio_context(bs)); + } + +- bdrv_parent_drained_begin(bs, parent); ++ bdrv_parent_drained_begin(bs, parent, ignore_bds_parents); + bdrv_drain_invoke(bs, true); + } + + static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, +- BdrvChild *parent, bool poll) ++ BdrvChild *parent, bool ignore_bds_parents, ++ bool poll) + { + BdrvChild *child, *next; + + if (qemu_in_coroutine()) { +- bdrv_co_yield_to_drain(bs, true, recursive, parent, poll); ++ bdrv_co_yield_to_drain(bs, true, recursive, parent, ignore_bds_parents, ++ poll); + return; + } + +- bdrv_do_drained_begin_quiesce(bs, parent); ++ bdrv_do_drained_begin_quiesce(bs, parent, ignore_bds_parents); + + if (recursive) { ++ assert(!ignore_bds_parents); + bs->recursive_quiesce_counter++; + QLIST_FOREACH_SAFE(child, &bs->children, next, next) { +- bdrv_do_drained_begin(child->bs, true, child, false); ++ bdrv_do_drained_begin(child->bs, true, child, ignore_bds_parents, ++ false); + } + } + +@@ -353,28 +368,30 @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, + * nodes. + */ + if (poll) { ++ assert(!ignore_bds_parents); + BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, recursive, parent)); + } + } + + void bdrv_drained_begin(BlockDriverState *bs) + { +- bdrv_do_drained_begin(bs, false, NULL, true); ++ bdrv_do_drained_begin(bs, false, NULL, false, true); + } + + void bdrv_subtree_drained_begin(BlockDriverState *bs) + { +- bdrv_do_drained_begin(bs, true, NULL, true); ++ bdrv_do_drained_begin(bs, true, NULL, false, true); + } + +-void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, +- BdrvChild *parent) ++static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, ++ BdrvChild *parent, bool ignore_bds_parents) + { + BdrvChild *child, *next; + int old_quiesce_counter; + + if (qemu_in_coroutine()) { +- bdrv_co_yield_to_drain(bs, false, recursive, parent, false); ++ bdrv_co_yield_to_drain(bs, false, recursive, parent, ignore_bds_parents, ++ false); + return; + } + assert(bs->quiesce_counter > 0); +@@ -382,27 +399,28 @@ void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, + + /* Re-enable things in child-to-parent order */ + bdrv_drain_invoke(bs, false); +- bdrv_parent_drained_end(bs, parent); ++ bdrv_parent_drained_end(bs, parent, ignore_bds_parents); + if (old_quiesce_counter == 1) { + aio_enable_external(bdrv_get_aio_context(bs)); + } + + if (recursive) { ++ assert(!ignore_bds_parents); + bs->recursive_quiesce_counter--; + QLIST_FOREACH_SAFE(child, &bs->children, next, next) { +- bdrv_do_drained_end(child->bs, true, child); ++ bdrv_do_drained_end(child->bs, true, child, ignore_bds_parents); + } + } + } + + void bdrv_drained_end(BlockDriverState *bs) + { +- bdrv_do_drained_end(bs, false, NULL); ++ bdrv_do_drained_end(bs, false, NULL, false); + } + + void bdrv_subtree_drained_end(BlockDriverState *bs) + { +- bdrv_do_drained_end(bs, true, NULL); ++ bdrv_do_drained_end(bs, true, NULL, false); + } + + void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent) +@@ -410,7 +428,7 @@ void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent) + int i; + + for (i = 0; i < new_parent->recursive_quiesce_counter; i++) { +- bdrv_do_drained_begin(child->bs, true, child, true); ++ bdrv_do_drained_begin(child->bs, true, child, false, true); + } + } + +@@ -419,7 +437,7 @@ void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent) + int i; + + for (i = 0; i < old_parent->recursive_quiesce_counter; i++) { +- bdrv_do_drained_end(child->bs, true, child); ++ bdrv_do_drained_end(child->bs, true, child, false); + } + } + +@@ -471,7 +489,7 @@ void bdrv_drain_all_begin(void) + BdrvNextIterator it; + + if (qemu_in_coroutine()) { +- bdrv_co_yield_to_drain(NULL, true, false, NULL, true); ++ bdrv_co_yield_to_drain(NULL, true, false, NULL, false, true); + return; + } + +@@ -485,7 +503,7 @@ void bdrv_drain_all_begin(void) + AioContext *aio_context = bdrv_get_aio_context(bs); + + aio_context_acquire(aio_context); +- bdrv_do_drained_begin(bs, true, NULL, true); ++ bdrv_do_drained_begin(bs, true, NULL, false, true); + aio_context_release(aio_context); + } + +@@ -503,7 +521,7 @@ void bdrv_drain_all_end(void) + AioContext *aio_context = bdrv_get_aio_context(bs); + + aio_context_acquire(aio_context); +- bdrv_do_drained_end(bs, true, NULL); ++ bdrv_do_drained_end(bs, true, NULL, false); + aio_context_release(aio_context); + } + } +diff --git a/block/vvfat.c b/block/vvfat.c +index 4595f33..c7d2ed2 100644 +--- a/block/vvfat.c ++++ b/block/vvfat.c +@@ -3134,6 +3134,7 @@ static void vvfat_qcow_options(int *child_flags, QDict *child_options, + } + + static const BdrvChildRole child_vvfat_qcow = { ++ .parent_is_bds = true, + .inherit_options = vvfat_qcow_options, + }; + +diff --git a/include/block/block.h b/include/block/block.h +index 43f29b5..6e91803 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -585,7 +585,8 @@ void bdrv_io_unplug(BlockDriverState *bs); + * Begin a quiesced section of all users of @bs. This is part of + * bdrv_drained_begin. + */ +-void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore); ++void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore, ++ bool ignore_bds_parents); + + /** + * bdrv_parent_drained_end: +@@ -593,18 +594,23 @@ void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore); + * End a quiesced section of all users of @bs. This is part of + * bdrv_drained_end. + */ +-void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore); ++void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore, ++ bool ignore_bds_parents); + + /** + * bdrv_drain_poll: + * + * Poll for pending requests in @bs, its parents (except for @ignore_parent), +- * and if @recursive is true its children as well. ++ * and if @recursive is true its children as well (used for subtree drain). ++ * ++ * 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 bdrv_drain_poll(BlockDriverState *bs, bool recursive, +- BdrvChild *ignore_parent); ++ BdrvChild *ignore_parent, bool ignore_bds_parents); + + /** + * bdrv_drained_begin: +@@ -625,7 +631,7 @@ void bdrv_drained_begin(BlockDriverState *bs); + * running requests to complete. + */ + void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, +- BdrvChild *parent); ++ BdrvChild *parent, bool ignore_bds_parents); + + /** + * Like bdrv_drained_begin, but recursively begins a quiesced section for +diff --git a/include/block/block_int.h b/include/block/block_int.h +index beeacde..0ad8a76 100644 +--- a/include/block/block_int.h ++++ b/include/block/block_int.h +@@ -582,6 +582,12 @@ struct BdrvChildRole { + * 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; ++ + void (*inherit_options)(int *child_flags, QDict *child_options, + int parent_flags, QDict *parent_options); + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-io-fix-copy_range.patch b/SOURCES/kvm-block-io-fix-copy_range.patch new file mode 100644 index 0000000..f411722 --- /dev/null +++ b/SOURCES/kvm-block-io-fix-copy_range.patch @@ -0,0 +1,152 @@ +From 75720bedd9d4ba79f7eff5213f30af5e6c475570 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Tue, 24 Jul 2018 12:59:12 +0200 +Subject: [PATCH 70/89] block/io: fix copy_range + +RH-Author: John Snow +Message-id: <20180718225511.14878-20-jsnow@redhat.com> +Patchwork-id: 81420 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 19/35] block/io: fix copy_range +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +Here two things are fixed: + +1. Architecture + +On each recursion step, we go to the child of src or dst, only for one +of them. So, it's wrong to create tracked requests for both on each +step. It leads to tracked requests duplication. + +2. Wait for serializing requests on write path independently of + BDRV_REQ_NO_SERIALISING + +Before commit 9ded4a01149 "backup: Use copy offloading", +BDRV_REQ_NO_SERIALISING was used for only one case: read in +copy-on-write operation during backup. Also, the flag was handled only +on read path (in bdrv_co_preadv and bdrv_aligned_preadv). + +After 9ded4a01149, flag is used for not waiting serializing operations +on backup target (in same case of copy-on-write operation). This +behavior change is unsubstantiated and potentially dangerous, let's +drop it and add additional asserts and documentation. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: Fam Zheng +Signed-off-by: Kevin Wolf +(cherry picked from commit 999658a05e61a8d87b65827da665302bb44ce8c9) +Signed-off-by: John Snow +--- + block/io.c | 43 +++++++++++++++++++++++++++---------------- + include/block/block.h | 12 ++++++++++++ + 2 files changed, 39 insertions(+), 16 deletions(-) + +diff --git a/block/io.c b/block/io.c +index b6754f3..f8de42f 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -1505,6 +1505,8 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, + max_transfer = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_transfer, INT_MAX), + align); + ++ /* BDRV_REQ_NO_SERIALISING is only for read operation */ ++ assert(!(flags & BDRV_REQ_NO_SERIALISING)); + waited = wait_serialising_requests(req); + assert(!waited || !req->serialising); + assert(req->overlap_offset <= offset); +@@ -2847,7 +2849,7 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, + BdrvRequestFlags flags, + bool recurse_src) + { +- BdrvTrackedRequest src_req, dst_req; ++ BdrvTrackedRequest req; + int ret; + + if (!dst || !dst->bs) { +@@ -2876,32 +2878,41 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, + return -ENOTSUP; + } + +- bdrv_inc_in_flight(src->bs); +- bdrv_inc_in_flight(dst->bs); +- tracked_request_begin(&src_req, src->bs, src_offset, +- bytes, BDRV_TRACKED_READ); +- tracked_request_begin(&dst_req, dst->bs, dst_offset, +- bytes, BDRV_TRACKED_WRITE); +- +- if (!(flags & BDRV_REQ_NO_SERIALISING)) { +- wait_serialising_requests(&src_req); +- wait_serialising_requests(&dst_req); +- } + if (recurse_src) { ++ bdrv_inc_in_flight(src->bs); ++ tracked_request_begin(&req, src->bs, src_offset, bytes, ++ BDRV_TRACKED_READ); ++ ++ if (!(flags & BDRV_REQ_NO_SERIALISING)) { ++ wait_serialising_requests(&req); ++ } ++ + ret = src->bs->drv->bdrv_co_copy_range_from(src->bs, + src, src_offset, + dst, dst_offset, + bytes, 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); ++ ++ /* BDRV_REQ_NO_SERIALISING is only for read operation, ++ * so we ignore it in flags. ++ */ ++ wait_serialising_requests(&req); ++ + ret = dst->bs->drv->bdrv_co_copy_range_to(dst->bs, + src, src_offset, + dst, dst_offset, + bytes, flags); ++ ++ tracked_request_end(&req); ++ bdrv_dec_in_flight(dst->bs); + } +- tracked_request_end(&src_req); +- tracked_request_end(&dst_req); +- bdrv_dec_in_flight(src->bs); +- bdrv_dec_in_flight(dst->bs); ++ + return ret; + } + +diff --git a/include/block/block.h b/include/block/block.h +index e1d5e47..716fb5b 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -50,6 +50,18 @@ typedef enum { + * opened with BDRV_O_UNMAP. + */ + BDRV_REQ_MAY_UNMAP = 0x4, ++ ++ /* ++ * The BDRV_REQ_NO_SERIALISING flag is only valid for reads and means that ++ * we don't want wait_serialising_requests() during the read operation. ++ * ++ * This flag is used for backup copy-on-write operations, when we need to ++ * read old data before write (write notifier triggered). It is okay since ++ * we already waited for other serializing requests in the initiating write ++ * (see bdrv_aligned_pwritev), and it is necessary if the initiating write ++ * is already serializing (without the flag, the read would deadlock ++ * waiting for the serialising write to complete). ++ */ + BDRV_REQ_NO_SERIALISING = 0x8, + BDRV_REQ_FUA = 0x10, + BDRV_REQ_WRITE_COMPRESSED = 0x20, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-iotest-to-catch-abort-on-forced-blockjob-cance.patch b/SOURCES/kvm-block-iotest-to-catch-abort-on-forced-blockjob-cance.patch new file mode 100644 index 0000000..fe82d9f --- /dev/null +++ b/SOURCES/kvm-block-iotest-to-catch-abort-on-forced-blockjob-cance.patch @@ -0,0 +1,171 @@ +From 981ab75d595c2aa8477cab9b514d3ba50e895c9f Mon Sep 17 00:00:00 2001 +From: Jeffrey Cody +Date: Tue, 28 Aug 2018 21:08:18 +0200 +Subject: [PATCH 05/29] block: iotest to catch abort on forced blockjob cancel + +RH-Author: Jeffrey Cody +Message-id: +Patchwork-id: 81956 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 3/3] block: iotest to catch abort on forced blockjob cancel +Bugzilla: 1605026 +RH-Acked-by: Max Reitz +RH-Acked-by: John Snow +RH-Acked-by: Miroslav Rezanina + +Signed-off-by: Jeff Cody +Reviewed-by: John Snow +Message-id: df317f617fbe5affcf699cb8560e7b0c2e028a64.1534868459.git.jcody@redhat.com +Signed-off-by: Jeff Cody +(cherry picked from commit 26bf474ba92c76e61bea51726e22da6dfd185296) +Signed-off-by: Jeff Cody +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/229 | 95 ++++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/229.out | 23 +++++++++++ + tests/qemu-iotests/group | 1 + + 3 files changed, 119 insertions(+) + create mode 100755 tests/qemu-iotests/229 + create mode 100644 tests/qemu-iotests/229.out + +diff --git a/tests/qemu-iotests/229 b/tests/qemu-iotests/229 +new file mode 100755 +index 0000000..ff851ec +--- /dev/null ++++ b/tests/qemu-iotests/229 +@@ -0,0 +1,95 @@ ++#!/bin/bash ++# ++# Test for force canceling a running blockjob that is paused in ++# an error state. ++# ++# Copyright (C) 2018 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 . ++# ++ ++# creator ++owner=jcody@redhat.com ++ ++seq="$(basename $0)" ++echo "QA output created by $seq" ++ ++here="$PWD" ++status=1 # failure is the default! ++ ++_cleanup() ++{ ++ _cleanup_qemu ++ _cleanup_test_img ++ rm -f "$TEST_IMG" "$DEST_IMG" ++} ++trap "_cleanup; exit \$status" 0 1 2 3 15 ++ ++# get standard environment, filters and checks ++. ./common.rc ++. ./common.filter ++. ./common.qemu ++ ++# Needs backing file and backing format support ++_supported_fmt qcow2 qed ++_supported_proto file ++_supported_os Linux ++ ++ ++DEST_IMG="$TEST_DIR/d.$IMGFMT" ++TEST_IMG="$TEST_DIR/b.$IMGFMT" ++ ++_make_test_img 2M ++ ++# destination for mirror will be too small, causing error ++TEST_IMG=$DEST_IMG _make_test_img 1M ++ ++$QEMU_IO -c 'write 0 2M' "$TEST_IMG" | _filter_qemu_io ++ ++_launch_qemu -drive id=testdisk,file="$TEST_IMG",format="$IMGFMT" ++ ++_send_qemu_cmd $QEMU_HANDLE \ ++ "{'execute': 'qmp_capabilities'}" \ ++ 'return' ++ ++echo ++echo '=== Starting drive-mirror, causing error & stop ===' ++echo ++ ++_send_qemu_cmd $QEMU_HANDLE \ ++ "{'execute': 'drive-mirror', ++ 'arguments': {'device': 'testdisk', ++ 'mode': 'absolute-paths', ++ 'format': '$IMGFMT', ++ 'target': '$DEST_IMG', ++ 'sync': 'full', ++ 'mode': 'existing', ++ 'on-source-error': 'stop', ++ 'on-target-error': 'stop' }}" \ ++ "JOB_STATUS_CHANGE.*pause" ++ ++echo ++echo '=== Force cancel job paused in error state ===' ++echo ++ ++success_or_failure="y" _send_qemu_cmd $QEMU_HANDLE \ ++ "{'execute': 'block-job-cancel', ++ 'arguments': { 'device': 'testdisk', ++ 'force': true}}" \ ++ "BLOCK_JOB_CANCELLED" "Assertion" ++ ++# success, all done ++echo "*** done" ++rm -f $seq.full ++status=0 +diff --git a/tests/qemu-iotests/229.out b/tests/qemu-iotests/229.out +new file mode 100644 +index 0000000..4c41128 +--- /dev/null ++++ b/tests/qemu-iotests/229.out +@@ -0,0 +1,23 @@ ++QA output created by 229 ++Formatting 'TEST_DIR/b.IMGFMT', fmt=IMGFMT size=2097152 ++Formatting 'TEST_DIR/d.IMGFMT', fmt=IMGFMT size=1048576 ++wrote 2097152/2097152 bytes at offset 0 ++2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++{"return": {}} ++ ++=== Starting drive-mirror, causing error & stop === ++ ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "testdisk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "testdisk"}} ++{"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "testdisk", "operation": "write", "action": "stop"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "testdisk"}} ++ ++=== Force cancel job paused in error state === ++ ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "testdisk"}} ++{"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "testdisk", "operation": "write", "action": "stop"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "testdisk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "testdisk", "len": 2097152, "offset": 1048576, "speed": 0, "type": "mirror"}} ++*** done +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index 6dd146a..3817d28 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -223,3 +223,4 @@ + 223 rw auto quick + 226 auto quick + 227 auto quick ++229 auto quick +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-linux-aio-acquire-AioContext-before-qemu_laio_.patch b/SOURCES/kvm-block-linux-aio-acquire-AioContext-before-qemu_laio_.patch new file mode 100644 index 0000000..ba9c1a8 --- /dev/null +++ b/SOURCES/kvm-block-linux-aio-acquire-AioContext-before-qemu_laio_.patch @@ -0,0 +1,132 @@ +From cd8c3589d4c99a1968cf7a37b50165fa7e927b2f Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:21 +0200 +Subject: [PATCH 30/49] block/linux-aio: acquire AioContext before + qemu_laio_process_completions + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-24-kwolf@redhat.com> +Patchwork-id: 82175 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 23/42] block/linux-aio: acquire AioContext before qemu_laio_process_completions +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +From: Sergio Lopez + +In qemu_laio_process_completions_and_submit, the AioContext is acquired +before the ioq_submit iteration and after qemu_laio_process_completions, +but the latter is not thread safe either. + +This change avoids a number of random crashes when the Main Thread and +an IO Thread collide processing completions for the same AioContext. +This is an example of such crash: + + - The IO Thread is trying to acquire the AioContext at aio_co_enter, + which evidences that it didn't lock it before: + +Thread 3 (Thread 0x7fdfd8bd8700 (LWP 36743)): + #0 0x00007fdfe0dd542d in __lll_lock_wait () at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135 + #1 0x00007fdfe0dd0de6 in _L_lock_870 () at /lib64/libpthread.so.0 + #2 0x00007fdfe0dd0cdf in __GI___pthread_mutex_lock (mutex=mutex@entry=0x5631fde0e6c0) + at ../nptl/pthread_mutex_lock.c:114 + #3 0x00005631fc0603a7 in qemu_mutex_lock_impl (mutex=0x5631fde0e6c0, file=0x5631fc23520f "util/async.c", line=511) at util/qemu-thread-posix.c:66 + #4 0x00005631fc05b558 in aio_co_enter (ctx=0x5631fde0e660, co=0x7fdfcc0c2b40) at util/async.c:493 + #5 0x00005631fc05b5ac in aio_co_wake (co=) at util/async.c:478 + #6 0x00005631fbfc51ad in qemu_laio_process_completion (laiocb=) at block/linux-aio.c:104 + #7 0x00005631fbfc523c in qemu_laio_process_completions (s=s@entry=0x7fdfc0297670) + at block/linux-aio.c:222 + #8 0x00005631fbfc5499 in qemu_laio_process_completions_and_submit (s=0x7fdfc0297670) + at block/linux-aio.c:237 + #9 0x00005631fc05d978 in aio_dispatch_handlers (ctx=ctx@entry=0x5631fde0e660) at util/aio-posix.c:406 + #10 0x00005631fc05e3ea in aio_poll (ctx=0x5631fde0e660, blocking=blocking@entry=true) + at util/aio-posix.c:693 + #11 0x00005631fbd7ad96 in iothread_run (opaque=0x5631fde0e1c0) at iothread.c:64 + #12 0x00007fdfe0dcee25 in start_thread (arg=0x7fdfd8bd8700) at pthread_create.c:308 + #13 0x00007fdfe0afc34d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:113 + + - The Main Thread is also processing completions from the same + AioContext, and crashes due to failed assertion at util/iov.c:78: + +Thread 1 (Thread 0x7fdfeb5eac80 (LWP 36740)): + #0 0x00007fdfe0a391f7 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56 + #1 0x00007fdfe0a3a8e8 in __GI_abort () at abort.c:90 + #2 0x00007fdfe0a32266 in __assert_fail_base (fmt=0x7fdfe0b84e68 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", assertion=assertion@entry=0x5631fc238ccb "offset == 0", file=file@entry=0x5631fc23698e "util/iov.c", line=line@entry=78, function=function@entry=0x5631fc236adc <__PRETTY_FUNCTION__.15220> "iov_memset") + at assert.c:92 + #3 0x00007fdfe0a32312 in __GI___assert_fail (assertion=assertion@entry=0x5631fc238ccb "offset == 0", file=file@entry=0x5631fc23698e "util/iov.c", line=line@entry=78, function=function@entry=0x5631fc236adc <__PRETTY_FUNCTION__.15220> "iov_memset") at assert.c:101 + #4 0x00005631fc065287 in iov_memset (iov=, iov_cnt=, offset=, offset@entry=65536, fillc=fillc@entry=0, bytes=15515191315812405248) at util/iov.c:78 + #5 0x00005631fc065a63 in qemu_iovec_memset (qiov=, offset=offset@entry=65536, fillc=fillc@entry=0, bytes=) at util/iov.c:410 + #6 0x00005631fbfc5178 in qemu_laio_process_completion (laiocb=0x7fdd920df630) at block/linux-aio.c:88 + #7 0x00005631fbfc523c in qemu_laio_process_completions (s=s@entry=0x7fdfc0297670) + at block/linux-aio.c:222 + #8 0x00005631fbfc5499 in qemu_laio_process_completions_and_submit (s=0x7fdfc0297670) + at block/linux-aio.c:237 + #9 0x00005631fbfc54ed in qemu_laio_poll_cb (opaque=) at block/linux-aio.c:272 + #10 0x00005631fc05d85e in run_poll_handlers_once (ctx=ctx@entry=0x5631fde0e660) at util/aio-posix.c:497 + #11 0x00005631fc05e2ca in aio_poll (blocking=false, ctx=0x5631fde0e660) at util/aio-posix.c:574 + #12 0x00005631fc05e2ca in aio_poll (ctx=0x5631fde0e660, blocking=blocking@entry=false) + at util/aio-posix.c:604 + #13 0x00005631fbfcb8a3 in bdrv_do_drained_begin (ignore_parent=, recursive=, bs=) at block/io.c:273 + #14 0x00005631fbfcb8a3 in bdrv_do_drained_begin (bs=0x5631fe8b6200, recursive=, parent=0x0, ignore_bds_parents=, poll=) at block/io.c:390 + #15 0x00005631fbfbcd2e in blk_drain (blk=0x5631fe83ac80) at block/block-backend.c:1590 + #16 0x00005631fbfbe138 in blk_remove_bs (blk=blk@entry=0x5631fe83ac80) at block/block-backend.c:774 + #17 0x00005631fbfbe3d6 in blk_unref (blk=0x5631fe83ac80) at block/block-backend.c:401 + #18 0x00005631fbfbe3d6 in blk_unref (blk=0x5631fe83ac80) at block/block-backend.c:449 + #19 0x00005631fbfc9a69 in commit_complete (job=0x5631fe8b94b0, opaque=0x7fdfcc1bb080) + at block/commit.c:92 + #20 0x00005631fbf7d662 in job_defer_to_main_loop_bh (opaque=0x7fdfcc1b4560) at job.c:973 + #21 0x00005631fc05ad41 in aio_bh_poll (bh=0x7fdfcc01ad90) at util/async.c:90 + #22 0x00005631fc05ad41 in aio_bh_poll (ctx=ctx@entry=0x5631fddffdb0) at util/async.c:118 + #23 0x00005631fc05e210 in aio_dispatch (ctx=0x5631fddffdb0) at util/aio-posix.c:436 + #24 0x00005631fc05ac1e in aio_ctx_dispatch (source=, callback=, user_data=) at util/async.c:261 + #25 0x00007fdfeaae44c9 in g_main_context_dispatch (context=0x5631fde00140) at gmain.c:3201 + #26 0x00007fdfeaae44c9 in g_main_context_dispatch (context=context@entry=0x5631fde00140) at gmain.c:3854 + #27 0x00005631fc05d503 in main_loop_wait () at util/main-loop.c:215 + #28 0x00005631fc05d503 in main_loop_wait (timeout=) at util/main-loop.c:238 + #29 0x00005631fc05d503 in main_loop_wait (nonblocking=nonblocking@entry=0) at util/main-loop.c:497 + #30 0x00005631fbd81412 in main_loop () at vl.c:1866 + #31 0x00005631fbc18ff3 in main (argc=, argv=, envp=) + at vl.c:4647 + + - A closer examination shows that s->io_q.in_flight appears to have + gone backwards: + +(gdb) frame 7 + #7 0x00005631fbfc523c in qemu_laio_process_completions (s=s@entry=0x7fdfc0297670) + at block/linux-aio.c:222 +222 qemu_laio_process_completion(laiocb); +(gdb) p s +$2 = (LinuxAioState *) 0x7fdfc0297670 +(gdb) p *s +$3 = {aio_context = 0x5631fde0e660, ctx = 0x7fdfeb43b000, e = {rfd = 33, wfd = 33}, io_q = {plugged = 0, + in_queue = 0, in_flight = 4294967280, blocked = false, pending = {sqh_first = 0x0, + sqh_last = 0x7fdfc0297698}}, completion_bh = 0x7fdfc0280ef0, event_idx = 21, event_max = 241} +(gdb) p/x s->io_q.in_flight +$4 = 0xfffffff0 + +Signed-off-by: Sergio Lopez +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/linux-aio.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/block/linux-aio.c b/block/linux-aio.c +index 88b8d55..abd8886 100644 +--- a/block/linux-aio.c ++++ b/block/linux-aio.c +@@ -233,9 +233,9 @@ static void qemu_laio_process_completions(LinuxAioState *s) + + static void qemu_laio_process_completions_and_submit(LinuxAioState *s) + { ++ aio_context_acquire(s->aio_context); + qemu_laio_process_completions(s); + +- aio_context_acquire(s->aio_context); + if (!s->io_q.plugged && !QSIMPLEQ_EMPTY(&s->io_q.pending)) { + ioq_submit(s); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-mirror-Make-cancel-always-cancel-pre-READY.patch b/SOURCES/kvm-block-mirror-Make-cancel-always-cancel-pre-READY.patch new file mode 100644 index 0000000..cfc3783 --- /dev/null +++ b/SOURCES/kvm-block-mirror-Make-cancel-always-cancel-pre-READY.patch @@ -0,0 +1,59 @@ +From ec4901d603e9f29f9111933bb14d48653a1d67fa Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 14:47:35 +0200 +Subject: [PATCH 25/54] block/mirror: Make cancel always cancel pre-READY + +RH-Author: Max Reitz +Message-id: <20180618144736.29873-3-mreitz@redhat.com> +Patchwork-id: 80748 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 2/3] block/mirror: Make cancel always cancel pre-READY +Bugzilla: 1572856 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf +RH-Acked-by: John Snow + +Commit b76e4458b1eb3c32e9824fe6aa51f67d2b251748 made the mirror block +job respect block-job-cancel's @force flag: With that flag set, it would +now always really cancel, even post-READY. + +Unfortunately, it had a side effect: Without that flag set, it would now +never cancel, not even before READY. Considering that is an +incompatible change and not noted anywhere in the commit or the +description of block-job-cancel's @force parameter, this seems +unintentional and we should revert to the previous behavior, which is to +immediately cancel the job when block-job-cancel is called before source +and target are in sync (i.e. before the READY event). + +Cc: qemu-stable@nongnu.org +Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1572856 +Reported-by: Yanan Fu +Signed-off-by: Max Reitz +Reviewed-by: Eric Blake +Message-id: 20180501220509.14152-2-mreitz@redhat.com +Reviewed-by: Jeff Cody +Signed-off-by: Jeff Cody +(cherry picked from commit eb36639f7bbc16055e551593b81365e8ae3b0b05) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/mirror.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/block/mirror.c b/block/mirror.c +index 9436a8d..99da9c0 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -874,7 +874,9 @@ static void coroutine_fn mirror_run(void *opaque) + } + trace_mirror_before_sleep(s, cnt, s->synced, delay_ns); + block_job_sleep_ns(&s->common, delay_ns); +- if (block_job_is_cancelled(&s->common) && s->common.force) { ++ if (block_job_is_cancelled(&s->common) && ++ (!s->synced || s->common.force)) ++ { + break; + } + s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-mirror-add-block-job-creation-flags.patch b/SOURCES/kvm-block-mirror-add-block-job-creation-flags.patch new file mode 100644 index 0000000..c5e0efa --- /dev/null +++ b/SOURCES/kvm-block-mirror-add-block-job-creation-flags.patch @@ -0,0 +1,100 @@ +From 504f6ad36108eb4d710cd1e29694bb4a2bcdb8bd Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:49 +0200 +Subject: [PATCH 11/25] block/mirror: add block job creation flags + +RH-Author: John Snow +Message-id: <20180910181803.11781-12-jsnow@redhat.com> +Patchwork-id: 82088 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 11/25] block/mirror: add block job creation flags +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Add support for taking and passing forward job creation flags. + +Signed-off-by: John Snow +Reviewed-by: Max Reitz +Reviewed-by: Jeff Cody +Message-id: 20180906130225.5118-3-jsnow@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit bbb6798d0caa79cd4e899b33777c577d0bd04399) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/mirror.c | 5 +++-- + blockdev.c | 3 ++- + include/block/block_int.h | 5 ++++- + 3 files changed, 9 insertions(+), 4 deletions(-) + +diff --git a/block/mirror.c b/block/mirror.c +index 0ab0822..a80c359 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -1270,7 +1270,8 @@ fail: + + void mirror_start(const char *job_id, BlockDriverState *bs, + BlockDriverState *target, const char *replaces, +- int64_t speed, uint32_t granularity, int64_t buf_size, ++ int creation_flags, int64_t speed, ++ uint32_t granularity, int64_t buf_size, + MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, + BlockdevOnError on_source_error, + BlockdevOnError on_target_error, +@@ -1285,7 +1286,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, + } + is_none_mode = mode == MIRROR_SYNC_MODE_NONE; + base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL; +- mirror_start_job(job_id, bs, JOB_DEFAULT, target, replaces, ++ mirror_start_job(job_id, bs, creation_flags, target, replaces, + speed, granularity, buf_size, backing_mode, + on_source_error, on_target_error, unmap, NULL, NULL, + &mirror_job_driver, is_none_mode, base, false, +diff --git a/blockdev.c b/blockdev.c +index 20dbcf5..5d38e17 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3746,6 +3746,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, + const char *filter_node_name, + Error **errp) + { ++ int job_flags = JOB_DEFAULT; + + if (!has_speed) { + speed = 0; +@@ -3795,7 +3796,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, + * and will allow to check whether the node still exist at mirror completion + */ + mirror_start(job_id, bs, target, +- has_replaces ? replaces : NULL, ++ has_replaces ? replaces : NULL, job_flags, + speed, granularity, buf_size, sync, backing_mode, + on_source_error, on_target_error, unmap, filter_node_name, + errp); +diff --git a/include/block/block_int.h b/include/block/block_int.h +index 25ad363..07517cf 100644 +--- a/include/block/block_int.h ++++ b/include/block/block_int.h +@@ -1000,6 +1000,8 @@ void commit_active_start(const char *job_id, BlockDriverState *bs, + * @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. +@@ -1020,7 +1022,8 @@ void commit_active_start(const char *job_id, BlockDriverState *bs, + */ + void mirror_start(const char *job_id, BlockDriverState *bs, + BlockDriverState *target, const char *replaces, +- int64_t speed, uint32_t granularity, int64_t buf_size, ++ int creation_flags, int64_t speed, ++ uint32_t granularity, int64_t buf_size, + MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, + BlockdevOnError on_source_error, + BlockdevOnError on_target_error, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-mirror-conservative-mirror_exit-refactor.patch b/SOURCES/kvm-block-mirror-conservative-mirror_exit-refactor.patch new file mode 100644 index 0000000..4f2ea05 --- /dev/null +++ b/SOURCES/kvm-block-mirror-conservative-mirror_exit-refactor.patch @@ -0,0 +1,141 @@ +From f27835b1c21b80477e828ed82c5afd764e6f90ca Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:53 +0200 +Subject: [PATCH 15/25] block/mirror: conservative mirror_exit refactor + +RH-Author: John Snow +Message-id: <20180910181803.11781-16-jsnow@redhat.com> +Patchwork-id: 82105 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 15/25] block/mirror: conservative mirror_exit refactor +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +For purposes of minimum code movement, refactor the mirror_exit +callback to use the post-finalization callbacks in a trivial way. + +Signed-off-by: John Snow +Message-id: 20180906130225.5118-7-jsnow@redhat.com +Reviewed-by: Jeff Cody +Reviewed-by: Max Reitz +[mreitz: Added comment for the mirror_exit() function] +Signed-off-by: Max Reitz +(cherry picked from commit 9fd426955da4239af7690c92b387fff0502aca84) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina + +Conflicts: + block/mirror.c: context conflict on job properties +--- + block/mirror.c | 44 +++++++++++++++++++++++++++++++++----------- + 1 file changed, 33 insertions(+), 11 deletions(-) + +diff --git a/block/mirror.c b/block/mirror.c +index 1945000..313e6e9 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -71,6 +71,7 @@ typedef struct MirrorBlockJob { + int target_cluster_size; + int max_iov; + bool initial_zeroing_ongoing; ++ bool prepared; + } MirrorBlockJob; + + typedef struct MirrorOp { +@@ -480,7 +481,12 @@ static void mirror_wait_for_all_io(MirrorBlockJob *s) + } + } + +-static void mirror_exit(Job *job) ++/** ++ * 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; +@@ -489,7 +495,13 @@ static void mirror_exit(Job *job) + BlockDriverState *target_bs = blk_bs(s->target); + BlockDriverState *mirror_top_bs = s->mirror_top_bs; + Error *local_err = NULL; +- int ret = job->ret; ++ bool abort = job->ret < 0; ++ int ret = 0; ++ ++ if (s->prepared) { ++ return 0; ++ } ++ s->prepared = true; + + bdrv_release_dirty_bitmap(src, s->dirty_bitmap); + +@@ -514,7 +526,7 @@ static void mirror_exit(Job *job) + * required before it could become a backing file of target_bs. */ + bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL, + &error_abort); +- if (ret == 0 && s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) { ++ if (!abort && s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) { + BlockDriverState *backing = s->is_none_mode ? src : s->base; + if (backing_bs(target_bs) != backing) { + bdrv_set_backing_hd(target_bs, backing, &local_err); +@@ -530,11 +542,8 @@ static void mirror_exit(Job *job) + aio_context_acquire(replace_aio_context); + } + +- if (s->should_complete && ret == 0) { +- BlockDriverState *to_replace = src; +- if (s->to_replace) { +- to_replace = s->to_replace; +- } ++ if (s->should_complete && !abort) { ++ BlockDriverState *to_replace = s->to_replace ?: src; + + if (bdrv_get_flags(target_bs) != bdrv_get_flags(to_replace)) { + bdrv_reopen(target_bs, bdrv_get_flags(to_replace), NULL); +@@ -581,7 +590,18 @@ static void mirror_exit(Job *job) + bdrv_unref(mirror_top_bs); + bdrv_unref(src); + +- job->ret = ret; ++ 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 mirror_throttle(MirrorBlockJob *s) +@@ -986,7 +1006,8 @@ static const BlockJobDriver mirror_job_driver = { + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .run = mirror_run, +- .exit = mirror_exit, ++ .prepare = mirror_prepare, ++ .abort = mirror_abort, + .pause = mirror_pause, + .complete = mirror_complete, + }, +@@ -1002,7 +1023,8 @@ static const BlockJobDriver commit_active_job_driver = { + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .run = mirror_run, +- .exit = mirror_exit, ++ .prepare = mirror_prepare, ++ .abort = mirror_abort, + .pause = mirror_pause, + .complete = mirror_complete, + }, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-mirror-don-t-install-backing-chain-on-abort.patch b/SOURCES/kvm-block-mirror-don-t-install-backing-chain-on-abort.patch new file mode 100644 index 0000000..4c85635 --- /dev/null +++ b/SOURCES/kvm-block-mirror-don-t-install-backing-chain-on-abort.patch @@ -0,0 +1,45 @@ +From ec75d6bf020a399a3caee05dcc37b463d438c332 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:52 +0200 +Subject: [PATCH 14/25] block/mirror: don't install backing chain on abort + +RH-Author: John Snow +Message-id: <20180910181803.11781-15-jsnow@redhat.com> +Patchwork-id: 82091 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 14/25] block/mirror: don't install backing chain on abort +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +In cases where we abort the block/mirror job, there's no point in +installing the new backing chain before we finish aborting. + +Signed-off-by: John Snow +Message-id: 20180906130225.5118-6-jsnow@redhat.com +Reviewed-by: Jeff Cody +Reviewed-by: Max Reitz +Signed-off-by: Max Reitz +(cherry picked from commit 46e2f0070a9bd82187c12b679ec5a69c6aabe128) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/mirror.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/block/mirror.c b/block/mirror.c +index a80c359..1945000 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -514,7 +514,7 @@ static void mirror_exit(Job *job) + * required before it could become a backing file of target_bs. */ + bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL, + &error_abort); +- if (s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) { ++ if (ret == 0 && s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) { + BlockDriverState *backing = s->is_none_mode ? src : s->base; + if (backing_bs(target_bs) != backing) { + bdrv_set_backing_hd(target_bs, backing, &local_err); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-mirror-honor-ratelimit-again.patch b/SOURCES/kvm-block-mirror-honor-ratelimit-again.patch new file mode 100644 index 0000000..9876f63 --- /dev/null +++ b/SOURCES/kvm-block-mirror-honor-ratelimit-again.patch @@ -0,0 +1,87 @@ +From 294916c8fdc6fdd221543c70b716e31591315379 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 14:47:34 +0200 +Subject: [PATCH 24/54] block/mirror: honor ratelimit again + +RH-Author: Max Reitz +Message-id: <20180618144736.29873-2-mreitz@redhat.com> +Patchwork-id: 80747 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/3] block/mirror: honor ratelimit again +Bugzilla: 1572856 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf +RH-Acked-by: John Snow + +From: Stefan Hajnoczi + +Commit b76e4458b1eb3c32e9824fe6aa51f67d2b251748 ("block/mirror: change +the semantic of 'force' of block-job-cancel") accidentally removed the +ratelimit in the mirror job. + +Reintroduce the ratelimit but keep the block-job-cancel force=true +behavior that was added in commit +b76e4458b1eb3c32e9824fe6aa51f67d2b251748. + +Note that block_job_sleep_ns() returns immediately when the job is +cancelled. Therefore it's safe to unconditionally call +block_job_sleep_ns() - a cancelled job does not sleep. + +This commit fixes the non-deterministic qemu-iotests 185 output. The +test relies on the ratelimit to make the job sleep until the 'quit' +command is processed. Previously the job could complete before the +'quit' command was received since there was no ratelimit. + +Cc: Liang Li +Cc: Jeff Cody +Cc: Kevin Wolf +Signed-off-by: Stefan Hajnoczi +Message-id: 20180424123527.19168-1-stefanha@redhat.com +Signed-off-by: Jeff Cody +(cherry picked from commit ddc4115efdfa6619689fe18871aa2d37890b3463) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/mirror.c | 8 +++++--- + tests/qemu-iotests/185.out | 2 +- + 2 files changed, 6 insertions(+), 4 deletions(-) + +diff --git a/block/mirror.c b/block/mirror.c +index 820f512..9436a8d 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -868,12 +868,14 @@ static void coroutine_fn mirror_run(void *opaque) + } + + ret = 0; ++ ++ if (s->synced && !should_complete) { ++ delay_ns = (s->in_flight == 0 && cnt == 0 ? SLICE_TIME : 0); ++ } + trace_mirror_before_sleep(s, cnt, s->synced, delay_ns); ++ block_job_sleep_ns(&s->common, delay_ns); + if (block_job_is_cancelled(&s->common) && s->common.force) { + break; +- } else if (!should_complete) { +- delay_ns = (s->in_flight == 0 && cnt == 0 ? SLICE_TIME : 0); +- block_job_sleep_ns(&s->common, delay_ns); + } + s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + } +diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out +index 2c4b04d..992162f 100644 +--- a/tests/qemu-iotests/185.out ++++ b/tests/qemu-iotests/185.out +@@ -36,9 +36,9 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q + {"return": {}} + Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 + {"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}} + + === Start backup job and exit qemu === +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-mirror-utilize-job_exit-shim.patch b/SOURCES/kvm-block-mirror-utilize-job_exit-shim.patch new file mode 100644 index 0000000..10d3210 --- /dev/null +++ b/SOURCES/kvm-block-mirror-utilize-job_exit-shim.patch @@ -0,0 +1,152 @@ +From a89c41261dee7c365995d9f5f98bcdb543e42775 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:43 +0200 +Subject: [PATCH 05/25] block/mirror: utilize job_exit shim + +RH-Author: John Snow +Message-id: <20180910181803.11781-6-jsnow@redhat.com> +Patchwork-id: 82098 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 05/25] block/mirror: utilize job_exit shim +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Change the manual deferment to mirror_exit into the implicit +callback to job_exit and the mirror_exit callback. + +This does change the order of some bdrv_unref calls and job_completed, +but thanks to the new context in which we call .exit, this is safe to +defer the possible flushing of any nodes to the job_finalize_single +cleanup stage. + +Signed-off-by: John Snow +Message-id: 20180830015734.19765-6-jsnow@redhat.com +Reviewed-by: Max Reitz +Reviewed-by: Jeff Cody +Signed-off-by: Max Reitz +(cherry picked from commit 7b508f6b7a38a8d9729772fa6e525da883fb120b) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/mirror.c | 30 +++++++++++------------------- + 1 file changed, 11 insertions(+), 19 deletions(-) + +diff --git a/block/mirror.c b/block/mirror.c +index 459f944..0ab0822 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -480,25 +480,21 @@ static void mirror_wait_for_all_io(MirrorBlockJob *s) + } + } + +-typedef struct { +- int ret; +-} MirrorExitData; +- +-static void mirror_exit(Job *job, void *opaque) ++static void mirror_exit(Job *job) + { + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); + BlockJob *bjob = &s->common; +- MirrorExitData *data = opaque; + AioContext *replace_aio_context = NULL; + BlockDriverState *src = s->source; + BlockDriverState *target_bs = blk_bs(s->target); + BlockDriverState *mirror_top_bs = s->mirror_top_bs; + Error *local_err = NULL; ++ int ret = job->ret; + + bdrv_release_dirty_bitmap(src, s->dirty_bitmap); + +- /* Make sure that the source BDS doesn't go away before we called +- * job_completed(). */ ++ /* 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); +@@ -524,7 +520,7 @@ static void mirror_exit(Job *job, void *opaque) + bdrv_set_backing_hd(target_bs, backing, &local_err); + if (local_err) { + error_report_err(local_err); +- data->ret = -EPERM; ++ ret = -EPERM; + } + } + } +@@ -534,7 +530,7 @@ static void mirror_exit(Job *job, void *opaque) + aio_context_acquire(replace_aio_context); + } + +- if (s->should_complete && data->ret == 0) { ++ if (s->should_complete && ret == 0) { + BlockDriverState *to_replace = src; + if (s->to_replace) { + to_replace = s->to_replace; +@@ -551,7 +547,7 @@ static void mirror_exit(Job *job, void *opaque) + bdrv_drained_end(target_bs); + if (local_err) { + error_report_err(local_err); +- data->ret = -EPERM; ++ ret = -EPERM; + } + } + if (s->to_replace) { +@@ -581,12 +577,11 @@ static void mirror_exit(Job *job, void *opaque) + blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort); + blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort); + +- job_completed(job, data->ret); +- +- g_free(data); + bdrv_drained_end(src); + bdrv_unref(mirror_top_bs); + bdrv_unref(src); ++ ++ job->ret = ret; + } + + static void mirror_throttle(MirrorBlockJob *s) +@@ -686,7 +681,6 @@ static int mirror_flush(MirrorBlockJob *s) + static int coroutine_fn mirror_run(Job *job, Error **errp) + { + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); +- MirrorExitData *data; + BlockDriverState *bs = s->source; + BlockDriverState *target_bs = blk_bs(s->target); + bool need_drain = true; +@@ -896,14 +890,10 @@ immediate_exit: + g_free(s->in_flight_bitmap); + bdrv_dirty_iter_free(s->dbi); + +- data = g_malloc(sizeof(*data)); +- data->ret = ret; +- + if (need_drain) { + bdrv_drained_begin(bs); + } + +- job_defer_to_main_loop(&s->common.job, mirror_exit, data); + return ret; + } + +@@ -996,6 +986,7 @@ static const BlockJobDriver mirror_job_driver = { + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .run = mirror_run, ++ .exit = mirror_exit, + .pause = mirror_pause, + .complete = mirror_complete, + }, +@@ -1011,6 +1002,7 @@ static const BlockJobDriver commit_active_job_driver = { + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .run = mirror_run, ++ .exit = mirror_exit, + .pause = mirror_pause, + .complete = mirror_complete, + }, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-nbd-client-don-t-check-ioc.patch b/SOURCES/kvm-block-nbd-client-don-t-check-ioc.patch new file mode 100644 index 0000000..fcac981 --- /dev/null +++ b/SOURCES/kvm-block-nbd-client-don-t-check-ioc.patch @@ -0,0 +1,87 @@ +From 9cfc0299d10213160701c07ed5f4feb1c98d4f2a Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:23:02 +0100 +Subject: [PATCH 124/163] block/nbd-client: don't check ioc + +RH-Author: John Snow +Message-id: <20190327172308.31077-50-jsnow@redhat.com> +Patchwork-id: 85214 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 49/55] block/nbd-client: don't check ioc +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +We have several paranoid checks for ioc != NULL. But ioc may become +NULL only on close, which should not happen during requests handling. +Also, we check ioc only sometimes, not after each yield, which is +inconsistent. Let's drop these checks. However, for safety, let's leave +asserts instead. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: Eric Blake +Message-Id: <20190201130138.94525-6-vsementsov@virtuozzo.com> +Signed-off-by: Eric Blake +(cherry picked from commit 88ed4e1bf0c7017c04fac2166ec9a7687aa21f97) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 16 +++++----------- + 1 file changed, 5 insertions(+), 11 deletions(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index 5d22f8b..5f3a67f 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -53,9 +53,7 @@ static void nbd_teardown_connection(BlockDriverState *bs) + { + NBDClientSession *client = nbd_get_client_session(bs); + +- if (!client->ioc) { /* Already closed */ +- return; +- } ++ assert(client->ioc); + + /* finish any pending coroutines */ + qio_channel_shutdown(client->ioc, +@@ -153,10 +151,7 @@ static int nbd_co_send_request(BlockDriverState *bs, + rc = -EIO; + goto err; + } +- if (!s->ioc) { +- rc = -EPIPE; +- goto err; +- } ++ assert(s->ioc); + + if (qiov) { + qio_channel_set_cork(s->ioc, true); +@@ -428,10 +423,11 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( + s->requests[i].receiving = true; + qemu_coroutine_yield(); + s->requests[i].receiving = false; +- if (!s->ioc || s->quit) { ++ if (s->quit) { + error_setg(errp, "Connection closed"); + return -EIO; + } ++ assert(s->ioc); + + assert(s->reply.handle == handle); + +@@ -981,9 +977,7 @@ void nbd_client_close(BlockDriverState *bs) + NBDClientSession *client = nbd_get_client_session(bs); + NBDRequest request = { .type = NBD_CMD_DISC }; + +- if (client->ioc == NULL) { +- return; +- } ++ assert(client->ioc); + + nbd_send_request(client->ioc, &request); + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-nbd-client-fix-nbd_reply_chunk_iter_receive.patch b/SOURCES/kvm-block-nbd-client-fix-nbd_reply_chunk_iter_receive.patch new file mode 100644 index 0000000..eb75f0c --- /dev/null +++ b/SOURCES/kvm-block-nbd-client-fix-nbd_reply_chunk_iter_receive.patch @@ -0,0 +1,46 @@ +From 49725596691ccd778e0d9328b2ee2064ec35595f Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:23:01 +0100 +Subject: [PATCH 123/163] block/nbd-client: fix nbd_reply_chunk_iter_receive + +RH-Author: John Snow +Message-id: <20190327172308.31077-49-jsnow@redhat.com> +Patchwork-id: 85209 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 48/55] block/nbd-client: fix nbd_reply_chunk_iter_receive +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +Use exported report, not the variable to be reused (should not really +matter). + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: Eric Blake +Message-Id: <20190201130138.94525-5-vsementsov@virtuozzo.com> +Signed-off-by: Eric Blake +(cherry picked from commit 65e01d47653eb127fa917bd9d2f1824a00ab2de6) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index 22e5042..5d22f8b 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -601,7 +601,7 @@ static bool nbd_reply_chunk_iter_receive(NBDClientSession *s, + } + + /* Do not execute the body of NBD_FOREACH_REPLY_CHUNK for simple reply. */ +- if (nbd_reply_is_simple(&s->reply) || s->quit) { ++ if (nbd_reply_is_simple(reply) || s->quit) { + goto break_loop; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-nbd-client-rename-read_reply_co-to-connection_.patch b/SOURCES/kvm-block-nbd-client-rename-read_reply_co-to-connection_.patch new file mode 100644 index 0000000..3ff15cf --- /dev/null +++ b/SOURCES/kvm-block-nbd-client-rename-read_reply_co-to-connection_.patch @@ -0,0 +1,160 @@ +From 309faf75d981afc47f5890bbf65e5251c01249ef Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:23:03 +0100 +Subject: [PATCH 125/163] block/nbd-client: rename read_reply_co to + connection_co + +RH-Author: John Snow +Message-id: <20190327172308.31077-51-jsnow@redhat.com> +Patchwork-id: 85221 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 50/55] block/nbd-client: rename read_reply_co to connection_co +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +This coroutine will serve nbd reconnects, so, rename it to be something +more generic. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: Eric Blake +Message-Id: <20190201130138.94525-7-vsementsov@virtuozzo.com> +Signed-off-by: Eric Blake +(cherry picked from commit bc5a03350c220698229e7d6929dd242d5d358345) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 24 ++++++++++++------------ + block/nbd-client.h | 4 ++-- + 2 files changed, 14 insertions(+), 14 deletions(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index 5f3a67f..1230850 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -59,7 +59,7 @@ static void nbd_teardown_connection(BlockDriverState *bs) + qio_channel_shutdown(client->ioc, + QIO_CHANNEL_SHUTDOWN_BOTH, + NULL); +- BDRV_POLL_WHILE(bs, client->read_reply_co); ++ BDRV_POLL_WHILE(bs, client->connection_co); + + nbd_client_detach_aio_context(bs); + object_unref(OBJECT(client->sioc)); +@@ -68,7 +68,7 @@ static void nbd_teardown_connection(BlockDriverState *bs) + client->ioc = NULL; + } + +-static coroutine_fn void nbd_read_reply_entry(void *opaque) ++static coroutine_fn void nbd_connection_entry(void *opaque) + { + NBDClientSession *s = opaque; + uint64_t i; +@@ -100,14 +100,14 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque) + } + + /* We're woken up again by the request itself. Note that there +- * is no race between yielding and reentering read_reply_co. This ++ * is no race between yielding and reentering connection_co. This + * is because: + * + * - if the request runs on the same AioContext, it is only + * entered after we yield + * + * - if the request runs on a different AioContext, reentering +- * read_reply_co happens through a bottom half, which can only ++ * connection_co happens through a bottom half, which can only + * run after we yield. + */ + aio_co_wake(s->requests[i].coroutine); +@@ -116,7 +116,7 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque) + + s->quit = true; + nbd_recv_coroutines_wake_all(s); +- s->read_reply_co = NULL; ++ s->connection_co = NULL; + } + + static int nbd_co_send_request(BlockDriverState *bs, +@@ -419,7 +419,7 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( + } + *request_ret = 0; + +- /* Wait until we're woken up by nbd_read_reply_entry. */ ++ /* Wait until we're woken up by nbd_connection_entry. */ + s->requests[i].receiving = true; + qemu_coroutine_yield(); + s->requests[i].receiving = false; +@@ -494,7 +494,7 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( + } + + /* nbd_co_receive_one_chunk +- * Read reply, wake up read_reply_co and set s->quit if needed. ++ * 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( +@@ -508,15 +508,15 @@ static coroutine_fn int nbd_co_receive_one_chunk( + if (ret < 0) { + s->quit = true; + } else { +- /* For assert at loop start in nbd_read_reply_entry */ ++ /* For assert at loop start in nbd_connection_entry */ + if (reply) { + *reply = s->reply; + } + s->reply.handle = 0; + } + +- if (s->read_reply_co) { +- aio_co_wake(s->read_reply_co); ++ if (s->connection_co) { ++ aio_co_wake(s->connection_co); + } + + return ret; +@@ -969,7 +969,7 @@ void nbd_client_attach_aio_context(BlockDriverState *bs, + { + NBDClientSession *client = nbd_get_client_session(bs); + qio_channel_attach_aio_context(QIO_CHANNEL(client->ioc), new_context); +- aio_co_schedule(new_context, client->read_reply_co); ++ aio_co_schedule(new_context, client->connection_co); + } + + void nbd_client_close(BlockDriverState *bs) +@@ -1074,7 +1074,7 @@ static int nbd_client_connect(BlockDriverState *bs, + /* Now that we're connected, set the socket to be non-blocking and + * kick the reply mechanism. */ + qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL); +- client->read_reply_co = qemu_coroutine_create(nbd_read_reply_entry, client); ++ client->connection_co = qemu_coroutine_create(nbd_connection_entry, client); + nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs)); + + logout("Established connection with NBD server\n"); +diff --git a/block/nbd-client.h b/block/nbd-client.h +index 2f047ba..d990207 100644 +--- a/block/nbd-client.h ++++ b/block/nbd-client.h +@@ -20,7 +20,7 @@ + typedef struct { + Coroutine *coroutine; + uint64_t offset; /* original offset of the request */ +- bool receiving; /* waiting for read_reply_co? */ ++ bool receiving; /* waiting for connection_co? */ + } NBDClientRequest; + + typedef struct NBDClientSession { +@@ -30,7 +30,7 @@ typedef struct NBDClientSession { + + CoMutex send_mutex; + CoQueue free_sema; +- Coroutine *read_reply_co; ++ Coroutine *connection_co; + int in_flight; + + NBDClientRequest requests[MAX_NBD_REQUESTS]; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-nbd-client-split-channel-errors-from-export-er.patch b/SOURCES/kvm-block-nbd-client-split-channel-errors-from-export-er.patch new file mode 100644 index 0000000..a164d77 --- /dev/null +++ b/SOURCES/kvm-block-nbd-client-split-channel-errors-from-export-er.patch @@ -0,0 +1,313 @@ +From 00ecc208207488fec5d793b13b1a967d8af2acfd Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:58 +0100 +Subject: [PATCH 120/163] block/nbd-client: split channel errors from export + errors + +RH-Author: John Snow +Message-id: <20190327172308.31077-46-jsnow@redhat.com> +Patchwork-id: 85208 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 45/55] block/nbd-client: split channel errors from export errors +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +To implement nbd reconnect in further patches, we need to distinguish +error codes, returned by nbd server, from channel errors, to reconnect +only in the latter case. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: Eric Blake +Message-Id: <20190201130138.94525-2-vsementsov@virtuozzo.com> +Signed-off-by: Eric Blake +(cherry picked from commit 7f86068dc1aceec8f6151a3a713e57e680f73b45) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 83 +++++++++++++++++++++++++++++++----------------------- + 1 file changed, 47 insertions(+), 36 deletions(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index 5c97052..f5b9aaa 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -503,11 +503,11 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( + */ + static coroutine_fn int nbd_co_receive_one_chunk( + NBDClientSession *s, uint64_t handle, bool only_structured, +- QEMUIOVector *qiov, NBDReply *reply, void **payload, Error **errp) ++ int *request_ret, QEMUIOVector *qiov, NBDReply *reply, void **payload, ++ Error **errp) + { +- int request_ret; + int ret = nbd_co_do_receive_one_chunk(s, handle, only_structured, +- &request_ret, qiov, payload, errp); ++ request_ret, qiov, payload, errp); + + if (ret < 0) { + s->quit = true; +@@ -517,7 +517,6 @@ static coroutine_fn int nbd_co_receive_one_chunk( + *reply = s->reply; + } + s->reply.handle = 0; +- ret = request_ret; + } + + if (s->read_reply_co) { +@@ -529,22 +528,17 @@ static coroutine_fn int nbd_co_receive_one_chunk( + + typedef struct NBDReplyChunkIter { + int ret; +- bool fatal; ++ int request_ret; + Error *err; + bool done, only_structured; + } NBDReplyChunkIter; + +-static void nbd_iter_error(NBDReplyChunkIter *iter, bool fatal, +- int ret, Error **local_err) ++static void nbd_iter_channel_error(NBDReplyChunkIter *iter, ++ int ret, Error **local_err) + { + assert(ret < 0); + +- if ((fatal && !iter->fatal) || iter->ret == 0) { +- if (iter->ret != 0) { +- error_free(iter->err); +- iter->err = NULL; +- } +- iter->fatal = fatal; ++ if (!iter->ret) { + iter->ret = ret; + error_propagate(&iter->err, *local_err); + } else { +@@ -554,6 +548,15 @@ static void nbd_iter_error(NBDReplyChunkIter *iter, bool fatal, + *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 + */ + #define NBD_FOREACH_REPLY_CHUNK(s, iter, handle, structured, \ +@@ -569,13 +572,13 @@ static bool nbd_reply_chunk_iter_receive(NBDClientSession *s, + QEMUIOVector *qiov, NBDReply *reply, + void **payload) + { +- int ret; ++ int ret, request_ret; + NBDReply local_reply; + NBDStructuredReplyChunk *chunk; + Error *local_err = NULL; + if (s->quit) { + error_setg(&local_err, "Connection closed"); +- nbd_iter_error(iter, true, -EIO, &local_err); ++ nbd_iter_channel_error(iter, -EIO, &local_err); + goto break_loop; + } + +@@ -589,10 +592,12 @@ static bool nbd_reply_chunk_iter_receive(NBDClientSession *s, + } + + ret = nbd_co_receive_one_chunk(s, handle, iter->only_structured, +- qiov, reply, payload, &local_err); ++ &request_ret, qiov, reply, payload, ++ &local_err); + if (ret < 0) { +- /* If it is a fatal error s->quit is set by nbd_co_receive_one_chunk */ +- nbd_iter_error(iter, s->quit, ret, &local_err); ++ 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. */ +@@ -629,7 +634,7 @@ break_loop: + } + + static int nbd_co_receive_return_code(NBDClientSession *s, uint64_t handle, +- Error **errp) ++ int *request_ret, Error **errp) + { + NBDReplyChunkIter iter; + +@@ -638,12 +643,13 @@ static int nbd_co_receive_return_code(NBDClientSession *s, uint64_t handle, + } + + error_propagate(errp, iter.err); ++ *request_ret = iter.request_ret; + return iter.ret; + } + + static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle, + uint64_t offset, QEMUIOVector *qiov, +- Error **errp) ++ int *request_ret, Error **errp) + { + NBDReplyChunkIter iter; + NBDReply reply; +@@ -668,7 +674,7 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle, + offset, qiov, &local_err); + if (ret < 0) { + s->quit = true; +- nbd_iter_error(&iter, true, ret, &local_err); ++ nbd_iter_channel_error(&iter, ret, &local_err); + } + break; + default: +@@ -678,7 +684,7 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle, + error_setg(&local_err, + "Unexpected reply type: %d (%s) for CMD_READ", + chunk->type, nbd_reply_type_lookup(chunk->type)); +- nbd_iter_error(&iter, true, -EINVAL, &local_err); ++ nbd_iter_channel_error(&iter, -EINVAL, &local_err); + } + } + +@@ -687,12 +693,14 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle, + } + + error_propagate(errp, iter.err); ++ *request_ret = iter.request_ret; + return iter.ret; + } + + static int nbd_co_receive_blockstatus_reply(NBDClientSession *s, + uint64_t handle, uint64_t length, +- NBDExtent *extent, Error **errp) ++ NBDExtent *extent, ++ int *request_ret, Error **errp) + { + NBDReplyChunkIter iter; + NBDReply reply; +@@ -714,7 +722,7 @@ static int nbd_co_receive_blockstatus_reply(NBDClientSession *s, + if (received) { + s->quit = true; + error_setg(&local_err, "Several BLOCK_STATUS chunks in reply"); +- nbd_iter_error(&iter, true, -EINVAL, &local_err); ++ nbd_iter_channel_error(&iter, -EINVAL, &local_err); + } + received = true; + +@@ -723,7 +731,7 @@ static int nbd_co_receive_blockstatus_reply(NBDClientSession *s, + &local_err); + if (ret < 0) { + s->quit = true; +- nbd_iter_error(&iter, true, ret, &local_err); ++ nbd_iter_channel_error(&iter, ret, &local_err); + } + break; + default: +@@ -733,7 +741,7 @@ static int nbd_co_receive_blockstatus_reply(NBDClientSession *s, + "Unexpected reply type: %d (%s) " + "for CMD_BLOCK_STATUS", + chunk->type, nbd_reply_type_lookup(chunk->type)); +- nbd_iter_error(&iter, true, -EINVAL, &local_err); ++ nbd_iter_channel_error(&iter, -EINVAL, &local_err); + } + } + +@@ -748,14 +756,16 @@ static int nbd_co_receive_blockstatus_reply(NBDClientSession *s, + iter.ret = -EIO; + } + } ++ + error_propagate(errp, iter.err); ++ *request_ret = iter.request_ret; + return iter.ret; + } + + static int nbd_co_request(BlockDriverState *bs, NBDRequest *request, + QEMUIOVector *write_qiov) + { +- int ret; ++ int ret, request_ret; + Error *local_err = NULL; + NBDClientSession *client = nbd_get_client_session(bs); + +@@ -771,7 +781,8 @@ static int nbd_co_request(BlockDriverState *bs, NBDRequest *request, + return ret; + } + +- ret = nbd_co_receive_return_code(client, request->handle, &local_err); ++ ret = nbd_co_receive_return_code(client, request->handle, ++ &request_ret, &local_err); + if (local_err) { + trace_nbd_co_request_fail(request->from, request->len, request->handle, + request->flags, request->type, +@@ -779,13 +790,13 @@ static int nbd_co_request(BlockDriverState *bs, NBDRequest *request, + ret, error_get_pretty(local_err)); + error_free(local_err); + } +- return ret; ++ return ret ? ret : request_ret; + } + + int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, QEMUIOVector *qiov, int flags) + { +- int ret; ++ int ret, request_ret; + Error *local_err = NULL; + NBDClientSession *client = nbd_get_client_session(bs); + NBDRequest request = { +@@ -806,7 +817,7 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, + } + + ret = nbd_co_receive_cmdread_reply(client, request.handle, offset, qiov, +- &local_err); ++ &request_ret, &local_err); + if (local_err) { + trace_nbd_co_request_fail(request.from, request.len, request.handle, + request.flags, request.type, +@@ -814,7 +825,7 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, + ret, error_get_pretty(local_err)); + error_free(local_err); + } +- return ret; ++ return ret ? ret : request_ret; + } + + int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset, +@@ -908,7 +919,7 @@ int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs, + int64_t *pnum, int64_t *map, + BlockDriverState **file) + { +- int64_t ret; ++ int ret, request_ret; + NBDExtent extent = { 0 }; + NBDClientSession *client = nbd_get_client_session(bs); + Error *local_err = NULL; +@@ -933,7 +944,7 @@ int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs, + } + + ret = nbd_co_receive_blockstatus_reply(client, request.handle, bytes, +- &extent, &local_err); ++ &extent, &request_ret, &local_err); + if (local_err) { + trace_nbd_co_request_fail(request.from, request.len, request.handle, + request.flags, request.type, +@@ -941,8 +952,8 @@ int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs, + ret, error_get_pretty(local_err)); + error_free(local_err); + } +- if (ret < 0) { +- return ret; ++ if (ret < 0 || request_ret < 0) { ++ return ret ? ret : request_ret; + } + + assert(extent.length); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-nbd-client-split-connection-from-initializatio.patch b/SOURCES/kvm-block-nbd-client-split-connection-from-initializatio.patch new file mode 100644 index 0000000..fab552f --- /dev/null +++ b/SOURCES/kvm-block-nbd-client-split-connection-from-initializatio.patch @@ -0,0 +1,88 @@ +From a5b79479ab6a364899c6a5c7444c86c00bf0e42d Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:23:00 +0100 +Subject: [PATCH 122/163] block/nbd-client: split connection from + initialization + +RH-Author: John Snow +Message-id: <20190327172308.31077-48-jsnow@redhat.com> +Patchwork-id: 85206 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 47/55] block/nbd-client: split connection from initialization +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +Split connection code to reuse it for reconnect. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: Eric Blake +Message-Id: <20190201130138.94525-4-vsementsov@virtuozzo.com> +Signed-off-by: Eric Blake +(cherry picked from commit b0e4b5a58fde6c0ed9edb9b683cfbbd8ab45c35d) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 33 ++++++++++++++++++++++++--------- + 1 file changed, 24 insertions(+), 9 deletions(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index d86c412..22e5042 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -1011,13 +1011,13 @@ static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr, + return sioc; + } + +-int nbd_client_init(BlockDriverState *bs, +- SocketAddress *saddr, +- const char *export, +- QCryptoTLSCreds *tlscreds, +- const char *hostname, +- const char *x_dirty_bitmap, +- Error **errp) ++static int nbd_client_connect(BlockDriverState *bs, ++ SocketAddress *saddr, ++ const char *export, ++ QCryptoTLSCreds *tlscreds, ++ const char *hostname, ++ const char *x_dirty_bitmap, ++ Error **errp) + { + NBDClientSession *client = nbd_get_client_session(bs); + int ret; +@@ -1070,8 +1070,6 @@ int nbd_client_init(BlockDriverState *bs, + bs->supported_zero_flags |= BDRV_REQ_MAY_UNMAP; + } + +- qemu_co_mutex_init(&client->send_mutex); +- qemu_co_queue_init(&client->free_sema); + client->sioc = sioc; + + if (!client->ioc) { +@@ -1104,3 +1102,20 @@ int nbd_client_init(BlockDriverState *bs, + return ret; + } + } ++ ++int nbd_client_init(BlockDriverState *bs, ++ SocketAddress *saddr, ++ const char *export, ++ QCryptoTLSCreds *tlscreds, ++ const char *hostname, ++ const char *x_dirty_bitmap, ++ Error **errp) ++{ ++ NBDClientSession *client = nbd_get_client_session(bs); ++ ++ qemu_co_mutex_init(&client->send_mutex); ++ qemu_co_queue_init(&client->free_sema); ++ ++ return nbd_client_connect(bs, saddr, export, tlscreds, hostname, ++ x_dirty_bitmap, errp); ++} +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-nbd-client-use-traces-instead-of-noisy-error_r.patch b/SOURCES/kvm-block-nbd-client-use-traces-instead-of-noisy-error_r.patch new file mode 100644 index 0000000..8c7cf77 --- /dev/null +++ b/SOURCES/kvm-block-nbd-client-use-traces-instead-of-noisy-error_r.patch @@ -0,0 +1,253 @@ +From 32850a2e3ed06394a0086acc19b484ddf4a2cef6 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:19 +0100 +Subject: [PATCH 080/163] block/nbd-client: use traces instead of noisy + error_report_err + +RH-Author: John Snow +Message-id: <20190327172308.31077-7-jsnow@redhat.com> +Patchwork-id: 85183 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 06/55] block/nbd-client: use traces instead of noisy error_report_err +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +Reduce extra noise of nbd-client, change 083 correspondingly. + +In various commits (be41c100 in 2.10, f140e300 in 2.11, 78a33ab +in 2.12), we added spots where qemu as an NBD client would report +problems communicating with the server to stderr, because there +was no where else to send the error to. However, this is racy, +particularly since the most common source of these errors is when +either the client or the server abruptly hangs up, leaving one +coroutine to report the error only if it wins (or loses) the +race in attempting the read from the server before another +thread completes its cleanup of a protocol error that caused the +disconnect in the first place. The race is also apparent in the +fact that differences in the flush behavior of the server can +alter the frequency of encountering the race in the client (see +commit 6d39db96). + +Rather than polluting stderr, it's better to just trace these +situations, for use by developers debugging a flaky connection, +particularly since the real error that either triggers the abrupt +disconnection in the first place, or that results from the EIO +when a request can't receive a reply, DOES make it back to the +user in the normal Error propagation channels. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20181102151152.288399-4-vsementsov@virtuozzo.com> +[eblake: drop depedence on error hint, enhance commit message] +Signed-off-by: Eric Blake +(cherry picked from commit d8b4bad846f08ff0f167b46dc156a5310b750484) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 23 +++++++++++++++++++---- + block/trace-events | 4 ++++ + tests/qemu-iotests/083.out | 28 ---------------------------- + 3 files changed, 23 insertions(+), 32 deletions(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index fc5b7ed..ef32075 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -28,6 +28,8 @@ + */ + + #include "qemu/osdep.h" ++ ++#include "trace.h" + #include "qapi/error.h" + #include "nbd-client.h" + +@@ -79,7 +81,8 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque) + assert(s->reply.handle == 0); + ret = nbd_receive_reply(s->ioc, &s->reply, &local_err); + if (local_err) { +- error_report_err(local_err); ++ trace_nbd_read_reply_entry_fail(ret, error_get_pretty(local_err)); ++ error_free(local_err); + } + if (ret <= 0) { + break; +@@ -771,7 +774,11 @@ static int nbd_co_request(BlockDriverState *bs, NBDRequest *request, + + ret = nbd_co_receive_return_code(client, request->handle, &local_err); + if (local_err) { +- error_report_err(local_err); ++ trace_nbd_co_request_fail(request->from, request->len, request->handle, ++ request->flags, request->type, ++ nbd_cmd_lookup(request->type), ++ ret, error_get_pretty(local_err)); ++ error_free(local_err); + } + return ret; + } +@@ -802,7 +809,11 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, + ret = nbd_co_receive_cmdread_reply(client, request.handle, offset, qiov, + &local_err); + if (local_err) { +- error_report_err(local_err); ++ trace_nbd_co_request_fail(request.from, request.len, request.handle, ++ request.flags, request.type, ++ nbd_cmd_lookup(request.type), ++ ret, error_get_pretty(local_err)); ++ error_free(local_err); + } + return ret; + } +@@ -925,7 +936,11 @@ int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs, + ret = nbd_co_receive_blockstatus_reply(client, request.handle, bytes, + &extent, &local_err); + if (local_err) { +- error_report_err(local_err); ++ trace_nbd_co_request_fail(request.from, request.len, request.handle, ++ request.flags, request.type, ++ nbd_cmd_lookup(request.type), ++ ret, error_get_pretty(local_err)); ++ error_free(local_err); + } + if (ret < 0) { + return ret; +diff --git a/block/trace-events b/block/trace-events +index c35287b..6d4d399 100644 +--- a/block/trace-events ++++ b/block/trace-events +@@ -150,3 +150,7 @@ nvme_free_req_queue_wait(void *q) "q %p" + nvme_cmd_map_qiov(void *s, void *cmd, void *req, void *qiov, int entries) "s %p cmd %p req %p qiov %p entries %d" + nvme_cmd_map_qiov_pages(void *s, int i, uint64_t page) "s %p page[%d] 0x%"PRIx64 + nvme_cmd_map_qiov_iov(void *s, int i, void *page, int pages) "s %p iov[%d] %p pages %d" ++ ++# block/nbd-client.c ++nbd_read_reply_entry_fail(int ret, const char *err) "ret = %d, err: %s" ++nbd_co_request_fail(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, uint16_t type, const char *name, int ret, const char *err) "Request failed { .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64 ", .flags = 0x%" PRIx16 ", .type = %" PRIu16 " (%s) } ret = %d, err: %s" +diff --git a/tests/qemu-iotests/083.out b/tests/qemu-iotests/083.out +index f9af8bb..7419722 100644 +--- a/tests/qemu-iotests/083.out ++++ b/tests/qemu-iotests/083.out +@@ -41,8 +41,6 @@ can't open device nbd+tcp://127.0.0.1:PORT/foo + + === Check disconnect after neg2 === + +-Unable to read from socket: Connection reset by peer +-Connection closed + read failed: Input/output error + + === Check disconnect 8 neg2 === +@@ -55,40 +53,30 @@ can't open device nbd+tcp://127.0.0.1:PORT/foo + + === Check disconnect before request === + +-Unable to read from socket: Connection reset by peer +-Connection closed + read failed: Input/output error + + === Check disconnect after request === + +-Connection closed + read failed: Input/output error + + === Check disconnect before reply === + +-Connection closed + read failed: Input/output error + + === Check disconnect after reply === + +-Unexpected end-of-file before all bytes were read + read failed: Input/output error + + === Check disconnect 4 reply === + +-Unexpected end-of-file before all bytes were read +-Connection closed + read failed: Input/output error + + === Check disconnect 8 reply === + +-Unexpected end-of-file before all bytes were read +-Connection closed + read failed: Input/output error + + === Check disconnect before data === + +-Unexpected end-of-file before all bytes were read + read failed: Input/output error + + === Check disconnect after data === +@@ -118,8 +106,6 @@ can't open device nbd+tcp://127.0.0.1:PORT/ + + === Check disconnect after neg-classic === + +-Unable to read from socket: Connection reset by peer +-Connection closed + read failed: Input/output error + + === Check disconnect before neg1 === +@@ -164,8 +150,6 @@ can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock + + === Check disconnect after neg2 === + +-Unable to read from socket: Connection reset by peer +-Connection closed + read failed: Input/output error + + === Check disconnect 8 neg2 === +@@ -178,40 +162,30 @@ can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock + + === Check disconnect before request === + +-Unable to read from socket: Connection reset by peer +-Connection closed + read failed: Input/output error + + === Check disconnect after request === + +-Connection closed + read failed: Input/output error + + === Check disconnect before reply === + +-Connection closed + read failed: Input/output error + + === Check disconnect after reply === + +-Unexpected end-of-file before all bytes were read + read failed: Input/output error + + === Check disconnect 4 reply === + +-Unexpected end-of-file before all bytes were read +-Connection closed + read failed: Input/output error + + === Check disconnect 8 reply === + +-Unexpected end-of-file before all bytes were read +-Connection closed + read failed: Input/output error + + === Check disconnect before data === + +-Unexpected end-of-file before all bytes were read + read failed: Input/output error + + === Check disconnect after data === +@@ -241,8 +215,6 @@ can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock + + === Check disconnect after neg-classic === + +-Unable to read from socket: Connection reset by peer +-Connection closed + read failed: Input/output error + + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-nbd-move-connection-code-from-block-nbd-to-blo.patch b/SOURCES/kvm-block-nbd-move-connection-code-from-block-nbd-to-blo.patch new file mode 100644 index 0000000..1515ea8 --- /dev/null +++ b/SOURCES/kvm-block-nbd-move-connection-code-from-block-nbd-to-blo.patch @@ -0,0 +1,197 @@ +From 44a47acaccb8455be42cb302885af0934c3360db Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:59 +0100 +Subject: [PATCH 121/163] block/nbd: move connection code from block/nbd to + block/nbd-client + +RH-Author: John Snow +Message-id: <20190327172308.31077-47-jsnow@redhat.com> +Patchwork-id: 85218 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 46/55] block/nbd: move connection code from block/nbd to block/nbd-client +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +Keep all connection code in one file, to be able to implement reconnect +in further patches. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20190201130138.94525-3-vsementsov@virtuozzo.com> +Reviewed-by: Eric Blake +[eblake: format tweak] +Signed-off-by: Eric Blake +(cherry picked from commit d42f78e9400c51f1ae30dadd52995e4d6b052d89) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 38 ++++++++++++++++++++++++++++++++++++-- + block/nbd-client.h | 2 +- + block/nbd.c | 40 ++-------------------------------------- + 3 files changed, 39 insertions(+), 41 deletions(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index f5b9aaa..d86c412 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -990,8 +990,29 @@ void nbd_client_close(BlockDriverState *bs) + nbd_teardown_connection(bs); + } + ++static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr, ++ Error **errp) ++{ ++ QIOChannelSocket *sioc; ++ Error *local_err = NULL; ++ ++ sioc = qio_channel_socket_new(); ++ qio_channel_set_name(QIO_CHANNEL(sioc), "nbd-client"); ++ ++ qio_channel_socket_connect_sync(sioc, saddr, &local_err); ++ if (local_err) { ++ object_unref(OBJECT(sioc)); ++ error_propagate(errp, local_err); ++ return NULL; ++ } ++ ++ qio_channel_set_delay(QIO_CHANNEL(sioc), false); ++ ++ return sioc; ++} ++ + int nbd_client_init(BlockDriverState *bs, +- QIOChannelSocket *sioc, ++ SocketAddress *saddr, + const char *export, + QCryptoTLSCreds *tlscreds, + const char *hostname, +@@ -1001,6 +1022,16 @@ int nbd_client_init(BlockDriverState *bs, + NBDClientSession *client = nbd_get_client_session(bs); + int ret; + ++ /* ++ * establish TCP connection, return error if it fails ++ * TODO: Configurable retry-until-timeout behaviour. ++ */ ++ QIOChannelSocket *sioc = nbd_establish_connection(saddr, errp); ++ ++ if (!sioc) { ++ return -ECONNREFUSED; ++ } ++ + /* NBD handshake */ + logout("session init %s\n", export); + qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL); +@@ -1016,6 +1047,7 @@ int nbd_client_init(BlockDriverState *bs, + g_free(client->info.name); + if (ret < 0) { + logout("Failed to negotiate with the NBD server\n"); ++ object_unref(OBJECT(sioc)); + return ret; + } + if (x_dirty_bitmap && !client->info.base_allocation) { +@@ -1041,7 +1073,6 @@ int nbd_client_init(BlockDriverState *bs, + qemu_co_mutex_init(&client->send_mutex); + qemu_co_queue_init(&client->free_sema); + client->sioc = sioc; +- object_ref(OBJECT(client->sioc)); + + if (!client->ioc) { + client->ioc = QIO_CHANNEL(sioc); +@@ -1067,6 +1098,9 @@ int nbd_client_init(BlockDriverState *bs, + NBDRequest request = { .type = NBD_CMD_DISC }; + + nbd_send_request(client->ioc ?: QIO_CHANNEL(sioc), &request); ++ ++ object_unref(OBJECT(sioc)); ++ + return ret; + } + } +diff --git a/block/nbd-client.h b/block/nbd-client.h +index cfc9055..2f047ba 100644 +--- a/block/nbd-client.h ++++ b/block/nbd-client.h +@@ -41,7 +41,7 @@ typedef struct NBDClientSession { + NBDClientSession *nbd_get_client_session(BlockDriverState *bs); + + int nbd_client_init(BlockDriverState *bs, +- QIOChannelSocket *sock, ++ SocketAddress *saddr, + const char *export_name, + QCryptoTLSCreds *tlscreds, + const char *hostname, +diff --git a/block/nbd.c b/block/nbd.c +index f29c10f..838a8fe 100644 +--- a/block/nbd.c ++++ b/block/nbd.c +@@ -295,30 +295,6 @@ NBDClientSession *nbd_get_client_session(BlockDriverState *bs) + return &s->client; + } + +-static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr, +- Error **errp) +-{ +- QIOChannelSocket *sioc; +- Error *local_err = NULL; +- +- sioc = qio_channel_socket_new(); +- qio_channel_set_name(QIO_CHANNEL(sioc), "nbd-client"); +- +- qio_channel_socket_connect_sync(sioc, +- saddr, +- &local_err); +- if (local_err) { +- object_unref(OBJECT(sioc)); +- error_propagate(errp, local_err); +- return NULL; +- } +- +- qio_channel_set_delay(QIO_CHANNEL(sioc), false); +- +- return sioc; +-} +- +- + static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) + { + Object *obj; +@@ -394,7 +370,6 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, + BDRVNBDState *s = bs->opaque; + QemuOpts *opts = NULL; + Error *local_err = NULL; +- QIOChannelSocket *sioc = NULL; + QCryptoTLSCreds *tlscreds = NULL; + const char *hostname = NULL; + int ret = -EINVAL; +@@ -434,22 +409,11 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, + hostname = s->saddr->u.inet.host; + } + +- /* establish TCP connection, return error if it fails +- * TODO: Configurable retry-until-timeout behaviour. +- */ +- sioc = nbd_establish_connection(s->saddr, errp); +- if (!sioc) { +- ret = -ECONNREFUSED; +- goto error; +- } +- + /* NBD handshake */ +- ret = nbd_client_init(bs, sioc, s->export, tlscreds, hostname, ++ ret = nbd_client_init(bs, s->saddr, s->export, tlscreds, hostname, + qemu_opt_get(opts, "x-dirty-bitmap"), errp); ++ + error: +- if (sioc) { +- object_unref(OBJECT(sioc)); +- } + if (tlscreds) { + object_unref(OBJECT(tlscreds)); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-pflash_cfi02-Fix-memory-leak-and-potential-use.patch b/SOURCES/kvm-block-pflash_cfi02-Fix-memory-leak-and-potential-use.patch new file mode 100644 index 0000000..8a1ab9a --- /dev/null +++ b/SOURCES/kvm-block-pflash_cfi02-Fix-memory-leak-and-potential-use.patch @@ -0,0 +1,97 @@ +From 689898009c6930b8a9ce598e85678bfc0f131594 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:50:51 +0200 +Subject: [PATCH 24/53] block/pflash_cfi02: Fix memory leak and potential + use-after-free +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-3-armbru@redhat.com> +Patchwork-id: 87984 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 02/31] block/pflash_cfi02: Fix memory leak and potential use-after-free +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +From: Stephen Checkoway + +Don't dynamically allocate the pflash's timer. But do use timer_del in +an unrealize function to make sure that the timer can't fire after the +pflash_t has been freed. + +Signed-off-by: Stephen Checkoway +Reviewed-by: Philippe Mathieu-Daudé +Reviewed-by: Wei Yang +Message-Id: <20190219153727.62279-1-stephen.checkoway@oberlin.edu> +Signed-off-by: Laurent Vivier +(cherry picked from commit d80cf1eb2e87df3a9bfb226bcc7fb3a1aa858817) +Signed-off-by: Miroslav Rezanina +--- + hw/block/pflash_cfi02.c | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c +index 75d1ae1..cbc3d4d 100644 +--- a/hw/block/pflash_cfi02.c ++++ b/hw/block/pflash_cfi02.c +@@ -84,7 +84,7 @@ struct pflash_t { + uint16_t unlock_addr0; + uint16_t unlock_addr1; + uint8_t cfi_table[0x52]; +- QEMUTimer *timer; ++ QEMUTimer timer; + /* The device replicates the flash memory across its memory space. Emulate + * that by having a container (.mem) filled with an array of aliases + * (.mem_mappings) pointing to the flash memory (.orig_mem). +@@ -431,7 +431,7 @@ static void pflash_write (pflash_t *pfl, hwaddr offset, + } + pfl->status = 0x00; + /* Let's wait 5 seconds before chip erase is done */ +- timer_mod(pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ++ timer_mod(&pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + (NANOSECONDS_PER_SECOND * 5)); + break; + case 0x30: +@@ -446,7 +446,7 @@ static void pflash_write (pflash_t *pfl, hwaddr offset, + } + pfl->status = 0x00; + /* Let's wait 1/2 second before sector erase is done */ +- timer_mod(pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ++ timer_mod(&pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + (NANOSECONDS_PER_SECOND / 2)); + break; + default: +@@ -658,7 +658,7 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp) + pfl->rom_mode = 1; + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem); + +- pfl->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pflash_timer, pfl); ++ timer_init_ns(&pfl->timer, QEMU_CLOCK_VIRTUAL, pflash_timer, pfl); + pfl->wcycle = 0; + pfl->cmd = 0; + pfl->status = 0; +@@ -757,11 +757,18 @@ static Property pflash_cfi02_properties[] = { + DEFINE_PROP_END_OF_LIST(), + }; + ++static void pflash_cfi02_unrealize(DeviceState *dev, Error **errp) ++{ ++ pflash_t *pfl = CFI_PFLASH02(dev); ++ timer_del(&pfl->timer); ++} ++ + static void pflash_cfi02_class_init(ObjectClass *klass, void *data) + { + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = pflash_cfi02_realize; ++ dc->unrealize = pflash_cfi02_unrealize; + dc->props = pflash_cfi02_properties; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-qapi-Add-qdev-field-to-query-blockstats-result.patch b/SOURCES/kvm-block-qapi-Add-qdev-field-to-query-blockstats-result.patch new file mode 100644 index 0000000..38a1ccb --- /dev/null +++ b/SOURCES/kvm-block-qapi-Add-qdev-field-to-query-blockstats-result.patch @@ -0,0 +1,111 @@ +From 95bfd60b406ed4cce708b5981d4bc3a007d0cec9 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 7 Aug 2018 14:03:59 +0200 +Subject: [PATCH 06/13] block/qapi: Add 'qdev' field to query-blockstats result + +RH-Author: Kevin Wolf +Message-id: <20180807140401.23995-2-kwolf@redhat.com> +Patchwork-id: 81666 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/3] block/qapi: Add 'qdev' field to query-blockstats result +Bugzilla: 1612114 +RH-Acked-by: John Snow +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Markus Armbruster + +Like for query-block, the client needs to identify which BlockBackend +the returned data is for. Anonymous BlockBackends are identified by the +device model they are attached to. Add a 'qdev' field that contains the +qdev ID or QOM path of the attached device model. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +(cherry picked from commit 5a9cb5a97f1e519f249d9ec482d498b78296b51d) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/qapi.c | 10 ++++++++++ + qapi/block-core.json | 14 ++++++++++---- + 2 files changed, 20 insertions(+), 4 deletions(-) + +diff --git a/block/qapi.c b/block/qapi.c +index e12968f..50f867d 100644 +--- a/block/qapi.c ++++ b/block/qapi.c +@@ -597,11 +597,21 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, + BlockStatsList *info = g_malloc0(sizeof(*info)); + AioContext *ctx = blk_get_aio_context(blk); + BlockStats *s; ++ char *qdev; + + aio_context_acquire(ctx); + s = bdrv_query_bds_stats(blk_bs(blk), true); + s->has_device = true; + s->device = g_strdup(blk_name(blk)); ++ ++ qdev = blk_get_attached_dev_id(blk); ++ if (qdev && *qdev) { ++ s->has_qdev = true; ++ s->qdev = qdev; ++ } else { ++ g_free(qdev); ++ } ++ + bdrv_query_blk_stats(s->stats, blk); + aio_context_release(ctx); + +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 8a00bec..c6b42eb 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -867,6 +867,9 @@ + # + # @node-name: The node name of the device. (Since 2.3) + # ++# @qdev: The qdev ID, or if no ID is assigned, the QOM path of the block ++# device. (since 3.0) ++# + # @stats: A @BlockDeviceStats for the device. + # + # @parent: This describes the file block device if it has one. +@@ -880,7 +883,7 @@ + # Since: 0.14.0 + ## + { 'struct': 'BlockStats', +- 'data': {'*device': 'str', '*node-name': 'str', ++ 'data': {'*device': 'str', '*qdev': 'str', '*node-name': 'str', + 'stats': 'BlockDeviceStats', + '*parent': 'BlockStats', + '*backing': 'BlockStats'} } +@@ -942,7 +945,8 @@ + # "idle_time_ns":2953431879, + # "account_invalid":true, + # "account_failed":false +-# } ++# }, ++# "qdev": "/machine/unattached/device[23]" + # }, + # { + # "device":"ide1-cd0", +@@ -960,7 +964,8 @@ + # "wr_merged":0, + # "account_invalid":false, + # "account_failed":false +-# } ++# }, ++# "qdev": "/machine/unattached/device[24]" + # }, + # { + # "device":"floppy0", +@@ -978,7 +983,8 @@ + # "wr_merged":0, + # "account_invalid":false, + # "account_failed":false +-# } ++# }, ++# "qdev": "/machine/unattached/device[16]" + # }, + # { + # "device":"sd0", +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-qapi-Include-anonymous-BBs-in-query-blockstats.patch b/SOURCES/kvm-block-qapi-Include-anonymous-BBs-in-query-blockstats.patch new file mode 100644 index 0000000..830a3fe --- /dev/null +++ b/SOURCES/kvm-block-qapi-Include-anonymous-BBs-in-query-blockstats.patch @@ -0,0 +1,52 @@ +From 50a74a6f33a66effb2c4f28e2c6ca8ca3041d312 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 7 Aug 2018 14:04:00 +0200 +Subject: [PATCH 07/13] block/qapi: Include anonymous BBs in query-blockstats + +RH-Author: Kevin Wolf +Message-id: <20180807140401.23995-3-kwolf@redhat.com> +Patchwork-id: 81664 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 2/3] block/qapi: Include anonymous BBs in query-blockstats +Bugzilla: 1612114 +RH-Acked-by: John Snow +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Markus Armbruster + +Consistent with query-block, query-blockstats should not only include +named BlockBackends, but also those that are anonymous, but belong to a +device model. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +(cherry picked from commit 567dcb31f23657fb71060067b0b1c9ac29110d16) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/qapi.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/block/qapi.c b/block/qapi.c +index 50f867d..339727f 100644 +--- a/block/qapi.c ++++ b/block/qapi.c +@@ -593,12 +593,16 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, + p_next = &info->next; + } + } else { +- for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { ++ for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) { + BlockStatsList *info = g_malloc0(sizeof(*info)); + AioContext *ctx = blk_get_aio_context(blk); + BlockStats *s; + char *qdev; + ++ if (!*blk_name(blk) && !blk_get_attached_dev(blk)) { ++ continue; ++ } ++ + aio_context_acquire(ctx); + s = bdrv_query_bds_stats(blk_bs(blk), true); + s->has_device = true; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-qcow2-bitmap-Allow-resizes-with-persistent-bit.patch b/SOURCES/kvm-block-qcow2-bitmap-Allow-resizes-with-persistent-bit.patch new file mode 100644 index 0000000..9a7d921 --- /dev/null +++ b/SOURCES/kvm-block-qcow2-bitmap-Allow-resizes-with-persistent-bit.patch @@ -0,0 +1,126 @@ +From ad64bca4d6cc7a258a546112b9494e6bfdf6cfbe Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 22:42:51 +0200 +Subject: [PATCH 156/163] block/qcow2-bitmap: Allow resizes with persistent + bitmaps + +RH-Author: John Snow +Message-id: <20190403224253.5251-4-jsnow@redhat.com> +Patchwork-id: 85433 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 3/5] block/qcow2-bitmap: Allow resizes with persistent bitmaps +Bugzilla: 1666884 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Sergio Lopez Pascual + +Since we now load all bitmaps into memory anyway, we can just truncate +them in-memory and then flush them back to disk. Just in case, we will +still check and enforce that this shortcut is valid -- i.e. that any +bitmap described on-disk is indeed in-memory and can be modified. + +If there are any inconsistent bitmaps, we refuse to allow the truncate +as we do not actually load these bitmaps into memory, and it isn't safe +or reasonable to attempt to truncate corrupted data. + +Signed-off-by: John Snow +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-id: 20190311185147.52309-4-vsementsov@virtuozzo.com + [vsementsov: drop bitmap flushing, fix block comments style] +Signed-off-by: John Snow +(cherry picked from commit d19c6b36ffe09cec7ce7ac6a3e979bfe923ebba9) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + block/qcow2-bitmap.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ + block/qcow2.c | 4 +--- + block/qcow2.h | 1 + + 3 files changed, 48 insertions(+), 3 deletions(-) + +diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c +index fe4a6a2..3150855 100644 +--- a/block/qcow2-bitmap.c ++++ b/block/qcow2-bitmap.c +@@ -1176,6 +1176,52 @@ int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp) + return qcow2_reopen_bitmaps_rw_hint(bs, NULL, errp); + } + ++/* Checks to see if it's safe to resize bitmaps */ ++int 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. + */ +diff --git a/block/qcow2.c b/block/qcow2.c +index 21f7556..b137480 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -3487,9 +3487,7 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, + } + + /* cannot proceed if image has bitmaps */ +- if (s->nb_bitmaps) { +- /* TODO: resize bitmaps in the image */ +- error_setg(errp, "Can't resize an image which has bitmaps"); ++ if (qcow2_truncate_bitmaps_check(bs, errp)) { + ret = -ENOTSUP; + goto fail; + } +diff --git a/block/qcow2.h b/block/qcow2.h +index 2633e33..3b1b972 100644 +--- a/block/qcow2.h ++++ b/block/qcow2.h +@@ -681,6 +681,7 @@ Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs, + int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated, + Error **errp); + int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp); ++int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp); + void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp); + int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp); + bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-qcow2-bitmap-Don-t-check-size-for-IN_USE-bitma.patch b/SOURCES/kvm-block-qcow2-bitmap-Don-t-check-size-for-IN_USE-bitma.patch new file mode 100644 index 0000000..73bcdc9 --- /dev/null +++ b/SOURCES/kvm-block-qcow2-bitmap-Don-t-check-size-for-IN_USE-bitma.patch @@ -0,0 +1,67 @@ +From 09fa7fdc08f3edc26fd143bd7f3f6a863e6f1f17 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 22:42:50 +0200 +Subject: [PATCH 155/163] block/qcow2-bitmap: Don't check size for IN_USE + bitmap + +RH-Author: John Snow +Message-id: <20190403224253.5251-3-jsnow@redhat.com> +Patchwork-id: 85435 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 2/5] block/qcow2-bitmap: Don't check size for IN_USE bitmap +Bugzilla: 1666884 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Sergio Lopez Pascual + +From: Vladimir Sementsov-Ogievskiy + +We are going to allow image resize when there are persistent bitmaps. +It may lead to appearing of inconsistent bitmaps (IN_USE=1) with +inconsistent size. But we still want to load them as inconsistent. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-id: 20190311185147.52309-3-vsementsov@virtuozzo.com +Signed-off-by: John Snow +(cherry picked from commit bf5f0cf5d819cce45dd578a19386d8b60022654f) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/qcow2-bitmap.c | 21 ++++++++++++++++++--- + 1 file changed, 18 insertions(+), 3 deletions(-) + +diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c +index 4d093da..fe4a6a2 100644 +--- a/block/qcow2-bitmap.c ++++ b/block/qcow2-bitmap.c +@@ -464,10 +464,25 @@ static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry) + return len; + } + +- fail = (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) || +- (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits)); ++ if (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) { ++ return -EINVAL; ++ } + +- return fail ? -EINVAL : 0; ++ 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) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-qcow2-bitmap-fix-free_bitmap_clusters.patch b/SOURCES/kvm-block-qcow2-bitmap-fix-free_bitmap_clusters.patch new file mode 100644 index 0000000..bc3942b --- /dev/null +++ b/SOURCES/kvm-block-qcow2-bitmap-fix-free_bitmap_clusters.patch @@ -0,0 +1,46 @@ +From fb76c3aaa2e3d54b20fef63fcc64d2ff5a394b08 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:54:44 +0200 +Subject: [PATCH 59/89] block/qcow2-bitmap: fix free_bitmap_clusters + +RH-Author: John Snow +Message-id: <20180718225511.14878-9-jsnow@redhat.com> +Patchwork-id: 81392 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 08/35] block/qcow2-bitmap: fix free_bitmap_clusters +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +This assert may fail, because bitmap_table is not initialized. Just +drop it, as it's obvious, that bitmap_table_load sets bitmap_table +parameter only when returning zero. + +Reported-by: Pavel Butsykin +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-id: 20180608101225.2575-1-vsementsov@virtuozzo.com +Signed-off-by: Max Reitz +(cherry picked from commit 7eb24009dbf8102fc27d5459bec1cb8a932540c4) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/qcow2-bitmap.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c +index 6e93ec4..3e4e4e4 100644 +--- a/block/qcow2-bitmap.c ++++ b/block/qcow2-bitmap.c +@@ -254,7 +254,6 @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb) + + ret = bitmap_table_load(bs, tb, &bitmap_table); + if (ret < 0) { +- assert(bitmap_table == NULL); + return ret; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-qcow2-improve-error-message-in-qcow2_inactivat.patch b/SOURCES/kvm-block-qcow2-improve-error-message-in-qcow2_inactivat.patch new file mode 100644 index 0000000..7818f0b --- /dev/null +++ b/SOURCES/kvm-block-qcow2-improve-error-message-in-qcow2_inactivat.patch @@ -0,0 +1,48 @@ +From f2f3456bff9d30ce3850ff4d97b114714e4fe90d Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:39 +0100 +Subject: [PATCH 29/33] block/qcow2: improve error message in qcow2_inactivate + +RH-Author: John Snow +Message-id: <20190206221243.7407-20-jsnow@redhat.com> +Patchwork-id: 84278 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 19/23] block/qcow2: improve error message in qcow2_inactivate +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +From: Vladimir Sementsov-Ogievskiy + +Signed-off-by: Vladimir Sementsov-Ogievskiy +[Maintainer edit -- touched up error message. --js] +Reviewed-by: John Snow +Signed-off-by: John Snow +(cherry picked from commit 132adb682098e9af40a2132ec4feec6850fce8cd) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + block/qcow2.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/block/qcow2.c b/block/qcow2.c +index 1ea7203..5c5530d 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -2116,9 +2116,9 @@ static int qcow2_inactivate(BlockDriverState *bs) + qcow2_store_persistent_dirty_bitmaps(bs, &local_err); + if (local_err != NULL) { + result = -EINVAL; +- error_report_err(local_err); +- error_report("Persistent bitmaps are lost for node '%s'", +- bdrv_get_device_or_node_name(bs)); ++ 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); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-qdict-Clean-up-qdict_crumple-a-bit.patch b/SOURCES/kvm-block-qdict-Clean-up-qdict_crumple-a-bit.patch new file mode 100644 index 0000000..d425ce0 --- /dev/null +++ b/SOURCES/kvm-block-qdict-Clean-up-qdict_crumple-a-bit.patch @@ -0,0 +1,91 @@ +From 096bb1ca93a22a0ca874b76c3ad03385a5667f41 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:24 +0200 +Subject: [PATCH 17/54] block-qdict: Clean up qdict_crumple() a bit + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-18-armbru@redhat.com> +Patchwork-id: 80729 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 17/23] block-qdict: Clean up qdict_crumple() a bit +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +When you mix scalar and non-scalar keys, whether you get an "already +set as scalar" or an "already set as dict" error depends on qdict +iteration order. Neither message makes much sense. Replace by +""Cannot mix scalar and non-scalar keys". This is similar to the +message we get for mixing list and non-list keys. + +I find qdict_crumple()'s first loop hard to understand. Rearrange it +and add a comment. + +Signed-off-by: Markus Armbruster +Signed-off-by: Kevin Wolf +(cherry picked from commit 3692b5d76819e573dedc9004c4b2b0e3dad83530) +Signed-off-by: Miroslav Rezanina +--- + qobject/block-qdict.c | 32 ++++++++++++++++---------------- + 1 file changed, 16 insertions(+), 16 deletions(-) + +diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c +index a4e1c8d..36cf58a 100644 +--- a/qobject/block-qdict.c ++++ b/qobject/block-qdict.c +@@ -403,7 +403,7 @@ static int qdict_is_list(QDict *maybe_list, Error **errp) + QObject *qdict_crumple(const QDict *src, Error **errp) + { + const QDictEntry *ent; +- QDict *two_level, *multi_level = NULL; ++ QDict *two_level, *multi_level = NULL, *child_dict; + QObject *dst = NULL, *child; + size_t i; + char *prefix = NULL; +@@ -422,28 +422,28 @@ QObject *qdict_crumple(const QDict *src, Error **errp) + } + + 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) { +- QDict *child_dict = qobject_to(QDict, child); + if (!child_dict) { +- if (child) { +- error_setg(errp, "Key %s prefix is already set as a scalar", +- prefix); +- goto error; +- } +- + child_dict = qdict_new(); +- qdict_put_obj(two_level, prefix, QOBJECT(child_dict)); ++ qdict_put(two_level, prefix, child_dict); + } +- + qdict_put_obj(child_dict, suffix, qobject_ref(ent->value)); + } else { +- if (child) { +- error_setg(errp, "Key %s prefix is already set as a dict", +- prefix); +- goto error; +- } + qdict_put_obj(two_level, prefix, qobject_ref(ent->value)); + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-qdict-Simplify-qdict_flatten_qdict.patch b/SOURCES/kvm-block-qdict-Simplify-qdict_flatten_qdict.patch new file mode 100644 index 0000000..7e46be6 --- /dev/null +++ b/SOURCES/kvm-block-qdict-Simplify-qdict_flatten_qdict.patch @@ -0,0 +1,82 @@ +From 637b26d723d07ce653b22c7882037db6661d0bbf Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:22 +0200 +Subject: [PATCH 15/54] block-qdict: Simplify qdict_flatten_qdict() + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-16-armbru@redhat.com> +Patchwork-id: 80723 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 15/23] block-qdict: Simplify qdict_flatten_qdict() +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +There's no need to restart the loop. We don't elsewhere, e.g. in +qdict_extract_subqdict(), qdict_join() and qemu_opts_absorb_qdict(). +Simplify accordingly. + +Signed-off-by: Markus Armbruster +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit eb0e0f7d3d4a9c585421d05b19ca71df5d69fc47) +Signed-off-by: Miroslav Rezanina +--- + qobject/block-qdict.c | 18 +++--------------- + 1 file changed, 3 insertions(+), 15 deletions(-) + +diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c +index 41f39ab..f32df34 100644 +--- a/qobject/block-qdict.c ++++ b/qobject/block-qdict.c +@@ -89,16 +89,13 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) + QObject *value; + const QDictEntry *entry, *next; + char *new_key; +- bool delete; + + entry = qdict_first(qdict); + + while (entry != NULL) { +- + next = qdict_next(qdict, entry); + value = qdict_entry_value(entry); + new_key = NULL; +- delete = false; + + if (prefix) { + new_key = g_strdup_printf("%s.%s", prefix, entry->key); +@@ -109,27 +106,18 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) + * itself disappears. */ + qdict_flatten_qdict(qobject_to(QDict, value), target, + new_key ? new_key : entry->key); +- delete = true; ++ qdict_del(qdict, entry->key); + } else if (qobject_type(value) == QTYPE_QLIST) { + qdict_flatten_qlist(qobject_to(QList, value), target, + new_key ? new_key : entry->key); +- delete = true; ++ qdict_del(qdict, entry->key); + } else if (prefix) { + /* All other objects are moved to the target unchanged. */ + qdict_put_obj(target, new_key, qobject_ref(value)); +- delete = true; +- } +- +- g_free(new_key); +- +- if (delete) { + qdict_del(qdict, entry->key); +- +- /* Restart loop after modifying the iterated QDict */ +- entry = qdict_first(qdict); +- continue; + } + ++ g_free(new_key); + entry = next; + } + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-qdict-Simplify-qdict_is_list-some.patch b/SOURCES/kvm-block-qdict-Simplify-qdict_is_list-some.patch new file mode 100644 index 0000000..3ea641d --- /dev/null +++ b/SOURCES/kvm-block-qdict-Simplify-qdict_is_list-some.patch @@ -0,0 +1,69 @@ +From 80e27f95206b2df21b9a53dd3a0956246de495db Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:25 +0200 +Subject: [PATCH 18/54] block-qdict: Simplify qdict_is_list() some + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-19-armbru@redhat.com> +Patchwork-id: 80741 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 18/23] block-qdict: Simplify qdict_is_list() some +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Signed-off-by: Markus Armbruster +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit c78b8cfbfd53737353dc94dfb99c57d72ce31ab5) +Signed-off-by: Miroslav Rezanina +--- + qobject/block-qdict.c | 27 +++++++++++---------------- + 1 file changed, 11 insertions(+), 16 deletions(-) + +diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c +index 36cf58a..e51a3d2 100644 +--- a/qobject/block-qdict.c ++++ b/qobject/block-qdict.c +@@ -317,27 +317,22 @@ static int qdict_is_list(QDict *maybe_list, Error **errp) + + 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 (qemu_strtoi64(ent->key, NULL, 10, &val) == 0) { +- if (is_list == -1) { +- is_list = 1; +- } else if (!is_list) { +- error_setg(errp, +- "Cannot mix list and non-list keys"); +- return -1; +- } ++ 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; + } +- } else { +- if (is_list == -1) { +- is_list = 0; +- } else if (is_list) { +- error_setg(errp, +- "Cannot mix list and non-list keys"); +- return -1; +- } + } + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-qdict-Tweak-qdict_flatten_qdict-qdict_flatten_.patch b/SOURCES/kvm-block-qdict-Tweak-qdict_flatten_qdict-qdict_flatten_.patch new file mode 100644 index 0000000..af99836 --- /dev/null +++ b/SOURCES/kvm-block-qdict-Tweak-qdict_flatten_qdict-qdict_flatten_.patch @@ -0,0 +1,77 @@ +From a558dd841d434d95377dadc45c79d1de1b6f8d2d Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:23 +0200 +Subject: [PATCH 16/54] block-qdict: Tweak qdict_flatten_qdict(), + qdict_flatten_qlist() + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-17-armbru@redhat.com> +Patchwork-id: 80725 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 16/23] block-qdict: Tweak qdict_flatten_qdict(), qdict_flatten_qlist() +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +qdict_flatten_qdict() skips copying scalars from @qdict to @target +when the two are the same. Fair enough, but it uses a non-obvious +test for "same". Replace it by the obvious one. While there, improve +comments. + +Signed-off-by: Markus Armbruster +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit f1b34a248e9785e8cc0d28a1685d2cf4460bb256) +Signed-off-by: Miroslav Rezanina +--- + qobject/block-qdict.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c +index f32df34..a4e1c8d 100644 +--- a/qobject/block-qdict.c ++++ b/qobject/block-qdict.c +@@ -71,12 +71,15 @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) + value = qlist_entry_obj(entry); + new_key = g_strdup_printf("%s.%i", prefix, i); + ++ /* ++ * Flatten non-empty QDict and QList recursively into @target, ++ * copy other objects to @target ++ */ + if (qobject_type(value) == QTYPE_QDICT) { + qdict_flatten_qdict(qobject_to(QDict, value), target, new_key); + } else if (qobject_type(value) == QTYPE_QLIST) { + qdict_flatten_qlist(qobject_to(QList, value), target, new_key); + } else { +- /* All other types are moved to the target unchanged. */ + qdict_put_obj(target, new_key, qobject_ref(value)); + } + +@@ -101,9 +104,11 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) + new_key = g_strdup_printf("%s.%s", prefix, entry->key); + } + ++ /* ++ * Flatten non-empty QDict and QList recursively into @target, ++ * copy other objects to @target ++ */ + if (qobject_type(value) == QTYPE_QDICT) { +- /* Entries of QDicts are processed recursively, the QDict object +- * itself disappears. */ + qdict_flatten_qdict(qobject_to(QDict, value), target, + new_key ? new_key : entry->key); + qdict_del(qdict, entry->key); +@@ -111,8 +116,7 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) + qdict_flatten_qlist(qobject_to(QList, value), target, + new_key ? new_key : entry->key); + qdict_del(qdict, entry->key); +- } else if (prefix) { +- /* All other objects are moved to the target unchanged. */ ++ } else if (target != qdict) { + qdict_put_obj(target, new_key, qobject_ref(value)); + qdict_del(qdict, entry->key); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-quorum-Support-BDRV_REQ_WRITE_UNCHANGED.patch b/SOURCES/kvm-block-quorum-Support-BDRV_REQ_WRITE_UNCHANGED.patch new file mode 100644 index 0000000..8803221 --- /dev/null +++ b/SOURCES/kvm-block-quorum-Support-BDRV_REQ_WRITE_UNCHANGED.patch @@ -0,0 +1,114 @@ +From 34e748aa7dc2215fd2d8f03e491f1a2a450ba5d0 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 16:12:08 +0200 +Subject: [PATCH 32/54] block/quorum: Support BDRV_REQ_WRITE_UNCHANGED + +RH-Author: Max Reitz +Message-id: <20180618161212.14444-7-mreitz@redhat.com> +Patchwork-id: 80770 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 06/10] block/quorum: Support BDRV_REQ_WRITE_UNCHANGED +Bugzilla: 1518738 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Miroslav Rezanina + +We just need to forward it to quorum's children (except in case of a +rewrite because of corruption), but for that we first have to support +flags in child requests at all. + +Signed-off-by: Max Reitz +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Alberto Garcia +Message-id: 20180421132929.21610-6-mreitz@redhat.com +Reviewed-by: Kevin Wolf +Signed-off-by: Max Reitz +(cherry picked from commit 1b1a920b713af6af795d49d0e3d2a8a65020bf82) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/quorum.c | 19 +++++++++++++------ + 1 file changed, 13 insertions(+), 6 deletions(-) + +diff --git a/block/quorum.c b/block/quorum.c +index f1f39ba..0d90a02 100644 +--- a/block/quorum.c ++++ b/block/quorum.c +@@ -116,6 +116,7 @@ struct QuorumAIOCB { + /* Request metadata */ + uint64_t offset; + uint64_t bytes; ++ int flags; + + QEMUIOVector *qiov; /* calling IOV */ + +@@ -158,7 +159,8 @@ static bool quorum_64bits_compare(QuorumVoteValue *a, QuorumVoteValue *b) + static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs, + QEMUIOVector *qiov, + uint64_t offset, +- uint64_t bytes) ++ uint64_t bytes, ++ int flags) + { + BDRVQuorumState *s = bs->opaque; + QuorumAIOCB *acb = g_new(QuorumAIOCB, 1); +@@ -169,6 +171,7 @@ static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs, + .bs = bs, + .offset = offset, + .bytes = bytes, ++ .flags = flags, + .qiov = qiov, + .votes.compare = quorum_sha256_compare, + .votes.vote_list = QLIST_HEAD_INITIALIZER(acb.votes.vote_list), +@@ -272,9 +275,11 @@ static void quorum_rewrite_entry(void *opaque) + BDRVQuorumState *s = acb->bs->opaque; + + /* Ignore any errors, it's just a correction attempt for already +- * corrupted data. */ ++ * corrupted data. ++ * Mask out BDRV_REQ_WRITE_UNCHANGED because this overwrites the ++ * area with different data from the other children. */ + bdrv_co_pwritev(s->children[co->idx], acb->offset, acb->bytes, +- acb->qiov, 0); ++ acb->qiov, acb->flags & ~BDRV_REQ_WRITE_UNCHANGED); + + /* Wake up the caller after the last rewrite */ + acb->rewrite_count--; +@@ -674,7 +679,7 @@ static int quorum_co_preadv(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, QEMUIOVector *qiov, int flags) + { + BDRVQuorumState *s = bs->opaque; +- QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes); ++ QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes, flags); + int ret; + + acb->is_read = true; +@@ -700,7 +705,7 @@ static void write_quorum_entry(void *opaque) + + sacb->bs = s->children[i]->bs; + sacb->ret = bdrv_co_pwritev(s->children[i], acb->offset, acb->bytes, +- acb->qiov, 0); ++ acb->qiov, acb->flags); + if (sacb->ret == 0) { + acb->success_count++; + } else { +@@ -720,7 +725,7 @@ static int quorum_co_pwritev(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, QEMUIOVector *qiov, int flags) + { + BDRVQuorumState *s = bs->opaque; +- QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes); ++ QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes, flags); + int i, ret; + + for (i = 0; i < s->num_children; i++) { +@@ -962,6 +967,8 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, + } + s->next_child_index = s->num_children; + ++ bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; ++ + g_free(opened); + goto exit; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-rbd-Attempt-to-parse-legacy-filenames.patch b/SOURCES/kvm-block-rbd-Attempt-to-parse-legacy-filenames.patch new file mode 100644 index 0000000..5601076 --- /dev/null +++ b/SOURCES/kvm-block-rbd-Attempt-to-parse-legacy-filenames.patch @@ -0,0 +1,117 @@ +From ba592010c82c1302c611f6023d4abda24ee3b689 Mon Sep 17 00:00:00 2001 +From: Jeffrey Cody +Date: Wed, 12 Sep 2018 13:45:43 +0200 +Subject: [PATCH 04/49] block/rbd: Attempt to parse legacy filenames + +RH-Author: Jeffrey Cody +Message-id: <819d451b971d87d47d5bc73feaf20be5bf42e6ef.1536759805.git.jcody@redhat.com> +Patchwork-id: 82140 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 2/3] block/rbd: Attempt to parse legacy filenames +Bugzilla: 1610605 +RH-Acked-by: Eric Blake +RH-Acked-by: John Snow +RH-Acked-by: Fam Zheng + +When we converted rbd to get rid of the older key/value-centric +encoding format, we broke compatibility with image files with backing +file strings encoded in the old format. + +This leaves a bit of an ugly conundrum, and a hacky solution. + +If the initial attempt to parse the "proper" options fails, it assumes +that we may have an older key/value encoded filename. Fall back to +attempting to parse the filename, and extract the required options from +it. If that fails, pass along the original error message. + +We do not support mixed modern usage alongside legacy keyvalue pair +usage. + +A deprecation warning has been added, although care should be taken +when actually deprecating since the impact is not limited to +commandline or qapi usage, but also opening existing images. + +Reviewed-by: Eric Blake +Signed-off-by: Jeff Cody +Message-id: 15b332e5432ad069441f7275a46080f465d789a0.1536704901.git.jcody@redhat.com +Signed-off-by: Jeff Cody +(cherry picked from commit 866543123c3cd190c44b4caf7c9ed82844c0bcc6) +Signed-off-by: Jeff Cody +Signed-off-by: Miroslav Rezanina +--- + block/rbd.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 51 insertions(+), 2 deletions(-) + +diff --git a/block/rbd.c b/block/rbd.c +index 1e4d339..8f81fbc 100644 +--- a/block/rbd.c ++++ b/block/rbd.c +@@ -671,6 +671,33 @@ static int qemu_rbd_convert_options(QDict *options, BlockdevOptionsRbd **opts, + return 0; + } + ++static int qemu_rbd_attempt_legacy_options(QDict *options, ++ BlockdevOptionsRbd **opts, ++ char **keypairs) ++{ ++ char *filename; ++ int r; ++ ++ filename = g_strdup(qdict_get_try_str(options, "filename")); ++ if (!filename) { ++ return -EINVAL; ++ } ++ qdict_del(options, "filename"); ++ ++ qemu_rbd_parse_filename(filename, options, NULL); ++ ++ /* keypairs freed by caller */ ++ *keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs")); ++ if (*keypairs) { ++ qdict_del(options, "=keyvalue-pairs"); ++ } ++ ++ r = qemu_rbd_convert_options(options, opts, NULL); ++ ++ g_free(filename); ++ return r; ++} ++ + static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) + { +@@ -693,8 +720,30 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, + + r = qemu_rbd_convert_options(options, &opts, &local_err); + if (local_err) { +- error_propagate(errp, local_err); +- goto out; ++ /* If keypairs are present, that means some options are present in ++ * the modern option format. Don't attempt to parse legacy option ++ * formats, as we won't support mixed usage. */ ++ if (keypairs) { ++ error_propagate(errp, local_err); ++ goto out; ++ } ++ ++ /* If the initial attempt to convert and process the options failed, ++ * we may be attempting to open an image file that has the rbd options ++ * specified in the older format consisting of all key/value pairs ++ * encoded in the filename. Go ahead and attempt to parse the ++ * filename, and see if we can pull out the required options. */ ++ r = qemu_rbd_attempt_legacy_options(options, &opts, &keypairs); ++ if (r < 0) { ++ error_propagate(errp, local_err); ++ goto out; ++ } ++ /* Take care whenever deciding to actually deprecate; once this ability ++ * is removed, we will not be able to open any images with legacy-styled ++ * backing image strings. */ ++ error_report("RBD options encoded in the filename as keyvalue pairs " ++ "is deprecated. Future versions may cease to parse " ++ "these options in the future."); + } + + /* Remove the processed options from the QDict (the visitor processes +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-rbd-add-iotest-for-rbd-legacy-keyvalue-filenam.patch b/SOURCES/kvm-block-rbd-add-iotest-for-rbd-legacy-keyvalue-filenam.patch new file mode 100644 index 0000000..db3bc3b --- /dev/null +++ b/SOURCES/kvm-block-rbd-add-iotest-for-rbd-legacy-keyvalue-filenam.patch @@ -0,0 +1,136 @@ +From b97181cd18e5427bcabcb5afe83f25b5352ec248 Mon Sep 17 00:00:00 2001 +From: Jeffrey Cody +Date: Wed, 12 Sep 2018 13:45:44 +0200 +Subject: [PATCH 05/49] block/rbd: add iotest for rbd legacy keyvalue filename + parsing + +RH-Author: Jeffrey Cody +Message-id: <5da98ec8bde6bc41a6848d526e689038ec8c8007.1536759805.git.jcody@redhat.com> +Patchwork-id: 82142 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 3/3] block/rbd: add iotest for rbd legacy keyvalue filename parsing +Bugzilla: 1610605 +RH-Acked-by: Eric Blake +RH-Acked-by: John Snow +RH-Acked-by: Fam Zheng + +This is a small test that will check for the ability to parse +both legacy and modern options for rbd. + +The way the test is set up is for failure to occur, but without +having to wait to timeout on a non-existent rbd server. The error +messages in the success path show that the arguments were parsed. + +The failure behavior prior to the patch series that has this test, is +qemu-img complaining about mandatory options (e.g. 'pool') not being +provided. + +Reviewed-by: Eric Blake +Signed-off-by: Jeff Cody +Message-id: f830580e339b974a83ed4870d11adcdc17f49a47.1536704901.git.jcody@redhat.com +Signed-off-by: Jeff Cody +(cherry picked from commit 4ea3b5e9136c39515750534b01c0ed8edaddfcd7) +Signed-off-by: Jeff Cody +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/231 | 62 ++++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/231.out | 9 +++++++ + tests/qemu-iotests/group | 1 + + 3 files changed, 72 insertions(+) + create mode 100755 tests/qemu-iotests/231 + create mode 100644 tests/qemu-iotests/231.out + +diff --git a/tests/qemu-iotests/231 b/tests/qemu-iotests/231 +new file mode 100755 +index 0000000..3e28370 +--- /dev/null ++++ b/tests/qemu-iotests/231 +@@ -0,0 +1,62 @@ ++#!/bin/bash ++# ++# Test legacy and modern option parsing for rbd/ceph. This will not ++# actually connect to a ceph server, but rather looks for the appropriate ++# error message that indicates we parsed the options correctly. ++# ++# Copyright (C) 2018 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 . ++# ++ ++# creator ++owner=jcody@redhat.com ++ ++seq=`basename $0` ++echo "QA output created by $seq" ++ ++here=`pwd` ++status=1 # failure is the default! ++ ++_cleanup() ++{ ++ rm "${BOGUS_CONF}" ++} ++trap "_cleanup; exit \$status" 0 1 2 3 15 ++ ++# get standard environment, filters and checks ++. ./common.rc ++. ./common.filter ++ ++_supported_fmt generic ++_supported_proto rbd ++_supported_os Linux ++ ++BOGUS_CONF=${TEST_DIR}/ceph-$$.conf ++touch "${BOGUS_CONF}" ++ ++_filter_conf() ++{ ++ sed -e "s#$BOGUS_CONF#BOGUS_CONF#g" ++} ++ ++# We expect this to fail, with no monitor ip provided and a null conf file. Just want it ++# to fail in the right way. ++$QEMU_IMG info "json:{'file.driver':'rbd','file.filename':'rbd:rbd/bogus:conf=${BOGUS_CONF}'}" 2>&1 | _filter_conf ++$QEMU_IMG info "json:{'file.driver':'rbd','file.pool':'rbd','file.image':'bogus','file.conf':'${BOGUS_CONF}'}" 2>&1 | _filter_conf ++ ++# success, all done ++echo "*** done" ++rm -f $seq.full ++status=0 +diff --git a/tests/qemu-iotests/231.out b/tests/qemu-iotests/231.out +new file mode 100644 +index 0000000..579ba11 +--- /dev/null ++++ b/tests/qemu-iotests/231.out +@@ -0,0 +1,9 @@ ++QA output created by 231 ++qemu-img: RBD options encoded in the filename as keyvalue pairs is deprecated. Future versions may cease to parse these options in the future. ++unable to get monitor info from DNS SRV with service name: ceph-mon ++no monitors specified to connect to. ++qemu-img: Could not open 'json:{'file.driver':'rbd','file.filename':'rbd:rbd/bogus:conf=BOGUS_CONF'}': error connecting: No such file or directory ++unable to get monitor info from DNS SRV with service name: ceph-mon ++no monitors specified to connect to. ++qemu-img: Could not open 'json:{'file.driver':'rbd','file.pool':'rbd','file.image':'bogus','file.conf':'BOGUS_CONF'}': error connecting: No such file or directory ++*** done +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index 3817d28..06a7107 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -224,3 +224,4 @@ + 226 auto quick + 227 auto quick + 229 auto quick ++231 auto quick +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-rbd-pull-out-qemu_rbd_convert_options.patch b/SOURCES/kvm-block-rbd-pull-out-qemu_rbd_convert_options.patch new file mode 100644 index 0000000..8b136d2 --- /dev/null +++ b/SOURCES/kvm-block-rbd-pull-out-qemu_rbd_convert_options.patch @@ -0,0 +1,93 @@ +From 0b239ab1df3f430f6d1163dc9571e89b99939812 Mon Sep 17 00:00:00 2001 +From: Jeffrey Cody +Date: Wed, 12 Sep 2018 13:45:42 +0200 +Subject: [PATCH 03/49] block/rbd: pull out qemu_rbd_convert_options + +RH-Author: Jeffrey Cody +Message-id: <695a2a1c89a6bfd5076d050f5396aa7937e00cc5.1536759805.git.jcody@redhat.com> +Patchwork-id: 82143 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/3] block/rbd: pull out qemu_rbd_convert_options +Bugzilla: 1610605 +RH-Acked-by: Eric Blake +RH-Acked-by: John Snow +RH-Acked-by: Fam Zheng + +Code movement to pull the conversion from Qdict to BlockdevOptionsRbd +into a helper function. + +Reviewed-by: Eric Blake +Reviewed-by: John Snow +Signed-off-by: Jeff Cody +Message-id: 5b49a980f2cde6610ab1df41bb0277d00b5db893.1536704901.git.jcody@redhat.com +Signed-off-by: Jeff Cody +(cherry picked from commit 862e99966b5b59c107255fa806af40cf00ecacec) +Signed-off-by: Jeff Cody +Signed-off-by: Miroslav Rezanina +--- + block/rbd.c | 36 ++++++++++++++++++++++++------------ + 1 file changed, 24 insertions(+), 12 deletions(-) + +diff --git a/block/rbd.c b/block/rbd.c +index b93046b..1e4d339 100644 +--- a/block/rbd.c ++++ b/block/rbd.c +@@ -648,12 +648,34 @@ failed_opts: + return r; + } + ++static int qemu_rbd_convert_options(QDict *options, BlockdevOptionsRbd **opts, ++ Error **errp) ++{ ++ Visitor *v; ++ Error *local_err = NULL; ++ ++ /* Convert the remaining options into a QAPI object */ ++ v = qobject_input_visitor_new_flat_confused(options, errp); ++ if (!v) { ++ return -EINVAL; ++ } ++ ++ visit_type_BlockdevOptionsRbd(v, NULL, opts, &local_err); ++ visit_free(v); ++ ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ + static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) + { + BDRVRBDState *s = bs->opaque; + BlockdevOptionsRbd *opts = NULL; +- Visitor *v; + const QDictEntry *e; + Error *local_err = NULL; + char *keypairs, *secretid; +@@ -669,19 +691,9 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, + qdict_del(options, "password-secret"); + } + +- /* Convert the remaining options into a QAPI object */ +- v = qobject_input_visitor_new_flat_confused(options, errp); +- if (!v) { +- r = -EINVAL; +- goto out; +- } +- +- visit_type_BlockdevOptionsRbd(v, NULL, &opts, &local_err); +- visit_free(v); +- ++ r = qemu_rbd_convert_options(options, &opts, &local_err); + if (local_err) { + error_propagate(errp, local_err); +- r = -EINVAL; + goto out; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-remove-bdrv_dirty_bitmap_make_anon.patch b/SOURCES/kvm-block-remove-bdrv_dirty_bitmap_make_anon.patch new file mode 100644 index 0000000..ea465d4 --- /dev/null +++ b/SOURCES/kvm-block-remove-bdrv_dirty_bitmap_make_anon.patch @@ -0,0 +1,82 @@ +From f803d3a0cda98654a4cf53ec64dcd179b4ccc6d4 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:21 +0100 +Subject: [PATCH 11/33] block: remove bdrv_dirty_bitmap_make_anon + +RH-Author: John Snow +Message-id: <20190206221243.7407-2-jsnow@redhat.com> +Patchwork-id: 84261 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 01/23] block: remove bdrv_dirty_bitmap_make_anon +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +From: Paolo Bonzini + +All this function is doing will be repeated by +bdrv_do_release_matching_dirty_bitmap_locked, except +resetting bm->persistent. But even that does not matter +because the bitmap will be freed. + +Signed-off-by: Paolo Bonzini +Reviewed-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: Stefan Hajnoczi +Message-id: 20180323164254.26487-1-pbonzini@redhat.com +Signed-off-by: John Snow +(cherry picked from commit ab41fc4853cc0cf01ed4903ffe7c36e3768b538f) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 9 --------- + blockdev.c | 1 - + include/block/dirty-bitmap.h | 1 - + 3 files changed, 11 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index bc662d3..50e855a 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -98,15 +98,6 @@ BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name) + } + + /* Called with BQL taken. */ +-void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap) +-{ +- assert(!bdrv_dirty_bitmap_frozen(bitmap)); +- g_free(bitmap->name); +- bitmap->name = NULL; +- bitmap->persistent = false; +-} +- +-/* Called with BQL taken. */ + BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, + uint32_t granularity, + const char *name, +diff --git a/blockdev.c b/blockdev.c +index f25ab15..745ed08 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3019,7 +3019,6 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name, + } + } + +- bdrv_dirty_bitmap_make_anon(bitmap); + bdrv_release_dirty_bitmap(bs, bitmap); + } + +diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h +index af9ba3c..bf68dd7 100644 +--- a/include/block/dirty-bitmap.h ++++ b/include/block/dirty-bitmap.h +@@ -24,7 +24,6 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, + void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap); + BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, + const char *name); +-void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap); + void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap); + void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs); + void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-remove-x-prefix-from-experimental-bitmap-APIs.patch b/SOURCES/kvm-block-remove-x-prefix-from-experimental-bitmap-APIs.patch new file mode 100644 index 0000000..062a4aa --- /dev/null +++ b/SOURCES/kvm-block-remove-x-prefix-from-experimental-bitmap-APIs.patch @@ -0,0 +1,286 @@ +From 73ad88009003572e654deca01a7c64655677bf39 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 16:16:17 +0100 +Subject: [PATCH 019/163] block: remove 'x' prefix from experimental bitmap + APIs + +RH-Author: John Snow +Message-id: <20190320161631.14841-6-jsnow@redhat.com> +Patchwork-id: 84949 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 05/19] block: remove 'x' prefix from experimental bitmap APIs +Bugzilla: 1668956 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +The 'x' prefix was added because I was uncertain of the direction we'd +take for the libvirt API. With the general approach solidified, I feel +comfortable committing to this API for 4.0. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20181221093529.23855-5-jsnow@redhat.com> +Signed-off-by: Eric Blake +(cherry picked from commit 0e2b7f09837f1e2828d428af1e4ebb61e3b3ea5f) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 22 +++++++++++----------- + qapi/block-core.json | 34 +++++++++++++++++----------------- + qapi/transaction.json | 12 ++++++------ + tests/qemu-iotests/223 | 4 ++-- + 4 files changed, 36 insertions(+), 36 deletions(-) + +diff --git a/blockdev.c b/blockdev.c +index 3755936..47db9bb 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -2116,7 +2116,7 @@ static void block_dirty_bitmap_add_prepare(BlkActionState *common, + action->has_granularity, action->granularity, + action->has_persistent, action->persistent, + action->has_autoload, action->autoload, +- action->has_x_disabled, action->x_disabled, ++ action->has_disabled, action->disabled, + &local_err); + + if (!local_err) { +@@ -2201,7 +2201,7 @@ static void block_dirty_bitmap_enable_prepare(BlkActionState *common, + return; + } + +- action = common->action->u.x_block_dirty_bitmap_enable.data; ++ action = common->action->u.block_dirty_bitmap_enable.data; + state->bitmap = block_dirty_bitmap_lookup(action->node, + action->name, + NULL, +@@ -2242,7 +2242,7 @@ static void block_dirty_bitmap_disable_prepare(BlkActionState *common, + return; + } + +- action = common->action->u.x_block_dirty_bitmap_disable.data; ++ action = common->action->u.block_dirty_bitmap_disable.data; + state->bitmap = block_dirty_bitmap_lookup(action->node, + action->name, + NULL, +@@ -2289,7 +2289,7 @@ static void block_dirty_bitmap_merge_prepare(BlkActionState *common, + return; + } + +- action = common->action->u.x_block_dirty_bitmap_merge.data; ++ action = common->action->u.block_dirty_bitmap_merge.data; + + state->bitmap = do_block_dirty_bitmap_merge(action->node, action->target, + action->bitmaps, &state->backup, +@@ -2357,17 +2357,17 @@ static const BlkActionOps actions[] = { + .commit = block_dirty_bitmap_free_backup, + .abort = block_dirty_bitmap_restore, + }, +- [TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_ENABLE] = { ++ [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ENABLE] = { + .instance_size = sizeof(BlockDirtyBitmapState), + .prepare = block_dirty_bitmap_enable_prepare, + .abort = block_dirty_bitmap_enable_abort, + }, +- [TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_DISABLE] = { ++ [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_DISABLE] = { + .instance_size = sizeof(BlockDirtyBitmapState), + .prepare = block_dirty_bitmap_disable_prepare, + .abort = block_dirty_bitmap_disable_abort, + }, +- [TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_MERGE] = { ++ [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_MERGE] = { + .instance_size = sizeof(BlockDirtyBitmapState), + .prepare = block_dirty_bitmap_merge_prepare, + .commit = block_dirty_bitmap_free_backup, +@@ -3083,7 +3083,7 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name, + bdrv_clear_dirty_bitmap(bitmap, NULL); + } + +-void qmp_x_block_dirty_bitmap_enable(const char *node, const char *name, ++void qmp_block_dirty_bitmap_enable(const char *node, const char *name, + Error **errp) + { + BlockDriverState *bs; +@@ -3104,7 +3104,7 @@ void qmp_x_block_dirty_bitmap_enable(const char *node, const char *name, + bdrv_enable_dirty_bitmap(bitmap); + } + +-void qmp_x_block_dirty_bitmap_disable(const char *node, const char *name, ++void qmp_block_dirty_bitmap_disable(const char *node, const char *name, + Error **errp) + { + BlockDriverState *bs; +@@ -3171,8 +3171,8 @@ static BdrvDirtyBitmap *do_block_dirty_bitmap_merge(const char *node, + return dst; + } + +-void qmp_x_block_dirty_bitmap_merge(const char *node, const char *target, +- strList *bitmaps, Error **errp) ++void qmp_block_dirty_bitmap_merge(const char *node, const char *target, ++ strList *bitmaps, Error **errp) + { + do_block_dirty_bitmap_merge(node, target, bitmaps, NULL, errp); + } +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 7e11392..176c04e 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -1786,15 +1786,15 @@ + # Currently, all dirty tracking bitmaps are loaded from Qcow2 on + # open. + # +-# @x-disabled: the bitmap is created in the disabled state, which means that +-# it will not track drive changes. The bitmap may be enabled with +-# x-block-dirty-bitmap-enable. Default is false. (Since: 3.0) ++# @disabled: the bitmap is created in the disabled state, which means that ++# it will not track drive changes. The bitmap may be enabled with ++# block-dirty-bitmap-enable. Default is false. (Since: 4.0) + # + # Since: 2.4 + ## + { 'struct': 'BlockDirtyBitmapAdd', + 'data': { 'node': 'str', 'name': 'str', '*granularity': 'uint32', +- '*persistent': 'bool', '*autoload': 'bool', '*x-disabled': 'bool' } } ++ '*persistent': 'bool', '*autoload': 'bool', '*disabled': 'bool' } } + + ## + # @BlockDirtyBitmapMerge: +@@ -1805,7 +1805,7 @@ + # + # @bitmaps: name(s) of the source dirty bitmap(s) + # +-# Since: 3.0 ++# Since: 4.0 + ## + { 'struct': 'BlockDirtyBitmapMerge', + 'data': { 'node': 'str', 'target': 'str', 'bitmaps': ['str'] } } +@@ -1879,7 +1879,7 @@ + 'data': 'BlockDirtyBitmap' } + + ## +-# @x-block-dirty-bitmap-enable: ++# @block-dirty-bitmap-enable: + # + # Enables a dirty bitmap so that it will begin tracking disk changes. + # +@@ -1887,20 +1887,20 @@ + # If @node is not a valid block device, DeviceNotFound + # If @name is not found, GenericError with an explanation + # +-# Since: 3.0 ++# Since: 4.0 + # + # Example: + # +-# -> { "execute": "x-block-dirty-bitmap-enable", ++# -> { "execute": "block-dirty-bitmap-enable", + # "arguments": { "node": "drive0", "name": "bitmap0" } } + # <- { "return": {} } + # + ## +- { 'command': 'x-block-dirty-bitmap-enable', ++ { 'command': 'block-dirty-bitmap-enable', + 'data': 'BlockDirtyBitmap' } + + ## +-# @x-block-dirty-bitmap-disable: ++# @block-dirty-bitmap-disable: + # + # Disables a dirty bitmap so that it will stop tracking disk changes. + # +@@ -1908,20 +1908,20 @@ + # If @node is not a valid block device, DeviceNotFound + # If @name is not found, GenericError with an explanation + # +-# Since: 3.0 ++# Since: 4.0 + # + # Example: + # +-# -> { "execute": "x-block-dirty-bitmap-disable", ++# -> { "execute": "block-dirty-bitmap-disable", + # "arguments": { "node": "drive0", "name": "bitmap0" } } + # <- { "return": {} } + # + ## +- { 'command': 'x-block-dirty-bitmap-disable', ++ { 'command': 'block-dirty-bitmap-disable', + 'data': 'BlockDirtyBitmap' } + + ## +-# @x-block-dirty-bitmap-merge: ++# @block-dirty-bitmap-merge: + # + # Merge dirty bitmaps listed in @bitmaps to the @target dirty bitmap. + # The @bitmaps dirty bitmaps are unchanged. +@@ -1933,17 +1933,17 @@ + # If any of the bitmaps have different sizes or granularities, + # GenericError + # +-# Since: 3.0 ++# Since: 4.0 + # + # Example: + # +-# -> { "execute": "x-block-dirty-bitmap-merge", ++# -> { "execute": "block-dirty-bitmap-merge", + # "arguments": { "node": "drive0", "target": "bitmap0", + # "bitmaps": ["bitmap1"] } } + # <- { "return": {} } + # + ## +- { 'command': 'x-block-dirty-bitmap-merge', ++ { 'command': 'block-dirty-bitmap-merge', + 'data': 'BlockDirtyBitmapMerge' } + + ## +diff --git a/qapi/transaction.json b/qapi/transaction.json +index 5875cdb..95edb78 100644 +--- a/qapi/transaction.json ++++ b/qapi/transaction.json +@@ -46,9 +46,9 @@ + # - @abort: since 1.6 + # - @block-dirty-bitmap-add: since 2.5 + # - @block-dirty-bitmap-clear: since 2.5 +-# - @x-block-dirty-bitmap-enable: since 3.0 +-# - @x-block-dirty-bitmap-disable: since 3.0 +-# - @x-block-dirty-bitmap-merge: since 3.1 ++# - @block-dirty-bitmap-enable: since 4.0 ++# - @block-dirty-bitmap-disable: since 4.0 ++# - @block-dirty-bitmap-merge: since 4.0 + # - @blockdev-backup: since 2.3 + # - @blockdev-snapshot: since 2.5 + # - @blockdev-snapshot-internal-sync: since 1.7 +@@ -62,9 +62,9 @@ + 'abort': 'Abort', + 'block-dirty-bitmap-add': 'BlockDirtyBitmapAdd', + 'block-dirty-bitmap-clear': 'BlockDirtyBitmap', +- 'x-block-dirty-bitmap-enable': 'BlockDirtyBitmap', +- 'x-block-dirty-bitmap-disable': 'BlockDirtyBitmap', +- 'x-block-dirty-bitmap-merge': 'BlockDirtyBitmapMerge', ++ 'block-dirty-bitmap-enable': 'BlockDirtyBitmap', ++ 'block-dirty-bitmap-disable': 'BlockDirtyBitmap', ++ 'block-dirty-bitmap-merge': 'BlockDirtyBitmapMerge', + 'blockdev-backup': 'BlockdevBackup', + 'blockdev-snapshot': 'BlockdevSnapshot', + 'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal', +diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 +index a462f41..e59411e 100755 +--- a/tests/qemu-iotests/223 ++++ b/tests/qemu-iotests/223 +@@ -111,9 +111,9 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"qmp_capabilities"}' "return" + _send_qemu_cmd $QEMU_HANDLE '{"execute":"blockdev-add", + "arguments":{"driver":"qcow2", "node-name":"n", + "file":{"driver":"file", "filename":"'"$TEST_IMG"'"}}}' "return" +-_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-block-dirty-bitmap-disable", ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"block-dirty-bitmap-disable", + "arguments":{"node":"n", "name":"b"}}' "return" +-_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-block-dirty-bitmap-disable", ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"block-dirty-bitmap-disable", + "arguments":{"node":"n", "name":"b2"}}' "return" + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start", + "arguments":{"addr":{"type":"unix", +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-simplify-code-around-releasing-bitmaps.patch b/SOURCES/kvm-block-simplify-code-around-releasing-bitmaps.patch new file mode 100644 index 0000000..84367d5 --- /dev/null +++ b/SOURCES/kvm-block-simplify-code-around-releasing-bitmaps.patch @@ -0,0 +1,166 @@ +From 4cf452add8a2760d325b79efdd484c8d37cd2158 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:22 +0100 +Subject: [PATCH 12/33] block: simplify code around releasing bitmaps + +RH-Author: John Snow +Message-id: <20190206221243.7407-3-jsnow@redhat.com> +Patchwork-id: 84262 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 02/23] block: simplify code around releasing bitmaps +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +From: Paolo Bonzini + +QLIST_REMOVE does not require walking the list, and once the "bitmap" +argument is removed from bdrv_do_release_matching_dirty_bitmap_locked +the code simplifies a lot and it is worth inlining everything in the +callers of bdrv_do_release_matching_dirty_bitmap. + +Signed-off-by: Paolo Bonzini +Reviewed-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: John Snow +Message-id: 20180326104037.6894-1-pbonzini@redhat.com +Signed-off-by: John Snow +(cherry picked from commit b133c27f5dc59969574b0715e5837d32c99caa86) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 84 ++++++++++++++++++++-------------------------------- + 1 file changed, 32 insertions(+), 52 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index 50e855a..cd39afd 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -256,49 +256,16 @@ void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap) + qemu_mutex_unlock(bitmap->mutex); + } + +-/* Called within bdrv_dirty_bitmap_lock..unlock */ +-static void bdrv_do_release_matching_dirty_bitmap_locked( +- BlockDriverState *bs, BdrvDirtyBitmap *bitmap, +- bool (*cond)(BdrvDirtyBitmap *bitmap)) ++/* Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken. */ ++static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) + { +- BdrvDirtyBitmap *bm, *next; +- +- QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { +- if ((!bitmap || bm == bitmap) && (!cond || cond(bm))) { +- assert(!bm->active_iterators); +- assert(!bdrv_dirty_bitmap_frozen(bm)); +- assert(!bm->meta); +- QLIST_REMOVE(bm, list); +- hbitmap_free(bm->bitmap); +- g_free(bm->name); +- g_free(bm); +- +- if (bitmap) { +- return; +- } +- } +- } +- +- if (bitmap) { +- abort(); +- } +-} +- +-/* Called with BQL taken. */ +-static void bdrv_do_release_matching_dirty_bitmap( +- BlockDriverState *bs, BdrvDirtyBitmap *bitmap, +- bool (*cond)(BdrvDirtyBitmap *bitmap)) +-{ +- bdrv_dirty_bitmaps_lock(bs); +- bdrv_do_release_matching_dirty_bitmap_locked(bs, bitmap, cond); +- bdrv_dirty_bitmaps_unlock(bs); +-} +- +-/* Called within bdrv_dirty_bitmap_lock..unlock */ +-static void bdrv_release_dirty_bitmap_locked(BlockDriverState *bs, +- BdrvDirtyBitmap *bitmap) +-{ +- bdrv_do_release_matching_dirty_bitmap_locked(bs, bitmap, NULL); ++ assert(!bitmap->active_iterators); ++ assert(!bdrv_dirty_bitmap_frozen(bitmap)); ++ assert(!bitmap->meta); ++ QLIST_REMOVE(bitmap, list); ++ hbitmap_free(bitmap->bitmap); ++ g_free(bitmap->name); ++ g_free(bitmap); + } + + /** +@@ -351,7 +318,7 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, + error_setg(errp, "Merging of parent and successor bitmap failed"); + return NULL; + } +- bdrv_release_dirty_bitmap_locked(bs, successor); ++ bdrv_release_dirty_bitmap_locked(successor); + parent->successor = NULL; + + return parent; +@@ -389,15 +356,12 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes) + bdrv_dirty_bitmaps_unlock(bs); + } + +-static bool bdrv_dirty_bitmap_has_name(BdrvDirtyBitmap *bitmap) +-{ +- return !!bdrv_dirty_bitmap_name(bitmap); +-} +- + /* Called with BQL taken. */ + void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) + { +- bdrv_do_release_matching_dirty_bitmap(bs, bitmap, NULL); ++ bdrv_dirty_bitmaps_lock(bs); ++ bdrv_release_dirty_bitmap_locked(bitmap); ++ bdrv_dirty_bitmaps_unlock(bs); + } + + /** +@@ -408,7 +372,15 @@ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) + */ + void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) + { +- bdrv_do_release_matching_dirty_bitmap(bs, NULL, bdrv_dirty_bitmap_has_name); ++ 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); + } + + /** +@@ -416,11 +388,19 @@ void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) + * bdrv_inactivate_recurse()). + * There must not be any frozen bitmaps attached. + * This function does not remove persistent bitmaps from the storage. ++ * Called with BQL taken. + */ + void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs) + { +- bdrv_do_release_matching_dirty_bitmap(bs, NULL, +- bdrv_dirty_bitmap_get_persistance); ++ BdrvDirtyBitmap *bm, *next; ++ ++ bdrv_dirty_bitmaps_lock(bs); ++ QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { ++ if (bdrv_dirty_bitmap_get_persistance(bm)) { ++ bdrv_release_dirty_bitmap_locked(bm); ++ } ++ } ++ bdrv_dirty_bitmaps_unlock(bs); + } + + /** +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-split-flags-in-copy_range.patch b/SOURCES/kvm-block-split-flags-in-copy_range.patch new file mode 100644 index 0000000..ba8198a --- /dev/null +++ b/SOURCES/kvm-block-split-flags-in-copy_range.patch @@ -0,0 +1,466 @@ +From 86b0f689bdc89a928f41ba737b43079df6574359 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Tue, 24 Jul 2018 13:01:08 +0200 +Subject: [PATCH 71/89] block: split flags in copy_range + +RH-Author: John Snow +Message-id: <20180718225511.14878-21-jsnow@redhat.com> +Patchwork-id: 81415 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 20/35] block: split flags in copy_range +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +Pass read flags and write flags separately. This is needed to handle +coming BDRV_REQ_NO_SERIALISING clearly in following patches. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: Fam Zheng +Signed-off-by: Kevin Wolf +(cherry picked from commit 67b51fb998c697afb5d744066fcbde53e04fe941) +Signed-off-by: John Snow +--- + block/backup.c | 2 +- + block/block-backend.c | 5 +++-- + block/file-posix.c | 21 +++++++++++-------- + block/io.c | 46 +++++++++++++++++++++++------------------- + block/iscsi.c | 9 ++++++--- + block/qcow2.c | 20 +++++++++--------- + block/raw-format.c | 24 ++++++++++++++-------- + include/block/block.h | 3 ++- + include/block/block_int.h | 14 +++++++++---- + include/sysemu/block-backend.h | 3 ++- + qemu-img.c | 2 +- + 11 files changed, 90 insertions(+), 59 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index adb3cbd..369155a 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -163,7 +163,7 @@ static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job, + hbitmap_reset(job->copy_bitmap, start / job->cluster_size, + nr_clusters); + ret = blk_co_copy_range(blk, start, job->target, start, nbytes, +- is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0); ++ is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0, 0); + if (ret < 0) { + trace_backup_do_cow_copy_range_fail(job, start, ret); + hbitmap_set(job->copy_bitmap, start / job->cluster_size, +diff --git a/block/block-backend.c b/block/block-backend.c +index a469fc6..2262506 100644 +--- a/block/block-backend.c ++++ b/block/block-backend.c +@@ -2244,7 +2244,8 @@ void blk_unregister_buf(BlockBackend *blk, void *host) + + int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, + BlockBackend *blk_out, int64_t off_out, +- int bytes, BdrvRequestFlags flags) ++ int bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) + { + int r; + r = blk_check_byte_request(blk_in, off_in, bytes); +@@ -2257,5 +2258,5 @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, + } + return bdrv_co_copy_range(blk_in->root, off_in, + blk_out->root, off_out, +- bytes, flags); ++ bytes, read_flags, write_flags); + } +diff --git a/block/file-posix.c b/block/file-posix.c +index 06ec67d..c12cdb7 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -2476,18 +2476,23 @@ static void raw_abort_perm_update(BlockDriverState *bs) + raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL); + } + +-static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs, +- BdrvChild *src, uint64_t src_offset, +- BdrvChild *dst, uint64_t dst_offset, +- uint64_t bytes, BdrvRequestFlags flags) ++static int coroutine_fn raw_co_copy_range_from( ++ BlockDriverState *bs, BdrvChild *src, uint64_t src_offset, ++ BdrvChild *dst, uint64_t dst_offset, uint64_t bytes, ++ BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) + { +- return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, flags); ++ return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, ++ read_flags, write_flags); + } + + static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, +- BdrvChild *src, uint64_t src_offset, +- BdrvChild *dst, uint64_t dst_offset, +- uint64_t bytes, BdrvRequestFlags flags) ++ BdrvChild *src, ++ uint64_t src_offset, ++ BdrvChild *dst, ++ uint64_t dst_offset, ++ uint64_t bytes, ++ BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) + { + BDRVRawState *s = bs->opaque; + BDRVRawState *src_s; +diff --git a/block/io.c b/block/io.c +index f8de42f..2d04289 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -2841,13 +2841,11 @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host) + } + } + +-static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, +- uint64_t src_offset, +- BdrvChild *dst, +- uint64_t dst_offset, +- uint64_t bytes, +- BdrvRequestFlags flags, +- bool recurse_src) ++static int coroutine_fn bdrv_co_copy_range_internal( ++ BdrvChild *src, uint64_t src_offset, BdrvChild *dst, ++ uint64_t dst_offset, uint64_t bytes, ++ BdrvRequestFlags read_flags, BdrvRequestFlags write_flags, ++ bool recurse_src) + { + BdrvTrackedRequest req; + int ret; +@@ -2860,8 +2858,8 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, + if (ret) { + return ret; + } +- if (flags & BDRV_REQ_ZERO_WRITE) { +- return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, flags); ++ if (write_flags & BDRV_REQ_ZERO_WRITE) { ++ return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, write_flags); + } + + if (!src || !src->bs) { +@@ -2883,14 +2881,15 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, + tracked_request_begin(&req, src->bs, src_offset, bytes, + BDRV_TRACKED_READ); + +- if (!(flags & BDRV_REQ_NO_SERIALISING)) { ++ if (!(read_flags & BDRV_REQ_NO_SERIALISING)) { + wait_serialising_requests(&req); + } + + ret = src->bs->drv->bdrv_co_copy_range_from(src->bs, + src, src_offset, + dst, dst_offset, +- bytes, flags); ++ bytes, ++ read_flags, write_flags); + + tracked_request_end(&req); + bdrv_dec_in_flight(src->bs); +@@ -2899,15 +2898,15 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, + tracked_request_begin(&req, dst->bs, dst_offset, bytes, + BDRV_TRACKED_WRITE); + +- /* BDRV_REQ_NO_SERIALISING is only for read operation, +- * so we ignore it in flags. +- */ ++ /* BDRV_REQ_NO_SERIALISING is only for read operation */ ++ assert(!(write_flags & BDRV_REQ_NO_SERIALISING)); + wait_serialising_requests(&req); + + ret = dst->bs->drv->bdrv_co_copy_range_to(dst->bs, + src, src_offset, + dst, dst_offset, +- bytes, flags); ++ bytes, ++ read_flags, write_flags); + + tracked_request_end(&req); + bdrv_dec_in_flight(dst->bs); +@@ -2922,10 +2921,12 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, + * semantics. */ + int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, +- uint64_t bytes, BdrvRequestFlags flags) ++ uint64_t bytes, ++ BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) + { + return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, +- bytes, flags, true); ++ bytes, read_flags, write_flags, true); + } + + /* Copy range from @src to @dst. +@@ -2934,19 +2935,22 @@ int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, + * semantics. */ + int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, +- uint64_t bytes, BdrvRequestFlags flags) ++ uint64_t bytes, ++ BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) + { + return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, +- bytes, flags, false); ++ bytes, read_flags, write_flags, false); + } + + int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, +- uint64_t bytes, BdrvRequestFlags flags) ++ uint64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) + { + return bdrv_co_copy_range_from(src, src_offset, + dst, dst_offset, +- bytes, flags); ++ bytes, read_flags, write_flags); + } + + static void bdrv_parent_cb_resize(BlockDriverState *bs) +diff --git a/block/iscsi.c b/block/iscsi.c +index 5047e83..2b45458 100644 +--- a/block/iscsi.c ++++ b/block/iscsi.c +@@ -2193,9 +2193,11 @@ static int coroutine_fn iscsi_co_copy_range_from(BlockDriverState *bs, + BdrvChild *dst, + uint64_t dst_offset, + uint64_t bytes, +- BdrvRequestFlags flags) ++ BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) + { +- return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, flags); ++ return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, ++ read_flags, write_flags); + } + + static struct scsi_task *iscsi_xcopy_task(int param_len) +@@ -2332,7 +2334,8 @@ static int coroutine_fn iscsi_co_copy_range_to(BlockDriverState *bs, + BdrvChild *dst, + uint64_t dst_offset, + uint64_t bytes, +- BdrvRequestFlags flags) ++ BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) + { + IscsiLun *dst_lun = dst->bs->opaque; + IscsiLun *src_lun; +diff --git a/block/qcow2.c b/block/qcow2.c +index e171a99..1ea7203 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -3248,13 +3248,14 @@ static int coroutine_fn + qcow2_co_copy_range_from(BlockDriverState *bs, + BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, +- uint64_t bytes, BdrvRequestFlags flags) ++ uint64_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_flags; ++ BdrvRequestFlags cur_write_flags; + + assert(!bs->encrypted); + qemu_co_mutex_lock(&s->lock); +@@ -3263,7 +3264,7 @@ qcow2_co_copy_range_from(BlockDriverState *bs, + uint64_t copy_offset = 0; + /* prepare next request */ + cur_bytes = MIN(bytes, INT_MAX); +- cur_flags = flags; ++ cur_write_flags = write_flags; + + ret = qcow2_get_cluster_offset(bs, src_offset, &cur_bytes, ©_offset); + if (ret < 0) { +@@ -3275,20 +3276,20 @@ qcow2_co_copy_range_from(BlockDriverState *bs, + if (bs->backing && bs->backing->bs) { + int64_t backing_length = bdrv_getlength(bs->backing->bs); + if (src_offset >= backing_length) { +- cur_flags |= BDRV_REQ_ZERO_WRITE; ++ 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_flags |= BDRV_REQ_ZERO_WRITE; ++ cur_write_flags |= BDRV_REQ_ZERO_WRITE; + } + break; + + case QCOW2_CLUSTER_ZERO_PLAIN: + case QCOW2_CLUSTER_ZERO_ALLOC: +- cur_flags |= BDRV_REQ_ZERO_WRITE; ++ cur_write_flags |= BDRV_REQ_ZERO_WRITE; + break; + + case QCOW2_CLUSTER_COMPRESSED: +@@ -3312,7 +3313,7 @@ qcow2_co_copy_range_from(BlockDriverState *bs, + ret = bdrv_co_copy_range_from(child, + copy_offset, + dst, dst_offset, +- cur_bytes, cur_flags); ++ cur_bytes, read_flags, cur_write_flags); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + goto out; +@@ -3333,7 +3334,8 @@ static int coroutine_fn + qcow2_co_copy_range_to(BlockDriverState *bs, + BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, +- uint64_t bytes, BdrvRequestFlags flags) ++ uint64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) + { + BDRVQcow2State *s = bs->opaque; + int offset_in_cluster; +@@ -3377,7 +3379,7 @@ qcow2_co_copy_range_to(BlockDriverState *bs, + ret = bdrv_co_copy_range_to(src, src_offset, + bs->file, + cluster_offset + offset_in_cluster, +- cur_bytes, flags); ++ cur_bytes, read_flags, write_flags); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + goto fail; +diff --git a/block/raw-format.c b/block/raw-format.c +index b78da56..a359198 100644 +--- a/block/raw-format.c ++++ b/block/raw-format.c +@@ -498,9 +498,13 @@ static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) + } + + static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs, +- BdrvChild *src, uint64_t src_offset, +- BdrvChild *dst, uint64_t dst_offset, +- uint64_t bytes, BdrvRequestFlags flags) ++ BdrvChild *src, ++ uint64_t src_offset, ++ BdrvChild *dst, ++ uint64_t dst_offset, ++ uint64_t bytes, ++ BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) + { + int ret; + +@@ -509,13 +513,17 @@ static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs, + return ret; + } + return bdrv_co_copy_range_from(bs->file, src_offset, dst, dst_offset, +- bytes, flags); ++ bytes, read_flags, write_flags); + } + + static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, +- BdrvChild *src, uint64_t src_offset, +- BdrvChild *dst, uint64_t dst_offset, +- uint64_t bytes, BdrvRequestFlags flags) ++ BdrvChild *src, ++ uint64_t src_offset, ++ BdrvChild *dst, ++ uint64_t dst_offset, ++ uint64_t bytes, ++ BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) + { + int ret; + +@@ -524,7 +532,7 @@ static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, + return ret; + } + return bdrv_co_copy_range_to(src, src_offset, bs->file, dst_offset, bytes, +- flags); ++ read_flags, write_flags); + } + + BlockDriver bdrv_raw = { +diff --git a/include/block/block.h b/include/block/block.h +index 716fb5b..409db21 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -661,5 +661,6 @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host); + **/ + int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, +- uint64_t bytes, BdrvRequestFlags flags); ++ uint64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags); + #endif +diff --git a/include/block/block_int.h b/include/block/block_int.h +index 27e168f..b05cf11 100644 +--- a/include/block/block_int.h ++++ b/include/block/block_int.h +@@ -220,7 +220,8 @@ struct BlockDriver { + BdrvChild *dst, + uint64_t dst_offset, + uint64_t bytes, +- BdrvRequestFlags flags); ++ 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 +@@ -236,7 +237,8 @@ struct BlockDriver { + BdrvChild *dst, + uint64_t dst_offset, + uint64_t bytes, +- BdrvRequestFlags flags); ++ BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags); + + /* + * Building block for bdrv_block_status[_above] and +@@ -1125,10 +1127,14 @@ void blockdev_close_all_bdrv_states(void); + + int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, +- uint64_t bytes, BdrvRequestFlags flags); ++ uint64_t bytes, ++ BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags); + int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, +- uint64_t bytes, BdrvRequestFlags flags); ++ uint64_t bytes, ++ BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags); + + int refresh_total_sectors(BlockDriverState *bs, int64_t hint); + +diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h +index 8d03d49..830d873 100644 +--- a/include/sysemu/block-backend.h ++++ b/include/sysemu/block-backend.h +@@ -234,6 +234,7 @@ void blk_unregister_buf(BlockBackend *blk, void *host); + + int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, + BlockBackend *blk_out, int64_t off_out, +- int bytes, BdrvRequestFlags flags); ++ int bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags); + + #endif +diff --git a/qemu-img.c b/qemu-img.c +index b2ef54e..eaee6d6 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -1786,7 +1786,7 @@ static int coroutine_fn convert_co_copy_range(ImgConvertState *s, int64_t sector + + ret = blk_co_copy_range(blk, offset, s->target, + sector_num << BDRV_SECTOR_BITS, +- n << BDRV_SECTOR_BITS, 0); ++ n << BDRV_SECTOR_BITS, 0, 0); + if (ret < 0) { + return ret; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-stream-add-block-job-creation-flags.patch b/SOURCES/kvm-block-stream-add-block-job-creation-flags.patch new file mode 100644 index 0000000..2b09163 --- /dev/null +++ b/SOURCES/kvm-block-stream-add-block-job-creation-flags.patch @@ -0,0 +1,100 @@ +From 360e16997de84bddd37f5262f33d98b96b8aae2f Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:50 +0200 +Subject: [PATCH 12/25] block/stream: add block job creation flags + +RH-Author: John Snow +Message-id: <20180910181803.11781-13-jsnow@redhat.com> +Patchwork-id: 82089 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 12/25] block/stream: add block job creation flags +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Add support for taking and passing forward job creation flags. + +Signed-off-by: John Snow +Reviewed-by: Max Reitz +Reviewed-by: Jeff Cody +Message-id: 20180906130225.5118-4-jsnow@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit 764cffd288722a4799e7cae0eb679d80fd636fdc) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/stream.c | 5 +++-- + blockdev.c | 3 ++- + include/block/block_int.h | 5 ++++- + 3 files changed, 9 insertions(+), 4 deletions(-) + +diff --git a/block/stream.c b/block/stream.c +index 67e1e72..700eb23 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -214,7 +214,8 @@ static const BlockJobDriver stream_job_driver = { + + void stream_start(const char *job_id, BlockDriverState *bs, + BlockDriverState *base, const char *backing_file_str, +- int64_t speed, BlockdevOnError on_error, Error **errp) ++ int creation_flags, int64_t speed, ++ BlockdevOnError on_error, Error **errp) + { + StreamBlockJob *s; + BlockDriverState *iter; +@@ -236,7 +237,7 @@ void stream_start(const char *job_id, BlockDriverState *bs, + BLK_PERM_GRAPH_MOD, + BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED | + BLK_PERM_WRITE, +- speed, JOB_DEFAULT, NULL, NULL, errp); ++ speed, creation_flags, NULL, NULL, errp); + if (!s) { + goto fail; + } +diff --git a/blockdev.c b/blockdev.c +index 5d38e17..c4a1801 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3280,6 +3280,7 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, + AioContext *aio_context; + Error *local_err = NULL; + const char *base_name = NULL; ++ int job_flags = JOB_DEFAULT; + + if (!has_on_error) { + on_error = BLOCKDEV_ON_ERROR_REPORT; +@@ -3342,7 +3343,7 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, + base_name = has_backing_file ? backing_file : base_name; + + stream_start(has_job_id ? job_id : NULL, bs, base_bs, base_name, +- has_speed ? speed : 0, on_error, &local_err); ++ job_flags, has_speed ? speed : 0, on_error, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out; +diff --git a/include/block/block_int.h b/include/block/block_int.h +index 07517cf..341cbe8 100644 +--- a/include/block/block_int.h ++++ b/include/block/block_int.h +@@ -929,6 +929,8 @@ int is_windows_drive(const char *filename); + * 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. ++ * @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. + * @errp: Error object. +@@ -942,7 +944,8 @@ int is_windows_drive(const char *filename); + */ + void stream_start(const char *job_id, BlockDriverState *bs, + BlockDriverState *base, const char *backing_file_str, +- int64_t speed, BlockdevOnError on_error, Error **errp); ++ int creation_flags, int64_t speed, ++ BlockdevOnError on_error, Error **errp); + + /** + * commit_start: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-block-stream-refactor-stream-to-use-job-callbacks.patch b/SOURCES/kvm-block-stream-refactor-stream-to-use-job-callbacks.patch new file mode 100644 index 0000000..2e030ec --- /dev/null +++ b/SOURCES/kvm-block-stream-refactor-stream-to-use-job-callbacks.patch @@ -0,0 +1,94 @@ +From eca4127792a09e2bf10fdabc5780a2294a2d07da Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:54 +0200 +Subject: [PATCH 16/25] block/stream: refactor stream to use job callbacks + +RH-Author: John Snow +Message-id: <20180910181803.11781-17-jsnow@redhat.com> +Patchwork-id: 82093 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 16/25] block/stream: refactor stream to use job callbacks +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Signed-off-by: John Snow +Reviewed-by: Max Reitz +Message-id: 20180906130225.5118-8-jsnow@redhat.com +Reviewed-by: Jeff Cody +Signed-off-by: Max Reitz +(cherry picked from commit 98ebc93c760372630ab181efbc2302f3b6919cd8) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/stream.c | 23 +++++++++++++++-------- + 1 file changed, 15 insertions(+), 8 deletions(-) + +diff --git a/block/stream.c b/block/stream.c +index 700eb23..81a7ec8 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -54,16 +54,16 @@ static int coroutine_fn stream_populate(BlockBackend *blk, + return blk_co_preadv(blk, offset, qiov.size, &qiov, BDRV_REQ_COPY_ON_READ); + } + +-static void stream_exit(Job *job) ++static int stream_prepare(Job *job) + { + StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); + BlockJob *bjob = &s->common; + BlockDriverState *bs = blk_bs(bjob->blk); + BlockDriverState *base = s->base; + Error *local_err = NULL; +- int ret = job->ret; ++ int ret = 0; + +- if (!job_is_cancelled(job) && bs->backing && ret == 0) { ++ if (bs->backing) { + const char *base_id = NULL, *base_fmt = NULL; + if (base) { + base_id = s->backing_file_str; +@@ -75,12 +75,19 @@ static void stream_exit(Job *job) + bdrv_set_backing_hd(bs, base, &local_err); + if (local_err) { + error_report_err(local_err); +- ret = -EPERM; +- goto out; ++ return -EPERM; + } + } + +-out: ++ return ret; ++} ++ ++static void stream_clean(Job *job) ++{ ++ StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); ++ BlockJob *bjob = &s->common; ++ BlockDriverState *bs = blk_bs(bjob->blk); ++ + /* Reopen the image back in read-only mode if necessary */ + if (s->bs_flags != bdrv_get_flags(bs)) { + /* Give up write permissions before making it read-only */ +@@ -89,7 +96,6 @@ out: + } + + g_free(s->backing_file_str); +- job->ret = ret; + } + + static int coroutine_fn stream_run(Job *job, Error **errp) +@@ -206,7 +212,8 @@ static const BlockJobDriver stream_job_driver = { + .job_type = JOB_TYPE_STREAM, + .free = block_job_free, + .run = stream_run, +- .exit = stream_exit, ++ .prepare = stream_prepare, ++ .clean = stream_clean, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + }, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockdev-abort-transactions-in-reverse-order.patch b/SOURCES/kvm-blockdev-abort-transactions-in-reverse-order.patch new file mode 100644 index 0000000..685ffe4 --- /dev/null +++ b/SOURCES/kvm-blockdev-abort-transactions-in-reverse-order.patch @@ -0,0 +1,97 @@ +From 68dbabe0167538bcf3996692b3dc3dce01e34f74 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Thu, 24 Jan 2019 00:55:10 +0100 +Subject: [PATCH 5/8] blockdev: abort transactions in reverse order + +RH-Author: John Snow +Message-id: <20190124005511.27662-2-jsnow@redhat.com> +Patchwork-id: 84105 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/2] blockdev: abort transactions in reverse order +Bugzilla: 1658426 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Eric Blake + +Presently, we abort transactions in the same order they were processed in. +Bitmap commands, though, attempt to restore backup data structures on abort. + +That's not valid, they need to be aborted in reverse chronological order. + +Replace the QSIMPLEQ data structure with a QTAILQ one, so we can iterate +in reverse for the abort phase of the transaction. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20181221093529.23855-2-jsnow@redhat.com> +[eblake: rebase] +Signed-off-by: Eric Blake +(cherry picked from commit f4de0f8c40b70c4c9308b4670e0a6ad9faed0262) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/blockdev.c b/blockdev.c +index 3eb1880..f25ab15 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -1491,7 +1491,7 @@ struct BlkActionState { + const BlkActionOps *ops; + JobTxn *block_job_txn; + TransactionProperties *txn_props; +- QSIMPLEQ_ENTRY(BlkActionState) entry; ++ QTAILQ_ENTRY(BlkActionState) entry; + }; + + /* internal snapshot private data */ +@@ -2376,8 +2376,8 @@ void qmp_transaction(TransactionActionList *dev_list, + BlkActionState *state, *next; + Error *local_err = NULL; + +- QSIMPLEQ_HEAD(snap_bdrv_states, BlkActionState) snap_bdrv_states; +- QSIMPLEQ_INIT(&snap_bdrv_states); ++ QTAILQ_HEAD(snap_bdrv_states, BlkActionState) snap_bdrv_states; ++ QTAILQ_INIT(&snap_bdrv_states); + + /* Does this transaction get canceled as a group on failure? + * If not, we don't really need to make a JobTxn. +@@ -2408,7 +2408,7 @@ void qmp_transaction(TransactionActionList *dev_list, + state->action = dev_info; + state->block_job_txn = block_job_txn; + state->txn_props = props; +- QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, state, entry); ++ QTAILQ_INSERT_TAIL(&snap_bdrv_states, state, entry); + + state->ops->prepare(state, &local_err); + if (local_err) { +@@ -2417,7 +2417,7 @@ void qmp_transaction(TransactionActionList *dev_list, + } + } + +- QSIMPLEQ_FOREACH(state, &snap_bdrv_states, entry) { ++ QTAILQ_FOREACH(state, &snap_bdrv_states, entry) { + if (state->ops->commit) { + state->ops->commit(state); + } +@@ -2428,13 +2428,13 @@ void qmp_transaction(TransactionActionList *dev_list, + + delete_and_fail: + /* failure, and it is all-or-none; roll back all operations */ +- QSIMPLEQ_FOREACH(state, &snap_bdrv_states, entry) { ++ QTAILQ_FOREACH_REVERSE(state, &snap_bdrv_states, snap_bdrv_states, entry) { + if (state->ops->abort) { + state->ops->abort(state); + } + } + exit: +- QSIMPLEQ_FOREACH_SAFE(state, &snap_bdrv_states, entry, next) { ++ QTAILQ_FOREACH_SAFE(state, &snap_bdrv_states, entry, next) { + if (state->ops->clean) { + state->ops->clean(state); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockdev-acquire-aio_context-for-bitmap-add-remove.patch b/SOURCES/kvm-blockdev-acquire-aio_context-for-bitmap-add-remove.patch new file mode 100644 index 0000000..7bae2e1 --- /dev/null +++ b/SOURCES/kvm-blockdev-acquire-aio_context-for-bitmap-add-remove.patch @@ -0,0 +1,115 @@ +From 04c5a9797c3bdf379ffad1f159eccfcdca12475f Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 23:55:08 +0100 +Subject: [PATCH 046/163] blockdev: acquire aio_context for bitmap add/remove + +RH-Author: John Snow +Message-id: <20190320235508.17673-2-jsnow@redhat.com> +Patchwork-id: 85031 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/1] blockdev: acquire aio_context for bitmap add/remove +Bugzilla: 1672010 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Kevin Wolf +RH-Acked-by: Max Reitz + +When bitmaps are persistent, they may incur a disk read or write when bitmaps +are added or removed. For configurations like virtio-dataplane, failing to +acquire this lock will abort QEMU when disk IO occurs. + +We used to acquire aio_context as part of the bitmap lookup, so re-introduce +the lock for just the cases that have an IO penalty. Commit 2119882c removed +these locks, and I failed to notice this when we committed fd5ae4cc, so this +has been broken since persistent bitmaps were introduced. + +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1672010 +Reported-By: Aihua Liang +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Message-id: 20190218233154.19303-1-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 0a6c86d024c52b1e66d4f7ec01a3bb8ea2600145) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 26 ++++++++++++++++++++------ + 1 file changed, 20 insertions(+), 6 deletions(-) + +diff --git a/blockdev.c b/blockdev.c +index 47db9bb..f437896 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -2973,6 +2973,7 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, + { + BlockDriverState *bs; + BdrvDirtyBitmap *bitmap; ++ AioContext *aio_context = NULL; + + if (!name || name[0] == '\0') { + error_setg(errp, "Bitmap name cannot be empty"); +@@ -3007,15 +3008,17 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, + disabled = false; + } + +- if (persistent && +- !bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) +- { +- return; ++ if (persistent) { ++ aio_context = bdrv_get_aio_context(bs); ++ aio_context_acquire(aio_context); ++ if (!bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) { ++ goto out; ++ } + } + + bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp); + if (bitmap == NULL) { +- return; ++ goto out; + } + + if (disabled) { +@@ -3023,6 +3026,10 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, + } + + bdrv_dirty_bitmap_set_persistance(bitmap, persistent); ++ out: ++ if (aio_context) { ++ aio_context_release(aio_context); ++ } + } + + void qmp_block_dirty_bitmap_remove(const char *node, const char *name, +@@ -3031,6 +3038,7 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name, + BlockDriverState *bs; + BdrvDirtyBitmap *bitmap; + Error *local_err = NULL; ++ AioContext *aio_context = NULL; + + bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); + if (!bitmap || !bs) { +@@ -3045,14 +3053,20 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name, + } + + if (bdrv_dirty_bitmap_get_persistance(bitmap)) { ++ aio_context = bdrv_get_aio_context(bs); ++ aio_context_acquire(aio_context); + bdrv_remove_persistent_dirty_bitmap(bs, name, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); +- return; ++ goto out; + } + } + + bdrv_release_dirty_bitmap(bs, bitmap); ++ out: ++ if (aio_context) { ++ aio_context_release(aio_context); ++ } + } + + /** +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockdev-backup-add-bitmap-argument.patch b/SOURCES/kvm-blockdev-backup-add-bitmap-argument.patch new file mode 100644 index 0000000..59abc5b --- /dev/null +++ b/SOURCES/kvm-blockdev-backup-add-bitmap-argument.patch @@ -0,0 +1,97 @@ +From f165f8b7819f9c9aa0358a1e0bc1f48cf4dfa98f Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 14 Sep 2018 04:08:14 +0200 +Subject: [PATCH 07/49] blockdev-backup: add bitmap argument + +RH-Author: John Snow +Message-id: <20180914040814.14950-2-jsnow@redhat.com> +Patchwork-id: 82149 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/1] blockdev-backup: add bitmap argument +Bugzilla: 1628373 +RH-Acked-by: Fam Zheng +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody + +It is only an oversight that we don't allow incremental backup with +blockdev-backup. Add the bitmap argument which enables this. + +Signed-off-by: John Snow +Message-id: 20180830211605.13683-2-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 4034c1a9fd503f9fa488dc9b851bc4497f374e98) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 16 +++++++++++++++- + qapi/block-core.json | 7 ++++++- + 2 files changed, 21 insertions(+), 2 deletions(-) + +diff --git a/blockdev.c b/blockdev.c +index fb355c8..6e8a1e9 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3701,6 +3701,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, + BlockDriverState *bs; + BlockDriverState *target_bs; + Error *local_err = NULL; ++ BdrvDirtyBitmap *bmap = NULL; + AioContext *aio_context; + BlockJob *job = NULL; + int job_flags = JOB_DEFAULT; +@@ -3751,6 +3752,19 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, + goto out; + } + } ++ if (backup->has_bitmap) { ++ bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap); ++ if (!bmap) { ++ error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap); ++ goto out; ++ } ++ if (bdrv_dirty_bitmap_qmp_locked(bmap)) { ++ error_setg(errp, ++ "Bitmap '%s' is currently locked and cannot be used for " ++ "backup", backup->bitmap); ++ goto out; ++ } ++ } + if (!backup->auto_finalize) { + job_flags |= JOB_MANUAL_FINALIZE; + } +@@ -3758,7 +3772,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, + job_flags |= JOB_MANUAL_DISMISS; + } + job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, +- backup->sync, NULL, backup->compress, ++ backup->sync, bmap, backup->compress, + backup->on_source_error, backup->on_target_error, + job_flags, NULL, NULL, txn, &local_err); + if (local_err != NULL) { +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 8da07cd..2706012 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -1299,6 +1299,10 @@ + # @speed: the maximum speed, in bytes per second. The default is 0, + # for unlimited. + # ++# @bitmap: the name of dirty bitmap if sync is "incremental". ++# Must be present if sync is "incremental", must NOT be present ++# otherwise. (Since 3.1) ++# + # @compress: true to compress data, if the target format supports it. + # (default: false) (since 2.8) + # +@@ -1331,7 +1335,8 @@ + ## + { 'struct': 'BlockdevBackup', + 'data': { '*job-id': 'str', 'device': 'str', 'target': 'str', +- 'sync': 'MirrorSyncMode', '*speed': 'int', '*compress': 'bool', ++ 'sync': 'MirrorSyncMode', '*speed': 'int', ++ '*bitmap': 'str', '*compress': 'bool', + '*on-source-error': 'BlockdevOnError', + '*on-target-error': 'BlockdevOnError', + '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockdev-document-transactional-shortcomings.patch b/SOURCES/kvm-blockdev-document-transactional-shortcomings.patch new file mode 100644 index 0000000..8145b13 --- /dev/null +++ b/SOURCES/kvm-blockdev-document-transactional-shortcomings.patch @@ -0,0 +1,53 @@ +From 3e0ff815da32944228b49c4ad33957c63dfd0f99 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:18:03 +0200 +Subject: [PATCH 25/25] blockdev: document transactional shortcomings + +RH-Author: John Snow +Message-id: <20180910181803.11781-26-jsnow@redhat.com> +Patchwork-id: 82087 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 25/25] blockdev: document transactional shortcomings +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Presently only the backup job really guarantees what one would consider +transactional semantics. To guard against someone helpfully adding them +in the future, document that there are shortcomings in the model that +would need to be audited at that time. + +Signed-off-by: John Snow +Message-id: 20180906130225.5118-17-jsnow@redhat.com +Reviewed-by: Jeff Cody +Reviewed-by: Max Reitz +Signed-off-by: Max Reitz +(cherry picked from commit 97169e9959811e92e14c42afa0ec1fdeb09715ea) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/blockdev.c b/blockdev.c +index 6325471..0eb6bba 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -2333,7 +2333,13 @@ static const BlkActionOps actions[] = { + .instance_size = sizeof(BlockDirtyBitmapState), + .prepare = block_dirty_bitmap_disable_prepare, + .abort = block_dirty_bitmap_disable_abort, +- } ++ }, ++ /* 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. ++ */ + }; + + /** +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockdev-enable-non-root-nodes-for-backup-source.patch b/SOURCES/kvm-blockdev-enable-non-root-nodes-for-backup-source.patch new file mode 100644 index 0000000..b661e6d --- /dev/null +++ b/SOURCES/kvm-blockdev-enable-non-root-nodes-for-backup-source.patch @@ -0,0 +1,61 @@ +From 305bd87b4d721989679d4c3407e355e032e2c005 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:54:46 +0200 +Subject: [PATCH 61/89] blockdev: enable non-root nodes for backup source + +RH-Author: John Snow +Message-id: <20180718225511.14878-11-jsnow@redhat.com> +Patchwork-id: 81395 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 10/35] blockdev: enable non-root nodes for backup source +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +This is needed to implement the image-fleecing workflow where we +create a temporary node backed by an active node, then start +backupdev-backup sync=none from the active node to the temp node. + +In this case, the active node is now a root node AND a backing node, +so it no longer qualifies as a root node, so we loosen the restriction +on which nodes can be considered as the source for a backup. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: Eric Blake +Signed-off-by: John Snow +Message-Id: <20180702194630.9360-2-jsnow@redhat.com> +Signed-off-by: Eric Blake +(cherry picked from commit 930fe17f9900e9c879834f2d2e5c301992623332) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/blockdev.c b/blockdev.c +index 9beef10..baa7e18 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -2010,7 +2010,7 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp) + assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); + backup = common->action->u.blockdev_backup.data; + +- bs = qmp_get_root_bs(backup->device, errp); ++ bs = bdrv_lookup_bs(backup->device, backup->device, errp); + if (!bs) { + return; + } +@@ -3674,7 +3674,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, + backup->compress = false; + } + +- bs = qmp_get_root_bs(backup->device, errp); ++ bs = bdrv_lookup_bs(backup->device, backup->device, errp); + if (!bs) { + return NULL; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockdev-fix-missed-target-unref-for-drive-backup.patch b/SOURCES/kvm-blockdev-fix-missed-target-unref-for-drive-backup.patch new file mode 100644 index 0000000..e16c424 --- /dev/null +++ b/SOURCES/kvm-blockdev-fix-missed-target-unref-for-drive-backup.patch @@ -0,0 +1,80 @@ +From 4f1adbc42c93d406d73e30243ddaa6cb11257a24 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Tue, 4 Jun 2019 17:47:22 +0200 +Subject: [PATCH 8/9] blockdev: fix missed target unref for drive-backup + +RH-Author: John Snow +Message-id: <20190604174722.30906-2-jsnow@redhat.com> +Patchwork-id: 88526 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/1] blockdev: fix missed target unref for drive-backup +Bugzilla: 1703916 +RH-Acked-by: Max Reitz +RH-Acked-by: Kevin Wolf +RH-Acked-by: Stefano Garzarella + +If the bitmap can't be used for whatever reason, we skip putting down +the reference. Fix that. + +In practice, this means that if you attempt to gracefully exit QEMU +after a backup command being rejected, bdrv_close_all will fail and +tell you some unpleasant things via assert(). + +Reported-by: aihua liang +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1703916 +Signed-off-by: John Snow +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit 4da26f138db06c9c6d7199d42bd3c2be552cb956) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 13 ++++++------- + 1 file changed, 6 insertions(+), 7 deletions(-) + +diff --git a/blockdev.c b/blockdev.c +index 61218b4..739441d 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3673,8 +3673,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, + if (set_backing_hd) { + bdrv_set_backing_hd(target_bs, source, &local_err); + if (local_err) { +- bdrv_unref(target_bs); +- goto out; ++ goto unref; + } + } + +@@ -3682,11 +3681,10 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, + bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap); + if (!bmap) { + error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap); +- bdrv_unref(target_bs); +- goto out; ++ goto unref; + } + if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_DEFAULT, errp)) { +- goto out; ++ goto unref; + } + } + if (!backup->auto_finalize) { +@@ -3700,12 +3698,13 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, + backup->sync, bmap, backup->compress, + backup->on_source_error, backup->on_target_error, + job_flags, NULL, NULL, txn, &local_err); +- bdrv_unref(target_bs); + if (local_err != NULL) { + error_propagate(errp, local_err); +- goto out; ++ goto unref; + } + ++unref: ++ bdrv_unref(target_bs); + out: + aio_context_release(aio_context); + return job; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockdev-n-ary-bitmap-merge.patch b/SOURCES/kvm-blockdev-n-ary-bitmap-merge.patch new file mode 100644 index 0000000..ea7a8d6 --- /dev/null +++ b/SOURCES/kvm-blockdev-n-ary-bitmap-merge.patch @@ -0,0 +1,207 @@ +From 80ea6f080180cd5aea1603405948d164442dd8d1 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 16:16:16 +0100 +Subject: [PATCH 018/163] blockdev: n-ary bitmap merge + +RH-Author: John Snow +Message-id: <20190320161631.14841-5-jsnow@redhat.com> +Patchwork-id: 84948 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 04/19] blockdev: n-ary bitmap merge +Bugzilla: 1668956 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +Especially outside of transactions, it is helpful to provide +all-or-nothing semantics for bitmap merges. This facilitates +the coalescing of multiple bitmaps into a single target for +the "checkpoint" interpretation when assembling bitmaps that +represent arbitrary points in time from component bitmaps. + +This is an incompatible change from the preliminary version +of the API. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20181221093529.23855-4-jsnow@redhat.com> +Signed-off-by: Eric Blake +(cherry picked from commit 360d4e4e9a501d92fb8866ac307d33a25f70c6d1) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 75 +++++++++++++++++++++++++++++++++++----------------- + qapi/block-core.json | 22 +++++++-------- + 2 files changed, 62 insertions(+), 35 deletions(-) + +diff --git a/blockdev.c b/blockdev.c +index c9bda43..3755936 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -2272,33 +2272,28 @@ static void block_dirty_bitmap_disable_abort(BlkActionState *common) + } + } + ++static BdrvDirtyBitmap *do_block_dirty_bitmap_merge(const char *node, ++ const char *target, ++ strList *bitmaps, ++ HBitmap **backup, ++ Error **errp); ++ + static void block_dirty_bitmap_merge_prepare(BlkActionState *common, + Error **errp) + { + BlockDirtyBitmapMerge *action; + BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, + common, common); +- BdrvDirtyBitmap *merge_source; + + if (action_check_completion_mode(common, errp) < 0) { + return; + } + + action = common->action->u.x_block_dirty_bitmap_merge.data; +- state->bitmap = block_dirty_bitmap_lookup(action->node, +- action->dst_name, +- &state->bs, +- errp); +- if (!state->bitmap) { +- return; +- } + +- merge_source = bdrv_find_dirty_bitmap(state->bs, action->src_name); +- if (!merge_source) { +- return; +- } +- +- bdrv_merge_dirty_bitmap(state->bitmap, merge_source, &state->backup, errp); ++ state->bitmap = do_block_dirty_bitmap_merge(action->node, action->target, ++ action->bitmaps, &state->backup, ++ errp); + } + + static void abort_prepare(BlkActionState *common, Error **errp) +@@ -3130,24 +3125,56 @@ void qmp_x_block_dirty_bitmap_disable(const char *node, const char *name, + bdrv_disable_dirty_bitmap(bitmap); + } + +-void qmp_x_block_dirty_bitmap_merge(const char *node, const char *dst_name, +- const char *src_name, Error **errp) ++static BdrvDirtyBitmap *do_block_dirty_bitmap_merge(const char *node, ++ const char *target, ++ strList *bitmaps, ++ HBitmap **backup, ++ Error **errp) + { + BlockDriverState *bs; +- BdrvDirtyBitmap *dst, *src; ++ BdrvDirtyBitmap *dst, *src, *anon; ++ strList *lst; ++ Error *local_err = NULL; + +- dst = block_dirty_bitmap_lookup(node, dst_name, &bs, errp); ++ dst = block_dirty_bitmap_lookup(node, target, &bs, errp); + if (!dst) { +- return; ++ return NULL; + } + +- src = bdrv_find_dirty_bitmap(bs, src_name); +- if (!src) { +- error_setg(errp, "Dirty bitmap '%s' not found", src_name); +- return; ++ anon = bdrv_create_dirty_bitmap(bs, bdrv_dirty_bitmap_granularity(dst), ++ NULL, errp); ++ if (!anon) { ++ return NULL; ++ } ++ ++ for (lst = bitmaps; lst; lst = lst->next) { ++ src = bdrv_find_dirty_bitmap(bs, lst->value); ++ if (!src) { ++ error_setg(errp, "Dirty bitmap '%s' not found", lst->value); ++ dst = NULL; ++ goto out; ++ } ++ ++ bdrv_merge_dirty_bitmap(anon, src, NULL, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ dst = NULL; ++ goto out; ++ } + } + +- bdrv_merge_dirty_bitmap(dst, src, NULL, errp); ++ /* Merge into dst; dst is unchanged on failure. */ ++ bdrv_merge_dirty_bitmap(dst, anon, backup, errp); ++ ++ out: ++ bdrv_release_dirty_bitmap(bs, anon); ++ return dst; ++} ++ ++void qmp_x_block_dirty_bitmap_merge(const char *node, const char *target, ++ strList *bitmaps, Error **errp) ++{ ++ do_block_dirty_bitmap_merge(node, target, bitmaps, NULL, errp); + } + + BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 0960449..7e11392 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -1801,14 +1801,14 @@ + # + # @node: name of device/node which the bitmap is tracking + # +-# @dst_name: name of the destination dirty bitmap ++# @target: name of the destination dirty bitmap + # +-# @src_name: name of the source dirty bitmap ++# @bitmaps: name(s) of the source dirty bitmap(s) + # + # Since: 3.0 + ## + { 'struct': 'BlockDirtyBitmapMerge', +- 'data': { 'node': 'str', 'dst_name': 'str', 'src_name': 'str' } } ++ 'data': { 'node': 'str', 'target': 'str', 'bitmaps': ['str'] } } + + ## + # @block-dirty-bitmap-add: +@@ -1923,23 +1923,23 @@ + ## + # @x-block-dirty-bitmap-merge: + # +-# FIXME: Rename @src_name and @dst_name to src-name and dst-name. +-# +-# Merge @src_name dirty bitmap to @dst_name dirty bitmap. @src_name dirty +-# bitmap is unchanged. On error, @dst_name is unchanged. ++# Merge dirty bitmaps listed in @bitmaps to the @target dirty bitmap. ++# The @bitmaps dirty bitmaps are unchanged. ++# On error, @target is unchanged. + # + # Returns: nothing on success + # If @node is not a valid block device, DeviceNotFound +-# If @dst_name or @src_name is not found, GenericError +-# If bitmaps has different sizes or granularities, GenericError ++# If any bitmap in @bitmaps or @target is not found, GenericError ++# If any of the bitmaps have different sizes or granularities, ++# GenericError + # + # Since: 3.0 + # + # Example: + # + # -> { "execute": "x-block-dirty-bitmap-merge", +-# "arguments": { "node": "drive0", "dst_name": "bitmap0", +-# "src_name": "bitmap1" } } ++# "arguments": { "node": "drive0", "target": "bitmap0", ++# "bitmaps": ["bitmap1"] } } + # <- { "return": {} } + # + ## +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockdev-remove-unused-paio-parameter-documentation.patch b/SOURCES/kvm-blockdev-remove-unused-paio-parameter-documentation.patch new file mode 100644 index 0000000..def8347 --- /dev/null +++ b/SOURCES/kvm-blockdev-remove-unused-paio-parameter-documentation.patch @@ -0,0 +1,43 @@ +From 7b60eac4fb7e3f0373381e148f963878e0325916 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 18:18:47 +0200 +Subject: [PATCH 142/163] blockdev: remove unused paio parameter documentation + +RH-Author: John Snow +Message-id: <20190403181857.9693-12-jsnow@redhat.com> +Patchwork-id: 85424 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 11/21] blockdev: remove unused paio parameter documentation +Bugzilla: 1677073 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Sergio Lopez Pascual + +This field isn't present anymore. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20190223000614.13894-10-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 2f158ca7b63175c5f0ac9a65e933e23deac46caa) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/blockdev.c b/blockdev.c +index c9ade12..a9e2e1d 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -1407,7 +1407,6 @@ out_aio_context: + * @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. +- * @paio: Output pointer for aio_context acquisition, if desired. Can be NULL. + * @errp: Output pointer for error information. Can be NULL. + * + * @return: A bitmap object on success, or NULL on failure. +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockdev-rename-block-dirty-bitmap-clear-transaction.patch b/SOURCES/kvm-blockdev-rename-block-dirty-bitmap-clear-transaction.patch new file mode 100644 index 0000000..df90b06 --- /dev/null +++ b/SOURCES/kvm-blockdev-rename-block-dirty-bitmap-clear-transaction.patch @@ -0,0 +1,65 @@ +From 09fa15417e6c4778f50c8396004efd74077afa81 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:29 +0100 +Subject: [PATCH 19/33] blockdev: rename block-dirty-bitmap-clear transaction + handlers + +RH-Author: John Snow +Message-id: <20190206221243.7407-10-jsnow@redhat.com> +Patchwork-id: 84274 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 09/23] blockdev: rename block-dirty-bitmap-clear transaction handlers +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +From: Vladimir Sementsov-Ogievskiy + +Rename block-dirty-bitmap-clear transaction handlers to reuse them for +x-block-dirty-bitmap-merge transaction in the following patch. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: John Snow +(cherry picked from commit 5c4cf8b294ee65c049d6c40f5f6ff7c1befdb3d9) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/blockdev.c b/blockdev.c +index 823a97f..853dd0e 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -2178,7 +2178,7 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common, + bdrv_clear_dirty_bitmap(state->bitmap, &state->backup); + } + +-static void block_dirty_bitmap_clear_abort(BlkActionState *common) ++static void block_dirty_bitmap_restore(BlkActionState *common) + { + BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, + common, common); +@@ -2188,7 +2188,7 @@ static void block_dirty_bitmap_clear_abort(BlkActionState *common) + } + } + +-static void block_dirty_bitmap_clear_commit(BlkActionState *common) ++static void block_dirty_bitmap_free_backup(BlkActionState *common) + { + BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, + common, common); +@@ -2322,8 +2322,8 @@ static const BlkActionOps actions[] = { + [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_CLEAR] = { + .instance_size = sizeof(BlockDirtyBitmapState), + .prepare = block_dirty_bitmap_clear_prepare, +- .commit = block_dirty_bitmap_clear_commit, +- .abort = block_dirty_bitmap_clear_abort, ++ .commit = block_dirty_bitmap_free_backup, ++ .abort = block_dirty_bitmap_restore, + }, + [TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_ENABLE] = { + .instance_size = sizeof(BlockDirtyBitmapState), +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockjob-Add-block_job_driver.patch b/SOURCES/kvm-blockjob-Add-block_job_driver.patch new file mode 100644 index 0000000..fb1dd60 --- /dev/null +++ b/SOURCES/kvm-blockjob-Add-block_job_driver.patch @@ -0,0 +1,105 @@ +From a80cabf1e33718c739f131c77b63a31d5cf6f5e1 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:47:56 +0200 +Subject: [PATCH 27/89] blockjob: Add block_job_driver() + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-14-kwolf@redhat.com> +Patchwork-id: 81064 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 13/73] blockjob: Add block_job_driver() +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +The backup block job directly accesses the driver field in BlockJob. Add +a wrapper for getting it. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +Reviewed-by: Max Reitz +Reviewed-by: John Snow +(cherry picked from commit bd21935b50d100d8da8c05cd3c2009f0f3432cb4) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 8 +++++--- + blockjob.c | 5 +++++ + include/block/blockjob.h | 7 +++++++ + 3 files changed, 17 insertions(+), 3 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index cfdb6ec..e14d995 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -47,6 +47,8 @@ typedef struct BackupBlockJob { + HBitmap *copy_bitmap; + } BackupBlockJob; + ++static const BlockJobDriver backup_job_driver; ++ + /* See if in-flight requests overlap and wait for them to complete */ + static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job, + int64_t start, +@@ -241,7 +243,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp) + BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); + int64_t len; + +- assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); ++ assert(block_job_driver(job) == &backup_job_driver); + + if (backup_job->sync_mode != MIRROR_SYNC_MODE_NONE) { + error_setg(errp, "The backup job only supports block checkpoint in" +@@ -259,7 +261,7 @@ void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset, + BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); + int64_t start, end; + +- assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); ++ assert(block_job_driver(job) == &backup_job_driver); + + start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size); + end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size); +@@ -272,7 +274,7 @@ void backup_cow_request_begin(CowRequest *req, BlockJob *job, + BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); + int64_t start, end; + +- assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); ++ assert(block_job_driver(job) == &backup_job_driver); + + start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size); + end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size); +diff --git a/blockjob.c b/blockjob.c +index e30f5ec..112672a 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -373,6 +373,11 @@ static bool block_job_started(BlockJob *job) + return job->co; + } + ++const BlockJobDriver *block_job_driver(BlockJob *job) ++{ ++ return job->driver; ++} ++ + /** + * 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. +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 82f52f4..0f56f72 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -452,4 +452,11 @@ void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job); + */ + 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 +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockjob-Fix-assertion-in-block_job_finalize.patch b/SOURCES/kvm-blockjob-Fix-assertion-in-block_job_finalize.patch new file mode 100644 index 0000000..7f1d57a --- /dev/null +++ b/SOURCES/kvm-blockjob-Fix-assertion-in-block_job_finalize.patch @@ -0,0 +1,52 @@ +From 7c3744d3960176798cde84b989bab4f7955f97c3 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:47:51 +0200 +Subject: [PATCH 22/89] blockjob: Fix assertion in block_job_finalize() + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-9-kwolf@redhat.com> +Patchwork-id: 81060 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 08/73] blockjob: Fix assertion in block_job_finalize() +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Every job gets a non-NULL job->txn on creation, but it doesn't +necessarily keep it until it is decommissioned: Finalising a job removes +it from its transaction. Therefore, calling 'blockdev-job-finalize' a +second time on an already concluded job causes an assertion failure. + +Remove job->txn from the assertion in block_job_finalize() to fix this. +block_job_do_finalize() still has the same assertion, but if a job is +already removed from its transaction, block_job_apply_verb() will +already error out before we run into that assertion. + +Cc: qemu-stable@nongnu.org +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +Reviewed-by: Max Reitz +Reviewed-by: John Snow +(cherry picked from commit 37aa19b63c46d933f1e4ea944cfccee54e2caf4a) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + blockjob.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/blockjob.c b/blockjob.c +index 6746cad..0033b96 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -710,7 +710,7 @@ void block_job_complete(BlockJob *job, Error **errp) + + void block_job_finalize(BlockJob *job, Error **errp) + { +- assert(job && job->id && job->txn); ++ assert(job && job->id); + if (block_job_apply_verb(job, BLOCK_JOB_VERB_FINALIZE, errp)) { + return; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockjob-Implement-block_job_set_speed-centrally.patch b/SOURCES/kvm-blockjob-Implement-block_job_set_speed-centrally.patch new file mode 100644 index 0000000..72ba875 --- /dev/null +++ b/SOURCES/kvm-blockjob-Implement-block_job_set_speed-centrally.patch @@ -0,0 +1,284 @@ +From f92e2e46e8d14701afb20a12a45423cd06d7f5c6 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:47:54 +0200 +Subject: [PATCH 25/89] blockjob: Implement block_job_set_speed() centrally + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-12-kwolf@redhat.com> +Patchwork-id: 81102 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 11/73] blockjob: Implement block_job_set_speed() centrally +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +All block job drivers support .set_speed and all of them duplicate the +same code to implement it. Move that code to blockjob.c and remove the +now useless callback. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +Reviewed-by: Max Reitz +Reviewed-by: John Snow +(cherry picked from commit 18bb69287ea522ab696e1bea818b93e5eaa85745) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 13 ------------- + block/commit.c | 14 -------------- + block/mirror.c | 26 ++++++-------------------- + block/stream.c | 14 -------------- + blockjob.c | 12 ++++-------- + include/block/blockjob.h | 2 ++ + include/block/blockjob_int.h | 3 --- + 7 files changed, 12 insertions(+), 72 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index 7585c43..8468fd9 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -27,7 +27,6 @@ + #include "qemu/error-report.h" + + #define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16) +-#define SLICE_TIME 100000000ULL /* ns */ + + typedef struct BackupBlockJob { + BlockJob common; +@@ -190,17 +189,6 @@ static int coroutine_fn backup_before_write_notify( + return backup_do_cow(job, req->offset, req->bytes, NULL, true); + } + +-static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp) +-{ +- BackupBlockJob *s = container_of(job, BackupBlockJob, common); +- +- if (speed < 0) { +- error_setg(errp, QERR_INVALID_PARAMETER, "speed"); +- return; +- } +- ratelimit_set_speed(&s->common.limit, speed, SLICE_TIME); +-} +- + static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) + { + BdrvDirtyBitmap *bm; +@@ -540,7 +528,6 @@ static const BlockJobDriver backup_job_driver = { + .instance_size = sizeof(BackupBlockJob), + .job_type = BLOCK_JOB_TYPE_BACKUP, + .start = backup_run, +- .set_speed = backup_set_speed, + .commit = backup_commit, + .abort = backup_abort, + .clean = backup_clean, +diff --git a/block/commit.c b/block/commit.c +index beec5d0..46cbeae 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -31,8 +31,6 @@ enum { + COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */ + }; + +-#define SLICE_TIME 100000000ULL /* ns */ +- + typedef struct CommitBlockJob { + BlockJob common; + BlockDriverState *commit_top_bs; +@@ -216,21 +214,9 @@ out: + block_job_defer_to_main_loop(&s->common, commit_complete, data); + } + +-static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp) +-{ +- CommitBlockJob *s = container_of(job, CommitBlockJob, common); +- +- if (speed < 0) { +- error_setg(errp, QERR_INVALID_PARAMETER, "speed"); +- return; +- } +- ratelimit_set_speed(&s->common.limit, speed, SLICE_TIME); +-} +- + static const BlockJobDriver commit_job_driver = { + .instance_size = sizeof(CommitBlockJob), + .job_type = BLOCK_JOB_TYPE_COMMIT, +- .set_speed = commit_set_speed, + .start = commit_run, + }; + +diff --git a/block/mirror.c b/block/mirror.c +index a515ec1..d5e0ff2 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -22,7 +22,6 @@ + #include "qemu/ratelimit.h" + #include "qemu/bitmap.h" + +-#define SLICE_TIME 100000000ULL /* ns */ + #define MAX_IN_FLIGHT 16 + #define MAX_IO_BYTES (1 << 20) /* 1 Mb */ + #define DEFAULT_MIRROR_BUF_SIZE (MAX_IN_FLIGHT * MAX_IO_BYTES) +@@ -596,7 +595,7 @@ static void mirror_throttle(MirrorBlockJob *s) + { + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + +- if (now - s->last_pause_ns > SLICE_TIME) { ++ if (now - s->last_pause_ns > BLOCK_JOB_SLICE_TIME) { + s->last_pause_ns = now; + block_job_sleep_ns(&s->common, 0); + } else { +@@ -799,11 +798,10 @@ static void coroutine_fn mirror_run(void *opaque) + + /* 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 SLICE_TIME nanoseconds, or when there is an error, +- * or when the source is clean, whichever comes first. +- */ ++ * 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; +- if (delta < SLICE_TIME && ++ if (delta < BLOCK_JOB_SLICE_TIME && + s->common.iostatus == BLOCK_DEVICE_IO_STATUS_OK) { + if (s->in_flight >= MAX_IN_FLIGHT || s->buf_free_count == 0 || + (cnt == 0 && s->in_flight > 0)) { +@@ -869,7 +867,8 @@ static void coroutine_fn mirror_run(void *opaque) + ret = 0; + + if (s->synced && !should_complete) { +- delay_ns = (s->in_flight == 0 && cnt == 0 ? SLICE_TIME : 0); ++ delay_ns = (s->in_flight == 0 && ++ cnt == 0 ? BLOCK_JOB_SLICE_TIME : 0); + } + trace_mirror_before_sleep(s, cnt, s->synced, delay_ns); + block_job_sleep_ns(&s->common, delay_ns); +@@ -908,17 +907,6 @@ immediate_exit: + block_job_defer_to_main_loop(&s->common, mirror_exit, data); + } + +-static void mirror_set_speed(BlockJob *job, int64_t speed, Error **errp) +-{ +- MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); +- +- if (speed < 0) { +- error_setg(errp, QERR_INVALID_PARAMETER, "speed"); +- return; +- } +- ratelimit_set_speed(&s->common.limit, speed, SLICE_TIME); +-} +- + static void mirror_complete(BlockJob *job, Error **errp) + { + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); +@@ -1003,7 +991,6 @@ static void mirror_drain(BlockJob *job) + static const BlockJobDriver mirror_job_driver = { + .instance_size = sizeof(MirrorBlockJob), + .job_type = BLOCK_JOB_TYPE_MIRROR, +- .set_speed = mirror_set_speed, + .start = mirror_run, + .complete = mirror_complete, + .pause = mirror_pause, +@@ -1014,7 +1001,6 @@ static const BlockJobDriver mirror_job_driver = { + static const BlockJobDriver commit_active_job_driver = { + .instance_size = sizeof(MirrorBlockJob), + .job_type = BLOCK_JOB_TYPE_COMMIT, +- .set_speed = mirror_set_speed, + .start = mirror_run, + .complete = mirror_complete, + .pause = mirror_pause, +diff --git a/block/stream.c b/block/stream.c +index a1d4768..797d7c4 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -29,8 +29,6 @@ enum { + STREAM_BUFFER_SIZE = 512 * 1024, /* in bytes */ + }; + +-#define SLICE_TIME 100000000ULL /* ns */ +- + typedef struct StreamBlockJob { + BlockJob common; + BlockDriverState *base; +@@ -210,21 +208,9 @@ out: + block_job_defer_to_main_loop(&s->common, stream_complete, data); + } + +-static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp) +-{ +- StreamBlockJob *s = container_of(job, StreamBlockJob, common); +- +- if (speed < 0) { +- error_setg(errp, QERR_INVALID_PARAMETER, "speed"); +- return; +- } +- ratelimit_set_speed(&s->common.limit, speed, SLICE_TIME); +-} +- + static const BlockJobDriver stream_job_driver = { + .instance_size = sizeof(StreamBlockJob), + .job_type = BLOCK_JOB_TYPE_STREAM, +- .set_speed = stream_set_speed, + .start = stream_run, + }; + +diff --git a/blockjob.c b/blockjob.c +index d0a2ac5..0f7214c 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -667,22 +667,18 @@ static void block_job_completed_txn_success(BlockJob *job) + + void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) + { +- Error *local_err = NULL; + int64_t old_speed = job->speed; + +- if (!job->driver->set_speed) { +- error_setg(errp, QERR_UNSUPPORTED); +- return; +- } + if (block_job_apply_verb(job, BLOCK_JOB_VERB_SET_SPEED, errp)) { + return; + } +- job->driver->set_speed(job, speed, &local_err); +- if (local_err) { +- error_propagate(errp, local_err); ++ if (speed < 0) { ++ error_setg(errp, QERR_INVALID_PARAMETER, "speed"); + return; + } + ++ ratelimit_set_speed(&job->limit, speed, BLOCK_JOB_SLICE_TIME); ++ + job->speed = speed; + if (speed && speed <= old_speed) { + return; +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 22bf418..82f52f4 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -29,6 +29,8 @@ + #include "block/block.h" + #include "qemu/ratelimit.h" + ++#define BLOCK_JOB_SLICE_TIME 100000000ULL /* ns */ ++ + typedef struct BlockJobDriver BlockJobDriver; + typedef struct BlockJobTxn BlockJobTxn; + +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index d5a515d..ad510d5 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -41,9 +41,6 @@ struct BlockJobDriver { + /** String describing the operation, part of query-block-jobs QMP API */ + BlockJobType job_type; + +- /** Optional callback for job types that support setting a speed limit */ +- void (*set_speed)(BlockJob *job, int64_t speed, Error **errp); +- + /** Mandatory: Entrypoint for the Coroutine. */ + CoroutineEntry *start; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockjob-Improve-BlockJobInfo.offset-len-documentati.patch b/SOURCES/kvm-blockjob-Improve-BlockJobInfo.offset-len-documentati.patch new file mode 100644 index 0000000..d2c7782 --- /dev/null +++ b/SOURCES/kvm-blockjob-Improve-BlockJobInfo.offset-len-documentati.patch @@ -0,0 +1,60 @@ +From 1dca4e49145a339e84b7cdc2a9b226f0a3afe502 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:47:58 +0200 +Subject: [PATCH 29/89] blockjob: Improve BlockJobInfo.offset/len documentation + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-16-kwolf@redhat.com> +Patchwork-id: 81087 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 15/73] blockjob: Improve BlockJobInfo.offset/len documentation +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Clarify that len is just an estimation of the end value of offset, and +that offset increases monotonically while len can change arbitrarily. + +While touching the documentation of offset, move it directly after len +to match the order of the declaration below. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +Reviewed-by: John Snow +(cherry picked from commit a81e0a825e3b89039a427bca037112f461b95fec) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + qapi/block-core.json | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/qapi/block-core.json b/qapi/block-core.json +index d24c12b..1f6d4bb 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -1148,7 +1148,12 @@ + # @device: The job identifier. Originally the device name but other + # values are allowed since QEMU 2.7 + # +-# @len: the maximum progress value ++# @len: Estimated @offset value at the completion of the job. This value can ++# arbitrarily change while the job is running, in both directions. ++# ++# @offset: Progress made until now. The unit is arbitrary and the value can ++# only meaningfully be used for the ratio of @offset to @len. The ++# value is monotonically increasing. + # + # @busy: false if the job is known to be in a quiescent state, with + # no pending I/O. Since 1.3. +@@ -1156,8 +1161,6 @@ + # @paused: whether the job is paused or, if @busy is true, will + # pause itself as soon as possible. Since 1.3. + # +-# @offset: the current progress value +-# + # @speed: the rate limit, bytes per second + # + # @io-status: the status of the job (since 1.3) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockjob-Introduce-block_job_ratelimit_get_delay.patch b/SOURCES/kvm-blockjob-Introduce-block_job_ratelimit_get_delay.patch new file mode 100644 index 0000000..4a37a04 --- /dev/null +++ b/SOURCES/kvm-blockjob-Introduce-block_job_ratelimit_get_delay.patch @@ -0,0 +1,154 @@ +From 0106ebf221d3cc30580249e7b9b4d90a0ad10c5c Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:47:55 +0200 +Subject: [PATCH 26/89] blockjob: Introduce block_job_ratelimit_get_delay() + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-13-kwolf@redhat.com> +Patchwork-id: 81079 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 12/73] blockjob: Introduce block_job_ratelimit_get_delay() +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This gets us rid of more direct accesses to BlockJob fields from the +job drivers. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +Reviewed-by: Max Reitz +Reviewed-by: John Snow +(cherry picked from commit dee81d5111ff0e24ac63ab0dbbd19e84c2f87904) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 18 +++++++----------- + block/commit.c | 4 ++-- + block/mirror.c | 5 +---- + block/stream.c | 4 ++-- + blockjob.c | 9 +++++++++ + include/block/blockjob_int.h | 8 ++++++++ + 6 files changed, 29 insertions(+), 19 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index 8468fd9..cfdb6ec 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -325,21 +325,17 @@ static void backup_complete(BlockJob *job, void *opaque) + + static bool coroutine_fn yield_and_check(BackupBlockJob *job) + { ++ uint64_t delay_ns; ++ + if (block_job_is_cancelled(&job->common)) { + return true; + } + +- /* we need to yield so that bdrv_drain_all() returns. +- * (without, VM does not reboot) +- */ +- if (job->common.speed) { +- uint64_t delay_ns = ratelimit_calculate_delay(&job->common.limit, +- job->bytes_read); +- job->bytes_read = 0; +- block_job_sleep_ns(&job->common, delay_ns); +- } else { +- block_job_sleep_ns(&job->common, 0); +- } ++ /* We need to yield even for delay_ns = 0 so that bdrv_drain_all() can ++ * return. Without a yield, the VM would not reboot. */ ++ delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read); ++ job->bytes_read = 0; ++ block_job_sleep_ns(&job->common, delay_ns); + + if (block_job_is_cancelled(&job->common)) { + return true; +diff --git a/block/commit.c b/block/commit.c +index 46cbeae..ba5df6a 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -197,8 +197,8 @@ static void coroutine_fn commit_run(void *opaque) + /* Publish progress */ + block_job_progress_update(&s->common, n); + +- if (copy && s->common.speed) { +- delay_ns = ratelimit_calculate_delay(&s->common.limit, n); ++ if (copy) { ++ delay_ns = block_job_ratelimit_get_delay(&s->common, n); + } else { + delay_ns = 0; + } +diff --git a/block/mirror.c b/block/mirror.c +index d5e0ff2..a4197bb 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -447,10 +447,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) + assert(io_bytes); + offset += io_bytes; + nb_chunks -= DIV_ROUND_UP(io_bytes, s->granularity); +- if (s->common.speed) { +- delay_ns = ratelimit_calculate_delay(&s->common.limit, +- io_bytes_acct); +- } ++ delay_ns = block_job_ratelimit_get_delay(&s->common, io_bytes_acct); + } + return delay_ns; + } +diff --git a/block/stream.c b/block/stream.c +index 797d7c4..df9660d 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -185,8 +185,8 @@ static void coroutine_fn stream_run(void *opaque) + + /* Publish progress */ + block_job_progress_update(&s->common, n); +- if (copy && s->common.speed) { +- delay_ns = ratelimit_calculate_delay(&s->common.limit, n); ++ if (copy) { ++ delay_ns = block_job_ratelimit_get_delay(&s->common, n); + } else { + delay_ns = 0; + } +diff --git a/blockjob.c b/blockjob.c +index 0f7214c..e30f5ec 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -688,6 +688,15 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) + block_job_enter_cond(job, block_job_timer_pending); + } + ++int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) ++{ ++ if (!job->speed) { ++ return 0; ++ } ++ ++ return ratelimit_calculate_delay(&job->limit, n); ++} ++ + void block_job_complete(BlockJob *job, Error **errp) + { + /* Should not be reachable via external interface for internal jobs */ +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index ad510d5..62ec964 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -166,6 +166,14 @@ void block_job_sleep_ns(BlockJob *job, int64_t ns); + void block_job_yield(BlockJob *job); + + /** ++ * block_job_ratelimit_get_delay: ++ * ++ * Calculate and return delay for the next request in ns. See the documentation ++ * of ratelimit_calculate_delay() for details. ++ */ ++int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n); ++ ++/** + * block_job_early_fail: + * @bs: The block device. + * +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockjob-Lie-better-in-child_job_drained_poll.patch b/SOURCES/kvm-blockjob-Lie-better-in-child_job_drained_poll.patch new file mode 100644 index 0000000..b7d191f --- /dev/null +++ b/SOURCES/kvm-blockjob-Lie-better-in-child_job_drained_poll.patch @@ -0,0 +1,102 @@ +From a174500971a42552a30d238bf9ec065d27fd4b32 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:36 +0200 +Subject: [PATCH 45/49] blockjob: Lie better in child_job_drained_poll() + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-39-kwolf@redhat.com> +Patchwork-id: 82192 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 38/42] blockjob: Lie better in child_job_drained_poll() +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Block jobs claim in .drained_poll() that they are in a quiescent state +as soon as job->deferred_to_main_loop is true. This is obviously wrong, +they still have a completion BH to run. We only get away with this +because commit 91af091f923 added an unconditional aio_poll(false) to the +drain functions, but this is bypassing the regular drain mechanisms. + +However, just removing this and telling that the job is still active +doesn't work either: The completion callbacks themselves call drain +functions (directly, or indirectly with bdrv_reopen), so they would +deadlock then. + +As a better lie, tell that the job is active as long as the BH is +pending, but falsely call it quiescent from the point in the BH when the +completion callback is called. At this point, nested drain calls won't +deadlock because they ignore the job, and outer drains will wait for the +job to really reach a quiescent state because the callback is already +running. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + blockjob.c | 2 +- + include/qemu/job.h | 3 +++ + job.c | 11 ++++++++++- + 3 files changed, 14 insertions(+), 2 deletions(-) + +diff --git a/blockjob.c b/blockjob.c +index 8d27e8e..617d86f 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -164,7 +164,7 @@ static bool child_job_drained_poll(BdrvChild *c) + /* 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. */ +- if (!job->busy || job_is_completed(job) || job->deferred_to_main_loop) { ++ if (!job->busy || job_is_completed(job)) { + return false; + } + +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 35ac7a9..d1710f3 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -76,6 +76,9 @@ typedef struct Job { + * 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 block_job_mutex (in blockjob.c). ++ * ++ * When the job is deferred to the main loop, busy is true as long as the ++ * bottom half is still pending. + */ + bool busy; + +diff --git a/job.c b/job.c +index 47b5a11..42af9e2 100644 +--- a/job.c ++++ b/job.c +@@ -852,7 +852,16 @@ static void job_exit(void *opaque) + AioContext *ctx = job->aio_context; + + aio_context_acquire(ctx); ++ ++ /* 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(job); ++ + job_completed(job); ++ + aio_context_release(ctx); + } + +@@ -867,8 +876,8 @@ static void coroutine_fn job_co_entry(void *opaque) + assert(job && job->driver && job->driver->run); + job_pause_point(job); + job->ret = job->driver->run(job, &job->err); +- job_event_idle(job); + job->deferred_to_main_loop = true; ++ job->busy = true; + aio_bh_schedule_oneshot(qemu_get_aio_context(), job_exit, job); + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockjob-Move-RateLimit-to-BlockJob.patch b/SOURCES/kvm-blockjob-Move-RateLimit-to-BlockJob.patch new file mode 100644 index 0000000..ce1bace --- /dev/null +++ b/SOURCES/kvm-blockjob-Move-RateLimit-to-BlockJob.patch @@ -0,0 +1,179 @@ +From cc5d2ab40aeaca344047ae0b6655a69c67fd7ada Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:47:53 +0200 +Subject: [PATCH 24/89] blockjob: Move RateLimit to BlockJob + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-11-kwolf@redhat.com> +Patchwork-id: 81088 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 10/73] blockjob: Move RateLimit to BlockJob +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Every block job has a RateLimit, and they all do the exact same thing +with it, so it should be common infrastructure. Move the struct field +for a start. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +Reviewed-by: Max Reitz +Reviewed-by: John Snow +(cherry picked from commit f05fee508f538ca262d2ab19bcd8772196efe848) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 5 ++--- + block/commit.c | 5 ++--- + block/mirror.c | 6 +++--- + block/stream.c | 5 ++--- + include/block/blockjob.h | 4 ++++ + 5 files changed, 13 insertions(+), 12 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index 5d95805..7585c43 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -35,7 +35,6 @@ typedef struct BackupBlockJob { + /* bitmap for sync=incremental */ + BdrvDirtyBitmap *sync_bitmap; + MirrorSyncMode sync_mode; +- RateLimit limit; + BlockdevOnError on_source_error; + BlockdevOnError on_target_error; + CoRwlock flush_rwlock; +@@ -199,7 +198,7 @@ static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp) + error_setg(errp, QERR_INVALID_PARAMETER, "speed"); + return; + } +- ratelimit_set_speed(&s->limit, speed, SLICE_TIME); ++ ratelimit_set_speed(&s->common.limit, speed, SLICE_TIME); + } + + static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) +@@ -346,7 +345,7 @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job) + * (without, VM does not reboot) + */ + if (job->common.speed) { +- uint64_t delay_ns = ratelimit_calculate_delay(&job->limit, ++ uint64_t delay_ns = ratelimit_calculate_delay(&job->common.limit, + job->bytes_read); + job->bytes_read = 0; + block_job_sleep_ns(&job->common, delay_ns); +diff --git a/block/commit.c b/block/commit.c +index 50b191c..beec5d0 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -35,7 +35,6 @@ enum { + + typedef struct CommitBlockJob { + BlockJob common; +- RateLimit limit; + BlockDriverState *commit_top_bs; + BlockBackend *top; + BlockBackend *base; +@@ -201,7 +200,7 @@ static void coroutine_fn commit_run(void *opaque) + block_job_progress_update(&s->common, n); + + if (copy && s->common.speed) { +- delay_ns = ratelimit_calculate_delay(&s->limit, n); ++ delay_ns = ratelimit_calculate_delay(&s->common.limit, n); + } else { + delay_ns = 0; + } +@@ -225,7 +224,7 @@ static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp) + error_setg(errp, QERR_INVALID_PARAMETER, "speed"); + return; + } +- ratelimit_set_speed(&s->limit, speed, SLICE_TIME); ++ ratelimit_set_speed(&s->common.limit, speed, SLICE_TIME); + } + + static const BlockJobDriver commit_job_driver = { +diff --git a/block/mirror.c b/block/mirror.c +index ed711b5..a515ec1 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -36,7 +36,6 @@ typedef struct MirrorBuffer { + + typedef struct MirrorBlockJob { + BlockJob common; +- RateLimit limit; + BlockBackend *target; + BlockDriverState *mirror_top_bs; + BlockDriverState *source; +@@ -450,7 +449,8 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) + offset += io_bytes; + nb_chunks -= DIV_ROUND_UP(io_bytes, s->granularity); + if (s->common.speed) { +- delay_ns = ratelimit_calculate_delay(&s->limit, io_bytes_acct); ++ delay_ns = ratelimit_calculate_delay(&s->common.limit, ++ io_bytes_acct); + } + } + return delay_ns; +@@ -916,7 +916,7 @@ static void mirror_set_speed(BlockJob *job, int64_t speed, Error **errp) + error_setg(errp, QERR_INVALID_PARAMETER, "speed"); + return; + } +- ratelimit_set_speed(&s->limit, speed, SLICE_TIME); ++ ratelimit_set_speed(&s->common.limit, speed, SLICE_TIME); + } + + static void mirror_complete(BlockJob *job, Error **errp) +diff --git a/block/stream.c b/block/stream.c +index 8369852..a1d4768 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -33,7 +33,6 @@ enum { + + typedef struct StreamBlockJob { + BlockJob common; +- RateLimit limit; + BlockDriverState *base; + BlockdevOnError on_error; + char *backing_file_str; +@@ -189,7 +188,7 @@ static void coroutine_fn stream_run(void *opaque) + /* Publish progress */ + block_job_progress_update(&s->common, n); + if (copy && s->common.speed) { +- delay_ns = ratelimit_calculate_delay(&s->limit, n); ++ delay_ns = ratelimit_calculate_delay(&s->common.limit, n); + } else { + delay_ns = 0; + } +@@ -219,7 +218,7 @@ static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp) + error_setg(errp, QERR_INVALID_PARAMETER, "speed"); + return; + } +- ratelimit_set_speed(&s->limit, speed, SLICE_TIME); ++ ratelimit_set_speed(&s->common.limit, speed, SLICE_TIME); + } + + static const BlockJobDriver stream_job_driver = { +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index a2cc522..22bf418 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -27,6 +27,7 @@ + #define BLOCKJOB_H + + #include "block/block.h" ++#include "qemu/ratelimit.h" + + typedef struct BlockJobDriver BlockJobDriver; + typedef struct BlockJobTxn BlockJobTxn; +@@ -118,6 +119,9 @@ typedef struct BlockJob { + /** Speed that was set with @block_job_set_speed. */ + int64_t speed; + ++ /** Rate limiting data structure for implementing @speed. */ ++ RateLimit limit; ++ + /** The completion function that will be called when the job completes. */ + BlockCompletionFunc *cb; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockjob-Remove-BlockJob.driver.patch b/SOURCES/kvm-blockjob-Remove-BlockJob.driver.patch new file mode 100644 index 0000000..439a4c9 --- /dev/null +++ b/SOURCES/kvm-blockjob-Remove-BlockJob.driver.patch @@ -0,0 +1,96 @@ +From 0991b141d8b65fb00b9261532faeb65a93d4d650 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:34 +0200 +Subject: [PATCH 65/89] blockjob: Remove BlockJob.driver + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-52-kwolf@redhat.com> +Patchwork-id: 81123 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 51/73] blockjob: Remove BlockJob.driver +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +BlockJob.driver is redundant with Job.driver and only used in very few +places any more. Remove it. + +Signed-off-by: Kevin Wolf +(cherry picked from commit 9f6bb4c004a6458227b9eec6aff3f79afe159699) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + blockjob.c | 17 ++++++++++------- + include/block/blockjob.h | 3 --- + 2 files changed, 10 insertions(+), 10 deletions(-) + +diff --git a/blockjob.c b/blockjob.c +index 5c8ff6f..0306533 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -103,10 +103,12 @@ static void block_job_attached_aio_context(AioContext *new_context, + void *opaque) + { + BlockJob *job = opaque; ++ const JobDriver *drv = job->job.driver; ++ BlockJobDriver *bjdrv = container_of(drv, BlockJobDriver, job_driver); + + job->job.aio_context = new_context; +- if (job->driver->attached_aio_context) { +- job->driver->attached_aio_context(job, new_context); ++ if (bjdrv->attached_aio_context) { ++ bjdrv->attached_aio_context(job, new_context); + } + + job_resume(&job->job); +@@ -115,10 +117,12 @@ static void block_job_attached_aio_context(AioContext *new_context, + void block_job_drain(Job *job) + { + BlockJob *bjob = container_of(job, BlockJob, job); ++ const JobDriver *drv = job->driver; ++ BlockJobDriver *bjdrv = container_of(drv, BlockJobDriver, job_driver); + + blk_drain(bjob->blk); +- if (bjob->driver->drain) { +- bjob->driver->drain(bjob); ++ if (bjdrv->drain) { ++ bjdrv->drain(bjob); + } + } + +@@ -201,7 +205,7 @@ bool block_job_is_internal(BlockJob *job) + + const BlockJobDriver *block_job_driver(BlockJob *job) + { +- return job->driver; ++ return container_of(job->job.driver, BlockJobDriver, job_driver); + } + + /* Assumes the job_mutex is held */ +@@ -386,8 +390,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + assert(job->job.driver->user_resume == &block_job_user_resume); + assert(job->job.driver->drain == &block_job_drain); + +- job->driver = driver; +- job->blk = blk; ++ job->blk = blk; + + job->finalize_cancelled_notifier.notify = block_job_event_cancelled; + job->finalize_completed_notifier.notify = block_job_event_completed; +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 3021d11..32c00b7 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -43,9 +43,6 @@ typedef struct BlockJob { + /** Data belonging to the generic Job infrastructure */ + Job job; + +- /** The job type, including the job vtable. */ +- const BlockJobDriver *driver; +- + /** The block device on which the job is operating. */ + BlockBackend *blk; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockjob-Split-block_job_event_pending.patch b/SOURCES/kvm-blockjob-Split-block_job_event_pending.patch new file mode 100644 index 0000000..fbf05b1 --- /dev/null +++ b/SOURCES/kvm-blockjob-Split-block_job_event_pending.patch @@ -0,0 +1,92 @@ +From 192def6a1acb30c3ff81e130fa73849e19a9ee87 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:14 +0200 +Subject: [PATCH 45/89] blockjob: Split block_job_event_pending() + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-32-kwolf@redhat.com> +Patchwork-id: 81071 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 31/73] blockjob: Split block_job_event_pending() +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +block_job_event_pending() doesn't only send a QMP event, but it also +transitions to the PENDING state. Split the function so that we get one +part only sending the event (like other block_job_event_* functions) and +another part that does the state transition. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit 5d4f376998bc6b01402b90634385b082b2eb5c5b) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + blockjob.c | 27 ++++++++++++++++++--------- + 1 file changed, 18 insertions(+), 9 deletions(-) + +diff --git a/blockjob.c b/blockjob.c +index d9d8ff7..b4334fb 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -38,7 +38,7 @@ + + static void block_job_event_cancelled(BlockJob *job); + static void block_job_event_completed(BlockJob *job, const char *msg); +-static int block_job_event_pending(BlockJob *job); ++static void block_job_event_pending(BlockJob *job); + + /* Transactional group of block jobs */ + struct BlockJobTxn { +@@ -500,6 +500,15 @@ static void block_job_do_finalize(BlockJob *job) + } + } + ++static int block_job_transition_to_pending(BlockJob *job) ++{ ++ job_state_transition(&job->job, JOB_STATUS_PENDING); ++ if (!job->job.auto_finalize) { ++ block_job_event_pending(job); ++ } ++ return 0; ++} ++ + static void block_job_completed_txn_success(BlockJob *job) + { + BlockJobTxn *txn = job->txn; +@@ -518,7 +527,7 @@ static void block_job_completed_txn_success(BlockJob *job) + assert(other_job->ret == 0); + } + +- block_job_txn_apply(txn, block_job_event_pending, false); ++ block_job_txn_apply(txn, block_job_transition_to_pending, false); + + /* If no jobs need manual finalization, automatically do so */ + if (block_job_txn_apply(txn, block_job_needs_finalize, false) == 0) { +@@ -733,15 +742,15 @@ static void block_job_event_completed(BlockJob *job, const char *msg) + &error_abort); + } + +-static int block_job_event_pending(BlockJob *job) ++static void block_job_event_pending(BlockJob *job) + { +- job_state_transition(&job->job, JOB_STATUS_PENDING); +- if (!job->job.auto_finalize && !block_job_is_internal(job)) { +- qapi_event_send_block_job_pending(job_type(&job->job), +- job->job.id, +- &error_abort); ++ if (block_job_is_internal(job)) { ++ return; + } +- return 0; ++ ++ qapi_event_send_block_job_pending(job_type(&job->job), ++ job->job.id, ++ &error_abort); + } + + /* +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockjob-Update-block-job-pause-resume-documentation.patch b/SOURCES/kvm-blockjob-Update-block-job-pause-resume-documentation.patch new file mode 100644 index 0000000..d0c0c69 --- /dev/null +++ b/SOURCES/kvm-blockjob-Update-block-job-pause-resume-documentation.patch @@ -0,0 +1,54 @@ +From f00178eea8078521e2d3020df4873d27b16adacc Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:47:57 +0200 +Subject: [PATCH 28/89] blockjob: Update block-job-pause/resume documentation + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-15-kwolf@redhat.com> +Patchwork-id: 81069 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 14/73] blockjob: Update block-job-pause/resume documentation +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Commit 0ec4dfb8d changed block-job_pause/resume so that they return an +error if they don't do anything because the job is already +paused/running. It forgot to update the documentation, so do that now. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +Reviewed-by: John Snow +(cherry picked from commit cd44d96be90e7767c6fb8f33b90939eb58814956) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + qapi/block-core.json | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 9012d00..d24c12b 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -2338,8 +2338,7 @@ + # + # This command returns immediately after marking the active background block + # operation for pausing. It is an error to call this command if no +-# operation is in progress. Pausing an already paused job has no cumulative +-# effect; a single block-job-resume command will resume the job. ++# operation is in progress or if the job is already paused. + # + # The operation will pause as soon as possible. No event is emitted when + # the operation is actually paused. Cancelling a paused job automatically +@@ -2363,7 +2362,7 @@ + # + # This command returns immediately after resuming a paused background block + # operation. It is an error to call this command if no operation is in +-# progress. Resuming an already running job is not an error. ++# progress or if the job is not paused. + # + # This command also clears the error status of the job. + # +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockjob-Wake-up-BDS-when-job-becomes-idle.patch b/SOURCES/kvm-blockjob-Wake-up-BDS-when-job-becomes-idle.patch new file mode 100644 index 0000000..32f79f2 --- /dev/null +++ b/SOURCES/kvm-blockjob-Wake-up-BDS-when-job-becomes-idle.patch @@ -0,0 +1,159 @@ +From 29117d8ea323429138020a8de63119f15da01d83 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:25 +0200 +Subject: [PATCH 34/49] blockjob: Wake up BDS when job becomes idle + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-28-kwolf@redhat.com> +Patchwork-id: 82180 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 27/42] blockjob: Wake up BDS when job becomes idle +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +In the context of draining a BDS, the .drained_poll callback of block +jobs is called. If this returns true (i.e. there is still some activity +pending), the drain operation may call aio_poll() with blocking=true to +wait for completion. + +As soon as the pending activity is completed and the job finally arrives +in a quiescent state (i.e. its coroutine either yields with busy=false +or terminates), the block job must notify the aio_poll() loop to wake +up, otherwise we get a deadlock if both are running in different +threads. + +Signed-off-by: Kevin Wolf +Reviewed-by: Fam Zheng +Reviewed-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + blockjob.c | 18 ++++++++++++++++++ + include/block/blockjob.h | 13 +++++++++++++ + include/qemu/job.h | 3 +++ + job.c | 7 +++++++ + 4 files changed, 41 insertions(+) + +diff --git a/blockjob.c b/blockjob.c +index be5903a..8d27e8e 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -221,6 +221,22 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, + return 0; + } + ++void block_job_wakeup_all_bdrv(BlockJob *job) ++{ ++ GSList *l; ++ ++ for (l = job->nodes; l; l = l->next) { ++ BdrvChild *c = l->data; ++ bdrv_wakeup(c->bs); ++ } ++} ++ ++static void block_job_on_idle(Notifier *n, void *opaque) ++{ ++ BlockJob *job = opaque; ++ block_job_wakeup_all_bdrv(job); ++} ++ + bool block_job_is_internal(BlockJob *job) + { + return (job->job.id == NULL); +@@ -419,6 +435,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + job->finalize_completed_notifier.notify = block_job_event_completed; + job->pending_notifier.notify = block_job_event_pending; + job->ready_notifier.notify = block_job_event_ready; ++ job->idle_notifier.notify = block_job_on_idle; + + notifier_list_add(&job->job.on_finalize_cancelled, + &job->finalize_cancelled_notifier); +@@ -426,6 +443,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + &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)); +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 32c00b7..2290bbb 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -70,6 +70,9 @@ typedef struct BlockJob { + /** 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 */ + GSList *nodes; + } BlockJob; +@@ -119,6 +122,16 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, + void block_job_remove_all_bdrv(BlockJob *job); + + /** ++ * block_job_wakeup_all_bdrv: ++ * @job: The block job ++ * ++ * Calls bdrv_wakeup() for all BlockDriverStates that have been added to the ++ * job. This function is to be called whenever child_job_drained_poll() would ++ * go from true to false to notify waiting drain requests. ++ */ ++void block_job_wakeup_all_bdrv(BlockJob *job); ++ ++/** + * block_job_set_speed: + * @job: The job to set the speed for. + * @speed: The new value +diff --git a/include/qemu/job.h b/include/qemu/job.h +index fdaa06f..407d549 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -156,6 +156,9 @@ typedef struct Job { + /** 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; + +diff --git a/job.c b/job.c +index db53163..5a0ccc7 100644 +--- a/job.c ++++ b/job.c +@@ -397,6 +397,11 @@ static void job_event_ready(Job *job) + notifier_list_notify(&job->on_ready, job); + } + ++static void job_event_idle(Job *job) ++{ ++ notifier_list_notify(&job->on_idle, job); ++} ++ + void job_enter_cond(Job *job, bool(*fn)(Job *job)) + { + if (!job_started(job)) { +@@ -442,6 +447,7 @@ static void coroutine_fn job_do_yield(Job *job, uint64_t ns) + timer_mod(&job->sleep_timer, ns); + } + job->busy = false; ++ job_event_idle(job); + job_unlock(); + qemu_coroutine_yield(); + +@@ -860,6 +866,7 @@ static void coroutine_fn job_co_entry(void *opaque) + assert(job && job->driver && job->driver->run); + job_pause_point(job); + job->ret = job->driver->run(job, &job->err); ++ job_event_idle(job); + job->deferred_to_main_loop = true; + aio_bh_schedule_oneshot(qemu_get_aio_context(), job_exit, job); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockjob-Wrappers-for-progress-counter-access.patch b/SOURCES/kvm-blockjob-Wrappers-for-progress-counter-access.patch new file mode 100644 index 0000000..eead363 --- /dev/null +++ b/SOURCES/kvm-blockjob-Wrappers-for-progress-counter-access.patch @@ -0,0 +1,311 @@ +From c8cf3cfff0a6fd9e059190a97d38b0d1a3223e8e Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:47:52 +0200 +Subject: [PATCH 23/89] blockjob: Wrappers for progress counter access + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-10-kwolf@redhat.com> +Patchwork-id: 81078 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 09/73] blockjob: Wrappers for progress counter access +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Block job drivers are not expected to mess with the internals of the +BlockJob object, so provide wrapper functions for one of the cases where +they still do it: Updating the progress counter. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +Reviewed-by: Max Reitz +Reviewed-by: John Snow +(cherry picked from commit 05df8a6a2b4e36e8d69de2130e616d5ac28e8837) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 22 +++++++++++++--------- + block/commit.c | 16 ++++++++-------- + block/mirror.c | 11 +++++------ + block/stream.c | 14 ++++++++------ + blockjob.c | 10 ++++++++++ + include/block/blockjob.h | 19 +++++++++++++++++++ + 6 files changed, 63 insertions(+), 29 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index 453cd62..5d95805 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -39,6 +39,7 @@ typedef struct BackupBlockJob { + BlockdevOnError on_source_error; + BlockdevOnError on_target_error; + CoRwlock flush_rwlock; ++ uint64_t len; + uint64_t bytes_read; + int64_t cluster_size; + bool compress; +@@ -118,7 +119,7 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job, + + trace_backup_do_cow_process(job, start); + +- n = MIN(job->cluster_size, job->common.len - start); ++ n = MIN(job->cluster_size, job->len - start); + + if (!bounce_buffer) { + bounce_buffer = blk_blockalign(blk, job->cluster_size); +@@ -159,7 +160,7 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job, + * offset field is an opaque progress value, it is not a disk offset. + */ + job->bytes_read += n; +- job->common.offset += n; ++ block_job_progress_update(&job->common, n); + } + + out: +@@ -261,7 +262,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp) + return; + } + +- len = DIV_ROUND_UP(backup_job->common.len, backup_job->cluster_size); ++ len = DIV_ROUND_UP(backup_job->len, backup_job->cluster_size); + hbitmap_set(backup_job->copy_bitmap, 0, len); + } + +@@ -420,8 +421,9 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) + bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size); + } + +- job->common.offset = job->common.len - +- hbitmap_count(job->copy_bitmap) * job->cluster_size; ++ /* TODO block_job_progress_set_remaining() would make more sense */ ++ block_job_progress_update(&job->common, ++ job->len - hbitmap_count(job->copy_bitmap) * job->cluster_size); + + bdrv_dirty_iter_free(dbi); + } +@@ -437,7 +439,9 @@ static void coroutine_fn backup_run(void *opaque) + QLIST_INIT(&job->inflight_reqs); + qemu_co_rwlock_init(&job->flush_rwlock); + +- nb_clusters = DIV_ROUND_UP(job->common.len, job->cluster_size); ++ nb_clusters = DIV_ROUND_UP(job->len, job->cluster_size); ++ block_job_progress_set_remaining(&job->common, job->len); ++ + job->copy_bitmap = hbitmap_alloc(nb_clusters, 0); + if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { + backup_incremental_init_copy_bitmap(job); +@@ -461,7 +465,7 @@ static void coroutine_fn backup_run(void *opaque) + ret = backup_run_incremental(job); + } else { + /* Both FULL and TOP SYNC_MODE's require copying.. */ +- for (offset = 0; offset < job->common.len; ++ for (offset = 0; offset < job->len; + offset += job->cluster_size) { + bool error_is_read; + int alloced = 0; +@@ -620,7 +624,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, + goto error; + } + +- /* job->common.len is fixed, so we can't allow resize */ ++ /* job->len is fixed, so we can't allow resize */ + job = block_job_create(job_id, &backup_job_driver, txn, bs, + BLK_PERM_CONSISTENT_READ, + BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | +@@ -676,7 +680,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, + /* Required permissions are already taken with target's blk_new() */ + block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, + &error_abort); +- job->common.len = len; ++ job->len = len; + + return &job->common; + +diff --git a/block/commit.c b/block/commit.c +index 1432bae..50b191c 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -146,21 +146,21 @@ static void coroutine_fn commit_run(void *opaque) + int64_t n = 0; /* bytes */ + void *buf = NULL; + int bytes_written = 0; +- int64_t base_len; ++ int64_t len, base_len; + +- ret = s->common.len = blk_getlength(s->top); +- +- if (s->common.len < 0) { ++ ret = len = blk_getlength(s->top); ++ if (len < 0) { + goto out; + } ++ block_job_progress_set_remaining(&s->common, len); + + ret = base_len = blk_getlength(s->base); + if (base_len < 0) { + goto out; + } + +- if (base_len < s->common.len) { +- ret = blk_truncate(s->base, s->common.len, PREALLOC_MODE_OFF, NULL); ++ if (base_len < len) { ++ ret = blk_truncate(s->base, len, PREALLOC_MODE_OFF, NULL); + if (ret) { + goto out; + } +@@ -168,7 +168,7 @@ static void coroutine_fn commit_run(void *opaque) + + buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE); + +- for (offset = 0; offset < s->common.len; offset += n) { ++ for (offset = 0; offset < len; offset += n) { + bool copy; + + /* Note that even when no rate limit is applied we need to yield +@@ -198,7 +198,7 @@ static void coroutine_fn commit_run(void *opaque) + } + } + /* Publish progress */ +- s->common.offset += n; ++ block_job_progress_update(&s->common, n); + + if (copy && s->common.speed) { + delay_ns = ratelimit_calculate_delay(&s->limit, n); +diff --git a/block/mirror.c b/block/mirror.c +index 003f957..ed711b5 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -121,7 +121,7 @@ static void mirror_iteration_done(MirrorOp *op, int ret) + bitmap_set(s->cow_bitmap, chunk_num, nb_chunks); + } + if (!s->initial_zeroing_ongoing) { +- s->common.offset += op->bytes; ++ block_job_progress_update(&s->common, op->bytes); + } + } + qemu_iovec_destroy(&op->qiov); +@@ -792,11 +792,10 @@ static void coroutine_fn mirror_run(void *opaque) + block_job_pause_point(&s->common); + + cnt = bdrv_get_dirty_count(s->dirty_bitmap); +- /* s->common.offset contains the number of bytes already processed so +- * far, 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 total operation length */ +- s->common.len = s->common.offset + s->bytes_in_flight + cnt; ++ /* 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 */ ++ block_job_progress_set_remaining(&s->common, s->bytes_in_flight + cnt); + + /* 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. +diff --git a/block/stream.c b/block/stream.c +index 1a85708..8369852 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -107,6 +107,7 @@ static void coroutine_fn stream_run(void *opaque) + BlockBackend *blk = s->common.blk; + BlockDriverState *bs = blk_bs(blk); + BlockDriverState *base = s->base; ++ int64_t len; + int64_t offset = 0; + uint64_t delay_ns = 0; + int error = 0; +@@ -118,11 +119,12 @@ static void coroutine_fn stream_run(void *opaque) + goto out; + } + +- s->common.len = bdrv_getlength(bs); +- if (s->common.len < 0) { +- ret = s->common.len; ++ len = bdrv_getlength(bs); ++ if (len < 0) { ++ ret = len; + goto out; + } ++ block_job_progress_set_remaining(&s->common, len); + + buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE); + +@@ -135,7 +137,7 @@ static void coroutine_fn stream_run(void *opaque) + bdrv_enable_copy_on_read(bs); + } + +- for ( ; offset < s->common.len; offset += n) { ++ for ( ; offset < len; offset += n) { + bool copy; + + /* Note that even when no rate limit is applied we need to yield +@@ -159,7 +161,7 @@ static void coroutine_fn stream_run(void *opaque) + + /* Finish early if end of backing file has been reached */ + if (ret == 0 && n == 0) { +- n = s->common.len - offset; ++ n = len - offset; + } + + copy = (ret == 1); +@@ -185,7 +187,7 @@ static void coroutine_fn stream_run(void *opaque) + ret = 0; + + /* Publish progress */ +- s->common.offset += n; ++ block_job_progress_update(&s->common, n); + if (copy && s->common.speed) { + delay_ns = ratelimit_calculate_delay(&s->limit, n); + } else { +diff --git a/blockjob.c b/blockjob.c +index 0033b96..d0a2ac5 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -818,6 +818,16 @@ int block_job_complete_sync(BlockJob *job, Error **errp) + return block_job_finish_sync(job, &block_job_complete, errp); + } + ++void block_job_progress_update(BlockJob *job, uint64_t done) ++{ ++ job->offset += done; ++} ++ ++void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining) ++{ ++ job->len = job->offset + remaining; ++} ++ + BlockJobInfo *block_job_query(BlockJob *job, Error **errp) + { + BlockJobInfo *info; +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index fc645da..a2cc522 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -278,6 +278,25 @@ void block_job_finalize(BlockJob *job, Error **errp); + void block_job_dismiss(BlockJob **job, Error **errp); + + /** ++ * block_job_progress_update: ++ * @job: The job that has made progress ++ * @done: How much progress the job made ++ * ++ * Updates the progress counter of the job. ++ */ ++void block_job_progress_update(BlockJob *job, uint64_t done); ++ ++/** ++ * block_job_progress_set_remaining: ++ * @job: The job whose expected progress end value is set ++ * @remaining: Expected end value of the progress counter of the job ++ * ++ * 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. ++ */ ++void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining); ++ ++/** + * block_job_query: + * @job: The job to get information about. + * +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockjob-do-not-cancel-timer-in-resume.patch b/SOURCES/kvm-blockjob-do-not-cancel-timer-in-resume.patch new file mode 100644 index 0000000..cf8b15c --- /dev/null +++ b/SOURCES/kvm-blockjob-do-not-cancel-timer-in-resume.patch @@ -0,0 +1,165 @@ +From c18e6451e1f5d1c060497f545a624ffdc9733fca Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:47:46 +0200 +Subject: [PATCH 17/89] blockjob: do not cancel timer in resume + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-4-kwolf@redhat.com> +Patchwork-id: 81089 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 03/73] blockjob: do not cancel timer in resume +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +From: Stefan Hajnoczi + +Currently the timer is cancelled and the block job is entered by +block_job_resume(). This behavior causes drain to run extra blockjob +iterations when the job was sleeping due to the ratelimit. + +This patch leaves the job asleep when block_job_resume() is called. +Jobs can still be forcibly woken up using block_job_enter(), which is +used to cancel jobs. + +After this patch drain no longer runs extra blockjob iterations. This +is the expected behavior that qemu-iotests 185 used to rely on. We +temporarily changed the 185 test output to make it pass for the QEMU +2.12 release but now it's time to address this issue. + +Cc: QingFeng Hao +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Eric Blake +Reviewed-by: QingFeng Hao +Message-id: 20180508135436.30140-3-stefanha@redhat.com +Reviewed-by: Jeff Cody +Signed-off-by: Jeff Cody +(cherry picked from commit 4c7e813ce964e230bb55cf4afc862ccb091ca3a3) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + blockjob.c | 22 +++++++++++++++------- + tests/qemu-iotests/185 | 5 +---- + tests/qemu-iotests/185.out | 12 +++++------- + 3 files changed, 21 insertions(+), 18 deletions(-) + +diff --git a/blockjob.c b/blockjob.c +index 27f957e..7064389 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -209,6 +209,18 @@ static void block_job_txn_del_job(BlockJob *job) + } + } + ++/* Assumes the block_job_mutex is held */ ++static bool block_job_timer_pending(BlockJob *job) ++{ ++ return timer_pending(&job->sleep_timer); ++} ++ ++/* Assumes the block_job_mutex is held */ ++static bool block_job_timer_not_pending(BlockJob *job) ++{ ++ return !block_job_timer_pending(job); ++} ++ + static void block_job_pause(BlockJob *job) + { + job->pause_count++; +@@ -221,7 +233,9 @@ static void block_job_resume(BlockJob *job) + if (job->pause_count) { + return; + } +- block_job_enter(job); ++ ++ /* kick only if no timer is pending */ ++ block_job_enter_cond(job, block_job_timer_not_pending); + } + + void block_job_ref(BlockJob *job) +@@ -651,12 +665,6 @@ static void block_job_completed_txn_success(BlockJob *job) + } + } + +-/* Assumes the block_job_mutex is held */ +-static bool block_job_timer_pending(BlockJob *job) +-{ +- return timer_pending(&job->sleep_timer); +-} +- + void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) + { + Error *local_err = NULL; +diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185 +index 975404c..9a2d317 100755 +--- a/tests/qemu-iotests/185 ++++ b/tests/qemu-iotests/185 +@@ -101,14 +101,11 @@ echo + # command to be received (after receiving the command, the rest runs + # synchronously, so jobs can arbitrarily continue or complete). + # +-# Jobs present while QEMU is terminating iterate once more due to +-# bdrv_drain_all(). +-# + # The buffer size for commit and streaming is 512k (waiting for 8 seconds after + # the first request), for active commit and mirror it's large enough to cover + # the full 4M, and for backup it's the qcow2 cluster size, which we know is + # 64k. As all of these are at least as large as the speed, we are sure that the +-# offset advances exactly twice before qemu exits. ++# offset advances exactly once before qemu exits. + + _send_qemu_cmd $h \ + "{ 'execute': 'block-commit', +diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out +index 992162f..57eaf8d 100644 +--- a/tests/qemu-iotests/185.out ++++ b/tests/qemu-iotests/185.out +@@ -20,7 +20,7 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q + {"return": {}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 1048576, "speed": 65536, "type": "commit"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 524288, "speed": 65536, "type": "commit"}} + + === Start active commit job and exit qemu === + +@@ -28,8 +28,7 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q + {"return": {}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "commit"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "commit"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "commit"}} + + === Start mirror job and exit qemu === + +@@ -38,8 +37,7 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l + {"return": {}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}} + + === Start backup job and exit qemu === + +@@ -48,7 +46,7 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l + {"return": {}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 131072, "speed": 65536, "type": "backup"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 65536, "speed": 65536, "type": "backup"}} + + === Start streaming job and exit qemu === + +@@ -56,6 +54,6 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l + {"return": {}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 1048576, "speed": 65536, "type": "stream"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 524288, "speed": 65536, "type": "stream"}} + No errors were found on the image. + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockjob-drop-block_job_pause-resume_all.patch b/SOURCES/kvm-blockjob-drop-block_job_pause-resume_all.patch new file mode 100644 index 0000000..9065eb5 --- /dev/null +++ b/SOURCES/kvm-blockjob-drop-block_job_pause-resume_all.patch @@ -0,0 +1,109 @@ +From 4dc02d3f416a291b48e803600302e69cbb6f92ff Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:47:49 +0200 +Subject: [PATCH 20/89] blockjob: drop block_job_pause/resume_all() + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-7-kwolf@redhat.com> +Patchwork-id: 81068 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 06/73] blockjob: drop block_job_pause/resume_all() +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +From: Stefan Hajnoczi + +Commit 8119334918e86f45877cfc139192d54f2449a239 ("block: Don't +block_job_pause_all() in bdrv_drain_all()") removed the only callers of +block_job_pause/resume_all(). + +Pausing and resuming now happens in child_job_drained_begin/end() so +it's no longer necessary to globally pause/resume jobs. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: John Snow +Reviewed-by: Alberto Garcia +Message-id: 20180424085240.5798-1-stefanha@redhat.com +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit 23d702d898bdd8e6772d83ea9789767ed589e17e) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + blockjob.c | 27 --------------------------- + include/block/blockjob_int.h | 14 -------------- + 2 files changed, 41 deletions(-) + +diff --git a/blockjob.c b/blockjob.c +index 7064389..b39d0f8 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -996,19 +996,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + return job; + } + +-void block_job_pause_all(void) +-{ +- BlockJob *job = NULL; +- while ((job = block_job_next(job))) { +- AioContext *aio_context = blk_get_aio_context(job->blk); +- +- aio_context_acquire(aio_context); +- block_job_ref(job); +- block_job_pause(job); +- aio_context_release(aio_context); +- } +-} +- + void block_job_early_fail(BlockJob *job) + { + assert(job->status == BLOCK_JOB_STATUS_CREATED); +@@ -1086,20 +1073,6 @@ void coroutine_fn block_job_pause_point(BlockJob *job) + } + } + +-void block_job_resume_all(void) +-{ +- BlockJob *job, *next; +- +- QLIST_FOREACH_SAFE(job, &block_jobs, job_list, next) { +- AioContext *aio_context = blk_get_aio_context(job->blk); +- +- aio_context_acquire(aio_context); +- block_job_resume(job); +- block_job_unref(job); +- aio_context_release(aio_context); +- } +-} +- + /* + * Conditionally enter a block_job pending a call to fn() while + * under the block_job_lock critical section. +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index 642adce..d5a515d 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -169,20 +169,6 @@ void block_job_sleep_ns(BlockJob *job, int64_t ns); + void block_job_yield(BlockJob *job); + + /** +- * block_job_pause_all: +- * +- * Asynchronously pause all jobs. +- */ +-void block_job_pause_all(void); +- +-/** +- * block_job_resume_all: +- * +- * Resume all block jobs. Must be paired with a preceding block_job_pause_all. +- */ +-void block_job_resume_all(void); +- +-/** + * block_job_early_fail: + * @bs: The block device. + * +-- +1.8.3.1 + diff --git a/SOURCES/kvm-blockjob-expose-error-string-via-query.patch b/SOURCES/kvm-blockjob-expose-error-string-via-query.patch new file mode 100644 index 0000000..23d006c --- /dev/null +++ b/SOURCES/kvm-blockjob-expose-error-string-via-query.patch @@ -0,0 +1,80 @@ +From 08fa409a1ec90e383103a12f3d06205ef5941062 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:47:50 +0200 +Subject: [PATCH 21/89] blockjob: expose error string via query + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-8-kwolf@redhat.com> +Patchwork-id: 81059 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 07/73] blockjob: expose error string via query +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +From: John Snow + +When we've reached the concluded state, we need to expose the error +state if applicable. Add the new field. + +This should be sufficient for determining if a job completed +successfully or not after concluding; if we want to discriminate +based on how it failed more mechanically, we can always add an +explicit return code enumeration later. + +I didn't bother to make it only show up if we are in the concluded +state; I don't think it's necessary. + +Cc: qemu-stable@nongnu.org +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Alberto Garcia +Signed-off-by: Kevin Wolf +(cherry picked from commit ab9ba614556ac5b0f8d96b99e0dba19f1e28d6c2) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + blockjob.c | 2 ++ + qapi/block-core.json | 6 +++++- + 2 files changed, 7 insertions(+), 1 deletion(-) + +diff --git a/blockjob.c b/blockjob.c +index b39d0f8..6746cad 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -839,6 +839,8 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) + info->status = job->status; + info->auto_finalize = job->auto_finalize; + info->auto_dismiss = job->auto_dismiss; ++ info->has_error = job->ret != 0; ++ info->error = job->ret ? g_strdup(strerror(-job->ret)) : NULL; + return info; + } + +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 5aac0c7..9012d00 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -1172,6 +1172,9 @@ + # @auto-dismiss: Job will dismiss itself when CONCLUDED, moving to the NULL + # state and disappearing from the query list. (since 2.12) + # ++# @error: Error information if the job did not complete successfully. ++# Not set if the job completed successfully. (since 2.12.1) ++# + # Since: 1.1 + ## + { 'struct': 'BlockJobInfo', +@@ -1179,7 +1182,8 @@ + 'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int', + 'io-status': 'BlockDeviceIoStatus', 'ready': 'bool', + 'status': 'BlockJobStatus', +- 'auto-finalize': 'bool', 'auto-dismiss': 'bool' } } ++ 'auto-finalize': 'bool', 'auto-dismiss': 'bool', ++ '*error': 'str' } } + + ## + # @query-block-jobs: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-ccid-Fix-dwProtocols-advertisement-of-T-0.patch b/SOURCES/kvm-ccid-Fix-dwProtocols-advertisement-of-T-0.patch new file mode 100644 index 0000000..b47f08b --- /dev/null +++ b/SOURCES/kvm-ccid-Fix-dwProtocols-advertisement-of-T-0.patch @@ -0,0 +1,66 @@ +From eb91002f4fb6ed6ae36034b178c5480caa5dccae Mon Sep 17 00:00:00 2001 +From: Maxim Levitsky +Date: Tue, 25 Jun 2019 13:30:12 +0200 +Subject: [PATCH 19/23] ccid: Fix dwProtocols advertisement of T=0 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Maxim Levitsky +Message-id: <20190625133012.8221-2-mlevitsk@redhat.com> +Patchwork-id: 88926 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/1] ccid: Fix dwProtocols advertisement of T=0 +Bugzilla: 1721522 +RH-Acked-by: John Snow +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Markus Armbruster + +From: Jason Andryuk + +Commit d7d218ef02d87c637d20d64da8f575d434ff6f78 attempted to change +dwProtocols to only advertise support for T=0 and not T=1. The change +was incorrect as it changed 0x00000003 to 0x00010000. + +lsusb -v in a linux guest shows: +"dwProtocols 65536 (Invalid values detected)", though the +smart card could still be accessed. Windows 7 does not detect inserted +smart cards and logs the the following Error in the Event Logs: + + Source: Smart Card Service + Event ID: 610 + Smart Card Reader 'QEMU QEMU USB CCID 0' rejected IOCTL SET_PROTOCOL: + Incorrect function. If this error persists, your smart card or reader + may not be functioning correctly + + Command Header: 03 00 00 00 + +Setting to 0x00000001 fixes the Windows issue. + +Signed-off-by: Jason Andryuk +Message-id: 20180420183219.20722-1-jandryuk@gmail.com +Cc: qemu-stable@nongnu.org +Signed-off-by: Gerd Hoffmann +(cherry picked from commit 0ee86bb6c5beb6498488850104f7557c376d0bef) +Signed-off-by: Miroslav Rezanina +--- + hw/usb/dev-smartcard-reader.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c +index 214d3e9..f745192 100644 +--- a/hw/usb/dev-smartcard-reader.c ++++ b/hw/usb/dev-smartcard-reader.c +@@ -329,8 +329,8 @@ static const uint8_t qemu_ccid_descriptor[] = { + */ + 0x07, /* u8 bVoltageSupport; 01h - 5.0v, 02h - 3.0, 03 - 1.8 */ + +- 0x00, 0x00, /* u32 dwProtocols; RRRR PPPP. RRRR = 0000h.*/ +- 0x01, 0x00, /* PPPP: 0001h = Protocol T=0, 0002h = Protocol T=1 */ ++ 0x01, 0x00, /* u32 dwProtocols; RRRR PPPP. RRRR = 0000h.*/ ++ 0x00, 0x00, /* PPPP: 0001h = Protocol T=0, 0002h = Protocol T=1 */ + /* u32 dwDefaultClock; in kHZ (0x0fa0 is 4 MHz) */ + 0xa0, 0x0f, 0x00, 0x00, + /* u32 dwMaximumClock; */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-ccid-card-passthru-fix-regression-in-realize.patch b/SOURCES/kvm-ccid-card-passthru-fix-regression-in-realize.patch new file mode 100644 index 0000000..73e4a39 --- /dev/null +++ b/SOURCES/kvm-ccid-card-passthru-fix-regression-in-realize.patch @@ -0,0 +1,51 @@ +From a0251ae2c973f3a5a103e7601fabed65cc24c1b0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= +Date: Mon, 11 Jun 2018 17:13:26 +0200 +Subject: [PATCH 2/9] ccid-card-passthru: fix regression in realize() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Marc-André Lureau +Message-id: <20180611171326.5043-1-marcandre.lureau@redhat.com> +Patchwork-id: 80632 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH] ccid-card-passthru: fix regression in realize() +Bugzilla: 1584984 +RH-Acked-by: Gerd Hoffmann +RH-Acked-by: Markus Armbruster +RH-Acked-by: Thomas Huth + +Since cc847bfd16d894fd8c1a2ce25f31772f6cdbbc74, CCID card-passthru +fails to intialize, because it changed a debug line to an error, +probably by mistake. Change it back to a DPRINTF debug. + +(solves Boxes creating VM with smartcard passthru failing to start) + +Signed-off-by: Marc-André Lureau +Reviewed-by: Philippe Mathieu-Daudé +Message-id: 20180515153039.27514-1-marcandre.lureau@redhat.com +Signed-off-by: Gerd Hoffmann + +(cherry picked from commit e58d64a16abc2304c4dcb644411eb9580bf63b1e) +Signed-off-by: Marc-André Lureau +Signed-off-by: Miroslav Rezanina +--- + hw/usb/ccid-card-passthru.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c +index b7dd360..668a22d 100644 +--- a/hw/usb/ccid-card-passthru.c ++++ b/hw/usb/ccid-card-passthru.c +@@ -345,7 +345,7 @@ static void passthru_realize(CCIDCardState *base, Error **errp) + card->vscard_in_pos = 0; + card->vscard_in_hdr = 0; + if (qemu_chr_fe_backend_connected(&card->cs)) { +- error_setg(errp, "ccid-card-passthru: initing chardev"); ++ DPRINTF(card, D_INFO, "ccid-card-passthru: initing chardev"); + qemu_chr_fe_set_handlers(&card->cs, + ccid_card_vscard_can_read, + ccid_card_vscard_read, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-check-block-qdict-Cover-flattening-of-empty-lists-an.patch b/SOURCES/kvm-check-block-qdict-Cover-flattening-of-empty-lists-an.patch new file mode 100644 index 0000000..0190a2b --- /dev/null +++ b/SOURCES/kvm-check-block-qdict-Cover-flattening-of-empty-lists-an.patch @@ -0,0 +1,76 @@ +From b90ea90ed89d27cadc704b664e6ca26ea32fa95d Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:27 +0200 +Subject: [PATCH 20/54] check-block-qdict: Cover flattening of empty lists and + dictionaries + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-21-armbru@redhat.com> +Patchwork-id: 80734 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 20/23] check-block-qdict: Cover flattening of empty lists and dictionaries +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Signed-off-by: Markus Armbruster +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit bef96b1549907b005ce1fa1456d2a0910d2a1aa5) +Signed-off-by: Miroslav Rezanina +--- + tests/check-block-qdict.c | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c +index 29f58a2..2da16f0 100644 +--- a/tests/check-block-qdict.c ++++ b/tests/check-block-qdict.c +@@ -41,6 +41,8 @@ static void qdict_flatten_test(void) + QList *e = qlist_new(); + QDict *e_1_2 = qdict_new(); + QDict *f = qdict_new(); ++ QList *y = qlist_new(); ++ QDict *z = qdict_new(); + QDict *root = qdict_new(); + + /* +@@ -62,7 +64,9 @@ static void qdict_flatten_test(void) + * "c": 2, + * "d": 3, + * }, +- * "g": 4 ++ * "g": 4, ++ * "y": [{}], ++ * "z": {"a": []} + * } + * + * to +@@ -77,6 +81,8 @@ static void qdict_flatten_test(void) + * "f.d": 3, + * "g": 4 + * } ++ * ++ * Note that "y" and "z" get eaten. + */ + + qdict_put_int(e_1_2, "a", 0); +@@ -91,9 +97,15 @@ static void qdict_flatten_test(void) + qdict_put_int(f, "c", 2); + qdict_put_int(f, "d", 3); + ++ qlist_append(y, qdict_new()); ++ ++ qdict_put(z, "a", qlist_new()); ++ + qdict_put(root, "e", e); + qdict_put(root, "f", f); + qdict_put_int(root, "g", 4); ++ qdict_put(root, "y", y); ++ qdict_put(root, "z", z); + + qdict_flatten(root); + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-check-block-qdict-Rename-qdict_flatten-s-variables-f.patch b/SOURCES/kvm-check-block-qdict-Rename-qdict_flatten-s-variables-f.patch new file mode 100644 index 0000000..aa90a2e --- /dev/null +++ b/SOURCES/kvm-check-block-qdict-Rename-qdict_flatten-s-variables-f.patch @@ -0,0 +1,108 @@ +From 34251ed7b01f81a0e62fa56548db657a5c238125 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:26 +0200 +Subject: [PATCH 19/54] check-block-qdict: Rename qdict_flatten()'s variables + for clarity + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-20-armbru@redhat.com> +Patchwork-id: 80721 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 19/23] check-block-qdict: Rename qdict_flatten()'s variables for clarity +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Signed-off-by: Markus Armbruster +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit cddec036830ada5d5d45023bcfba09015b8ab394) +Signed-off-by: Miroslav Rezanina +--- + tests/check-block-qdict.c | 57 ++++++++++++++++++++++++----------------------- + 1 file changed, 29 insertions(+), 28 deletions(-) + +diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c +index 5b9f4d5..29f58a2 100644 +--- a/tests/check-block-qdict.c ++++ b/tests/check-block-qdict.c +@@ -37,11 +37,11 @@ static void qdict_defaults_test(void) + + static void qdict_flatten_test(void) + { +- QList *list1 = qlist_new(); +- QList *list2 = qlist_new(); +- QDict *dict1 = qdict_new(); +- QDict *dict2 = qdict_new(); +- QDict *dict3 = qdict_new(); ++ QList *e_1 = qlist_new(); ++ QList *e = qlist_new(); ++ QDict *e_1_2 = qdict_new(); ++ QDict *f = qdict_new(); ++ QDict *root = qdict_new(); + + /* + * Test the flattening of +@@ -79,35 +79,36 @@ static void qdict_flatten_test(void) + * } + */ + +- qdict_put_int(dict1, "a", 0); +- qdict_put_int(dict1, "b", 1); ++ qdict_put_int(e_1_2, "a", 0); ++ qdict_put_int(e_1_2, "b", 1); + +- qlist_append_int(list1, 23); +- qlist_append_int(list1, 66); +- qlist_append(list1, dict1); +- qlist_append_int(list2, 42); +- qlist_append(list2, list1); ++ qlist_append_int(e_1, 23); ++ qlist_append_int(e_1, 66); ++ qlist_append(e_1, e_1_2); ++ qlist_append_int(e, 42); ++ qlist_append(e, e_1); + +- qdict_put_int(dict2, "c", 2); +- qdict_put_int(dict2, "d", 3); +- qdict_put(dict3, "e", list2); +- qdict_put(dict3, "f", dict2); +- qdict_put_int(dict3, "g", 4); ++ qdict_put_int(f, "c", 2); ++ qdict_put_int(f, "d", 3); + +- qdict_flatten(dict3); ++ qdict_put(root, "e", e); ++ qdict_put(root, "f", f); ++ qdict_put_int(root, "g", 4); + +- g_assert(qdict_get_int(dict3, "e.0") == 42); +- g_assert(qdict_get_int(dict3, "e.1.0") == 23); +- g_assert(qdict_get_int(dict3, "e.1.1") == 66); +- g_assert(qdict_get_int(dict3, "e.1.2.a") == 0); +- g_assert(qdict_get_int(dict3, "e.1.2.b") == 1); +- g_assert(qdict_get_int(dict3, "f.c") == 2); +- g_assert(qdict_get_int(dict3, "f.d") == 3); +- g_assert(qdict_get_int(dict3, "g") == 4); ++ qdict_flatten(root); + +- g_assert(qdict_size(dict3) == 8); ++ g_assert(qdict_get_int(root, "e.0") == 42); ++ g_assert(qdict_get_int(root, "e.1.0") == 23); ++ g_assert(qdict_get_int(root, "e.1.1") == 66); ++ g_assert(qdict_get_int(root, "e.1.2.a") == 0); ++ g_assert(qdict_get_int(root, "e.1.2.b") == 1); ++ g_assert(qdict_get_int(root, "f.c") == 2); ++ g_assert(qdict_get_int(root, "f.d") == 3); ++ g_assert(qdict_get_int(root, "g") == 4); + +- qobject_unref(dict3); ++ g_assert(qdict_size(root) == 8); ++ ++ qobject_unref(root); + } + + static void qdict_array_split_test(void) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-commit-Add-top-node-base-node-options.patch b/SOURCES/kvm-commit-Add-top-node-base-node-options.patch new file mode 100644 index 0000000..347b256 --- /dev/null +++ b/SOURCES/kvm-commit-Add-top-node-base-node-options.patch @@ -0,0 +1,138 @@ +From 1ac2f161e5585d1ed71b79e8279c9d78b2efe35e Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Mon, 17 Sep 2018 07:48:43 +0200 +Subject: [PATCH 01/49] commit: Add top-node/base-node options + +RH-Author: Kevin Wolf +Message-id: <20180911130704.6641-2-kwolf@redhat.com> +Patchwork-id: 82114 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/2] commit: Add top-node/base-node options +Bugzilla: 1624012 +RH-Acked-by: Max Reitz +RH-Acked-by: John Snow +RH-Acked-by: Fam Zheng + +The block-commit QMP command required specifying the top and base nodes +of the commit jobs using the file name of that node. While this works +in simple cases (local files with absolute paths), the file names +generated for more complicated setups can be hard to predict. + +The block-commit command has more problems than just this, so we want to +replace it altogether in the long run, but libvirt needs a reliable way +to address nodes now. So we don't want to wait for a new, cleaner +command, but just add the minimal thing needed right now. + +This adds two new options top-node and base-node to the command, which +allow specifying node names instead. They are mutually exclusive with +the old options. + +Signed-off-by: Kevin Wolf +--- + blockdev.c | 32 ++++++++++++++++++++++++++++++-- + qapi/block-core.json | 24 ++++++++++++++++++------ + 2 files changed, 48 insertions(+), 8 deletions(-) + +diff --git a/blockdev.c b/blockdev.c +index 0eb6bba..fb355c8 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3371,7 +3371,9 @@ out: + } + + void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, ++ bool has_base_node, const char *base_node, + bool has_base, const char *base, ++ bool has_top_node, const char *top_node, + bool has_top, const char *top, + bool has_backing_file, const char *backing_file, + bool has_speed, int64_t speed, +@@ -3432,7 +3434,20 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, + /* default top_bs is the active layer */ + top_bs = bs; + +- if (has_top && top) { ++ if (has_top_node && has_top) { ++ error_setg(errp, "'top-node' and 'top' are mutually exclusive"); ++ goto out; ++ } else if (has_top_node) { ++ top_bs = bdrv_lookup_bs(NULL, top_node, errp); ++ if (top_bs == NULL) { ++ goto out; ++ } ++ if (!bdrv_chain_contains(bs, top_bs)) { ++ error_setg(errp, "'%s' is not in this backing file chain", ++ top_node); ++ goto out; ++ } ++ } else if (has_top && top) { + if (strcmp(bs->filename, top) != 0) { + top_bs = bdrv_find_backing_image(bs, top); + } +@@ -3445,7 +3460,20 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, + + assert(bdrv_get_aio_context(top_bs) == aio_context); + +- if (has_base && base) { ++ if (has_base_node && has_base) { ++ error_setg(errp, "'base-node' and 'base' are mutually exclusive"); ++ goto out; ++ } else if (has_base_node) { ++ base_bs = bdrv_lookup_bs(NULL, base_node, errp); ++ if (base_bs == NULL) { ++ goto out; ++ } ++ if (!bdrv_chain_contains(top_bs, base_bs)) { ++ error_setg(errp, "'%s' is not in this backing file chain", ++ base_node); ++ goto out; ++ } ++ } else if (has_base && base) { + base_bs = bdrv_find_backing_image(top_bs, base); + } else { + base_bs = bdrv_find_base(top_bs); +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 56937db..8da07cd 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -1440,12 +1440,23 @@ + # + # @device: the device name or node-name of a root node + # +-# @base: The file name of the backing image to write data into. +-# If not specified, this is the deepest backing image. ++# @base-node: The node name of the backing image to write data into. ++# If not specified, this is the deepest backing image. ++# (since: 3.1) + # +-# @top: The file name of the backing image within the image chain, +-# which contains the topmost data to be committed down. If +-# not specified, this is the active layer. ++# @base: Same as @base-node, except that it is a file name rather than a node ++# name. This must be the exact filename string that was used to open the ++# node; other strings, even if addressing the same file, are not ++# accepted (deprecated, use @base-node instead) ++# ++# @top-node: The node name of the backing image within the image chain ++# which contains the topmost data to be committed down. If ++# not specified, this is the active layer. (since: 3.1) ++# ++# @top: Same as @top-node, except that it is a file name rather than a node ++# name. This must be the exact filename string that was used to open the ++# node; other strings, even if addressing the same file, are not ++# accepted (deprecated, use @base-node instead) + # + # @backing-file: The backing file string to write into the overlay + # image of 'top'. If 'top' is the active layer, +@@ -1514,7 +1525,8 @@ + # + ## + { 'command': 'block-commit', +- 'data': { '*job-id': 'str', 'device': 'str', '*base': 'str', '*top': 'str', ++ 'data': { '*job-id': 'str', 'device': 'str', '*base-node': 'str', ++ '*base': 'str', '*top-node': 'str', '*top': 'str', + '*backing-file': 'str', '*speed': 'int', + '*filter-node-name': 'str', + '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-configure-add-libpmem-support.patch b/SOURCES/kvm-configure-add-libpmem-support.patch new file mode 100644 index 0000000..57bd26c --- /dev/null +++ b/SOURCES/kvm-configure-add-libpmem-support.patch @@ -0,0 +1,130 @@ +From 17e8fa1ceb357d0008f651cd88a72f2630b24fc5 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Fri, 31 Aug 2018 16:25:55 +0200 +Subject: [PATCH 13/29] configure: add libpmem support + +RH-Author: plai@redhat.com +Message-id: <1535732759-22481-6-git-send-email-plai@redhat.com> +Patchwork-id: 82010 +O-Subject: [RHEL7.6 PATCH BZ 1539280 5/9] configure: add libpmem support +Bugzilla: 1539280 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Pankaj Gupta +RH-Acked-by: Miroslav Rezanina + +From: Junyan He + +Add a pair of configure options --{enable,disable}-libpmem to control +whether QEMU is compiled with PMDK libpmem [1]. + +QEMU may write to the host persistent memory (e.g. in vNVDIMM label +emulation and live migration), so it must take the proper operations +to ensure the persistence of its own writes. Depending on the CPU +models and available instructions, the optimal operation can vary [2]. +PMDK libpmem have already implemented those operations on multiple CPU +models (x86 and ARM) and the logic to select the optimal ones, so QEMU +can just use libpmem rather than re-implement them. + +Libpem is a part of PMDK project(formerly known as NMVL). +The project's home page is: http://pmem.io/pmdk/ +And the project's repository is: https://github.com/pmem/pmdk/ + +For more information about libpmem APIs, you can refer to the comments +in source code of: pmdk/src/libpmem/pmem.c, begin at line 33. + +Signed-off-by: Junyan He +Signed-off-by: Haozhong Zhang +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Igor Mammedov +Reviewed-by: Richard Henderson +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 17824406fa55b303379f2e4af715c1e876c3535f) +Signed-off-by: Paul Lai + +Resolved Conflicts: + configure + +Signed-off-by: Miroslav Rezanina +--- + configure | 29 +++++++++++++++++++++++++++++ + 1 file changed, 29 insertions(+) + +diff --git a/configure b/configure +index a869f19..f9c8365 100755 +--- a/configure ++++ b/configure +@@ -454,6 +454,7 @@ vxhs="" + libxml2="" + vtd="yes" + rhel_target="rhv" ++libpmem="" + + supported_cpu="no" + supported_os="no" +@@ -1387,6 +1388,10 @@ for opt do + ;; + --rhel-target=*) rhel_target="$optarg" + ;; ++ --enable-libpmem) libpmem=yes ++ ;; ++ --disable-libpmem) libpmem=no ++ ;; + *) + echo "ERROR: unknown option $opt" + echo "Try '$0 --help' for more information" +@@ -1648,6 +1653,7 @@ disabled with --disable-FEATURE, default is enabled if available: + crypto-afalg Linux AF_ALG crypto backend driver + vhost-user vhost-user support + capstone capstone disassembler support ++ libpmem libpmem support + + NOTE: The object files are built at the place where configure is launched + EOF +@@ -5410,6 +5416,24 @@ EOF + fi + + ########################################## ++# check for libpmem ++ ++if test "$libpmem" != "no"; then ++ if $pkg_config --exists "libpmem"; then ++ libpmem="yes" ++ libpmem_libs=$($pkg_config --libs libpmem) ++ libpmem_cflags=$($pkg_config --cflags libpmem) ++ libs_softmmu="$libs_softmmu $libpmem_libs" ++ QEMU_CFLAGS="$QEMU_CFLAGS $libpmem_cflags" ++ else ++ if test "$libpmem" = "yes" ; then ++ feature_not_found "libpmem" "Install nvml or pmdk" ++ fi ++ libpmem="no" ++ fi ++fi ++ ++########################################## + # End of CC checks + # After here, no more $cc or $ld runs + +@@ -5873,6 +5897,7 @@ echo "VxHS block device $vxhs" + echo "capstone $capstone" + echo "VT-d emulation $vtd" + echo "RHEL target $rhel_target" ++echo "libpmem support $libpmem" + + if test "$sdl_too_old" = "yes"; then + echo "-> Your SDL version is too old - please upgrade to have SDL support" +@@ -6632,6 +6657,10 @@ if test "$rhel_target" = "rhv" ; then + echo "CONFIG_RHV=y" >> $config_host_mak + fi + ++if test "$libpmem" = "yes" ; then ++ echo "CONFIG_LIBPMEM=y" >> $config_host_mak ++fi ++ + if test "$tcg_interpreter" = "yes"; then + QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/tci $QEMU_INCLUDES" + elif test "$ARCH" = "sparc64" ; then +-- +1.8.3.1 + diff --git a/SOURCES/kvm-cpus-Fix-event-order-on-resume-of-stopped-guest.patch b/SOURCES/kvm-cpus-Fix-event-order-on-resume-of-stopped-guest.patch new file mode 100644 index 0000000..b9d8fae --- /dev/null +++ b/SOURCES/kvm-cpus-Fix-event-order-on-resume-of-stopped-guest.patch @@ -0,0 +1,149 @@ +From f94e1c832c510a3ca1b8bf0d383e7be2dce4909c Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Wed, 9 May 2018 14:42:21 +0200 +Subject: [PATCH 13/13] cpus: Fix event order on resume of stopped guest + +RH-Author: Markus Armbruster +Message-id: <20180509144221.14799-2-armbru@redhat.com> +Patchwork-id: 80191 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/1] cpus: Fix event order on resume of stopped guest +Bugzilla: 1566153 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Igor Mammedov + +When resume of a stopped guest immediately runs into block device +errors, the BLOCK_IO_ERROR event is sent before the RESUME event. + +Reproducer: + +1. Create a scratch image + $ dd if=/dev/zero of=scratch.img bs=1M count=100 + + Size doesn't actually matter. + +2. Prepare blkdebug configuration: + + $ cat >blkdebug.conf < ' + + Issue QMP command 'qmp_capabilities': + QMP> { "execute": "qmp_capabilities" } + +5. Boot the guest. + +6. In the guest, write to the scratch disk, e.g. like this: + + # dd if=/dev/zero of=/dev/vdb count=1 + + Do double-check the device specified with of= is actually the + scratch device! + +7. Issue QMP command 'cont': + QMP> { "execute": "cont" } + +After step 6, I get a BLOCK_IO_ERROR event followed by a STOP event. Good. + +After step 7, I get BLOCK_IO_ERROR, then RESUME, then STOP. Not so +good; I'd expect RESUME, then BLOCK_IO_ERROR, then STOP. + +The funny event order confuses libvirt: virsh -r domstate DOMAIN +--reason reports "paused (unknown)" rather than "paused (I/O error)". + +The culprit is vm_prepare_start(). + + /* 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(&error_abort); + res = -1; + } else { + replay_enable_events(); + cpu_enable_ticks(); + runstate_set(RUN_STATE_RUNNING); + vm_state_notify(1, RUN_STATE_RUNNING); + } + + /* We are sending this now, but the CPUs will be resumed shortly later */ + qapi_event_send_resume(&error_abort); + return res; + +When resuming a stopped guest, we take the else branch before we get +to sending RESUME. vm_state_notify() runs virtio_vmstate_change(), +among other things. This restarts I/O, triggering the BLOCK_IO_ERROR +event. + +Reshuffle vm_prepare_start() to send the RESUME event earlier. + +Fixes RHBZ 1566153. + +Cc: Paolo Bonzini +Signed-off-by: Markus Armbruster +Message-Id: <20180423084518.2426-1-armbru@redhat.com> +Signed-off-by: Paolo Bonzini +(cherry picked from commit f056158d694d2adc63ff120ca71c73ae8b14426c) +Signed-off-by: Miroslav Rezanina +--- + cpus.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/cpus.c b/cpus.c +index 38eba8b..398392b 100644 +--- a/cpus.c ++++ b/cpus.c +@@ -2043,7 +2043,6 @@ int vm_stop(RunState state) + int vm_prepare_start(void) + { + RunState requested; +- int res = 0; + + qemu_vmstop_requested(&requested); + if (runstate_is_running() && requested == RUN_STATE__MAX) { +@@ -2057,17 +2056,18 @@ int vm_prepare_start(void) + */ + if (runstate_is_running()) { + qapi_event_send_stop(&error_abort); +- res = -1; +- } else { +- replay_enable_events(); +- cpu_enable_ticks(); +- runstate_set(RUN_STATE_RUNNING); +- vm_state_notify(1, RUN_STATE_RUNNING); ++ qapi_event_send_resume(&error_abort); ++ return -1; + } + + /* We are sending this now, but the CPUs will be resumed shortly later */ + qapi_event_send_resume(&error_abort); +- return res; ++ ++ replay_enable_events(); ++ cpu_enable_ticks(); ++ runstate_set(RUN_STATE_RUNNING); ++ vm_state_notify(1, RUN_STATE_RUNNING); ++ return 0; + } + + void vm_start(void) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-cpus-ignore-ESRCH-in-qemu_cpu_kick_thread.patch b/SOURCES/kvm-cpus-ignore-ESRCH-in-qemu_cpu_kick_thread.patch new file mode 100644 index 0000000..23a69c4 --- /dev/null +++ b/SOURCES/kvm-cpus-ignore-ESRCH-in-qemu_cpu_kick_thread.patch @@ -0,0 +1,55 @@ +From 9e6a36459015ac8ac3c8e550bd2242c127203370 Mon Sep 17 00:00:00 2001 +From: Laurent Vivier +Date: Wed, 23 Jan 2019 08:48:57 +0100 +Subject: [PATCH 4/8] cpus: ignore ESRCH in qemu_cpu_kick_thread() + +RH-Author: Laurent Vivier +Message-id: <20190123084857.16841-1-lvivier@redhat.com> +Patchwork-id: 84094 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH] cpus: ignore ESRCH in qemu_cpu_kick_thread() +Bugzilla: 1614610 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Serhii Popovych +RH-Acked-by: Thomas Huth + +We can have a race condition between qemu_cpu_kick_thread() and +qemu_kvm_cpu_thread_fn() when we hotunplug a CPU. In this case, +qemu_cpu_kick_thread() can try to kick a thread that is exiting. +pthread_kill() returns an error and qemu is stopped by an exit(1). + + qemu:qemu_cpu_kick_thread: No such process + +We can ignore safely this error. + +Signed-off-by: Laurent Vivier +Signed-off-by: Paolo Bonzini +(cherry picked from commit e9979ef245549b8e1fd240ec9937271c7fda0b57) + +BZ: https://bugzilla.redhat.com/show_bug.cgi?id=1614610 +BREW: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=19894361 +UPSTREAM: In maintainer pull request + git://github.com/bonzini/qemu.git tags/for-upstream + 3cf01054d896fa88ea0dd31c5abb605c2e68bb29 +TEST: Upstream version tested by QE + +Signed-off-by: Miroslav Rezanina +--- + cpus.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/cpus.c b/cpus.c +index be3a4eb..f9798e3 100644 +--- a/cpus.c ++++ b/cpus.c +@@ -1700,7 +1700,7 @@ static void qemu_cpu_kick_thread(CPUState *cpu) + } + cpu->thread_kicked = true; + err = pthread_kill(cpu->thread->thread, SIG_IPI); +- if (err) { ++ if (err && err != ESRCH) { + fprintf(stderr, "qemu:%s: %s", __func__, strerror(err)); + exit(1); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-crypto-add-testing-for-unaligned-buffers-with-XTS-ci.patch b/SOURCES/kvm-crypto-add-testing-for-unaligned-buffers-with-XTS-ci.patch new file mode 100644 index 0000000..bd9a534 --- /dev/null +++ b/SOURCES/kvm-crypto-add-testing-for-unaligned-buffers-with-XTS-ci.patch @@ -0,0 +1,136 @@ +From 97c4305758431f95fb64841dfec1b589e42de11f Mon Sep 17 00:00:00 2001 +From: "Daniel P. Berrange" +Date: Wed, 24 Apr 2019 10:30:30 +0200 +Subject: [PATCH 11/12] crypto: add testing for unaligned buffers with XTS + cipher mode +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Daniel P. Berrange +Message-id: <20190424103030.2925-10-berrange@redhat.com> +Patchwork-id: 85896 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 9/9] crypto: add testing for unaligned buffers with XTS cipher mode +Bugzilla: 1666336 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: John Snow +RH-Acked-by: Eric Blake + +Validate that the XTS cipher mode will correctly operate with plain +text, cipher text and IV buffers that are not 64-bit aligned. + +Reviewed-by: Alberto Garcia +Signed-off-by: Daniel P. Berrangé +(cherry picked from commit 1e0fa32c6c952d2ce9c19d35717c609804dd55d5) +Signed-off-by: Miroslav Rezanina +--- + tests/test-crypto-xts.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 86 insertions(+) + +diff --git a/tests/test-crypto-xts.c b/tests/test-crypto-xts.c +index 81606d9..6fb61cf 100644 +--- a/tests/test-crypto-xts.c ++++ b/tests/test-crypto-xts.c +@@ -416,6 +416,88 @@ static void test_xts_split(const void *opaque) + } + + ++static void test_xts_unaligned(const void *opaque) ++{ ++#define BAD_ALIGN 3 ++ const QCryptoXTSTestData *data = opaque; ++ uint8_t in[512 + BAD_ALIGN], out[512 + BAD_ALIGN]; ++ uint8_t Torg[16], T[16 + BAD_ALIGN]; ++ uint64_t seq; ++ struct TestAES aesdata; ++ struct TestAES aestweak; ++ ++ AES_set_encrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.enc); ++ AES_set_decrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.dec); ++ AES_set_encrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.enc); ++ AES_set_decrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.dec); ++ ++ seq = data->seqnum; ++ STORE64L(seq, Torg); ++ memset(Torg + 8, 0, 8); ++ ++ /* IV not aligned */ ++ memcpy(T + BAD_ALIGN, Torg, 16); ++ memcpy(in, data->PTX, data->PTLEN); ++ xts_encrypt(&aesdata, &aestweak, ++ test_xts_aes_encrypt, ++ test_xts_aes_decrypt, ++ T + BAD_ALIGN, data->PTLEN, out, in); ++ ++ g_assert(memcmp(out, data->CTX, data->PTLEN) == 0); ++ ++ /* plain text not aligned */ ++ memcpy(T, Torg, 16); ++ memcpy(in + BAD_ALIGN, data->PTX, data->PTLEN); ++ xts_encrypt(&aesdata, &aestweak, ++ test_xts_aes_encrypt, ++ test_xts_aes_decrypt, ++ T, data->PTLEN, out, in + BAD_ALIGN); ++ ++ g_assert(memcmp(out, data->CTX, data->PTLEN) == 0); ++ ++ /* cipher text not aligned */ ++ memcpy(T, Torg, 16); ++ memcpy(in, data->PTX, data->PTLEN); ++ xts_encrypt(&aesdata, &aestweak, ++ test_xts_aes_encrypt, ++ test_xts_aes_decrypt, ++ T, data->PTLEN, out + BAD_ALIGN, in); ++ ++ g_assert(memcmp(out + BAD_ALIGN, data->CTX, data->PTLEN) == 0); ++ ++ ++ /* IV not aligned */ ++ memcpy(T + BAD_ALIGN, Torg, 16); ++ memcpy(in, data->CTX, data->PTLEN); ++ xts_decrypt(&aesdata, &aestweak, ++ test_xts_aes_encrypt, ++ test_xts_aes_decrypt, ++ T + BAD_ALIGN, data->PTLEN, out, in); ++ ++ g_assert(memcmp(out, data->PTX, data->PTLEN) == 0); ++ ++ /* cipher text not aligned */ ++ memcpy(T, Torg, 16); ++ memcpy(in + BAD_ALIGN, data->CTX, data->PTLEN); ++ xts_decrypt(&aesdata, &aestweak, ++ test_xts_aes_encrypt, ++ test_xts_aes_decrypt, ++ T, data->PTLEN, out, in + BAD_ALIGN); ++ ++ g_assert(memcmp(out, data->PTX, data->PTLEN) == 0); ++ ++ /* plain text not aligned */ ++ memcpy(T, Torg, 16); ++ memcpy(in, data->CTX, data->PTLEN); ++ xts_decrypt(&aesdata, &aestweak, ++ test_xts_aes_encrypt, ++ test_xts_aes_decrypt, ++ T, data->PTLEN, out + BAD_ALIGN, in); ++ ++ g_assert(memcmp(out + BAD_ALIGN, data->PTX, data->PTLEN) == 0); ++} ++ ++ + int main(int argc, char **argv) + { + size_t i; +@@ -437,6 +519,10 @@ int main(int argc, char **argv) + g_test_add_data_func(path, &test_data[i], test_xts_split); + g_free(path); + } ++ ++ path = g_strdup_printf("%s/unaligned", test_data[i].path); ++ g_test_add_data_func(path, &test_data[i], test_xts_unaligned); ++ g_free(path); + } + + return g_test_run(); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-crypto-annotate-xts_tweak_encdec-as-inlineable.patch b/SOURCES/kvm-crypto-annotate-xts_tweak_encdec-as-inlineable.patch new file mode 100644 index 0000000..29bd77c --- /dev/null +++ b/SOURCES/kvm-crypto-annotate-xts_tweak_encdec-as-inlineable.patch @@ -0,0 +1,55 @@ +From a728b4ea94c406513e3d3167ba74c7b3314df3ce Mon Sep 17 00:00:00 2001 +From: "Daniel P. Berrange" +Date: Wed, 24 Apr 2019 10:30:28 +0200 +Subject: [PATCH 09/12] crypto: annotate xts_tweak_encdec as inlineable +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Daniel P. Berrange +Message-id: <20190424103030.2925-8-berrange@redhat.com> +Patchwork-id: 85893 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 7/9] crypto: annotate xts_tweak_encdec as inlineable +Bugzilla: 1666336 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: John Snow +RH-Acked-by: Eric Blake + +Encouraging the compiler to inline xts_tweak_encdec increases the +performance for xts-aes-128 when built with gcrypt: + + Encrypt: 545 MB/s -> 580 MB/s + Decrypt: 568 MB/s -> 602 MB/s + +Reviewed-by: Alberto Garcia +Signed-off-by: Daniel P. Berrangé +(cherry picked from commit aa895bd439341a8f218d8f1a3d21359ba058c13f) +Signed-off-by: Miroslav Rezanina +--- + crypto/xts.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/crypto/xts.c b/crypto/xts.c +index 10ec83f..4277ad4 100644 +--- a/crypto/xts.c ++++ b/crypto/xts.c +@@ -81,11 +81,11 @@ static void xts_mult_x(xts_uint128 *I) + * + * Encrypt/decrypt data with a tweak + */ +-static void xts_tweak_encdec(const void *ctx, +- xts_cipher_func *func, +- const xts_uint128 *src, +- xts_uint128 *dst, +- xts_uint128 *iv) ++static inline void xts_tweak_encdec(const void *ctx, ++ xts_cipher_func *func, ++ const xts_uint128 *src, ++ xts_uint128 *dst, ++ xts_uint128 *iv) + { + /* tweak encrypt block i */ + xts_uint128_xor(dst, src, iv); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-crypto-convert-xts_mult_x-to-use-xts_uint128-type.patch b/SOURCES/kvm-crypto-convert-xts_mult_x-to-use-xts_uint128-type.patch new file mode 100644 index 0000000..df3adf1 --- /dev/null +++ b/SOURCES/kvm-crypto-convert-xts_mult_x-to-use-xts_uint128-type.patch @@ -0,0 +1,109 @@ +From dfbbe38a74bec140652071dce9dad71514ec7091 Mon Sep 17 00:00:00 2001 +From: "Daniel P. Berrange" +Date: Wed, 24 Apr 2019 10:30:27 +0200 +Subject: [PATCH 08/12] crypto: convert xts_mult_x to use xts_uint128 type +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Daniel P. Berrange +Message-id: <20190424103030.2925-7-berrange@redhat.com> +Patchwork-id: 85895 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 6/9] crypto: convert xts_mult_x to use xts_uint128 type +Bugzilla: 1666336 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: John Snow +RH-Acked-by: Eric Blake + +Using 64-bit arithmetic increases the performance for xts-aes-128 +when built with gcrypt: + + Encrypt: 355 MB/s -> 545 MB/s + Decrypt: 362 MB/s -> 568 MB/s + +Reviewed-by: Alberto Garcia +Signed-off-by: Daniel P. Berrangé +(cherry picked from commit 7dac0dd67426753646df0c23c819609b9e704f59) +Signed-off-by: Miroslav Rezanina +--- + crypto/xts.c | 40 ++++++++++++++++++++++++++++------------ + 1 file changed, 28 insertions(+), 12 deletions(-) + +diff --git a/crypto/xts.c b/crypto/xts.c +index 0ad231f..10ec83f 100644 +--- a/crypto/xts.c ++++ b/crypto/xts.c +@@ -24,6 +24,7 @@ + */ + + #include "qemu/osdep.h" ++#include "qemu/bswap.h" + #include "crypto/xts.h" + + typedef union { +@@ -39,19 +40,34 @@ static inline void xts_uint128_xor(xts_uint128 *D, + D->u[1] = S1->u[1] ^ S2->u[1]; + } + +-static void xts_mult_x(uint8_t *I) ++static inline void xts_uint128_cpu_to_les(xts_uint128 *v) + { +- int x; +- uint8_t t, tt; ++ cpu_to_le64s(&v->u[0]); ++ cpu_to_le64s(&v->u[1]); ++} + +- for (x = t = 0; x < 16; x++) { +- tt = I[x] >> 7; +- I[x] = ((I[x] << 1) | t) & 0xFF; +- t = tt; +- } +- if (tt) { +- I[0] ^= 0x87; ++static inline void xts_uint128_le_to_cpus(xts_uint128 *v) ++{ ++ le64_to_cpus(&v->u[0]); ++ le64_to_cpus(&v->u[1]); ++} ++ ++static void xts_mult_x(xts_uint128 *I) ++{ ++ uint64_t tt; ++ ++ xts_uint128_le_to_cpus(I); ++ ++ tt = I->u[0] >> 63; ++ I->u[0] <<= 1; ++ ++ if (I->u[1] >> 63) { ++ I->u[0] ^= 0x87; + } ++ I->u[1] <<= 1; ++ I->u[1] |= tt; ++ ++ xts_uint128_cpu_to_les(I); + } + + +@@ -79,7 +95,7 @@ static void xts_tweak_encdec(const void *ctx, + xts_uint128_xor(dst, dst, iv); + + /* LFSR the tweak */ +- xts_mult_x(iv->b); ++ xts_mult_x(iv); + } + + +@@ -134,7 +150,7 @@ void xts_decrypt(const void *datactx, + if (mo > 0) { + xts_uint128 S, D; + memcpy(&CC, &T, XTS_BLOCK_SIZE); +- xts_mult_x(CC.b); ++ xts_mult_x(&CC); + + /* PP = tweak decrypt block m-1 */ + memcpy(&S, src, XTS_BLOCK_SIZE); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-crypto-convert-xts_tweak_encdec-to-use-xts_uint128-t.patch b/SOURCES/kvm-crypto-convert-xts_tweak_encdec-to-use-xts_uint128-t.patch new file mode 100644 index 0000000..1c937b2 --- /dev/null +++ b/SOURCES/kvm-crypto-convert-xts_tweak_encdec-to-use-xts_uint128-t.patch @@ -0,0 +1,185 @@ +From 8c411fd4449466ba46b272d60b739015a6b47e19 Mon Sep 17 00:00:00 2001 +From: "Daniel P. Berrange" +Date: Wed, 24 Apr 2019 10:30:26 +0200 +Subject: [PATCH 07/12] crypto: convert xts_tweak_encdec to use xts_uint128 + type +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Daniel P. Berrange +Message-id: <20190424103030.2925-6-berrange@redhat.com> +Patchwork-id: 85890 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 5/9] crypto: convert xts_tweak_encdec to use xts_uint128 type +Bugzilla: 1666336 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: John Snow +RH-Acked-by: Eric Blake + +Using 64-bit arithmetic increases the performance for xts-aes-128 +when built with gcrypt: + + Encrypt: 272 MB/s -> 355 MB/s + Decrypt: 275 MB/s -> 362 MB/s + +Reviewed-by: Alberto Garcia +Signed-off-by: Daniel P. Berrangé +(cherry picked from commit db217c69f0849add67cfa2cd6601c329398be12c) +Signed-off-by: Miroslav Rezanina +--- + crypto/xts.c | 84 +++++++++++++++++++++++++++++++++++++++++------------------- + 1 file changed, 58 insertions(+), 26 deletions(-) + +diff --git a/crypto/xts.c b/crypto/xts.c +index bee23f8..0ad231f 100644 +--- a/crypto/xts.c ++++ b/crypto/xts.c +@@ -31,6 +31,13 @@ typedef union { + uint64_t u[2]; + } xts_uint128; + ++static inline void xts_uint128_xor(xts_uint128 *D, ++ const xts_uint128 *S1, ++ const xts_uint128 *S2) ++{ ++ D->u[0] = S1->u[0] ^ S2->u[0]; ++ D->u[1] = S1->u[1] ^ S2->u[1]; ++} + + static void xts_mult_x(uint8_t *I) + { +@@ -60,25 +67,19 @@ static void xts_mult_x(uint8_t *I) + */ + static void xts_tweak_encdec(const void *ctx, + xts_cipher_func *func, +- const uint8_t *src, +- uint8_t *dst, +- uint8_t *iv) ++ const xts_uint128 *src, ++ xts_uint128 *dst, ++ xts_uint128 *iv) + { +- unsigned long x; +- + /* tweak encrypt block i */ +- for (x = 0; x < XTS_BLOCK_SIZE; x++) { +- dst[x] = src[x] ^ iv[x]; +- } ++ xts_uint128_xor(dst, src, iv); + +- func(ctx, XTS_BLOCK_SIZE, dst, dst); ++ func(ctx, XTS_BLOCK_SIZE, dst->b, dst->b); + +- for (x = 0; x < XTS_BLOCK_SIZE; x++) { +- dst[x] = dst[x] ^ iv[x]; +- } ++ xts_uint128_xor(dst, dst, iv); + + /* LFSR the tweak */ +- xts_mult_x(iv); ++ xts_mult_x(iv->b); + } + + +@@ -110,20 +111,34 @@ void xts_decrypt(const void *datactx, + /* encrypt the iv */ + encfunc(tweakctx, XTS_BLOCK_SIZE, T.b, iv); + +- for (i = 0; i < lim; i++) { +- xts_tweak_encdec(datactx, decfunc, src, dst, T.b); +- +- src += XTS_BLOCK_SIZE; +- dst += XTS_BLOCK_SIZE; ++ if (QEMU_PTR_IS_ALIGNED(src, sizeof(uint64_t)) && ++ QEMU_PTR_IS_ALIGNED(dst, sizeof(uint64_t))) { ++ xts_uint128 *S = (xts_uint128 *)src; ++ xts_uint128 *D = (xts_uint128 *)dst; ++ for (i = 0; i < lim; i++, S++, D++) { ++ xts_tweak_encdec(datactx, decfunc, S, D, &T); ++ } ++ } else { ++ xts_uint128 D; ++ ++ for (i = 0; i < lim; i++) { ++ memcpy(&D, src, XTS_BLOCK_SIZE); ++ xts_tweak_encdec(datactx, decfunc, &D, &D, &T); ++ memcpy(dst, &D, XTS_BLOCK_SIZE); ++ src += XTS_BLOCK_SIZE; ++ dst += XTS_BLOCK_SIZE; ++ } + } + + /* if length is not a multiple of XTS_BLOCK_SIZE then */ + if (mo > 0) { ++ xts_uint128 S, D; + memcpy(&CC, &T, XTS_BLOCK_SIZE); + xts_mult_x(CC.b); + + /* PP = tweak decrypt block m-1 */ +- xts_tweak_encdec(datactx, decfunc, src, PP.b, CC.b); ++ memcpy(&S, src, XTS_BLOCK_SIZE); ++ xts_tweak_encdec(datactx, decfunc, &S, &PP, &CC); + + /* Pm = first length % XTS_BLOCK_SIZE bytes of PP */ + for (i = 0; i < mo; i++) { +@@ -135,7 +150,8 @@ void xts_decrypt(const void *datactx, + } + + /* Pm-1 = Tweak uncrypt CC */ +- xts_tweak_encdec(datactx, decfunc, CC.b, dst, T.b); ++ xts_tweak_encdec(datactx, decfunc, &CC, &D, &T); ++ memcpy(dst, &D, XTS_BLOCK_SIZE); + } + + /* Decrypt the iv back */ +@@ -171,17 +187,32 @@ void xts_encrypt(const void *datactx, + /* encrypt the iv */ + encfunc(tweakctx, XTS_BLOCK_SIZE, T.b, iv); + +- for (i = 0; i < lim; i++) { +- xts_tweak_encdec(datactx, encfunc, src, dst, T.b); ++ if (QEMU_PTR_IS_ALIGNED(src, sizeof(uint64_t)) && ++ QEMU_PTR_IS_ALIGNED(dst, sizeof(uint64_t))) { ++ xts_uint128 *S = (xts_uint128 *)src; ++ xts_uint128 *D = (xts_uint128 *)dst; ++ for (i = 0; i < lim; i++, S++, D++) { ++ xts_tweak_encdec(datactx, encfunc, S, D, &T); ++ } ++ } else { ++ xts_uint128 D; ++ ++ for (i = 0; i < lim; i++) { ++ memcpy(&D, src, XTS_BLOCK_SIZE); ++ xts_tweak_encdec(datactx, encfunc, &D, &D, &T); ++ memcpy(dst, &D, XTS_BLOCK_SIZE); + +- dst += XTS_BLOCK_SIZE; +- src += XTS_BLOCK_SIZE; ++ dst += XTS_BLOCK_SIZE; ++ src += XTS_BLOCK_SIZE; ++ } + } + + /* if length is not a multiple of XTS_BLOCK_SIZE then */ + if (mo > 0) { ++ xts_uint128 S, D; + /* CC = tweak encrypt block m-1 */ +- xts_tweak_encdec(datactx, encfunc, src, CC.b, T.b); ++ memcpy(&S, src, XTS_BLOCK_SIZE); ++ xts_tweak_encdec(datactx, encfunc, &S, &CC, &T); + + /* Cm = first length % XTS_BLOCK_SIZE bytes of CC */ + for (i = 0; i < mo; i++) { +@@ -194,7 +225,8 @@ void xts_encrypt(const void *datactx, + } + + /* Cm-1 = Tweak encrypt PP */ +- xts_tweak_encdec(datactx, encfunc, PP.b, dst, T.b); ++ xts_tweak_encdec(datactx, encfunc, &PP, &D, &T); ++ memcpy(dst, &D, XTS_BLOCK_SIZE); + } + + /* Decrypt the iv back */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-crypto-expand-algorithm-coverage-for-cipher-benchmar.patch b/SOURCES/kvm-crypto-expand-algorithm-coverage-for-cipher-benchmar.patch new file mode 100644 index 0000000..70828fc --- /dev/null +++ b/SOURCES/kvm-crypto-expand-algorithm-coverage-for-cipher-benchmar.patch @@ -0,0 +1,228 @@ +From f6c73edd44ee862b9b965cad1789a5dece3c5974 Mon Sep 17 00:00:00 2001 +From: "Daniel P. Berrange" +Date: Wed, 24 Apr 2019 10:30:23 +0200 +Subject: [PATCH 04/12] crypto: expand algorithm coverage for cipher benchmark +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Daniel P. Berrange +Message-id: <20190424103030.2925-3-berrange@redhat.com> +Patchwork-id: 85888 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 2/9] crypto: expand algorithm coverage for cipher benchmark +Bugzilla: 1666336 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: John Snow +RH-Acked-by: Eric Blake + +Add testing coverage for AES with XTS, ECB and CTR modes + +Reviewed-by: Marc-André Lureau +Reviewed-by: Alberto Garcia +Signed-off-by: Daniel P. Berrangé +(cherry picked from commit a9e08155bd4ae096688a8ff19669023ab0fbc170) +Signed-off-by: Miroslav Rezanina +--- + tests/benchmark-crypto-cipher.c | 149 +++++++++++++++++++++++++++++++++------- + 1 file changed, 126 insertions(+), 23 deletions(-) + +diff --git a/tests/benchmark-crypto-cipher.c b/tests/benchmark-crypto-cipher.c +index f5a0d0b..67fdf8c 100644 +--- a/tests/benchmark-crypto-cipher.c ++++ b/tests/benchmark-crypto-cipher.c +@@ -15,17 +15,27 @@ + #include "crypto/init.h" + #include "crypto/cipher.h" + +-static void test_cipher_speed(const void *opaque) ++static void test_cipher_speed(size_t chunk_size, ++ QCryptoCipherMode mode, ++ QCryptoCipherAlgorithm alg) + { + QCryptoCipher *cipher; + Error *err = NULL; + double total = 0.0; +- size_t chunk_size = (size_t)opaque; + uint8_t *key = NULL, *iv = NULL; + uint8_t *plaintext = NULL, *ciphertext = NULL; +- size_t nkey = qcrypto_cipher_get_key_len(QCRYPTO_CIPHER_ALG_AES_128); +- size_t niv = qcrypto_cipher_get_iv_len(QCRYPTO_CIPHER_ALG_AES_128, +- QCRYPTO_CIPHER_MODE_CBC); ++ size_t nkey; ++ size_t niv; ++ ++ if (!qcrypto_cipher_supports(alg, mode)) { ++ return; ++ } ++ ++ nkey = qcrypto_cipher_get_key_len(alg); ++ niv = qcrypto_cipher_get_iv_len(alg, mode); ++ if (mode == QCRYPTO_CIPHER_MODE_XTS) { ++ nkey *= 2; ++ } + + key = g_new0(uint8_t, nkey); + memset(key, g_test_rand_int(), nkey); +@@ -38,14 +48,14 @@ static void test_cipher_speed(const void *opaque) + plaintext = g_new0(uint8_t, chunk_size); + memset(plaintext, g_test_rand_int(), chunk_size); + +- cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_128, +- QCRYPTO_CIPHER_MODE_CBC, ++ cipher = qcrypto_cipher_new(alg, mode, + key, nkey, &err); + g_assert(cipher != NULL); + +- g_assert(qcrypto_cipher_setiv(cipher, +- iv, niv, +- &err) == 0); ++ if (mode != QCRYPTO_CIPHER_MODE_ECB) ++ g_assert(qcrypto_cipher_setiv(cipher, ++ iv, niv, ++ &err) == 0); + + g_test_timer_start(); + do { +@@ -55,13 +65,26 @@ static void test_cipher_speed(const void *opaque) + chunk_size, + &err) == 0); + total += chunk_size; +- } while (g_test_timer_elapsed() < 5.0); ++ } while (g_test_timer_elapsed() < 1.0); + + total /= MiB; +- g_print("cbc(aes128): "); +- g_print("Testing chunk_size %zu bytes ", chunk_size); +- g_print("done: %.2f MB in %.2f secs: ", total, g_test_timer_last()); +- g_print("%.2f MB/sec\n", total / g_test_timer_last()); ++ g_print("Enc chunk %zu bytes ", chunk_size); ++ g_print("%.2f MB/sec ", total / g_test_timer_last()); ++ ++ total = 0.0; ++ g_test_timer_start(); ++ do { ++ g_assert(qcrypto_cipher_decrypt(cipher, ++ plaintext, ++ ciphertext, ++ chunk_size, ++ &err) == 0); ++ total += chunk_size; ++ } while (g_test_timer_elapsed() < 1.0); ++ ++ total /= MiB; ++ g_print("Dec chunk %zu bytes ", chunk_size); ++ g_print("%.2f MB/sec ", total / g_test_timer_last()); + + qcrypto_cipher_free(cipher); + g_free(plaintext); +@@ -70,19 +93,99 @@ static void test_cipher_speed(const void *opaque) + g_free(key); + } + +-int main(int argc, char **argv) ++ ++static void test_cipher_speed_ecb_aes_128(const void *opaque) ++{ ++ size_t chunk_size = (size_t)opaque; ++ test_cipher_speed(chunk_size, ++ QCRYPTO_CIPHER_MODE_ECB, ++ QCRYPTO_CIPHER_ALG_AES_128); ++} ++ ++static void test_cipher_speed_ecb_aes_256(const void *opaque) + { +- size_t i; +- char name[64]; ++ size_t chunk_size = (size_t)opaque; ++ test_cipher_speed(chunk_size, ++ QCRYPTO_CIPHER_MODE_ECB, ++ QCRYPTO_CIPHER_ALG_AES_256); ++} ++ ++static void test_cipher_speed_cbc_aes_128(const void *opaque) ++{ ++ size_t chunk_size = (size_t)opaque; ++ test_cipher_speed(chunk_size, ++ QCRYPTO_CIPHER_MODE_CBC, ++ QCRYPTO_CIPHER_ALG_AES_128); ++} + ++static void test_cipher_speed_cbc_aes_256(const void *opaque) ++{ ++ size_t chunk_size = (size_t)opaque; ++ test_cipher_speed(chunk_size, ++ QCRYPTO_CIPHER_MODE_CBC, ++ QCRYPTO_CIPHER_ALG_AES_256); ++} ++ ++static void test_cipher_speed_ctr_aes_128(const void *opaque) ++{ ++ size_t chunk_size = (size_t)opaque; ++ test_cipher_speed(chunk_size, ++ QCRYPTO_CIPHER_MODE_CTR, ++ QCRYPTO_CIPHER_ALG_AES_128); ++} ++ ++static void test_cipher_speed_ctr_aes_256(const void *opaque) ++{ ++ size_t chunk_size = (size_t)opaque; ++ test_cipher_speed(chunk_size, ++ QCRYPTO_CIPHER_MODE_CTR, ++ QCRYPTO_CIPHER_ALG_AES_256); ++} ++ ++static void test_cipher_speed_xts_aes_128(const void *opaque) ++{ ++ size_t chunk_size = (size_t)opaque; ++ test_cipher_speed(chunk_size, ++ QCRYPTO_CIPHER_MODE_XTS, ++ QCRYPTO_CIPHER_ALG_AES_128); ++} ++ ++static void test_cipher_speed_xts_aes_256(const void *opaque) ++{ ++ size_t chunk_size = (size_t)opaque; ++ test_cipher_speed(chunk_size, ++ QCRYPTO_CIPHER_MODE_XTS, ++ QCRYPTO_CIPHER_ALG_AES_256); ++} ++ ++ ++int main(int argc, char **argv) ++{ + g_test_init(&argc, &argv, NULL); + g_assert(qcrypto_init(NULL) == 0); + +- for (i = 512; i <= 64 * KiB; i *= 2) { +- memset(name, 0 , sizeof(name)); +- snprintf(name, sizeof(name), "/crypto/cipher/speed-%zu", i); +- g_test_add_data_func(name, (void *)i, test_cipher_speed); +- } ++#define ADD_TEST(mode, cipher, keysize, chunk) \ ++ g_test_add_data_func( \ ++ "/crypto/cipher/" #mode "-" #cipher "-" #keysize "/chunk-" #chunk, \ ++ (void *)chunk, \ ++ test_cipher_speed_ ## mode ## _ ## cipher ## _ ## keysize) ++ ++#define ADD_TESTS(chunk) \ ++ do { \ ++ ADD_TEST(ecb, aes, 128, chunk); \ ++ ADD_TEST(ecb, aes, 256, chunk); \ ++ ADD_TEST(cbc, aes, 128, chunk); \ ++ ADD_TEST(cbc, aes, 256, chunk); \ ++ ADD_TEST(ctr, aes, 128, chunk); \ ++ ADD_TEST(ctr, aes, 256, chunk); \ ++ ADD_TEST(xts, aes, 128, chunk); \ ++ ADD_TEST(xts, aes, 256, chunk); \ ++ } while (0) ++ ++ ADD_TESTS(512); ++ ADD_TESTS(4096); ++ ADD_TESTS(16384); ++ ADD_TESTS(65536); + + return g_test_run(); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-crypto-introduce-a-xts_uint128-data-type.patch b/SOURCES/kvm-crypto-introduce-a-xts_uint128-data-type.patch new file mode 100644 index 0000000..6e91816 --- /dev/null +++ b/SOURCES/kvm-crypto-introduce-a-xts_uint128-data-type.patch @@ -0,0 +1,158 @@ +From 880c18a0bffadfda3197a18daab03881025eb37c Mon Sep 17 00:00:00 2001 +From: "Daniel P. Berrange" +Date: Wed, 24 Apr 2019 10:30:25 +0200 +Subject: [PATCH 06/12] crypto: introduce a xts_uint128 data type +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Daniel P. Berrange +Message-id: <20190424103030.2925-5-berrange@redhat.com> +Patchwork-id: 85894 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 4/9] crypto: introduce a xts_uint128 data type +Bugzilla: 1666336 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: John Snow +RH-Acked-by: Eric Blake + +The new type is designed to allow use of 64-bit arithmetic instead +of operating 1-byte at a time. The following patches will use this to +improve performance. + +Reviewed-by: Alberto Garcia +Signed-off-by: Daniel P. Berrangé +(cherry picked from commit cc36930e4077eb3dbee6cd30d2d826ec62b3490a) +Signed-off-by: Miroslav Rezanina +--- + crypto/xts.c | 46 ++++++++++++++++++++++++++-------------------- + 1 file changed, 26 insertions(+), 20 deletions(-) + +diff --git a/crypto/xts.c b/crypto/xts.c +index 3c1a92f..bee23f8 100644 +--- a/crypto/xts.c ++++ b/crypto/xts.c +@@ -26,6 +26,12 @@ + #include "qemu/osdep.h" + #include "crypto/xts.h" + ++typedef union { ++ uint8_t b[XTS_BLOCK_SIZE]; ++ uint64_t u[2]; ++} xts_uint128; ++ ++ + static void xts_mult_x(uint8_t *I) + { + int x; +@@ -85,7 +91,7 @@ void xts_decrypt(const void *datactx, + uint8_t *dst, + const uint8_t *src) + { +- uint8_t PP[XTS_BLOCK_SIZE], CC[XTS_BLOCK_SIZE], T[XTS_BLOCK_SIZE]; ++ xts_uint128 PP, CC, T; + unsigned long i, m, mo, lim; + + /* get number of blocks */ +@@ -102,10 +108,10 @@ void xts_decrypt(const void *datactx, + } + + /* encrypt the iv */ +- encfunc(tweakctx, XTS_BLOCK_SIZE, T, iv); ++ encfunc(tweakctx, XTS_BLOCK_SIZE, T.b, iv); + + for (i = 0; i < lim; i++) { +- xts_tweak_encdec(datactx, decfunc, src, dst, T); ++ xts_tweak_encdec(datactx, decfunc, src, dst, T.b); + + src += XTS_BLOCK_SIZE; + dst += XTS_BLOCK_SIZE; +@@ -113,27 +119,27 @@ void xts_decrypt(const void *datactx, + + /* if length is not a multiple of XTS_BLOCK_SIZE then */ + if (mo > 0) { +- memcpy(CC, T, XTS_BLOCK_SIZE); +- xts_mult_x(CC); ++ memcpy(&CC, &T, XTS_BLOCK_SIZE); ++ xts_mult_x(CC.b); + + /* PP = tweak decrypt block m-1 */ +- xts_tweak_encdec(datactx, decfunc, src, PP, CC); ++ xts_tweak_encdec(datactx, decfunc, src, PP.b, CC.b); + + /* Pm = first length % XTS_BLOCK_SIZE bytes of PP */ + for (i = 0; i < mo; i++) { +- CC[i] = src[XTS_BLOCK_SIZE + i]; +- dst[XTS_BLOCK_SIZE + i] = PP[i]; ++ CC.b[i] = src[XTS_BLOCK_SIZE + i]; ++ dst[XTS_BLOCK_SIZE + i] = PP.b[i]; + } + for (; i < XTS_BLOCK_SIZE; i++) { +- CC[i] = PP[i]; ++ CC.b[i] = PP.b[i]; + } + + /* Pm-1 = Tweak uncrypt CC */ +- xts_tweak_encdec(datactx, decfunc, CC, dst, T); ++ xts_tweak_encdec(datactx, decfunc, CC.b, dst, T.b); + } + + /* Decrypt the iv back */ +- decfunc(tweakctx, XTS_BLOCK_SIZE, iv, T); ++ decfunc(tweakctx, XTS_BLOCK_SIZE, iv, T.b); + } + + +@@ -146,7 +152,7 @@ void xts_encrypt(const void *datactx, + uint8_t *dst, + const uint8_t *src) + { +- uint8_t PP[XTS_BLOCK_SIZE], CC[XTS_BLOCK_SIZE], T[XTS_BLOCK_SIZE]; ++ xts_uint128 PP, CC, T; + unsigned long i, m, mo, lim; + + /* get number of blocks */ +@@ -163,10 +169,10 @@ void xts_encrypt(const void *datactx, + } + + /* encrypt the iv */ +- encfunc(tweakctx, XTS_BLOCK_SIZE, T, iv); ++ encfunc(tweakctx, XTS_BLOCK_SIZE, T.b, iv); + + for (i = 0; i < lim; i++) { +- xts_tweak_encdec(datactx, encfunc, src, dst, T); ++ xts_tweak_encdec(datactx, encfunc, src, dst, T.b); + + dst += XTS_BLOCK_SIZE; + src += XTS_BLOCK_SIZE; +@@ -175,22 +181,22 @@ void xts_encrypt(const void *datactx, + /* if length is not a multiple of XTS_BLOCK_SIZE then */ + if (mo > 0) { + /* CC = tweak encrypt block m-1 */ +- xts_tweak_encdec(datactx, encfunc, src, CC, T); ++ xts_tweak_encdec(datactx, encfunc, src, CC.b, T.b); + + /* Cm = first length % XTS_BLOCK_SIZE bytes of CC */ + for (i = 0; i < mo; i++) { +- PP[i] = src[XTS_BLOCK_SIZE + i]; +- dst[XTS_BLOCK_SIZE + i] = CC[i]; ++ PP.b[i] = src[XTS_BLOCK_SIZE + i]; ++ dst[XTS_BLOCK_SIZE + i] = CC.b[i]; + } + + for (; i < XTS_BLOCK_SIZE; i++) { +- PP[i] = CC[i]; ++ PP.b[i] = CC.b[i]; + } + + /* Cm-1 = Tweak encrypt PP */ +- xts_tweak_encdec(datactx, encfunc, PP, dst, T); ++ xts_tweak_encdec(datactx, encfunc, PP.b, dst, T.b); + } + + /* Decrypt the iv back */ +- decfunc(tweakctx, XTS_BLOCK_SIZE, iv, T); ++ decfunc(tweakctx, XTS_BLOCK_SIZE, iv, T.b); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-crypto-refactor-XTS-cipher-mode-test-suite.patch b/SOURCES/kvm-crypto-refactor-XTS-cipher-mode-test-suite.patch new file mode 100644 index 0000000..50102a3 --- /dev/null +++ b/SOURCES/kvm-crypto-refactor-XTS-cipher-mode-test-suite.patch @@ -0,0 +1,203 @@ +From 35952167bb355997dcc327d22b7868a25969c933 Mon Sep 17 00:00:00 2001 +From: "Daniel P. Berrange" +Date: Wed, 24 Apr 2019 10:30:29 +0200 +Subject: [PATCH 10/12] crypto: refactor XTS cipher mode test suite +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Daniel P. Berrange +Message-id: <20190424103030.2925-9-berrange@redhat.com> +Patchwork-id: 85889 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 8/9] crypto: refactor XTS cipher mode test suite +Bugzilla: 1666336 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: John Snow +RH-Acked-by: Eric Blake + +The current XTS test overloads two different tests in a single function +making the code a little hard to follow. Split it into distinct test +cases. + +Reviewed-by: Alberto Garcia +Signed-off-by: Daniel P. Berrangé +(cherry picked from commit a61f682fde664467c4b4dd498ea84338598c8cbd) +Signed-off-by: Miroslav Rezanina +--- + tests/test-crypto-xts.c | 140 +++++++++++++++++++++++++++--------------------- + 1 file changed, 80 insertions(+), 60 deletions(-) + +diff --git a/tests/test-crypto-xts.c b/tests/test-crypto-xts.c +index 1f1412c..81606d9 100644 +--- a/tests/test-crypto-xts.c ++++ b/tests/test-crypto-xts.c +@@ -1,7 +1,7 @@ + /* + * QEMU Crypto XTS cipher mode + * +- * Copyright (c) 2015-2016 Red Hat, Inc. ++ * Copyright (c) 2015-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 +@@ -340,70 +340,79 @@ static void test_xts_aes_decrypt(const void *ctx, + static void test_xts(const void *opaque) + { + const QCryptoXTSTestData *data = opaque; +- unsigned char out[512], Torg[16], T[16]; ++ uint8_t out[512], Torg[16], T[16]; + uint64_t seq; +- int j; +- unsigned long len; + struct TestAES aesdata; + struct TestAES aestweak; + +- for (j = 0; j < 2; j++) { +- /* skip the cases where +- * the length is smaller than 2*blocklen +- * or the length is not a multiple of 32 +- */ +- if ((j == 1) && ((data->PTLEN < 32) || (data->PTLEN % 32))) { +- continue; +- } +- len = data->PTLEN / 2; +- +- AES_set_encrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.enc); +- AES_set_decrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.dec); +- AES_set_encrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.enc); +- AES_set_decrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.dec); +- +- seq = data->seqnum; +- STORE64L(seq, Torg); +- memset(Torg + 8, 0, 8); +- +- memcpy(T, Torg, sizeof(T)); +- if (j == 0) { +- xts_encrypt(&aesdata, &aestweak, +- test_xts_aes_encrypt, +- test_xts_aes_decrypt, +- T, data->PTLEN, out, data->PTX); +- } else { +- xts_encrypt(&aesdata, &aestweak, +- test_xts_aes_encrypt, +- test_xts_aes_decrypt, +- T, len, out, data->PTX); +- xts_encrypt(&aesdata, &aestweak, +- test_xts_aes_encrypt, +- test_xts_aes_decrypt, +- T, len, &out[len], &data->PTX[len]); +- } ++ AES_set_encrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.enc); ++ AES_set_decrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.dec); ++ AES_set_encrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.enc); ++ AES_set_decrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.dec); + +- g_assert(memcmp(out, data->CTX, data->PTLEN) == 0); +- +- memcpy(T, Torg, sizeof(T)); +- if (j == 0) { +- xts_decrypt(&aesdata, &aestweak, +- test_xts_aes_encrypt, +- test_xts_aes_decrypt, +- T, data->PTLEN, out, data->CTX); +- } else { +- xts_decrypt(&aesdata, &aestweak, +- test_xts_aes_encrypt, +- test_xts_aes_decrypt, +- T, len, out, data->CTX); +- xts_decrypt(&aesdata, &aestweak, +- test_xts_aes_encrypt, +- test_xts_aes_decrypt, +- T, len, &out[len], &data->CTX[len]); +- } ++ seq = data->seqnum; ++ STORE64L(seq, Torg); ++ memset(Torg + 8, 0, 8); + +- g_assert(memcmp(out, data->PTX, data->PTLEN) == 0); +- } ++ memcpy(T, Torg, sizeof(T)); ++ xts_encrypt(&aesdata, &aestweak, ++ test_xts_aes_encrypt, ++ test_xts_aes_decrypt, ++ T, data->PTLEN, out, data->PTX); ++ ++ g_assert(memcmp(out, data->CTX, data->PTLEN) == 0); ++ ++ memcpy(T, Torg, sizeof(T)); ++ xts_decrypt(&aesdata, &aestweak, ++ test_xts_aes_encrypt, ++ test_xts_aes_decrypt, ++ T, data->PTLEN, out, data->CTX); ++ ++ g_assert(memcmp(out, data->PTX, data->PTLEN) == 0); ++} ++ ++ ++static void test_xts_split(const void *opaque) ++{ ++ const QCryptoXTSTestData *data = opaque; ++ uint8_t out[512], Torg[16], T[16]; ++ uint64_t seq; ++ unsigned long len = data->PTLEN / 2; ++ struct TestAES aesdata; ++ struct TestAES aestweak; ++ ++ AES_set_encrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.enc); ++ AES_set_decrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.dec); ++ AES_set_encrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.enc); ++ AES_set_decrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.dec); ++ ++ seq = data->seqnum; ++ STORE64L(seq, Torg); ++ memset(Torg + 8, 0, 8); ++ ++ memcpy(T, Torg, sizeof(T)); ++ xts_encrypt(&aesdata, &aestweak, ++ test_xts_aes_encrypt, ++ test_xts_aes_decrypt, ++ T, len, out, data->PTX); ++ xts_encrypt(&aesdata, &aestweak, ++ test_xts_aes_encrypt, ++ test_xts_aes_decrypt, ++ T, len, &out[len], &data->PTX[len]); ++ ++ g_assert(memcmp(out, data->CTX, data->PTLEN) == 0); ++ ++ memcpy(T, Torg, sizeof(T)); ++ xts_decrypt(&aesdata, &aestweak, ++ test_xts_aes_encrypt, ++ test_xts_aes_decrypt, ++ T, len, out, data->CTX); ++ xts_decrypt(&aesdata, &aestweak, ++ test_xts_aes_encrypt, ++ test_xts_aes_decrypt, ++ T, len, &out[len], &data->CTX[len]); ++ ++ g_assert(memcmp(out, data->PTX, data->PTLEN) == 0); + } + + +@@ -416,7 +425,18 @@ int main(int argc, char **argv) + g_assert(qcrypto_init(NULL) == 0); + + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { +- g_test_add_data_func(test_data[i].path, &test_data[i], test_xts); ++ gchar *path = g_strdup_printf("%s/basic", test_data[i].path); ++ g_test_add_data_func(path, &test_data[i], test_xts); ++ g_free(path); ++ ++ /* skip the cases where the length is smaller than 2*blocklen ++ * or the length is not a multiple of 32 ++ */ ++ if ((test_data[i].PTLEN >= 32) && !(test_data[i].PTLEN % 32)) { ++ path = g_strdup_printf("%s/split", test_data[i].path); ++ g_test_add_data_func(path, &test_data[i], test_xts_split); ++ g_free(path); ++ } + } + + return g_test_run(); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-crypto-remove-code-duplication-in-tweak-encrypt-decr.patch b/SOURCES/kvm-crypto-remove-code-duplication-in-tweak-encrypt-decr.patch new file mode 100644 index 0000000..43d36b6 --- /dev/null +++ b/SOURCES/kvm-crypto-remove-code-duplication-in-tweak-encrypt-decr.patch @@ -0,0 +1,163 @@ +From 272c652c38c9f9bcce2eb8e4e32ab68d48c3531a Mon Sep 17 00:00:00 2001 +From: "Daniel P. Berrange" +Date: Wed, 24 Apr 2019 10:30:24 +0200 +Subject: [PATCH 05/12] crypto: remove code duplication in tweak + encrypt/decrypt +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Daniel P. Berrange +Message-id: <20190424103030.2925-4-berrange@redhat.com> +Patchwork-id: 85891 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 3/9] crypto: remove code duplication in tweak encrypt/decrypt +Bugzilla: 1666336 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: John Snow +RH-Acked-by: Eric Blake + +The tweak encrypt/decrypt functions are identical except for the +comments, so can be merged. Profiling data shows that the compiler is +in fact already merging the two merges in the object files. + +Reviewed-by: Marc-André Lureau +Reviewed-by: Alberto Garcia +Signed-off-by: Daniel P. Berrangé +(cherry picked from commit 299ec87838babdf38be618cf2d81aef2500758bd) +Signed-off-by: Miroslav Rezanina +--- + crypto/xts.c | 64 ++++++++++++++---------------------------------------------- + 1 file changed, 15 insertions(+), 49 deletions(-) + +diff --git a/crypto/xts.c b/crypto/xts.c +index 9521234..3c1a92f 100644 +--- a/crypto/xts.c ++++ b/crypto/xts.c +@@ -43,20 +43,20 @@ static void xts_mult_x(uint8_t *I) + + + /** +- * xts_tweak_uncrypt: ++ * xts_tweak_encdec: + * @param ctxt: the cipher context + * @param func: the cipher function +- * @src: buffer providing the cipher text of XTS_BLOCK_SIZE bytes +- * @dst: buffer to output the plain text of XTS_BLOCK_SIZE bytes ++ * @src: buffer providing the input text of XTS_BLOCK_SIZE bytes ++ * @dst: buffer to output the output text of XTS_BLOCK_SIZE bytes + * @iv: the initialization vector tweak of XTS_BLOCK_SIZE bytes + * +- * Decrypt data with a tweak ++ * Encrypt/decrypt data with a tweak + */ +-static void xts_tweak_decrypt(const void *ctx, +- xts_cipher_func *func, +- const uint8_t *src, +- uint8_t *dst, +- uint8_t *iv) ++static void xts_tweak_encdec(const void *ctx, ++ xts_cipher_func *func, ++ const uint8_t *src, ++ uint8_t *dst, ++ uint8_t *iv) + { + unsigned long x; + +@@ -105,7 +105,7 @@ void xts_decrypt(const void *datactx, + encfunc(tweakctx, XTS_BLOCK_SIZE, T, iv); + + for (i = 0; i < lim; i++) { +- xts_tweak_decrypt(datactx, decfunc, src, dst, T); ++ xts_tweak_encdec(datactx, decfunc, src, dst, T); + + src += XTS_BLOCK_SIZE; + dst += XTS_BLOCK_SIZE; +@@ -117,7 +117,7 @@ void xts_decrypt(const void *datactx, + xts_mult_x(CC); + + /* PP = tweak decrypt block m-1 */ +- xts_tweak_decrypt(datactx, decfunc, src, PP, CC); ++ xts_tweak_encdec(datactx, decfunc, src, PP, CC); + + /* Pm = first length % XTS_BLOCK_SIZE bytes of PP */ + for (i = 0; i < mo; i++) { +@@ -129,7 +129,7 @@ void xts_decrypt(const void *datactx, + } + + /* Pm-1 = Tweak uncrypt CC */ +- xts_tweak_decrypt(datactx, decfunc, CC, dst, T); ++ xts_tweak_encdec(datactx, decfunc, CC, dst, T); + } + + /* Decrypt the iv back */ +@@ -137,40 +137,6 @@ void xts_decrypt(const void *datactx, + } + + +-/** +- * xts_tweak_crypt: +- * @param ctxt: the cipher context +- * @param func: the cipher function +- * @src: buffer providing the plain text of XTS_BLOCK_SIZE bytes +- * @dst: buffer to output the cipher text of XTS_BLOCK_SIZE bytes +- * @iv: the initialization vector tweak of XTS_BLOCK_SIZE bytes +- * +- * Encrypt data with a tweak +- */ +-static void xts_tweak_encrypt(const void *ctx, +- xts_cipher_func *func, +- const uint8_t *src, +- uint8_t *dst, +- uint8_t *iv) +-{ +- unsigned long x; +- +- /* tweak encrypt block i */ +- for (x = 0; x < XTS_BLOCK_SIZE; x++) { +- dst[x] = src[x] ^ iv[x]; +- } +- +- func(ctx, XTS_BLOCK_SIZE, dst, dst); +- +- for (x = 0; x < XTS_BLOCK_SIZE; x++) { +- dst[x] = dst[x] ^ iv[x]; +- } +- +- /* LFSR the tweak */ +- xts_mult_x(iv); +-} +- +- + void xts_encrypt(const void *datactx, + const void *tweakctx, + xts_cipher_func *encfunc, +@@ -200,7 +166,7 @@ void xts_encrypt(const void *datactx, + encfunc(tweakctx, XTS_BLOCK_SIZE, T, iv); + + for (i = 0; i < lim; i++) { +- xts_tweak_encrypt(datactx, encfunc, src, dst, T); ++ xts_tweak_encdec(datactx, encfunc, src, dst, T); + + dst += XTS_BLOCK_SIZE; + src += XTS_BLOCK_SIZE; +@@ -209,7 +175,7 @@ void xts_encrypt(const void *datactx, + /* if length is not a multiple of XTS_BLOCK_SIZE then */ + if (mo > 0) { + /* CC = tweak encrypt block m-1 */ +- xts_tweak_encrypt(datactx, encfunc, src, CC, T); ++ xts_tweak_encdec(datactx, encfunc, src, CC, T); + + /* Cm = first length % XTS_BLOCK_SIZE bytes of CC */ + for (i = 0; i < mo; i++) { +@@ -222,7 +188,7 @@ void xts_encrypt(const void *datactx, + } + + /* Cm-1 = Tweak encrypt PP */ +- xts_tweak_encrypt(datactx, encfunc, PP, dst, T); ++ xts_tweak_encdec(datactx, encfunc, PP, dst, T); + } + + /* Decrypt the iv back */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-curl-Support-auto-read-only-option.patch b/SOURCES/kvm-curl-Support-auto-read-only-option.patch new file mode 100644 index 0000000..d14a997 --- /dev/null +++ b/SOURCES/kvm-curl-Support-auto-read-only-option.patch @@ -0,0 +1,49 @@ +From a09f5141da57a969a5f7eb89adbf1429680e8ab2 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 23 Nov 2018 10:41:49 +0100 +Subject: [PATCH 08/34] curl: Support auto-read-only option + +RH-Author: Kevin Wolf +Message-id: <20181123104154.13541-8-kwolf@redhat.com> +Patchwork-id: 83117 +O-Subject: [RHEL-7.7/7.6.z qemu-kvm-rhev PATCH v2 07/12] curl: Support auto-read-only option +Bugzilla: 1623986 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina +RH-Acked-by: John Snow + +If read-only=off, but auto-read-only=on is given, just degrade to +read-only. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +(cherry picked from commit 6ceef36acb11819b255732b1de0ca62885da04bd) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/curl.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/block/curl.c b/block/curl.c +index aa42535..402f21e 100644 +--- a/block/curl.c ++++ b/block/curl.c +@@ -682,10 +682,10 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, + const char *protocol_delimiter; + int ret; + +- +- if (flags & BDRV_O_RDWR) { +- error_setg(errp, "curl block device does not support writes"); +- return -EROFS; ++ ret = bdrv_apply_auto_read_only(bs, "curl driver does not support writes", ++ errp); ++ if (ret < 0) { ++ return ret; + } + + if (!libcurl_initialized) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-device_tree-Fix-integer-overflowing-in-load_device_t.patch b/SOURCES/kvm-device_tree-Fix-integer-overflowing-in-load_device_t.patch new file mode 100644 index 0000000..32f5950 --- /dev/null +++ b/SOURCES/kvm-device_tree-Fix-integer-overflowing-in-load_device_t.patch @@ -0,0 +1,60 @@ +From e530c18fd567718e38683d73354fb56d4d3eb6aa Mon Sep 17 00:00:00 2001 +From: Sergio Lopez Pascual +Date: Mon, 15 Apr 2019 10:50:01 +0200 +Subject: [PATCH 161/163] device_tree: Fix integer overflowing in + load_device_tree() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Sergio Lopez Pascual +Message-id: <20190415105001.42933-2-slp@redhat.com> +Patchwork-id: 85665 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/1] device_tree: Fix integer overflowing in load_device_tree() +Bugzilla: 1693115 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow + +From: Markus Armbruster + +If the value of get_image_size() exceeds INT_MAX / 2 - 10000, the +computation of @dt_size overflows to a negative number, which then +gets converted to a very large size_t for g_malloc0() and +load_image_size(). In the (fortunately improbable) case g_malloc0() +succeeds and load_image_size() survives, we'd assign the negative +number to *sizep. What that would do to the callers I can't say, but +it's unlikely to be good. + +Fix by rejecting images whose size would overflow. + +Reported-by: Kurtis Miller +Signed-off-by: Markus Armbruster +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Alistair Francis +Message-Id: <20190409174018.25798-1-armbru@redhat.com> +(cherry picked from commit 065e6298a75164b4347682b63381dbe752c2b156) +Signed-off-by: Sergio Lopez +Signed-off-by: Miroslav Rezanina +--- + device_tree.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/device_tree.c b/device_tree.c +index 19458b3..2457f58 100644 +--- a/device_tree.c ++++ b/device_tree.c +@@ -84,6 +84,10 @@ void *load_device_tree(const char *filename_path, int *sizep) + filename_path); + goto fail; + } ++ if (dt_size > INT_MAX / 2 - 10000) { ++ error_report("Device tree file '%s' is too large", filename_path); ++ goto fail; ++ } + + /* Expand to 2x size to give enough room for manipulation. */ + dt_size += 10000; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-dirty-bitmap-Expose-persistent-flag-to-query-block.patch b/SOURCES/kvm-dirty-bitmap-Expose-persistent-flag-to-query-block.patch new file mode 100644 index 0000000..e6e04f9 --- /dev/null +++ b/SOURCES/kvm-dirty-bitmap-Expose-persistent-flag-to-query-block.patch @@ -0,0 +1,190 @@ +From 7fecc102918b8945abefa36ee01da3b707849b41 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 18:18:37 +0200 +Subject: [PATCH 132/163] dirty-bitmap: Expose persistent flag to 'query-block' + +RH-Author: John Snow +Message-id: <20190403181857.9693-2-jsnow@redhat.com> +Patchwork-id: 85420 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 01/21] dirty-bitmap: Expose persistent flag to 'query-block' +Bugzilla: 1677073 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Sergio Lopez Pascual + +From: Eric Blake + +Since qemu currently doesn't flush persistent bitmaps to disk until +shutdown (which might be MUCH later), it's useful if 'query-block' +at least shows WHICH bitmaps will (eventually) make it to persistent +storage. Update affected iotests. + +Signed-off-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: John Snow +Message-id: 20190204210512.27458-1-eblake@redhat.com +Signed-off-by: John Snow +(cherry picked from commit f67cf661f8b88afe8a5ea2f120583924cba9087f) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 1 + + qapi/block-core.json | 5 ++++- + tests/qemu-iotests/124 | 1 + + tests/qemu-iotests/236.out | 14 ++++++++++++++ + 4 files changed, 20 insertions(+), 1 deletion(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index 00ea36f..e46f72b 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -440,6 +440,7 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) + info->has_name = !!bm->name; + info->name = g_strdup(bm->name); + info->status = bdrv_dirty_bitmap_status(bm); ++ info->persistent = bm->persistent; + entry->value = info; + *plist = entry; + plist = &entry->next; +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 5d6bb14..23c9462 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -451,11 +451,14 @@ + # + # @status: current status of the dirty bitmap (since 2.4) + # ++# @persistent: true if the bitmap will eventually be flushed to persistent ++# storage (since 4.0) ++# + # Since: 1.3 + ## + { 'struct': 'BlockDirtyInfo', + 'data': {'*name': 'str', 'count': 'int', 'granularity': 'uint32', +- 'status': 'DirtyBitmapStatus'} } ++ 'status': 'DirtyBitmapStatus', 'persistent': 'bool' } } + + ## + # @Qcow2BitmapInfoFlags: +diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124 +index 3ea4ac5..439a86a 100755 +--- a/tests/qemu-iotests/124 ++++ b/tests/qemu-iotests/124 +@@ -350,6 +350,7 @@ class TestIncrementalBackup(TestIncrementalBackupBase): + self.assert_qmp(result, 'return[0]/dirty-bitmaps[0]/count', 458752) + self.assert_qmp(result, 'return[0]/dirty-bitmaps[0]/granularity', 65536) + self.assert_qmp(result, 'return[0]/dirty-bitmaps[0]/status', 'active') ++ self.assert_qmp(result, 'return[0]/dirty-bitmaps[0]/persistent', False) + + # Prepare a cluster_size=128k backup target without a backing file. + (target, _) = bitmap0.new_target() +diff --git a/tests/qemu-iotests/236.out b/tests/qemu-iotests/236.out +index bb2d71e..5006f7b 100644 +--- a/tests/qemu-iotests/236.out ++++ b/tests/qemu-iotests/236.out +@@ -25,12 +25,14 @@ write -P0xcd 0x3ff0000 64k + "count": 262144, + "granularity": 65536, + "name": "bitmapB", ++ "persistent": false, + "status": "active" + }, + { + "count": 262144, + "granularity": 65536, + "name": "bitmapA", ++ "persistent": false, + "status": "active" + } + ] +@@ -85,12 +87,14 @@ write -P0xcd 0x3ff0000 64k + "count": 262144, + "granularity": 65536, + "name": "bitmapB", ++ "persistent": false, + "status": "active" + }, + { + "count": 262144, + "granularity": 65536, + "name": "bitmapA", ++ "persistent": false, + "status": "active" + } + ] +@@ -183,18 +187,21 @@ write -P0xea 0x3fe0000 64k + "count": 393216, + "granularity": 65536, + "name": "bitmapC", ++ "persistent": false, + "status": "disabled" + }, + { + "count": 262144, + "granularity": 65536, + "name": "bitmapB", ++ "persistent": false, + "status": "disabled" + }, + { + "count": 458752, + "granularity": 65536, + "name": "bitmapA", ++ "persistent": false, + "status": "disabled" + } + ] +@@ -247,18 +254,21 @@ write -P0xea 0x3fe0000 64k + "count": 393216, + "granularity": 65536, + "name": "bitmapC", ++ "persistent": false, + "status": "disabled" + }, + { + "count": 262144, + "granularity": 65536, + "name": "bitmapB", ++ "persistent": false, + "status": "disabled" + }, + { + "count": 458752, + "granularity": 65536, + "name": "bitmapA", ++ "persistent": false, + "status": "disabled" + } + ] +@@ -304,24 +314,28 @@ write -P0xea 0x3fe0000 64k + "count": 458752, + "granularity": 65536, + "name": "bitmapD", ++ "persistent": false, + "status": "disabled" + }, + { + "count": 393216, + "granularity": 65536, + "name": "bitmapC", ++ "persistent": false, + "status": "disabled" + }, + { + "count": 262144, + "granularity": 65536, + "name": "bitmapB", ++ "persistent": false, + "status": "disabled" + }, + { + "count": 458752, + "granularity": 65536, + "name": "bitmapA", ++ "persistent": false, + "status": "disabled" + } + ] +-- +1.8.3.1 + diff --git a/SOURCES/kvm-dirty-bitmap-add-bdrv_dirty_bitmap_next_dirty_area.patch b/SOURCES/kvm-dirty-bitmap-add-bdrv_dirty_bitmap_next_dirty_area.patch new file mode 100644 index 0000000..61dbad4 --- /dev/null +++ b/SOURCES/kvm-dirty-bitmap-add-bdrv_dirty_bitmap_next_dirty_area.patch @@ -0,0 +1,141 @@ +From afaa0a6c9269fc840a8a48b2ca2693c1c09f78ed Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 21:48:31 +0100 +Subject: [PATCH 038/163] dirty-bitmap: add bdrv_dirty_bitmap_next_dirty_area + +RH-Author: John Snow +Message-id: <20190320214838.22027-4-jsnow@redhat.com> +Patchwork-id: 84998 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 03/10] dirty-bitmap: add bdrv_dirty_bitmap_next_dirty_area +Bugzilla: 1691048 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +The function alters bdrv_dirty_iter_next_area(), which is wrong and +less efficient (see further commit +"block/mirror: fix and improve do_sync_target_write" for description). + +Signed-off-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit a78a1a48cd1a2e86f23f113bb05e2d3dd8ae0bf6) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 6 ++++++ + include/block/dirty-bitmap.h | 2 ++ + include/qemu/hbitmap.h | 16 ++++++++++++++++ + util/hbitmap.c | 39 +++++++++++++++++++++++++++++++++++++++ + 4 files changed, 63 insertions(+) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index b162f4a..c151837 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -787,6 +787,12 @@ int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset, + return hbitmap_next_zero(bitmap->bitmap, offset, bytes); + } + ++bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap, ++ uint64_t *offset, uint64_t *bytes) ++{ ++ return hbitmap_next_dirty_area(bitmap->bitmap, offset, bytes); ++} ++ + void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, + HBitmap **backup, Error **errp) + { +diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h +index 102ccdd..4ef00ca 100644 +--- a/include/block/dirty-bitmap.h ++++ b/include/block/dirty-bitmap.h +@@ -101,6 +101,8 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs, + char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp); + int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset, + uint64_t bytes); ++bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap, ++ uint64_t *offset, uint64_t *bytes); + BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, + Error **errp); +diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h +index 1359755..097dce3 100644 +--- a/include/qemu/hbitmap.h ++++ b/include/qemu/hbitmap.h +@@ -311,6 +311,22 @@ unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi); + */ + int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, uint64_t count); + ++/* hbitmap_next_dirty_area: ++ * @hb: The HBitmap to operate on ++ * @start: in-out parameter. ++ * in: the offset to start from ++ * out: (if area found) start of found area ++ * @count: in-out parameter. ++ * in: length of requested region ++ * out: length of found area ++ * ++ * If dirty area found within [@start, @start + @count), returns true and sets ++ * @offset and @bytes appropriately. Otherwise returns false and leaves @offset ++ * and @bytes unchanged. ++ */ ++bool hbitmap_next_dirty_area(const HBitmap *hb, uint64_t *start, ++ uint64_t *count); ++ + /* hbitmap_create_meta: + * Create a "meta" hbitmap to track dirtiness of the bits in this HBitmap. + * The caller owns the created bitmap and must call hbitmap_free_meta(hb) to +diff --git a/util/hbitmap.c b/util/hbitmap.c +index 09b3719..fa35652 100644 +--- a/util/hbitmap.c ++++ b/util/hbitmap.c +@@ -246,6 +246,45 @@ int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, uint64_t count) + return res; + } + ++bool hbitmap_next_dirty_area(const HBitmap *hb, uint64_t *start, ++ uint64_t *count) ++{ ++ HBitmapIter hbi; ++ int64_t firt_dirty_off, area_end; ++ uint32_t granularity = 1UL << hb->granularity; ++ uint64_t end; ++ ++ if (*start >= hb->orig_size || *count == 0) { ++ return false; ++ } ++ ++ end = *count > hb->orig_size - *start ? hb->orig_size : *start + *count; ++ ++ hbitmap_iter_init(&hbi, hb, *start); ++ firt_dirty_off = hbitmap_iter_next(&hbi, false); ++ ++ if (firt_dirty_off < 0 || firt_dirty_off >= end) { ++ return false; ++ } ++ ++ if (firt_dirty_off + granularity >= end) { ++ area_end = end; ++ } else { ++ area_end = hbitmap_next_zero(hb, firt_dirty_off + granularity, ++ end - firt_dirty_off - granularity); ++ if (area_end < 0) { ++ area_end = end; ++ } ++ } ++ ++ if (firt_dirty_off > *start) { ++ *start = firt_dirty_off; ++ } ++ *count = area_end - *start; ++ ++ return true; ++} ++ + bool hbitmap_empty(const HBitmap *hb) + { + return hb->count == 0; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-dirty-bitmap-fix-double-lock-on-bitmap-enabling.patch b/SOURCES/kvm-dirty-bitmap-fix-double-lock-on-bitmap-enabling.patch new file mode 100644 index 0000000..8823cde --- /dev/null +++ b/SOURCES/kvm-dirty-bitmap-fix-double-lock-on-bitmap-enabling.patch @@ -0,0 +1,74 @@ +From a37c990a113d5e205d928e629c96d3a82c00d920 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:54:43 +0200 +Subject: [PATCH 58/89] dirty-bitmap: fix double lock on bitmap enabling + +RH-Author: John Snow +Message-id: <20180718225511.14878-8-jsnow@redhat.com> +Patchwork-id: 81403 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 07/35] dirty-bitmap: fix double lock on bitmap enabling +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +Bitmap lock/unlock were added to bdrv_enable_dirty_bitmap in +8b1402ce80d, but some places were not updated correspondingly, which +leads to trying to take this lock twice, which is dead-lock. Fix this. + +Actually, iotest 199 (about dirty bitmap postcopy migration) is broken +now, and this fixes it. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-id: 20180625165745.25259-3-vsementsov@virtuozzo.com +Signed-off-by: John Snow +(cherry picked from commit 58f72b965e9e1820d246329461216c4d13140122) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 3 ++- + migration/block-dirty-bitmap.c | 4 ++-- + 2 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index 4d6ae8b..f580c1a 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -259,8 +259,9 @@ void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) + /* Called with BQL taken. */ + void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap) + { ++ assert(bitmap->mutex == bitmap->successor->mutex); + qemu_mutex_lock(bitmap->mutex); +- bdrv_enable_dirty_bitmap(bitmap->successor); ++ bdrv_enable_dirty_bitmap_locked(bitmap->successor); + qemu_mutex_unlock(bitmap->mutex); + } + +diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c +index 2c541c9..fefbc6a 100644 +--- a/migration/block-dirty-bitmap.c ++++ b/migration/block-dirty-bitmap.c +@@ -511,7 +511,7 @@ void dirty_bitmap_mig_before_vm_start(void) + DirtyBitmapLoadBitmapState *b = item->data; + + if (b->migrated) { +- bdrv_enable_dirty_bitmap(b->bitmap); ++ bdrv_enable_dirty_bitmap_locked(b->bitmap); + } else { + bdrv_dirty_bitmap_enable_successor(b->bitmap); + } +@@ -547,7 +547,7 @@ static void dirty_bitmap_load_complete(QEMUFile *f, DirtyBitmapLoadState *s) + if (enabled_bitmaps == NULL) { + /* in postcopy */ + bdrv_reclaim_dirty_bitmap_locked(s->bs, s->bitmap, &error_abort); +- bdrv_enable_dirty_bitmap(s->bitmap); ++ bdrv_enable_dirty_bitmap_locked(s->bitmap); + } else { + /* target not started, successor must be empty */ + int64_t count = bdrv_get_dirty_count(s->bitmap); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-dirty-bitmap-improve-bdrv_dirty_bitmap_next_zero.patch b/SOURCES/kvm-dirty-bitmap-improve-bdrv_dirty_bitmap_next_zero.patch new file mode 100644 index 0000000..c21d5c5 --- /dev/null +++ b/SOURCES/kvm-dirty-bitmap-improve-bdrv_dirty_bitmap_next_zero.patch @@ -0,0 +1,195 @@ +From 31e5ab1930795584338fe2769a002b31c89c04b6 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 21:48:29 +0100 +Subject: [PATCH 036/163] dirty-bitmap: improve bdrv_dirty_bitmap_next_zero + +RH-Author: John Snow +Message-id: <20190320214838.22027-2-jsnow@redhat.com> +Patchwork-id: 84993 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 01/10] dirty-bitmap: improve bdrv_dirty_bitmap_next_zero +Bugzilla: 1691048 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +Add bytes parameter to the function, to limit searched range. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit 76d570dc495c56bbdcc4574bfc6d512dcb8e9aa9) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 3 ++- + block/dirty-bitmap.c | 5 +++-- + include/block/dirty-bitmap.h | 3 ++- + include/qemu/hbitmap.h | 10 +++++++--- + nbd/server.c | 2 +- + tests/test-hbitmap.c | 2 +- + util/hbitmap.c | 27 ++++++++++++++++++++++----- + 7 files changed, 38 insertions(+), 14 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index ac17db6..6a66b1c 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -446,7 +446,8 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) + break; + } + +- offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset); ++ offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset, ++ UINT64_MAX); + if (offset == -1) { + hbitmap_set(job->copy_bitmap, cluster, end - cluster); + break; +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index 6b68839..b162f4a 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -781,9 +781,10 @@ char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp) + return hbitmap_sha256(bitmap->bitmap, errp); + } + +-int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset) ++int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset, ++ uint64_t bytes) + { +- return hbitmap_next_zero(bitmap->bitmap, offset); ++ return hbitmap_next_zero(bitmap->bitmap, offset, bytes); + } + + void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, +diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h +index 8f38a3d..102ccdd 100644 +--- a/include/block/dirty-bitmap.h ++++ b/include/block/dirty-bitmap.h +@@ -99,7 +99,8 @@ bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs); + BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap); + char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp); +-int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t start); ++int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset, ++ uint64_t bytes); + BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, + Error **errp); +diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h +index a7cb780..1359755 100644 +--- a/include/qemu/hbitmap.h ++++ b/include/qemu/hbitmap.h +@@ -300,12 +300,16 @@ void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first); + unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi); + + /* 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. +- * +- * Find next not dirty bit. ++ * @count: Number of bits to proceed. If @start+@count > bitmap size, the whole ++ * bitmap is looked through. You can use UINT64_MAX as @count to search up to ++ * the bitmap end. + */ +-int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start); ++int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, uint64_t count); + + /* hbitmap_create_meta: + * Create a "meta" hbitmap to track dirtiness of the bits in this HBitmap. +diff --git a/nbd/server.c b/nbd/server.c +index e094300..0ab0dbd 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -1952,7 +1952,7 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset, + assert(begin < overall_end && nb_extents); + while (begin < overall_end && i < nb_extents) { + if (dirty) { +- end = bdrv_dirty_bitmap_next_zero(bitmap, begin); ++ end = bdrv_dirty_bitmap_next_zero(bitmap, begin, UINT64_MAX); + } else { + bdrv_set_dirty_iter(it, begin); + end = bdrv_dirty_iter_next(it); +diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c +index 5e67ac1..b04a45a 100644 +--- a/tests/test-hbitmap.c ++++ b/tests/test-hbitmap.c +@@ -939,7 +939,7 @@ static void test_hbitmap_iter_and_reset(TestHBitmapData *data, + + static void test_hbitmap_next_zero_check(TestHBitmapData *data, int64_t start) + { +- int64_t ret1 = hbitmap_next_zero(data->hb, start); ++ int64_t ret1 = hbitmap_next_zero(data->hb, start, UINT64_MAX); + int64_t ret2 = start; + for ( ; ret2 < data->size && hbitmap_get(data->hb, ret2); ret2++) { + ; +diff --git a/util/hbitmap.c b/util/hbitmap.c +index 8d402c5..09b3719 100644 +--- a/util/hbitmap.c ++++ b/util/hbitmap.c +@@ -53,6 +53,9 @@ + */ + + struct HBitmap { ++ /* Size of the bitmap, as requested in hbitmap_alloc. */ ++ uint64_t orig_size; ++ + /* Number of total bits in the bottom level. */ + uint64_t size; + +@@ -192,16 +195,28 @@ void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first) + } + } + +-int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start) ++int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, uint64_t count) + { + size_t pos = (start >> hb->granularity) >> BITS_PER_LEVEL; + unsigned long *last_lev = hb->levels[HBITMAP_LEVELS - 1]; +- uint64_t sz = hb->sizes[HBITMAP_LEVELS - 1]; + unsigned long cur = last_lev[pos]; +- unsigned start_bit_offset = +- (start >> hb->granularity) & (BITS_PER_LONG - 1); ++ unsigned start_bit_offset; ++ uint64_t end_bit, sz; + int64_t res; + ++ 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); + +@@ -218,7 +233,7 @@ int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start) + } + + res = (pos << BITS_PER_LEVEL) + ctol(cur); +- if (res >= hb->size) { ++ if (res >= end_bit) { + return -1; + } + +@@ -652,6 +667,8 @@ HBitmap *hbitmap_alloc(uint64_t size, int granularity) + HBitmap *hb = g_new0(struct HBitmap, 1); + unsigned i; + ++ hb->orig_size = size; ++ + assert(granularity >= 0 && granularity < 64); + size = (size + (1ULL << granularity) - 1) >> granularity; + assert(size <= ((uint64_t)1 << HBITMAP_LOG_MAX_SIZE)); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-dirty-bitmap-make-it-possible-to-restore-bitmap-afte.patch b/SOURCES/kvm-dirty-bitmap-make-it-possible-to-restore-bitmap-afte.patch new file mode 100644 index 0000000..1603ed0 --- /dev/null +++ b/SOURCES/kvm-dirty-bitmap-make-it-possible-to-restore-bitmap-afte.patch @@ -0,0 +1,192 @@ +From 8767d53aa1dd2df28d3093bb1c6d99e54916eb4a Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:28 +0100 +Subject: [PATCH 18/33] dirty-bitmap: make it possible to restore bitmap after + merge + +RH-Author: John Snow +Message-id: <20190206221243.7407-9-jsnow@redhat.com> +Patchwork-id: 84267 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 08/23] dirty-bitmap: make it possible to restore bitmap after merge +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +From: Vladimir Sementsov-Ogievskiy + +Add backup parameter to bdrv_merge_dirty_bitmap() to be used then with +bdrv_restore_dirty_bitmap() if it needed to restore the bitmap after +merge operation. + +This is needed to implement bitmap merge transaction action in further +commit. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: John Snow +(cherry picked from commit fa000f2f9fd96a75a0a33d50ead247fce11da92a) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 17 ++++++++++++++--- + blockdev.c | 2 +- + include/block/dirty-bitmap.h | 2 +- + include/qemu/hbitmap.h | 25 ++++++++++++++++--------- + util/hbitmap.c | 11 ++++++++--- + 5 files changed, 40 insertions(+), 17 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index cadde5c..a9421cd 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -314,7 +314,7 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, + return NULL; + } + +- if (!hbitmap_merge(parent->bitmap, successor->bitmap)) { ++ if (!hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap)) { + error_setg(errp, "Merging of parent and successor bitmap failed"); + return NULL; + } +@@ -790,8 +790,10 @@ int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset) + } + + void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, +- Error **errp) ++ HBitmap **backup, Error **errp) + { ++ bool ret; ++ + /* only bitmaps from one bds are supported */ + assert(dest->mutex == src->mutex); + +@@ -809,11 +811,20 @@ void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, + goto out; + } + +- if (!hbitmap_merge(dest->bitmap, src->bitmap)) { ++ if (!hbitmap_can_merge(dest->bitmap, src->bitmap)) { + error_setg(errp, "Bitmaps are incompatible and can't be merged"); + goto out; + } + ++ if (backup) { ++ *backup = dest->bitmap; ++ dest->bitmap = hbitmap_alloc(dest->size, hbitmap_granularity(*backup)); ++ ret = hbitmap_merge(*backup, src->bitmap, dest->bitmap); ++ } else { ++ ret = hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap); ++ } ++ assert(ret); ++ + out: + qemu_mutex_unlock(dest->mutex); + } +diff --git a/blockdev.c b/blockdev.c +index 5d9508c..823a97f 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3119,7 +3119,7 @@ void qmp_x_block_dirty_bitmap_merge(const char *node, const char *dst_name, + return; + } + +- bdrv_merge_dirty_bitmap(dst, src, errp); ++ bdrv_merge_dirty_bitmap(dst, src, NULL, errp); + } + + BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, +diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h +index 259bd27..201ff7f 100644 +--- a/include/block/dirty-bitmap.h ++++ b/include/block/dirty-bitmap.h +@@ -71,7 +71,7 @@ void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, + bool persistent); + void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked); + void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, +- Error **errp); ++ HBitmap **backup, Error **errp); + + /* Functions that require manual locking. */ + void bdrv_dirty_bitmap_lock(BdrvDirtyBitmap *bitmap); +diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h +index ddca52c..a7cb780 100644 +--- a/include/qemu/hbitmap.h ++++ b/include/qemu/hbitmap.h +@@ -73,16 +73,23 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size); + + /** + * hbitmap_merge: +- * @a: The bitmap to store the result in. +- * @b: The bitmap to merge into @a. +- * @return true if the merge was successful, +- * false if it was not attempted. +- * +- * Merge two bitmaps together. +- * A := A (BITOR) B. +- * B is left unmodified. ++ * ++ * Store result of merging @a and @b into @result. ++ * @result is allowed to be equal to @a or @b. ++ * ++ * Return true if the merge was successful, ++ * false if it was not attempted. ++ */ ++bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result); ++ ++/** ++ * hbitmap_can_merge: ++ * ++ * hbitmap_can_merge(a, b) && hbitmap_can_merge(a, result) is sufficient and ++ * necessary for hbitmap_merge will not fail. ++ * + */ +-bool hbitmap_merge(HBitmap *a, const HBitmap *b); ++bool hbitmap_can_merge(const HBitmap *a, const HBitmap *b); + + /** + * hbitmap_empty: +diff --git a/util/hbitmap.c b/util/hbitmap.c +index bcd3040..d5aca51 100644 +--- a/util/hbitmap.c ++++ b/util/hbitmap.c +@@ -723,6 +723,10 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size) + } + } + ++bool hbitmap_can_merge(const HBitmap *a, const HBitmap *b) ++{ ++ return (a->size == b->size) && (a->granularity == b->granularity); ++} + + /** + * Given HBitmaps A and B, let A := A (BITOR) B. +@@ -731,14 +735,15 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size) + * @return true if the merge was successful, + * false if it was not attempted. + */ +-bool hbitmap_merge(HBitmap *a, const HBitmap *b) ++bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result) + { + int i; + uint64_t j; + +- if ((a->size != b->size) || (a->granularity != b->granularity)) { ++ if (!hbitmap_can_merge(a, b) || !hbitmap_can_merge(a, result)) { + return false; + } ++ assert(hbitmap_can_merge(b, result)); + + if (hbitmap_count(b) == 0) { + return true; +@@ -750,7 +755,7 @@ bool hbitmap_merge(HBitmap *a, const HBitmap *b) + */ + for (i = HBITMAP_LEVELS - 1; i >= 0; i--) { + for (j = 0; j < a->sizes[i]; j++) { +- a->levels[i][j] |= b->levels[i][j]; ++ result->levels[i][j] = a->levels[i][j] | b->levels[i][j]; + } + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-dirty-bitmap-rename-bdrv_undo_clear_dirty_bitmap.patch b/SOURCES/kvm-dirty-bitmap-rename-bdrv_undo_clear_dirty_bitmap.patch new file mode 100644 index 0000000..9bf262e --- /dev/null +++ b/SOURCES/kvm-dirty-bitmap-rename-bdrv_undo_clear_dirty_bitmap.patch @@ -0,0 +1,77 @@ +From 3cccc41aae7526469ca941d12857ed3d6195b782 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:27 +0100 +Subject: [PATCH 17/33] dirty-bitmap: rename bdrv_undo_clear_dirty_bitmap + +RH-Author: John Snow +Message-id: <20190206221243.7407-8-jsnow@redhat.com> +Patchwork-id: 84271 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 07/23] dirty-bitmap: rename bdrv_undo_clear_dirty_bitmap +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +From: Vladimir Sementsov-Ogievskiy + +Use more generic names to reuse the function for bitmap merge in the +following commit. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: John Snow +(cherry picked from commit 56bd662497259400b7c9f155aaebaddde4450028) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 4 ++-- + blockdev.c | 2 +- + include/block/block_int.h | 2 +- + 3 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index 999a40c..cadde5c 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -633,11 +633,11 @@ void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out) + bdrv_dirty_bitmap_unlock(bitmap); + } + +-void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in) ++void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup) + { + HBitmap *tmp = bitmap->bitmap; + assert(!bdrv_dirty_bitmap_readonly(bitmap)); +- bitmap->bitmap = in; ++ bitmap->bitmap = backup; + hbitmap_free(tmp); + } + +diff --git a/blockdev.c b/blockdev.c +index cef1bfe..5d9508c 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -2184,7 +2184,7 @@ static void block_dirty_bitmap_clear_abort(BlkActionState *common) + common, common); + + if (state->backup) { +- bdrv_undo_clear_dirty_bitmap(state->bitmap, state->backup); ++ bdrv_restore_dirty_bitmap(state->bitmap, state->backup); + } + } + +diff --git a/include/block/block_int.h b/include/block/block_int.h +index ff923b7..f457acb 100644 +--- a/include/block/block_int.h ++++ b/include/block/block_int.h +@@ -1141,7 +1141,7 @@ bool blk_dev_is_medium_locked(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_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in); ++void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup); + + void bdrv_inc_in_flight(BlockDriverState *bs); + void bdrv_dec_in_flight(BlockDriverState *bs); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-dirty-bitmap-switch-assert-fails-to-errors-in-bdrv_m.patch b/SOURCES/kvm-dirty-bitmap-switch-assert-fails-to-errors-in-bdrv_m.patch new file mode 100644 index 0000000..b3bf5b8 --- /dev/null +++ b/SOURCES/kvm-dirty-bitmap-switch-assert-fails-to-errors-in-bdrv_m.patch @@ -0,0 +1,88 @@ +From 33de34ca77050003d06f36c9228c6408312f4be0 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:26 +0100 +Subject: [PATCH 16/33] dirty-bitmap: switch assert-fails to errors in + bdrv_merge_dirty_bitmap + +RH-Author: John Snow +Message-id: <20190206221243.7407-7-jsnow@redhat.com> +Patchwork-id: 84263 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 06/23] dirty-bitmap: switch assert-fails to errors in bdrv_merge_dirty_bitmap +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +From: Vladimir Sementsov-Ogievskiy + +Move checks from qmp_x_block_dirty_bitmap_merge() to +bdrv_merge_dirty_bitmap(), to share them with dirty bitmap merge +transaction action in future commit. + +Note: for now, only qmp_x_block_dirty_bitmap_merge() calls +bdrv_merge_dirty_bitmap(). + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: John Snow +(cherry picked from commit 06bf50068a7e952afff8c4f6470ec54a712570f7) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 15 +++++++++++++-- + blockdev.c | 10 ---------- + 2 files changed, 13 insertions(+), 12 deletions(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index 59027d4..999a40c 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -797,12 +797,23 @@ void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, + + qemu_mutex_lock(dest->mutex); + +- assert(bdrv_dirty_bitmap_enabled(dest)); +- assert(!bdrv_dirty_bitmap_readonly(dest)); ++ if (bdrv_dirty_bitmap_frozen(dest)) { ++ error_setg(errp, "Bitmap '%s' is frozen and cannot be modified", ++ dest->name); ++ goto out; ++ } ++ ++ if (bdrv_dirty_bitmap_readonly(dest)) { ++ error_setg(errp, "Bitmap '%s' is readonly and cannot be modified", ++ dest->name); ++ goto out; ++ } + + if (!hbitmap_merge(dest->bitmap, src->bitmap)) { + error_setg(errp, "Bitmaps are incompatible and can't be merged"); ++ goto out; + } + ++out: + qemu_mutex_unlock(dest->mutex); + } +diff --git a/blockdev.c b/blockdev.c +index 745ed08..cef1bfe 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3113,16 +3113,6 @@ void qmp_x_block_dirty_bitmap_merge(const char *node, const char *dst_name, + return; + } + +- if (bdrv_dirty_bitmap_frozen(dst)) { +- error_setg(errp, "Bitmap '%s' is frozen and cannot be modified", +- dst_name); +- return; +- } else if (bdrv_dirty_bitmap_readonly(dst)) { +- error_setg(errp, "Bitmap '%s' is readonly and cannot be modified", +- dst_name); +- return; +- } +- + src = bdrv_find_dirty_bitmap(bs, src_name); + if (!src) { + error_setg(errp, "Dirty bitmap '%s' not found", src_name); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-dirty-bitmaps-clean-up-bitmaps-loading-and-migration.patch b/SOURCES/kvm-dirty-bitmaps-clean-up-bitmaps-loading-and-migration.patch new file mode 100644 index 0000000..e150188 --- /dev/null +++ b/SOURCES/kvm-dirty-bitmaps-clean-up-bitmaps-loading-and-migration.patch @@ -0,0 +1,323 @@ +From 5e3d8cd537a1742fcca824bca2e49e6c9b2db080 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:41 +0100 +Subject: [PATCH 31/33] dirty-bitmaps: clean-up bitmaps loading and migration + logic + +RH-Author: John Snow +Message-id: <20190206221243.7407-22-jsnow@redhat.com> +Patchwork-id: 84280 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 21/23] dirty-bitmaps: clean-up bitmaps loading and migration logic +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +From: Vladimir Sementsov-Ogievskiy + +This patch aims to bring the following behavior: + +1. We don't load bitmaps, when started in inactive mode. It's the case +of incoming migration. In this case we wait for bitmaps migration +through migration channel (if 'dirty-bitmaps' capability is enabled) or +for invalidation (to load bitmaps from the image). + +2. We don't remove persistent bitmaps on inactivation. Instead, we only +remove bitmaps after storing. This is the only way to restore bitmaps, +if we decided to resume source after [failed] migration with +'dirty-bitmaps' capability enabled (which means, that bitmaps were not +stored). + +3. We load bitmaps on open and any invalidation, it's ok for all cases: + - normal open + - migration target invalidation with dirty-bitmaps capability + (bitmaps are migrating through migration channel, the are not + stored, so they should have IN_USE flag set and will be skipped + when loading. However, it would fail if bitmaps are read-only[1]) + - migration target invalidation without dirty-bitmaps capability + (normal load of the bitmaps, if migrated with shared storage) + - source invalidation with dirty-bitmaps capability + (skip because IN_USE) + - source invalidation without dirty-bitmaps capability + (bitmaps were dropped, reload them) + +[1]: to accurately handle this, migration of read-only bitmaps is + explicitly forbidden in this patch. + +New mechanism for not storing bitmaps when migrate with dirty-bitmaps +capability is introduced: migration filed in BdrvDirtyBitmap. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Signed-off-by: John Snow +(cherry picked from commit 9c98f145dfb994e1e9d68a4d606ee5693891280d) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block.c | 11 ++++--- + block/dirty-bitmap.c | 36 +++++++++-------------- + block/qcow2-bitmap.c | 16 +++++++++++ + block/qcow2.c | 65 ++++++++++++++++++++++++++++++++++++++++-- + include/block/dirty-bitmap.h | 2 +- + migration/block-dirty-bitmap.c | 10 +++++-- + 6 files changed, 109 insertions(+), 31 deletions(-) + +diff --git a/block.c b/block.c +index ce85c65..82b16df 100644 +--- a/block.c ++++ b/block.c +@@ -4350,6 +4350,7 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, + uint64_t perm, shared_perm; + Error *local_err = NULL; + int ret; ++ BdrvDirtyBitmap *bm; + + if (!bs->drv) { + return; +@@ -4399,6 +4400,12 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, + } + } + ++ for (bm = bdrv_dirty_bitmap_next(bs, NULL); bm; ++ bm = bdrv_dirty_bitmap_next(bs, bm)) ++ { ++ bdrv_dirty_bitmap_set_migration(bm, false); ++ } ++ + ret = refresh_total_sectors(bs, bs->total_sectors); + if (ret < 0) { + bs->open_flags |= BDRV_O_INACTIVE; +@@ -4537,10 +4544,6 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs) + } + } + +- /* At this point persistent bitmaps should be already stored by the format +- * driver */ +- bdrv_release_persistent_dirty_bitmaps(bs); +- + return 0; + } + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index db8021a..6b68839 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -55,6 +55,10 @@ struct BdrvDirtyBitmap { + and this bitmap must remain unchanged while + this flag is set. */ + bool persistent; /* bitmap must be saved to owner disk image */ ++ bool migration; /* Bitmap is selected for migration, it should ++ not be stored on the next inactivation ++ (persistent flag doesn't matter until next ++ invalidation).*/ + QLIST_ENTRY(BdrvDirtyBitmap) list; + }; + +@@ -390,26 +394,6 @@ void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) + } + + /** +- * Release all persistent dirty bitmaps attached to a BDS (for use in +- * bdrv_inactivate_recurse()). +- * There must not be any frozen bitmaps attached. +- * This function does not remove persistent bitmaps from the storage. +- * Called with BQL taken. +- */ +-void bdrv_release_persistent_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_get_persistance(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 +@@ -760,16 +744,24 @@ void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent) + qemu_mutex_unlock(bitmap->mutex); + } + ++/* Called with BQL taken. */ ++void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration) ++{ ++ qemu_mutex_lock(bitmap->mutex); ++ bitmap->migration = migration; ++ qemu_mutex_unlock(bitmap->mutex); ++} ++ + bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap) + { +- return bitmap->persistent; ++ return bitmap->persistent && !bitmap->migration; + } + + bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs) + { + BdrvDirtyBitmap *bm; + QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { +- if (bm->persistent && !bm->readonly) { ++ if (bm->persistent && !bm->readonly && !bm->migration) { + return true; + } + } +diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c +index 14050eb..a36773c 100644 +--- a/block/qcow2-bitmap.c ++++ b/block/qcow2-bitmap.c +@@ -1418,6 +1418,22 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp) + g_free(tb); + } + ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ /* For safety, we remove bitmap after storing. ++ * We may be here in two cases: ++ * 1. bdrv_close. It's ok to drop bitmap. ++ * 2. inactivation. It means migration without 'dirty-bitmaps' ++ * capability, so bitmaps are not marked with ++ * BdrvDirtyBitmap.migration flags. It's not bad to drop them too, ++ * and reload on invalidation. ++ */ ++ if (bm->dirty_bitmap == NULL) { ++ continue; ++ } ++ ++ bdrv_release_dirty_bitmap(bs, bm->dirty_bitmap); ++ } ++ + bitmap_list_free(bm_list); + return; + +diff --git a/block/qcow2.c b/block/qcow2.c +index d260cd6..36d1152 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -1487,8 +1487,69 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, + s->autoclear_features &= QCOW2_AUTOCLEAR_MASK; + } + +- if (qcow2_load_dirty_bitmaps(bs, &local_err)) { +- update_header = false; ++ /* == 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 = qcow2_load_dirty_bitmaps(bs, &local_err); ++ ++ update_header = update_header && !header_updated; + } + if (local_err != NULL) { + error_propagate(errp, local_err); +diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h +index 1463943..8f38a3d 100644 +--- a/include/block/dirty-bitmap.h ++++ b/include/block/dirty-bitmap.h +@@ -26,7 +26,6 @@ BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, + const char *name); + void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap); + void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs); +-void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs); + void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, + const char *name, + Error **errp); +@@ -72,6 +71,7 @@ void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, + void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked); + void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, + HBitmap **backup, Error **errp); ++void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration); + + /* Functions that require manual locking. */ + void bdrv_dirty_bitmap_lock(BdrvDirtyBitmap *bitmap); +diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c +index 47251af..ffe7aca 100644 +--- a/migration/block-dirty-bitmap.c ++++ b/migration/block-dirty-bitmap.c +@@ -307,6 +307,12 @@ static int init_dirty_bitmap_migration(void) + goto fail; + } + ++ if (bdrv_dirty_bitmap_readonly(bitmap)) { ++ error_report("Can't migrate read-only dirty bitmap: '%s", ++ bdrv_dirty_bitmap_name(bitmap)); ++ goto fail; ++ } ++ + bdrv_ref(bs); + bdrv_dirty_bitmap_set_qmp_locked(bitmap, true); + +@@ -329,9 +335,9 @@ static int init_dirty_bitmap_migration(void) + } + } + +- /* unset persistance here, to not roll back it */ ++ /* unset migration flags here, to not roll back it */ + QSIMPLEQ_FOREACH(dbms, &dirty_bitmap_mig_state.dbms_list, entry) { +- bdrv_dirty_bitmap_set_persistance(dbms->bitmap, false); ++ bdrv_dirty_bitmap_set_migration(dbms->bitmap, true); + } + + if (QSIMPLEQ_EMPTY(&dirty_bitmap_mig_state.dbms_list)) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-docs-Document-the-new-default-sizes-of-the-qcow2-cac.patch b/SOURCES/kvm-docs-Document-the-new-default-sizes-of-the-qcow2-cac.patch new file mode 100644 index 0000000..33a6eec --- /dev/null +++ b/SOURCES/kvm-docs-Document-the-new-default-sizes-of-the-qcow2-cac.patch @@ -0,0 +1,87 @@ +From f05cd76313a9d1e461eb66ee85f9f45fdb235f85 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 19 Feb 2019 17:00:12 +0100 +Subject: [PATCH 11/23] docs: Document the new default sizes of the qcow2 + caches + +RH-Author: Kevin Wolf +Message-id: <20190219170023.27826-3-kwolf@redhat.com> +Patchwork-id: 84542 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 02/13] docs: Document the new default sizes of the qcow2 caches +Bugzilla: 1656913 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Alberto Garcia + +We have just reduced the refcount cache size to the minimum unless +the user explicitly requests a larger one, so we have to update the +documentation to reflect this change. + +Signed-off-by: Alberto Garcia +Message-id: c5f0bde23558dd9d33b21fffc76ac9953cc19c56.1523968389.git.berto@igalia.com +Reviewed-by: Eric Blake +Signed-off-by: Max Reitz +(cherry picked from commit 603790ef3aec6a19b1c095188a1d2171934a27de) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + docs/qcow2-cache.txt | 33 ++++++++++++++++----------------- + 1 file changed, 16 insertions(+), 17 deletions(-) + +diff --git a/docs/qcow2-cache.txt b/docs/qcow2-cache.txt +index 170191a..8a09a5c 100644 +--- a/docs/qcow2-cache.txt ++++ b/docs/qcow2-cache.txt +@@ -116,31 +116,30 @@ There are three options available, and all of them take bytes: + "refcount-cache-size": maximum size of the refcount block cache + "cache-size": maximum size of both caches combined + +-There are two things that need to be taken into account: ++There are a few things that need to be taken into account: + + - Both caches must have a size that is a multiple of the cluster size + (or the cache entry size: see "Using smaller cache sizes" below). + +- - If you only set one of the options above, QEMU will automatically +- adjust the others so that the L2 cache is 4 times bigger than the +- refcount cache. ++ - The default L2 cache size is 8 clusters or 1MB (whichever is more), ++ and the minimum is 2 clusters (or 2 cache entries, see below). + +-This means that these options are equivalent: ++ - The default (and minimum) refcount cache size is 4 clusters. + +- -drive file=hd.qcow2,l2-cache-size=2097152 +- -drive file=hd.qcow2,refcount-cache-size=524288 +- -drive file=hd.qcow2,cache-size=2621440 ++ - If only "cache-size" is specified then QEMU will assign as much ++ memory as possible to the L2 cache before increasing the refcount ++ cache size. + +-The reason for this 1/4 ratio is to ensure that both caches cover the +-same amount of disk space. Note however that this is only valid with +-the default value of refcount_bits (16). If you are using a different +-value you might want to calculate both cache sizes yourself since QEMU +-will always use the same 1/4 ratio. ++Unlike L2 tables, refcount blocks are not used during normal I/O but ++only during allocations and internal snapshots. In most cases they are ++accessed sequentially (even during random guest I/O) so increasing the ++refcount cache size won't have any measurable effect in performance ++(this can change if you are using internal snapshots, so you may want ++to think about increasing the cache size if you use them heavily). + +-It's also worth mentioning that there's no strict need for both caches +-to cover the same amount of disk space. The refcount cache is used +-much less often than the L2 cache, so it's perfectly reasonable to +-keep it small. ++Before QEMU 2.12 the refcount cache had a default size of 1/4 of the ++L2 cache size. This resulted in unnecessarily large caches, so now the ++refcount cache is as small as possible unless overridden by the user. + + + Using smaller cache entries +-- +1.8.3.1 + diff --git a/SOURCES/kvm-docs-interop-add-firmware.json.patch b/SOURCES/kvm-docs-interop-add-firmware.json.patch new file mode 100644 index 0000000..d65785d --- /dev/null +++ b/SOURCES/kvm-docs-interop-add-firmware.json.patch @@ -0,0 +1,598 @@ +From ec98b4315beedbb1c61277d47a11dccaecfa3fb1 Mon Sep 17 00:00:00 2001 +From: Laszlo Ersek +Date: Tue, 13 Nov 2018 18:16:39 +0100 +Subject: [PATCH 20/22] docs/interop: add "firmware.json" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Laszlo Ersek +Message-id: <20181113181639.4999-7-lersek@redhat.com> +Patchwork-id: 83009 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 6/6] docs/interop: add "firmware.json" +Bugzilla: 1607406 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Marc-André Lureau +RH-Acked-by: Markus Armbruster + +Add a schema that describes the different uses and properties of virtual +machine firmware. + +Each firmware executable installed on a host system should come with at +least one JSON file that conforms to this schema. Each file informs the +management applications about +- the firmware's properties and one possible use case / feature set, +- configuration bits that are required to run the firmware binary. + +In addition, define rules for management apps for picking the highest +priority firmware JSON file when multiple such files match the search +criteria. + +Cc: "Daniel P. Berrange" +Cc: David Gibson +Cc: Eric Blake +Cc: Gerd Hoffmann +Cc: Kashyap Chamarthy +Cc: Markus Armbruster +Cc: Paolo Bonzini +Cc: Thomas Huth +Reviewed-by: Gerd Hoffmann +Signed-off-by: Laszlo Ersek +Message-Id: <20180509152608.9343-1-lersek@redhat.com> +Signed-off-by: Paolo Bonzini +(cherry picked from commit 3a0adfc9bfcf217017bfc49d00c9a9b845e7118d) +Signed-off-by: Miroslav Rezanina +--- + docs/interop/firmware.json | 540 +++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 540 insertions(+) + create mode 100644 docs/interop/firmware.json + +diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json +new file mode 100644 +index 0000000..28f9bc1 +--- /dev/null ++++ b/docs/interop/firmware.json +@@ -0,0 +1,540 @@ ++# -*- Mode: Python -*- ++# ++# Copyright (C) 2018 Red Hat, Inc. ++# ++# Authors: ++# Daniel P. Berrange ++# Laszlo Ersek ++# ++# This work is licensed under the terms of the GNU GPL, version 2 or ++# later. See the COPYING file in the top-level directory. ++ ++## ++# = Firmware ++## ++ ++{ 'include' : 'common.json' } ++{ 'include' : 'block-core.json' } ++ ++## ++# @FirmwareOSInterface: ++# ++# Lists the firmware-OS interface types provided by various firmware ++# that is commonly used with QEMU virtual machines. ++# ++# @bios: Traditional x86 BIOS interface. For example, firmware built ++# from the SeaBIOS project usually provides this interface. ++# ++# @openfirmware: The interface is defined by the (historical) IEEE ++# 1275-1994 standard. Examples for firmware projects that ++# provide this interface are: OpenBIOS, OpenHackWare, ++# SLOF. ++# ++# @uboot: Firmware interface defined by the U-Boot project. ++# ++# @uefi: Firmware interface defined by the UEFI specification. For ++# example, firmware built from the edk2 (EFI Development Kit II) ++# project usually provides this interface. ++# ++# Since: 3.0 ++## ++{ 'enum' : 'FirmwareOSInterface', ++ 'data' : [ 'bios', 'openfirmware', 'uboot', 'uefi' ] } ++ ++## ++# @FirmwareDevice: ++# ++# Defines the device types that firmware can be mapped into. ++# ++# @flash: The firmware executable and its accompanying NVRAM file are to ++# be mapped into a pflash chip each. ++# ++# @kernel: The firmware is to be loaded like a Linux kernel. This is ++# similar to @memory but may imply additional processing that ++# is specific to the target architecture and machine type. ++# ++# @memory: The firmware is to be mapped into memory. ++# ++# Since: 3.0 ++## ++{ 'enum' : 'FirmwareDevice', ++ 'data' : [ 'flash', 'kernel', 'memory' ] } ++ ++## ++# @FirmwareTarget: ++# ++# Defines the machine types that firmware may execute on. ++# ++# @architecture: Determines the emulation target (the QEMU system ++# emulator) that can execute the firmware. ++# ++# @machines: Lists the machine types (known by the emulator that is ++# specified through @architecture) that can execute the ++# firmware. Elements of @machines are supposed to be concrete ++# machine types, not aliases. Glob patterns are understood, ++# which is especially useful for versioned machine types. ++# (For example, the glob pattern "pc-i440fx-*" matches ++# "pc-i440fx-2.12".) On the QEMU command line, "-machine ++# type=..." specifies the requested machine type (but that ++# option does not accept glob patterns). ++# ++# Since: 3.0 ++## ++{ 'struct' : 'FirmwareTarget', ++ 'data' : { 'architecture' : 'SysEmuTarget', ++ 'machines' : [ 'str' ] } } ++ ++## ++# @FirmwareFeature: ++# ++# Defines the features that firmware may support, and the platform ++# requirements that firmware may present. ++# ++# @acpi-s3: The firmware supports S3 sleep (suspend to RAM), as defined ++# in the ACPI specification. On the "pc-i440fx-*" machine ++# types of the @i386 and @x86_64 emulation targets, S3 can be ++# enabled with "-global PIIX4_PM.disable_s3=0" and disabled ++# with "-global PIIX4_PM.disable_s3=1". On the "pc-q35-*" ++# machine types of the @i386 and @x86_64 emulation targets, S3 ++# can be enabled with "-global ICH9-LPC.disable_s3=0" and ++# disabled with "-global ICH9-LPC.disable_s3=1". ++# ++# @acpi-s4: The firmware supports S4 hibernation (suspend to disk), as ++# defined in the ACPI specification. On the "pc-i440fx-*" ++# machine types of the @i386 and @x86_64 emulation targets, S4 ++# can be enabled with "-global PIIX4_PM.disable_s4=0" and ++# disabled with "-global PIIX4_PM.disable_s4=1". On the ++# "pc-q35-*" machine types of the @i386 and @x86_64 emulation ++# targets, S4 can be enabled with "-global ++# ICH9-LPC.disable_s4=0" and disabled with "-global ++# ICH9-LPC.disable_s4=1". ++# ++# @amd-sev: The firmware supports running under AMD Secure Encrypted ++# Virtualization, as specified in the AMD64 Architecture ++# Programmer's Manual. QEMU command line options related to ++# this feature are documented in ++# "docs/amd-memory-encryption.txt". ++# ++# @enrolled-keys: The variable store (NVRAM) template associated with ++# the firmware binary has the UEFI Secure Boot ++# operational mode turned on, with certificates ++# enrolled. ++# ++# @requires-smm: The firmware requires the platform to emulate SMM ++# (System Management Mode), as defined in the AMD64 ++# Architecture Programmer's Manual, and in the Intel(R)64 ++# and IA-32 Architectures Software Developer's Manual. On ++# the "pc-q35-*" machine types of the @i386 and @x86_64 ++# emulation targets, SMM emulation can be enabled with ++# "-machine smm=on". (On the "pc-q35-*" machine types of ++# the @i386 emulation target, @requires-smm presents ++# further CPU requirements; one combination known to work ++# is "-cpu coreduo,-nx".) If the firmware is marked as ++# both @secure-boot and @requires-smm, then write ++# accesses to the pflash chip (NVRAM) that holds the UEFI ++# variable store must be restricted to code that executes ++# in SMM, using the additional option "-global ++# driver=cfi.pflash01,property=secure,value=on". ++# Furthermore, a large guest-physical address space ++# (comprising guest RAM, memory hotplug range, and 64-bit ++# PCI MMIO aperture), and/or a high VCPU count, may ++# present high SMRAM requirements from the firmware. On ++# the "pc-q35-*" machine types of the @i386 and @x86_64 ++# emulation targets, the SMRAM size may be increased ++# above the default 16MB with the "-global ++# mch.extended-tseg-mbytes=uint16" option. As a rule of ++# thumb, the default 16MB size suffices for 1TB of ++# guest-phys address space and a few tens of VCPUs; for ++# every further TB of guest-phys address space, add 8MB ++# of SMRAM. 48MB should suffice for 4TB of guest-phys ++# address space and 2-3 hundred VCPUs. ++# ++# @secure-boot: The firmware implements the software interfaces for UEFI ++# Secure Boot, as defined in the UEFI specification. Note ++# that without @requires-smm, guest code running with ++# kernel privileges can undermine the security of Secure ++# Boot. ++# ++# @verbose-dynamic: When firmware log capture is enabled, the firmware ++# logs a large amount of debug messages, which may ++# impact boot performance. With log capture disabled, ++# there is no boot performance impact. On the ++# "pc-i440fx-*" and "pc-q35-*" machine types of the ++# @i386 and @x86_64 emulation targets, firmware log ++# capture can be enabled with the QEMU command line ++# options "-chardev file,id=fwdebug,path=LOGFILEPATH ++# -device isa-debugcon,iobase=0x402,chardev=fwdebug". ++# @verbose-dynamic is mutually exclusive with ++# @verbose-static. ++# ++# @verbose-static: The firmware unconditionally produces a large amount ++# of debug messages, which may impact boot performance. ++# This feature may typically be carried by certain UEFI ++# firmware for the "virt-*" machine types of the @arm ++# and @aarch64 emulation targets, where the debug ++# messages are written to the first (always present) ++# PL011 UART. @verbose-static is mutually exclusive ++# with @verbose-dynamic. ++# ++# Since: 3.0 ++## ++{ 'enum' : 'FirmwareFeature', ++ 'data' : [ 'acpi-s3', 'acpi-s4', 'amd-sev', 'enrolled-keys', ++ 'requires-smm', 'secure-boot', 'verbose-dynamic', ++ 'verbose-static' ] } ++ ++## ++# @FirmwareFlashFile: ++# ++# Defines common properties that are necessary for loading a firmware ++# file into a pflash chip. The corresponding QEMU command line option is ++# "-drive file=@filename,format=@format". Note however that the ++# option-argument shown here is incomplete; it is completed under ++# @FirmwareMappingFlash. ++# ++# @filename: Specifies the filename on the host filesystem where the ++# firmware file can be found. ++# ++# @format: Specifies the block format of the file pointed-to by ++# @filename, such as @raw or @qcow2. ++# ++# Since: 3.0 ++## ++{ 'struct' : 'FirmwareFlashFile', ++ 'data' : { 'filename' : 'str', ++ 'format' : 'BlockdevDriver' } } ++ ++## ++# @FirmwareMappingFlash: ++# ++# Describes loading and mapping properties for the firmware executable ++# and its accompanying NVRAM file, when @FirmwareDevice is @flash. ++# ++# @executable: Identifies the firmware executable. The firmware ++# executable may be shared by multiple virtual machine ++# definitions. The corresponding QEMU command line option ++# is "-drive ++# if=pflash,unit=0,readonly=on,file=@executable.@filename,format=@executable.@format". ++# ++# @nvram-template: Identifies the NVRAM template compatible with ++# @executable. Management software instantiates an ++# individual copy -- a specific NVRAM file -- from ++# @nvram-template.@filename for each new virtual ++# machine definition created. @nvram-template.@filename ++# itself is never mapped into virtual machines, only ++# individual copies of it are. An NVRAM file is ++# typically used for persistently storing the ++# non-volatile UEFI variables of a virtual machine ++# definition. The corresponding QEMU command line ++# option is "-drive ++# if=pflash,unit=1,readonly=off,file=FILENAME_OF_PRIVATE_NVRAM_FILE,format=@nvram-template.@format". ++# ++# Since: 3.0 ++## ++{ 'struct' : 'FirmwareMappingFlash', ++ 'data' : { 'executable' : 'FirmwareFlashFile', ++ 'nvram-template' : 'FirmwareFlashFile' } } ++ ++## ++# @FirmwareMappingKernel: ++# ++# Describes loading and mapping properties for the firmware executable, ++# when @FirmwareDevice is @kernel. ++# ++# @filename: Identifies the firmware executable. The firmware executable ++# may be shared by multiple virtual machine definitions. The ++# corresponding QEMU command line option is "-kernel ++# @filename". ++# ++# Since: 3.0 ++## ++{ 'struct' : 'FirmwareMappingKernel', ++ 'data' : { 'filename' : 'str' } } ++ ++## ++# @FirmwareMappingMemory: ++# ++# Describes loading and mapping properties for the firmware executable, ++# when @FirmwareDevice is @memory. ++# ++# @filename: Identifies the firmware executable. The firmware executable ++# may be shared by multiple virtual machine definitions. The ++# corresponding QEMU command line option is "-bios ++# @filename". ++# ++# Since: 3.0 ++## ++{ 'struct' : 'FirmwareMappingMemory', ++ 'data' : { 'filename' : 'str' } } ++ ++## ++# @FirmwareMapping: ++# ++# Provides a discriminated structure for firmware to describe its ++# loading / mapping properties. ++# ++# @device: Selects the device type that the firmware must be mapped ++# into. ++# ++# Since: 3.0 ++## ++{ 'union' : 'FirmwareMapping', ++ 'base' : { 'device' : 'FirmwareDevice' }, ++ 'discriminator' : 'device', ++ 'data' : { 'flash' : 'FirmwareMappingFlash', ++ 'kernel' : 'FirmwareMappingKernel', ++ 'memory' : 'FirmwareMappingMemory' } } ++ ++## ++# @Firmware: ++# ++# Describes a firmware (or a firmware use case) to management software. ++# ++# It is possible for multiple @Firmware elements to match the search ++# criteria of management software. Applications thus need rules to pick ++# one of the many matches, and users need the ability to override distro ++# defaults. ++# ++# It is recommended to create firmware JSON files (each containing a ++# single @Firmware root element) with a double-digit prefix, for example ++# "50-ovmf.json", "50-seabios-256k.json", etc, so they can be sorted in ++# predictable order. The firmware JSON files should be searched for in ++# three directories: ++# ++# - /usr/share/qemu/firmware -- populated by distro-provided firmware ++# packages (XDG_DATA_DIRS covers ++# /usr/share by default), ++# ++# - /etc/qemu/firmware -- exclusively for sysadmins' local additions, ++# ++# - $XDG_CONFIG_HOME/qemu/firmware -- exclusively for per-user local ++# additions (XDG_CONFIG_HOME ++# defaults to $HOME/.config). ++# ++# Top-down, the list of directories goes from general to specific. ++# ++# Management software should build a list of files from all three ++# locations, then sort the list by filename (i.e., last pathname ++# component). Management software should choose the first JSON file on ++# the sorted list that matches the search criteria. If a more specific ++# directory has a file with same name as a less specific directory, then ++# the file in the more specific directory takes effect. If the more ++# specific file is zero length, it hides the less specific one. ++# ++# For example, if a distro ships ++# ++# - /usr/share/qemu/firmware/50-ovmf.json ++# ++# - /usr/share/qemu/firmware/50-seabios-256k.json ++# ++# then the sysadmin can prevent the default OVMF being used at all with ++# ++# $ touch /etc/qemu/firmware/50-ovmf.json ++# ++# The sysadmin can replace/alter the distro default OVMF with ++# ++# $ vim /etc/qemu/firmware/50-ovmf.json ++# ++# or they can provide a parallel OVMF with higher priority ++# ++# $ vim /etc/qemu/firmware/10-ovmf.json ++# ++# or they can provide a parallel OVMF with lower priority ++# ++# $ vim /etc/qemu/firmware/99-ovmf.json ++# ++# @description: Provides a human-readable description of the firmware. ++# Management software may or may not display @description. ++# ++# @interface-types: Lists the types of interfaces that the firmware can ++# expose to the guest OS. This is a non-empty, ordered ++# list; entries near the beginning of @interface-types ++# are considered more native to the firmware, and/or ++# to have a higher quality implementation in the ++# firmware, than entries near the end of ++# @interface-types. ++# ++# @mapping: Describes the loading / mapping properties of the firmware. ++# ++# @targets: Collects the target architectures (QEMU system emulators) ++# and their machine types that may execute the firmware. ++# ++# @features: Lists the features that the firmware supports, and the ++# platform requirements it presents. ++# ++# @tags: A list of auxiliary strings associated with the firmware for ++# which @description is not appropriate, due to the latter's ++# possible exposure to the end-user. @tags serves development and ++# debugging purposes only, and management software shall ++# explicitly ignore it. ++# ++# Since: 3.0 ++# ++# Examples: ++# ++# { ++# "description": "SeaBIOS", ++# "interface-types": [ ++# "bios" ++# ], ++# "mapping": { ++# "device": "memory", ++# "filename": "/usr/share/seabios/bios-256k.bin" ++# }, ++# "targets": [ ++# { ++# "architecture": "i386", ++# "machines": [ ++# "pc-i440fx-*", ++# "pc-q35-*" ++# ] ++# }, ++# { ++# "architecture": "x86_64", ++# "machines": [ ++# "pc-i440fx-*", ++# "pc-q35-*" ++# ] ++# } ++# ], ++# "features": [ ++# "acpi-s3", ++# "acpi-s4" ++# ], ++# "tags": [ ++# "CONFIG_BOOTSPLASH=n", ++# "CONFIG_ROM_SIZE=256", ++# "CONFIG_USE_SMM=n" ++# ] ++# } ++# ++# { ++# "description": "OVMF with SB+SMM, empty varstore", ++# "interface-types": [ ++# "uefi" ++# ], ++# "mapping": { ++# "device": "flash", ++# "executable": { ++# "filename": "/usr/share/OVMF/OVMF_CODE.secboot.fd", ++# "format": "raw" ++# }, ++# "nvram-template": { ++# "filename": "/usr/share/OVMF/OVMF_VARS.fd", ++# "format": "raw" ++# } ++# }, ++# "targets": [ ++# { ++# "architecture": "x86_64", ++# "machines": [ ++# "pc-q35-*" ++# ] ++# } ++# ], ++# "features": [ ++# "acpi-s3", ++# "amd-sev", ++# "requires-smm", ++# "secure-boot", ++# "verbose-dynamic" ++# ], ++# "tags": [ ++# "-a IA32", ++# "-a X64", ++# "-p OvmfPkg/OvmfPkgIa32X64.dsc", ++# "-t GCC48", ++# "-b DEBUG", ++# "-D SMM_REQUIRE", ++# "-D SECURE_BOOT_ENABLE", ++# "-D FD_SIZE_4MB" ++# ] ++# } ++# ++# { ++# "description": "OVMF with SB+SMM, SB enabled, MS certs enrolled", ++# "interface-types": [ ++# "uefi" ++# ], ++# "mapping": { ++# "device": "flash", ++# "executable": { ++# "filename": "/usr/share/OVMF/OVMF_CODE.secboot.fd", ++# "format": "raw" ++# }, ++# "nvram-template": { ++# "filename": "/usr/share/OVMF/OVMF_VARS.secboot.fd", ++# "format": "raw" ++# } ++# }, ++# "targets": [ ++# { ++# "architecture": "x86_64", ++# "machines": [ ++# "pc-q35-*" ++# ] ++# } ++# ], ++# "features": [ ++# "acpi-s3", ++# "amd-sev", ++# "enrolled-keys", ++# "requires-smm", ++# "secure-boot", ++# "verbose-dynamic" ++# ], ++# "tags": [ ++# "-a IA32", ++# "-a X64", ++# "-p OvmfPkg/OvmfPkgIa32X64.dsc", ++# "-t GCC48", ++# "-b DEBUG", ++# "-D SMM_REQUIRE", ++# "-D SECURE_BOOT_ENABLE", ++# "-D FD_SIZE_4MB" ++# ] ++# } ++# ++# { ++# "description": "UEFI firmware for ARM64 virtual machines", ++# "interface-types": [ ++# "uefi" ++# ], ++# "mapping": { ++# "device": "flash", ++# "executable": { ++# "filename": "/usr/share/AAVMF/AAVMF_CODE.fd", ++# "format": "raw" ++# }, ++# "nvram-template": { ++# "filename": "/usr/share/AAVMF/AAVMF_VARS.fd", ++# "format": "raw" ++# } ++# }, ++# "targets": [ ++# { ++# "architecture": "aarch64", ++# "machines": [ ++# "virt-*" ++# ] ++# } ++# ], ++# "features": [ ++# ++# ], ++# "tags": [ ++# "-a AARCH64", ++# "-p ArmVirtPkg/ArmVirtQemu.dsc", ++# "-t GCC48", ++# "-b DEBUG", ++# "-D DEBUG_PRINT_ERROR_LEVEL=0x80000000" ++# ] ++# } ++## ++{ 'struct' : 'Firmware', ++ 'data' : { 'description' : 'str', ++ 'interface-types' : [ 'FirmwareOSInterface' ], ++ 'mapping' : 'FirmwareMapping', ++ 'targets' : [ 'FirmwareTarget' ], ++ 'features' : [ 'FirmwareFeature' ], ++ 'tags' : [ 'str' ] } } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-docs-interop-add-nbd.txt.patch b/SOURCES/kvm-docs-interop-add-nbd.txt.patch new file mode 100644 index 0000000..9343065 --- /dev/null +++ b/SOURCES/kvm-docs-interop-add-nbd.txt.patch @@ -0,0 +1,92 @@ +From 2386a193f4e199ee0e706e2879e125e78a273073 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:55:05 +0200 +Subject: [PATCH 80/89] docs/interop: add nbd.txt + +RH-Author: John Snow +Message-id: <20180718225511.14878-30-jsnow@redhat.com> +Patchwork-id: 81416 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 29/35] docs/interop: add nbd.txt +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +Describe new metadata namespace: "qemu". + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20180609151758.17343-7-vsementsov@virtuozzo.com> +Reviewed-by: Eric Blake +[eblake: grammar tweaks] +Signed-off-by: Eric Blake +(cherry picked from commit 3229a835a3c574a8ebc605e007785c4e01c61623) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + MAINTAINERS | 1 + + docs/interop/nbd.txt | 38 ++++++++++++++++++++++++++++++++++++++ + 2 files changed, 39 insertions(+) + create mode 100644 docs/interop/nbd.txt + +diff --git a/MAINTAINERS b/MAINTAINERS +index a783c92..53d5216 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -1927,6 +1927,7 @@ F: nbd/ + F: include/block/nbd* + F: qemu-nbd.* + F: blockdev-nbd.c ++F: docs/interop/nbd.txt + T: git git://repo.or.cz/qemu/ericb.git nbd + + NFS +diff --git a/docs/interop/nbd.txt b/docs/interop/nbd.txt +new file mode 100644 +index 0000000..77b5f45 +--- /dev/null ++++ b/docs/interop/nbd.txt +@@ -0,0 +1,38 @@ ++Qemu supports the NBD protocol, and has an internal NBD client (see ++block/nbd.c), an internal NBD server (see blockdev-nbd.c), and an ++external NBD server tool (see qemu-nbd.c). The common code is placed ++in nbd/*. ++ ++The NBD protocol is specified here: ++https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md ++ ++The following paragraphs describe some specific properties of NBD ++protocol realization in Qemu. ++ ++= Metadata namespaces = ++ ++Qemu supports the "base:allocation" metadata context as defined in the ++NBD protocol specification, and also defines an additional metadata ++namespace "qemu". ++ ++ ++== "qemu" namespace == ++ ++The "qemu" namespace currently contains only one type of context, ++related to exposing the contents of a dirty bitmap alongside the ++associated disk contents. That context has the following form: ++ ++ qemu:dirty-bitmap: ++ ++Each dirty-bitmap metadata context defines only one flag for extents ++in reply for NBD_CMD_BLOCK_STATUS: ++ ++ bit 0: NBD_STATE_DIRTY, means that the extent is "dirty" ++ ++For NBD_OPT_LIST_META_CONTEXT the following queries are supported ++in addition to "qemu:dirty-bitmap:": ++ ++* "qemu:" - returns list of all available metadata contexts in the ++ namespace. ++* "qemu:dirty-bitmap:" - returns list of all available dirty-bitmap ++ metadata contexts. +-- +1.8.3.1 + diff --git a/SOURCES/kvm-docs-interop-firmware.json-Prefer-machine-to-if-pfla.patch b/SOURCES/kvm-docs-interop-firmware.json-Prefer-machine-to-if-pfla.patch new file mode 100644 index 0000000..584b115 --- /dev/null +++ b/SOURCES/kvm-docs-interop-firmware.json-Prefer-machine-to-if-pfla.patch @@ -0,0 +1,72 @@ +From 11a0638c7c56d8653e99b8eab194d1adc70a35f3 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:18 +0200 +Subject: [PATCH 51/53] docs/interop/firmware.json: Prefer -machine to + if=pflash +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-30-armbru@redhat.com> +Patchwork-id: 87994 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 29/31] docs/interop/firmware.json: Prefer -machine to if=pflash +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +The previous commit added a way to configure firmware with -blockdev +rather than -drive if=pflash. Document it as the preferred way. + +Signed-off-by: Markus Armbruster +Message-Id: <20190308131445.17502-13-armbru@redhat.com> +Reviewed-by: Michael S. Tsirkin +Reviewed-by: Laszlo Ersek +(cherry picked from commit e33763be7cd3769c9ae48e67d775348863fdabdb) +Signed-off-by: Miroslav Rezanina +--- + docs/interop/firmware.json | 20 ++++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json +index 28f9bc1..ff8c2ce 100644 +--- a/docs/interop/firmware.json ++++ b/docs/interop/firmware.json +@@ -212,9 +212,13 @@ + # + # @executable: Identifies the firmware executable. The firmware + # executable may be shared by multiple virtual machine +-# definitions. The corresponding QEMU command line option +-# is "-drive +-# if=pflash,unit=0,readonly=on,file=@executable.@filename,format=@executable.@format". ++# definitions. The preferred corresponding QEMU command ++# line options are ++# -drive if=none,id=pflash0,readonly=on,file=@executable.@filename,format=@executable.@format ++# -machine pflash0=pflash0 ++# or equivalent -blockdev instead of -drive. ++# With QEMU versions older than 4.0, you have to use ++# -drive if=pflash,unit=0,readonly=on,file=@executable.@filename,format=@executable.@format + # + # @nvram-template: Identifies the NVRAM template compatible with + # @executable. Management software instantiates an +@@ -225,9 +229,13 @@ + # individual copies of it are. An NVRAM file is + # typically used for persistently storing the + # non-volatile UEFI variables of a virtual machine +-# definition. The corresponding QEMU command line +-# option is "-drive +-# if=pflash,unit=1,readonly=off,file=FILENAME_OF_PRIVATE_NVRAM_FILE,format=@nvram-template.@format". ++# definition. The preferred corresponding QEMU ++# command line options are ++# -drive if=none,id=pflash1,readonly=off,file=FILENAME_OF_PRIVATE_NVRAM_FILE,format=@nvram-template.@format ++# -machine pflash1=pflash1 ++# or equivalent -blockdev instead of -drive. ++# With QEMU versions older than 4.0, you have to use ++# -drive if=pflash,unit=1,readonly=off,file=FILENAME_OF_PRIVATE_NVRAM_FILE,format=@nvram-template.@format + # + # Since: 3.0 + ## +-- +1.8.3.1 + diff --git a/SOURCES/kvm-docs-interop-qcow2-Improve-bitmap-flag-in_use-specif.patch b/SOURCES/kvm-docs-interop-qcow2-Improve-bitmap-flag-in_use-specif.patch new file mode 100644 index 0000000..c65d0ff --- /dev/null +++ b/SOURCES/kvm-docs-interop-qcow2-Improve-bitmap-flag-in_use-specif.patch @@ -0,0 +1,62 @@ +From 0b5f477725d2652d671b42238ea58dbbdd4763a9 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 22:42:49 +0200 +Subject: [PATCH 154/163] docs/interop/qcow2: Improve bitmap flag in_use + specification + +RH-Author: John Snow +Message-id: <20190403224253.5251-2-jsnow@redhat.com> +Patchwork-id: 85437 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/5] docs/interop/qcow2: Improve bitmap flag in_use specification +Bugzilla: 1666884 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Sergio Lopez Pascual + +From: Vladimir Sementsov-Ogievskiy + +We already use (we didn't notice it) IN_USE flag for marking bitmap +metadata outdated, such as AUTO flag, which mirrors enabled/disabled +bitmaps. Now we are going to support bitmap resize, so it's good to +write IN_USE meaning with more details. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-id: 20190311185147.52309-2-vsementsov@virtuozzo.com +Signed-off-by: John Snow +(cherry picked from commit 2fd490c614500fc669386eaf8710cd2d015f548e) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + docs/interop/qcow2.txt | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/docs/interop/qcow2.txt b/docs/interop/qcow2.txt +index feb711f..cba1787 100644 +--- a/docs/interop/qcow2.txt ++++ b/docs/interop/qcow2.txt +@@ -555,7 +555,10 @@ Structure of a bitmap directory entry: + Bit + 0: in_use + The bitmap was not saved correctly and may be +- inconsistent. ++ inconsistent. Although the bitmap metadata is still ++ well-formed from a qcow2 perspective, the metadata ++ (such as the auto flag or bitmap size) or data ++ contents may be outdated. + + 1: auto + The bitmap must reflect all changes of the virtual +@@ -683,8 +686,8 @@ corresponding range of the virtual disk (see above) was written to while the + bitmap was 'enabled'. An unset bit means that this range was not written to. + + The software doesn't have to sync the bitmap in the image file with its +-representation in RAM after each write. Flag 'in_use' should be set while the +-bitmap is not synced. ++representation in RAM after each write or metadata change. Flag 'in_use' ++should be set while the bitmap is not synced. + + In the image file the 'enabled' state is reflected by the 'auto' flag. If this + flag is set, the software must consider the bitmap as 'enabled' and start +-- +1.8.3.1 + diff --git a/SOURCES/kvm-e1000-Fix-tso_props-compat-for-82540em.patch b/SOURCES/kvm-e1000-Fix-tso_props-compat-for-82540em.patch new file mode 100644 index 0000000..0e96447 --- /dev/null +++ b/SOURCES/kvm-e1000-Fix-tso_props-compat-for-82540em.patch @@ -0,0 +1,47 @@ +From bddd766e9d3109a4dfd6f099edf8f5870a24bbf1 Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Thu, 26 Jul 2018 16:40:11 +0200 +Subject: [PATCH 11/15] e1000: Fix tso_props compat for 82540em + +RH-Author: Dr. David Alan Gilbert +Message-id: <20180726164011.21660-2-dgilbert@redhat.com> +Patchwork-id: 81522 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 1/1] e1000: Fix tso_props compat for 82540em +Bugzilla: 1608778 +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Eduardo Habkost +RH-Acked-by: Juan Quintela +RH-Acked-by: Xiao Wang + +From: "Dr. David Alan Gilbert" + +In RHEL when you ask for an e1000-82540em that's the device you get, +however in upstream it's aliased to an e1000. +Because of the difference, we need to change the compat entry +for migrate_tso_props to the base type that changes e1000 as well. + +Signed-off-by: Dr. David Alan Gilbert +Signed-off-by: Miroslav Rezanina +--- + include/hw/compat.h | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/include/hw/compat.h b/include/hw/compat.h +index 5802e61..5804853 100644 +--- a/include/hw/compat.h ++++ b/include/hw/compat.h +@@ -464,8 +464,9 @@ + .driver = "vhost-user-blk-pci",\ + .property = "vectors",\ + .value = "2",\ +- },{ /* HW_COMPAT_RHEL7_5 from HW_COMPAT_2_11 */ \ +- .driver = "e1000",\ ++ },{ /* HW_COMPAT_RHEL7_5 from HW_COMPAT_2_11 but \ ++ bz 1608778 modified for our naming */ \ ++ .driver = "e1000-82540em",\ + .property = "migrate_tso_props",\ + .value = "off",\ + },{ /* HW_COMPAT_RHEL7_5 from HW_COMPAT_2_10 */ \ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-e1000e-Do-not-auto-clear-ICR-bits-which-aren-t-set-i.patch b/SOURCES/kvm-e1000e-Do-not-auto-clear-ICR-bits-which-aren-t-set-i.patch new file mode 100644 index 0000000..29fb7d2 --- /dev/null +++ b/SOURCES/kvm-e1000e-Do-not-auto-clear-ICR-bits-which-aren-t-set-i.patch @@ -0,0 +1,49 @@ +From d77dfec42fe78f5010780dadd10d7e20e0877867 Mon Sep 17 00:00:00 2001 +From: Xiao Wang +Date: Thu, 16 Aug 2018 06:09:02 +0200 +Subject: [PATCH 1/5] e1000e: Do not auto-clear ICR bits which aren't set in + EIAC + +RH-Author: Xiao Wang +Message-id: <1534399743-23973-2-git-send-email-jasowang@redhat.com> +Patchwork-id: 81850 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/2] e1000e: Do not auto-clear ICR bits which aren't set in EIAC +Bugzilla: 1596010 +RH-Acked-by: wexu@redhat.com +RH-Acked-by: Thomas Huth +RH-Acked-by: Paolo Bonzini + +From: Jan Kiszka + +The spec does not justify clearing of any E1000_ICR_OTHER_CAUSES when +E1000_ICR_OTHER is set in EIAC. In fact, removing this code fixes the +issue the Linux driver runs into since 4aea7a5c5e94 ("e1000e: Avoid +receiver overrun interrupt bursts") and was worked around by +745d0bd3af99 ("e1000e: Remove Other from EIAC"). + +Signed-off-by: Jan Kiszka +Signed-off-by: Jason Wang +(cherry picked from commit 2285a00c113469bb3e750ca4921cdb7baaae9e25) +Signed-off-by: Miroslav Rezanina +--- + hw/net/e1000e_core.c | 4 ---- + 1 file changed, 4 deletions(-) + +diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c +index c93c466..9504891 100644 +--- a/hw/net/e1000e_core.c ++++ b/hw/net/e1000e_core.c +@@ -2022,10 +2022,6 @@ e1000e_msix_notify_one(E1000ECore *core, uint32_t cause, uint32_t int_cfg) + + effective_eiac = core->mac[EIAC] & cause; + +- if (effective_eiac == E1000_ICR_OTHER) { +- effective_eiac |= E1000_ICR_OTHER_CAUSES; +- } +- + core->mac[ICR] &= ~effective_eiac; + + if (!(core->mac[CTRL_EXT] & E1000_CTRL_EXT_IAME)) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-e1000e-Prevent-MSI-MSI-X-storms.patch b/SOURCES/kvm-e1000e-Prevent-MSI-MSI-X-storms.patch new file mode 100644 index 0000000..b71c846 --- /dev/null +++ b/SOURCES/kvm-e1000e-Prevent-MSI-MSI-X-storms.patch @@ -0,0 +1,85 @@ +From 77782045cff9546feaf8863c0e7552343a488c6e Mon Sep 17 00:00:00 2001 +From: Xiao Wang +Date: Thu, 16 Aug 2018 06:09:03 +0200 +Subject: [PATCH 2/5] e1000e: Prevent MSI/MSI-X storms + +RH-Author: Xiao Wang +Message-id: <1534399743-23973-3-git-send-email-jasowang@redhat.com> +Patchwork-id: 81849 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 2/2] e1000e: Prevent MSI/MSI-X storms +Bugzilla: 1596010 +RH-Acked-by: wexu@redhat.com +RH-Acked-by: Thomas Huth +RH-Acked-by: Paolo Bonzini + +From: Jan Kiszka + +Only signal MSI/MSI-X events on rising edges. So far we re-triggered the +interrupt sources even if the guest did no consumed the pending one, +easily causing interrupt storms. + +Issue was observable with Linux 4.16 e1000e driver when MSI-X was used. +Vector 2 was causing interrupt storms after the driver activated the +device. + +Signed-off-by: Jan Kiszka +Signed-off-by: Jason Wang +(cherry picked from commit 4712c158c5276fd3c401152f4bb5c3fccf185946) +Signed-off-by: Miroslav Rezanina +--- + hw/net/e1000e_core.c | 11 +++++++++++ + hw/net/e1000e_core.h | 2 ++ + 2 files changed, 13 insertions(+) + +diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c +index 9504891..2a221c2 100644 +--- a/hw/net/e1000e_core.c ++++ b/hw/net/e1000e_core.c +@@ -2023,6 +2023,7 @@ e1000e_msix_notify_one(E1000ECore *core, uint32_t cause, uint32_t int_cfg) + effective_eiac = core->mac[EIAC] & cause; + + core->mac[ICR] &= ~effective_eiac; ++ core->msi_causes_pending &= ~effective_eiac; + + if (!(core->mac[CTRL_EXT] & E1000_CTRL_EXT_IAME)) { + core->mac[IMS] &= ~effective_eiac; +@@ -2119,6 +2120,13 @@ e1000e_send_msi(E1000ECore *core, bool msix) + { + uint32_t causes = core->mac[ICR] & core->mac[IMS] & ~E1000_ICR_ASSERTED; + ++ core->msi_causes_pending &= causes; ++ causes ^= core->msi_causes_pending; ++ if (causes == 0) { ++ return; ++ } ++ core->msi_causes_pending |= causes; ++ + if (msix) { + e1000e_msix_notify(core, causes); + } else { +@@ -2156,6 +2164,9 @@ e1000e_update_interrupt_state(E1000ECore *core) + core->mac[ICS] = core->mac[ICR]; + + interrupts_pending = (core->mac[IMS] & core->mac[ICR]) ? true : false; ++ if (!interrupts_pending) { ++ core->msi_causes_pending = 0; ++ } + + trace_e1000e_irq_pending_interrupts(core->mac[ICR] & core->mac[IMS], + core->mac[ICR], core->mac[IMS]); +diff --git a/hw/net/e1000e_core.h b/hw/net/e1000e_core.h +index 7d8ff41..63a1551 100644 +--- a/hw/net/e1000e_core.h ++++ b/hw/net/e1000e_core.h +@@ -109,6 +109,8 @@ struct E1000Core { + NICState *owner_nic; + PCIDevice *owner; + void (*owner_start_recv)(PCIDevice *d); ++ ++ uint32_t msi_causes_pending; + }; + + void +-- +1.8.3.1 + diff --git a/SOURCES/kvm-egl-headless-parse-rendernode.patch b/SOURCES/kvm-egl-headless-parse-rendernode.patch new file mode 100644 index 0000000..8c971ae --- /dev/null +++ b/SOURCES/kvm-egl-headless-parse-rendernode.patch @@ -0,0 +1,50 @@ +From a2832a5633f66f85843184c223517bdb124104c2 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 5 Mar 2019 08:26:17 +0100 +Subject: [PATCH 9/9] egl-headless: parse rendernode +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Gerd Hoffmann +Message-id: <20190305082617.14614-5-kraxel@redhat.com> +Patchwork-id: 84797 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 4/4] egl-headless: parse rendernode +Bugzilla: 1648236 +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Marc-André Lureau +RH-Acked-by: John Snow +RH-Acked-by: Erik Skultety + +RHEL-7 hasn't the qapi-based -display parser, so we need a +few lines of code to explicitly handle the new option. + +Signed-off-by: Gerd Hoffmann +Signed-off-by: Miroslav Rezanina +--- + vl.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/vl.c b/vl.c +index 8b79eb9..dfe261d 100644 +--- a/vl.c ++++ b/vl.c +@@ -2202,6 +2202,15 @@ static void parse_display(const char *p) + } + } else if (strstart(p, "egl-headless", &opts)) { + dpy.type = DISPLAY_TYPE_EGL_HEADLESS; ++ if (*opts) { ++ const char *nextopt; ++ if (strstart(opts, ",rendernode=", &nextopt)) { ++ dpy.u.egl_headless.rendernode = strdup(nextopt); ++ } else { ++ error_report("invalid egl-headless option string"); ++ exit(1); ++ } ++ } + } else if (strstart(p, "curses", &opts)) { + dpy.type = DISPLAY_TYPE_CURSES; + } else if (strstart(p, "gtk", &opts)) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-exec-Fix-MAP_RAM-for-cached-access.patch b/SOURCES/kvm-exec-Fix-MAP_RAM-for-cached-access.patch new file mode 100644 index 0000000..f2df3cf --- /dev/null +++ b/SOURCES/kvm-exec-Fix-MAP_RAM-for-cached-access.patch @@ -0,0 +1,275 @@ +From 8bd172d00ceb35cd2a625f1a86fe50a786d8564a Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 25 Jan 2019 22:50:06 +0100 +Subject: [PATCH 06/23] exec: Fix MAP_RAM for cached access +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190125225007.8197-7-jsnow@redhat.com> +Patchwork-id: 84123 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 6/7] exec: Fix MAP_RAM for cached access +Bugzilla: 1597482 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Peter Xu +RH-Acked-by: Stefano Garzarella + +From: Eric Auger + +When an IOMMUMemoryRegion is in front of a virtio device, +address_space_cache_init does not set cache->ptr as the memory +region is not RAM. However when the device performs an access, +we end up in glue() which performs the translation and then uses +MAP_RAM. This latter uses the unset ptr and returns a wrong value +which leads to a SIGSEV in address_space_lduw_internal_cached_slow, +for instance. + +In slow path cache->ptr is NULL and MAP_RAM must redirect to +qemu_map_ram_ptr((mr)->ram_block, ofs). + +As MAP_RAM, IS_DIRECT and INVALIDATE are the same in _cached_slow +and non cached mode, let's remove those macros. + +This fixes the use cases featuring vIOMMU (Intel and ARM SMMU) +which lead to a SIGSEV. + +Fixes: 48564041a73a (exec: reintroduce MemoryRegion caching) +Signed-off-by: Eric Auger + +Message-Id: <1528895946-28677-1-git-send-email-eric.auger@redhat.com> +Signed-off-by: Paolo Bonzini +(cherry picked from commit a99761d3c85679da380c0f597468acd3dc1b53b3) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + exec.c | 6 ------ + memory_ldst.inc.c | 47 ++++++++++++++++++++++------------------------- + 2 files changed, 22 insertions(+), 31 deletions(-) + +diff --git a/exec.c b/exec.c +index 805a2d4..d87a51a 100644 +--- a/exec.c ++++ b/exec.c +@@ -3643,9 +3643,6 @@ void cpu_physical_memory_unmap(void *buffer, hwaddr len, + #define ARG1 as + #define SUFFIX + #define TRANSLATE(...) address_space_translate(as, __VA_ARGS__) +-#define IS_DIRECT(mr, is_write) memory_access_is_direct(mr, is_write) +-#define MAP_RAM(mr, ofs) qemu_map_ram_ptr((mr)->ram_block, ofs) +-#define INVALIDATE(mr, ofs, len) invalidate_and_set_dirty(mr, ofs, len) + #define RCU_READ_LOCK(...) rcu_read_lock() + #define RCU_READ_UNLOCK(...) rcu_read_unlock() + #include "memory_ldst.inc.c" +@@ -3775,9 +3772,6 @@ address_space_write_cached_slow(MemoryRegionCache *cache, hwaddr addr, + #define ARG1 cache + #define SUFFIX _cached_slow + #define TRANSLATE(...) address_space_translate_cached(cache, __VA_ARGS__) +-#define IS_DIRECT(mr, is_write) memory_access_is_direct(mr, is_write) +-#define MAP_RAM(mr, ofs) (cache->ptr + (ofs - cache->xlat)) +-#define INVALIDATE(mr, ofs, len) invalidate_and_set_dirty(mr, ofs, len) + #define RCU_READ_LOCK() ((void)0) + #define RCU_READ_UNLOCK() ((void)0) + #include "memory_ldst.inc.c" +diff --git a/memory_ldst.inc.c b/memory_ldst.inc.c +index 25d6125..e09c2b5 100644 +--- a/memory_ldst.inc.c ++++ b/memory_ldst.inc.c +@@ -34,7 +34,7 @@ static inline uint32_t glue(address_space_ldl_internal, SUFFIX)(ARG1_DECL, + + RCU_READ_LOCK(); + mr = TRANSLATE(addr, &addr1, &l, false); +- if (l < 4 || !IS_DIRECT(mr, false)) { ++ if (l < 4 || !memory_access_is_direct(mr, false)) { + release_lock |= prepare_mmio_access(mr); + + /* I/O case */ +@@ -50,7 +50,7 @@ static inline uint32_t glue(address_space_ldl_internal, SUFFIX)(ARG1_DECL, + #endif + } else { + /* RAM case */ +- ptr = MAP_RAM(mr, addr1); ++ ptr = qemu_map_ram_ptr(mr->ram_block, addr1); + switch (endian) { + case DEVICE_LITTLE_ENDIAN: + val = ldl_le_p(ptr); +@@ -110,7 +110,7 @@ static inline uint64_t glue(address_space_ldq_internal, SUFFIX)(ARG1_DECL, + + RCU_READ_LOCK(); + mr = TRANSLATE(addr, &addr1, &l, false); +- if (l < 8 || !IS_DIRECT(mr, false)) { ++ if (l < 8 || !memory_access_is_direct(mr, false)) { + release_lock |= prepare_mmio_access(mr); + + /* I/O case */ +@@ -126,7 +126,7 @@ static inline uint64_t glue(address_space_ldq_internal, SUFFIX)(ARG1_DECL, + #endif + } else { + /* RAM case */ +- ptr = MAP_RAM(mr, addr1); ++ ptr = qemu_map_ram_ptr(mr->ram_block, addr1); + switch (endian) { + case DEVICE_LITTLE_ENDIAN: + val = ldq_le_p(ptr); +@@ -184,14 +184,14 @@ uint32_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, + + RCU_READ_LOCK(); + mr = TRANSLATE(addr, &addr1, &l, false); +- if (!IS_DIRECT(mr, false)) { ++ if (!memory_access_is_direct(mr, false)) { + release_lock |= prepare_mmio_access(mr); + + /* I/O case */ + r = memory_region_dispatch_read(mr, addr1, &val, 1, attrs); + } else { + /* RAM case */ +- ptr = MAP_RAM(mr, addr1); ++ ptr = qemu_map_ram_ptr(mr->ram_block, addr1); + val = ldub_p(ptr); + r = MEMTX_OK; + } +@@ -220,7 +220,7 @@ static inline uint32_t glue(address_space_lduw_internal, SUFFIX)(ARG1_DECL, + + RCU_READ_LOCK(); + mr = TRANSLATE(addr, &addr1, &l, false); +- if (l < 2 || !IS_DIRECT(mr, false)) { ++ if (l < 2 || !memory_access_is_direct(mr, false)) { + release_lock |= prepare_mmio_access(mr); + + /* I/O case */ +@@ -236,7 +236,7 @@ static inline uint32_t glue(address_space_lduw_internal, SUFFIX)(ARG1_DECL, + #endif + } else { + /* RAM case */ +- ptr = MAP_RAM(mr, addr1); ++ ptr = qemu_map_ram_ptr(mr->ram_block, addr1); + switch (endian) { + case DEVICE_LITTLE_ENDIAN: + val = lduw_le_p(ptr); +@@ -297,12 +297,12 @@ void glue(address_space_stl_notdirty, SUFFIX)(ARG1_DECL, + + RCU_READ_LOCK(); + mr = TRANSLATE(addr, &addr1, &l, true); +- if (l < 4 || !IS_DIRECT(mr, true)) { ++ if (l < 4 || !memory_access_is_direct(mr, true)) { + release_lock |= prepare_mmio_access(mr); + + r = memory_region_dispatch_write(mr, addr1, val, 4, attrs); + } else { +- ptr = MAP_RAM(mr, addr1); ++ ptr = qemu_map_ram_ptr(mr->ram_block, addr1); + stl_p(ptr, val); + + dirty_log_mask = memory_region_get_dirty_log_mask(mr); +@@ -334,7 +334,7 @@ static inline void glue(address_space_stl_internal, SUFFIX)(ARG1_DECL, + + RCU_READ_LOCK(); + mr = TRANSLATE(addr, &addr1, &l, true); +- if (l < 4 || !IS_DIRECT(mr, true)) { ++ if (l < 4 || !memory_access_is_direct(mr, true)) { + release_lock |= prepare_mmio_access(mr); + + #if defined(TARGET_WORDS_BIGENDIAN) +@@ -349,7 +349,7 @@ static inline void glue(address_space_stl_internal, SUFFIX)(ARG1_DECL, + r = memory_region_dispatch_write(mr, addr1, val, 4, attrs); + } else { + /* RAM case */ +- ptr = MAP_RAM(mr, addr1); ++ ptr = qemu_map_ram_ptr(mr->ram_block, addr1); + switch (endian) { + case DEVICE_LITTLE_ENDIAN: + stl_le_p(ptr, val); +@@ -361,7 +361,7 @@ static inline void glue(address_space_stl_internal, SUFFIX)(ARG1_DECL, + stl_p(ptr, val); + break; + } +- INVALIDATE(mr, addr1, 4); ++ invalidate_and_set_dirty(mr, addr1, 4); + r = MEMTX_OK; + } + if (result) { +@@ -406,14 +406,14 @@ void glue(address_space_stb, SUFFIX)(ARG1_DECL, + + RCU_READ_LOCK(); + mr = TRANSLATE(addr, &addr1, &l, true); +- if (!IS_DIRECT(mr, true)) { ++ if (!memory_access_is_direct(mr, true)) { + release_lock |= prepare_mmio_access(mr); + r = memory_region_dispatch_write(mr, addr1, val, 1, attrs); + } else { + /* RAM case */ +- ptr = MAP_RAM(mr, addr1); ++ ptr = qemu_map_ram_ptr(mr->ram_block, addr1); + stb_p(ptr, val); +- INVALIDATE(mr, addr1, 1); ++ invalidate_and_set_dirty(mr, addr1, 1); + r = MEMTX_OK; + } + if (result) { +@@ -439,7 +439,7 @@ static inline void glue(address_space_stw_internal, SUFFIX)(ARG1_DECL, + + RCU_READ_LOCK(); + mr = TRANSLATE(addr, &addr1, &l, true); +- if (l < 2 || !IS_DIRECT(mr, true)) { ++ if (l < 2 || !memory_access_is_direct(mr, true)) { + release_lock |= prepare_mmio_access(mr); + + #if defined(TARGET_WORDS_BIGENDIAN) +@@ -454,7 +454,7 @@ static inline void glue(address_space_stw_internal, SUFFIX)(ARG1_DECL, + r = memory_region_dispatch_write(mr, addr1, val, 2, attrs); + } else { + /* RAM case */ +- ptr = MAP_RAM(mr, addr1); ++ ptr = qemu_map_ram_ptr(mr->ram_block, addr1); + switch (endian) { + case DEVICE_LITTLE_ENDIAN: + stw_le_p(ptr, val); +@@ -466,7 +466,7 @@ static inline void glue(address_space_stw_internal, SUFFIX)(ARG1_DECL, + stw_p(ptr, val); + break; + } +- INVALIDATE(mr, addr1, 2); ++ invalidate_and_set_dirty(mr, addr1, 2); + r = MEMTX_OK; + } + if (result) { +@@ -512,7 +512,7 @@ static void glue(address_space_stq_internal, SUFFIX)(ARG1_DECL, + + RCU_READ_LOCK(); + mr = TRANSLATE(addr, &addr1, &l, true); +- if (l < 8 || !IS_DIRECT(mr, true)) { ++ if (l < 8 || !memory_access_is_direct(mr, true)) { + release_lock |= prepare_mmio_access(mr); + + #if defined(TARGET_WORDS_BIGENDIAN) +@@ -527,7 +527,7 @@ static void glue(address_space_stq_internal, SUFFIX)(ARG1_DECL, + r = memory_region_dispatch_write(mr, addr1, val, 8, attrs); + } else { + /* RAM case */ +- ptr = MAP_RAM(mr, addr1); ++ ptr = qemu_map_ram_ptr(mr->ram_block, addr1); + switch (endian) { + case DEVICE_LITTLE_ENDIAN: + stq_le_p(ptr, val); +@@ -539,7 +539,7 @@ static void glue(address_space_stq_internal, SUFFIX)(ARG1_DECL, + stq_p(ptr, val); + break; + } +- INVALIDATE(mr, addr1, 8); ++ invalidate_and_set_dirty(mr, addr1, 8); + r = MEMTX_OK; + } + if (result) { +@@ -576,8 +576,5 @@ void glue(address_space_stq_be, SUFFIX)(ARG1_DECL, + #undef ARG1 + #undef SUFFIX + #undef TRANSLATE +-#undef IS_DIRECT +-#undef MAP_RAM +-#undef INVALIDATE + #undef RCU_READ_LOCK + #undef RCU_READ_UNLOCK +-- +1.8.3.1 + diff --git a/SOURCES/kvm-exec-check-that-alignment-is-a-power-of-two.patch b/SOURCES/kvm-exec-check-that-alignment-is-a-power-of-two.patch new file mode 100644 index 0000000..83ba857 --- /dev/null +++ b/SOURCES/kvm-exec-check-that-alignment-is-a-power-of-two.patch @@ -0,0 +1,60 @@ +From 16f855a65838c81a35ac65a8a8b1084fa0b39689 Mon Sep 17 00:00:00 2001 +From: David Hildenbrand +Date: Fri, 21 Sep 2018 09:43:58 +0200 +Subject: [PATCH 04/22] exec: check that alignment is a power of two + +RH-Author: David Hildenbrand +Message-id: <20180921094358.12256-1-david@redhat.com> +Patchwork-id: 82228 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH] exec: check that alignment is a power of two +Bugzilla: 1629717 +RH-Acked-by: Pankaj Gupta +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Igor Mammedov +RH-Acked-by: Paolo Bonzini + +BZ: https://bugzilla.redhat.com/show_bug.cgi?id=1629717 +Brew: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=18439720 +Upstream: 61362b71c105ccb151ca16897a7d56534423f390 + +Right now we can crash QEMU using e.g. + +qemu-system-x86_64 -m 256M,maxmem=20G,slots=2 \ + -object memory-backend-file,id=mem0,size=12288,mem-path=/dev/zero,align=12288 \ + -device pc-dimm,id=dimm1,memdev=mem0 + +qemu-system-x86_64: util/mmap-alloc.c:115: + qemu_ram_mmap: Assertion `is_power_of_2(align)' failed + +Fix this by adding a proper check. + +Signed-off-by: David Hildenbrand +Message-Id: <20180607154705.6316-3-david@redhat.com> +Reviewed-by: Michael S. Tsirkin +Reviewed-by: Igor Mammedov +Signed-off-by: Paolo Bonzini +(cherry picked from commit 61362b71c105ccb151ca16897a7d56534423f390) +Signed-off-by: David Hildenbrand +Signed-off-by: Miroslav Rezanina +--- + exec.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/exec.c b/exec.c +index c670185..9028700 100644 +--- a/exec.c ++++ b/exec.c +@@ -1629,6 +1629,10 @@ static void *file_ram_alloc(RAMBlock *block, + " must be multiples of page size 0x%zx", + block->mr->align, block->page_size); + return NULL; ++ } else if (block->mr->align && !is_power_of_2(block->mr->align)) { ++ error_setg(errp, "alignment 0x%" PRIx64 ++ " must be a power of two", block->mr->align); ++ return NULL; + } + block->mr->align = MAX(block->page_size, block->mr->align); + #if defined(__s390x__) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-exec-extract-address_space_translate_iommu-fix-page_.patch b/SOURCES/kvm-exec-extract-address_space_translate_iommu-fix-page_.patch new file mode 100644 index 0000000..04b6804 --- /dev/null +++ b/SOURCES/kvm-exec-extract-address_space_translate_iommu-fix-page_.patch @@ -0,0 +1,177 @@ +From 2a1d22e7758fe0ae11050d24f489ce2d76b6e5dc Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 25 Jan 2019 22:50:03 +0100 +Subject: [PATCH 03/23] exec: extract address_space_translate_iommu, fix + page_mask corner case + +RH-Author: John Snow +Message-id: <20190125225007.8197-4-jsnow@redhat.com> +Patchwork-id: 84116 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 3/7] exec: extract address_space_translate_iommu, fix page_mask corner case +Bugzilla: 1597482 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Peter Xu +RH-Acked-by: Stefano Garzarella + +From: Paolo Bonzini + +This will be used to process IOMMUs in a MemoryRegionCache. This +includes a small bugfix, in that the returned page_mask is now +correctly -1 if the IOMMU memory region maps the entire address +space directly. Previously, address_space_get_iotlb_entry would +return ~TARGET_PAGE_MASK. + +Reviewed-by: Peter Xu +Signed-off-by: Paolo Bonzini +(cherry picked from commit a411c84b561baa94b28165c52f21c33517ee8f59) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + exec.c | 110 ++++++++++++++++++++++++++++++++++++++++++++--------------------- + 1 file changed, 75 insertions(+), 35 deletions(-) + +diff --git a/exec.c b/exec.c +index c6aeded..1bd0e6c 100644 +--- a/exec.c ++++ b/exec.c +@@ -445,6 +445,70 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x + } + + /** ++ * address_space_translate_iommu - translate an address through an IOMMU ++ * memory region and then through the target address space. ++ * ++ * @iommu_mr: the IOMMU memory region that we start the translation from ++ * @addr: the address to be translated through the MMU ++ * @xlat: the translated address offset within the destination memory region. ++ * It cannot be %NULL. ++ * @plen_out: valid read/write length of the translated address. It ++ * cannot be %NULL. ++ * @page_mask_out: page mask for the translated address. This ++ * should only be meaningful for IOMMU translated ++ * addresses, since there may be huge pages that this bit ++ * would tell. It can be %NULL if we don't care about it. ++ * @is_write: whether the translation operation is for write ++ * @is_mmio: whether this can be MMIO, set true if it can ++ * @target_as: the address space targeted by the IOMMU ++ * ++ * This function is called from RCU critical section. It is the common ++ * part of flatview_do_translate and address_space_translate_cached. ++ */ ++static MemoryRegionSection address_space_translate_iommu(IOMMUMemoryRegion *iommu_mr, ++ hwaddr *xlat, ++ hwaddr *plen_out, ++ hwaddr *page_mask_out, ++ bool is_write, ++ bool is_mmio, ++ AddressSpace **target_as) ++{ ++ MemoryRegionSection *section; ++ hwaddr page_mask = (hwaddr)-1; ++ ++ do { ++ hwaddr addr = *xlat; ++ IOMMUMemoryRegionClass *imrc = memory_region_get_iommu_class_nocheck(iommu_mr); ++ IOMMUTLBEntry iotlb = imrc->translate(iommu_mr, addr, is_write ? ++ IOMMU_WO : IOMMU_RO); ++ ++ if (!(iotlb.perm & (1 << is_write))) { ++ goto unassigned; ++ } ++ ++ addr = ((iotlb.translated_addr & ~iotlb.addr_mask) ++ | (addr & iotlb.addr_mask)); ++ page_mask &= iotlb.addr_mask; ++ *plen_out = MIN(*plen_out, (addr | iotlb.addr_mask) - addr + 1); ++ *target_as = iotlb.target_as; ++ ++ section = address_space_translate_internal( ++ address_space_to_dispatch(iotlb.target_as), addr, xlat, ++ plen_out, is_mmio); ++ ++ iommu_mr = memory_region_get_iommu(section->mr); ++ } while (unlikely(iommu_mr)); ++ ++ if (page_mask_out) { ++ *page_mask_out = page_mask; ++ } ++ return *section; ++ ++unassigned: ++ return (MemoryRegionSection) { .mr = &io_mem_unassigned }; ++} ++ ++/** + * flatview_do_translate - translate an address in FlatView + * + * @fv: the flat view that we want to translate on +@@ -472,55 +536,31 @@ static MemoryRegionSection flatview_do_translate(FlatView *fv, + bool is_mmio, + AddressSpace **target_as) + { +- IOMMUTLBEntry iotlb; + MemoryRegionSection *section; + IOMMUMemoryRegion *iommu_mr; +- IOMMUMemoryRegionClass *imrc; +- hwaddr page_mask = (hwaddr)(-1); + hwaddr plen = (hwaddr)(-1); + + if (!plen_out) { + plen_out = &plen; + } + +- for (;;) { +- section = address_space_translate_internal( +- flatview_to_dispatch(fv), addr, xlat, +- plen_out, is_mmio); +- +- iommu_mr = memory_region_get_iommu(section->mr); +- if (!iommu_mr) { +- break; +- } +- imrc = memory_region_get_iommu_class_nocheck(iommu_mr); +- +- addr = *xlat; +- iotlb = imrc->translate(iommu_mr, addr, is_write ? +- IOMMU_WO : IOMMU_RO); +- if (!(iotlb.perm & (1 << is_write))) { +- goto translate_fail; +- } ++ section = address_space_translate_internal( ++ flatview_to_dispatch(fv), addr, xlat, ++ plen_out, is_mmio); + +- addr = ((iotlb.translated_addr & ~iotlb.addr_mask) +- | (addr & iotlb.addr_mask)); +- page_mask &= iotlb.addr_mask; +- *plen_out = MIN(*plen_out, (addr | iotlb.addr_mask) - addr + 1); +- fv = address_space_to_flatview(iotlb.target_as); +- *target_as = iotlb.target_as; ++ iommu_mr = memory_region_get_iommu(section->mr); ++ if (unlikely(iommu_mr)) { ++ return address_space_translate_iommu(iommu_mr, xlat, ++ plen_out, page_mask_out, ++ is_write, is_mmio, ++ target_as); + } +- + if (page_mask_out) { +- if (page_mask == (hwaddr)(-1)) { +- /* Not behind an IOMMU, use default page size. */ +- page_mask = ~TARGET_PAGE_MASK; +- } +- *page_mask_out = page_mask; ++ /* Not behind an IOMMU, use default page size. */ ++ *page_mask_out = ~TARGET_PAGE_MASK; + } + + return *section; +- +-translate_fail: +- return (MemoryRegionSection) { .mr = &io_mem_unassigned }; + } + + /* Called from RCU critical section */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-exec-move-memory-access-declarations-to-a-common-hea.patch b/SOURCES/kvm-exec-move-memory-access-declarations-to-a-common-hea.patch new file mode 100644 index 0000000..b318a1d --- /dev/null +++ b/SOURCES/kvm-exec-move-memory-access-declarations-to-a-common-hea.patch @@ -0,0 +1,734 @@ +From f710b114f0f1d3e742dddd93158a7592b5fa01a1 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 25 Jan 2019 22:50:01 +0100 +Subject: [PATCH 01/23] exec: move memory access declarations to a common + header, inline *_phys functions +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190125225007.8197-2-jsnow@redhat.com> +Patchwork-id: 84119 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 1/7] exec: move memory access declarations to a common header, inline *_phys functions +Bugzilla: 1597482 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Peter Xu +RH-Acked-by: Stefano Garzarella + +From: Paolo Bonzini + +For now, this reduces the text size very slightly due to the newly-added +inlining: + + text size before: 9301965 + text size after: 9300645 + +Later, however, the declarations in include/exec/memory_ldst.inc.h will be +reused for the MemoryRegionCache slow path functions. + +Signed-off-by: Paolo Bonzini +(cherry picked from commit 4269c82bf72f7e171a03a09b9264b0db76ae0050) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + include/exec/cpu-all.h | 75 +++++++----------- + include/exec/memory.h | 153 +++++++++++------------------------- + include/exec/memory_ldst.inc.h | 71 +++++++++++++++++ + include/exec/memory_ldst_phys.inc.h | 147 ++++++++++++++++++++++++++++++++++ + memory_ldst.inc.c | 126 ----------------------------- + 5 files changed, 292 insertions(+), 280 deletions(-) + create mode 100644 include/exec/memory_ldst.inc.h + create mode 100644 include/exec/memory_ldst_phys.inc.h + +diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h +index f4fa94e..173edd1 100644 +--- a/include/exec/cpu-all.h ++++ b/include/exec/cpu-all.h +@@ -168,51 +168,36 @@ extern unsigned long reserved_va; + #else + + #include "exec/hwaddr.h" +-uint32_t lduw_phys(AddressSpace *as, hwaddr addr); +-uint32_t ldl_phys(AddressSpace *as, hwaddr addr); +-uint64_t ldq_phys(AddressSpace *as, hwaddr addr); +-void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val); +-void stw_phys(AddressSpace *as, hwaddr addr, uint32_t val); +-void stl_phys(AddressSpace *as, hwaddr addr, uint32_t val); +-void stq_phys(AddressSpace *as, hwaddr addr, uint64_t val); +- +-uint32_t address_space_lduw(AddressSpace *as, hwaddr addr, +- MemTxAttrs attrs, MemTxResult *result); +-uint32_t address_space_ldl(AddressSpace *as, hwaddr addr, +- MemTxAttrs attrs, MemTxResult *result); +-uint64_t address_space_ldq(AddressSpace *as, hwaddr addr, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stl_notdirty(AddressSpace *as, hwaddr addr, uint32_t val, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stw(AddressSpace *as, hwaddr addr, uint32_t val, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stl(AddressSpace *as, hwaddr addr, uint32_t val, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stq(AddressSpace *as, hwaddr addr, uint64_t val, +- MemTxAttrs attrs, MemTxResult *result); +- +-uint32_t lduw_phys_cached(MemoryRegionCache *cache, hwaddr addr); +-uint32_t ldl_phys_cached(MemoryRegionCache *cache, hwaddr addr); +-uint64_t ldq_phys_cached(MemoryRegionCache *cache, hwaddr addr); +-void stl_phys_notdirty_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); +-void stw_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); +-void stl_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); +-void stq_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint64_t val); +- +-uint32_t address_space_lduw_cached(MemoryRegionCache *cache, hwaddr addr, +- MemTxAttrs attrs, MemTxResult *result); +-uint32_t address_space_ldl_cached(MemoryRegionCache *cache, hwaddr addr, +- MemTxAttrs attrs, MemTxResult *result); +-uint64_t address_space_ldq_cached(MemoryRegionCache *cache, hwaddr addr, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stl_notdirty_cached(MemoryRegionCache *cache, hwaddr addr, +- uint32_t val, MemTxAttrs attrs, MemTxResult *result); +-void address_space_stw_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stl_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stq_cached(MemoryRegionCache *cache, hwaddr addr, uint64_t val, +- MemTxAttrs attrs, MemTxResult *result); ++ ++#define SUFFIX ++#define ARG1 as ++#define ARG1_DECL AddressSpace *as ++#define TARGET_ENDIANNESS ++#include "exec/memory_ldst.inc.h" ++ ++#define SUFFIX _cached ++#define ARG1 cache ++#define ARG1_DECL MemoryRegionCache *cache ++#define TARGET_ENDIANNESS ++#include "exec/memory_ldst.inc.h" ++ ++static inline void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val) ++{ ++ address_space_stl_notdirty(as, addr, val, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++ ++#define SUFFIX ++#define ARG1 as ++#define ARG1_DECL AddressSpace *as ++#define TARGET_ENDIANNESS ++#include "exec/memory_ldst_phys.inc.h" ++ ++#define SUFFIX _cached ++#define ARG1 cache ++#define ARG1_DECL MemoryRegionCache *cache ++#define TARGET_ENDIANNESS ++#include "exec/memory_ldst_phys.inc.h" + #endif + + /* page related stuff */ +diff --git a/include/exec/memory.h b/include/exec/memory.h +index fd2c574..84de0d4 100644 +--- a/include/exec/memory.h ++++ b/include/exec/memory.h +@@ -1703,49 +1703,16 @@ MemTxResult address_space_write(AddressSpace *as, hwaddr addr, + * @result: location to write the success/failure of the transaction; + * if NULL, this information is discarded + */ +-uint32_t address_space_ldub(AddressSpace *as, hwaddr addr, +- MemTxAttrs attrs, MemTxResult *result); +-uint32_t address_space_lduw_le(AddressSpace *as, hwaddr addr, +- MemTxAttrs attrs, MemTxResult *result); +-uint32_t address_space_lduw_be(AddressSpace *as, hwaddr addr, +- MemTxAttrs attrs, MemTxResult *result); +-uint32_t address_space_ldl_le(AddressSpace *as, hwaddr addr, +- MemTxAttrs attrs, MemTxResult *result); +-uint32_t address_space_ldl_be(AddressSpace *as, hwaddr addr, +- MemTxAttrs attrs, MemTxResult *result); +-uint64_t address_space_ldq_le(AddressSpace *as, hwaddr addr, +- MemTxAttrs attrs, MemTxResult *result); +-uint64_t address_space_ldq_be(AddressSpace *as, hwaddr addr, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stb(AddressSpace *as, hwaddr addr, uint32_t val, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stw_le(AddressSpace *as, hwaddr addr, uint32_t val, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stw_be(AddressSpace *as, hwaddr addr, uint32_t val, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stl_le(AddressSpace *as, hwaddr addr, uint32_t val, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stl_be(AddressSpace *as, hwaddr addr, uint32_t val, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stq_le(AddressSpace *as, hwaddr addr, uint64_t val, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stq_be(AddressSpace *as, hwaddr addr, uint64_t val, +- MemTxAttrs attrs, MemTxResult *result); +- +-uint32_t ldub_phys(AddressSpace *as, hwaddr addr); +-uint32_t lduw_le_phys(AddressSpace *as, hwaddr addr); +-uint32_t lduw_be_phys(AddressSpace *as, hwaddr addr); +-uint32_t ldl_le_phys(AddressSpace *as, hwaddr addr); +-uint32_t ldl_be_phys(AddressSpace *as, hwaddr addr); +-uint64_t ldq_le_phys(AddressSpace *as, hwaddr addr); +-uint64_t ldq_be_phys(AddressSpace *as, hwaddr addr); +-void stb_phys(AddressSpace *as, hwaddr addr, uint32_t val); +-void stw_le_phys(AddressSpace *as, hwaddr addr, uint32_t val); +-void stw_be_phys(AddressSpace *as, hwaddr addr, uint32_t val); +-void stl_le_phys(AddressSpace *as, hwaddr addr, uint32_t val); +-void stl_be_phys(AddressSpace *as, hwaddr addr, uint32_t val); +-void stq_le_phys(AddressSpace *as, hwaddr addr, uint64_t val); +-void stq_be_phys(AddressSpace *as, hwaddr addr, uint64_t val); ++ ++#define SUFFIX ++#define ARG1 as ++#define ARG1_DECL AddressSpace *as ++#include "exec/memory_ldst.inc.h" ++ ++#define SUFFIX ++#define ARG1 as ++#define ARG1_DECL AddressSpace *as ++#include "exec/memory_ldst_phys.inc.h" + + struct MemoryRegionCache { + hwaddr xlat; +@@ -1755,6 +1722,40 @@ struct MemoryRegionCache { + + #define MEMORY_REGION_CACHE_INVALID ((MemoryRegionCache) { .as = NULL }) + ++/* address_space_ld*_cached: load from a cached #MemoryRegion ++ * address_space_st*_cached: store into a cached #MemoryRegion ++ * ++ * These functions perform a load or store of the byte, word, ++ * longword or quad to the specified address. The address is ++ * a physical address in the AddressSpace, but it must lie within ++ * a #MemoryRegion that was mapped with address_space_cache_init. ++ * ++ * The _le suffixed functions treat the data as little endian; ++ * _be indicates big endian; no suffix indicates "same endianness ++ * as guest CPU". ++ * ++ * The "guest CPU endianness" accessors are deprecated for use outside ++ * target-* code; devices should be CPU-agnostic and use either the LE ++ * or the BE accessors. ++ * ++ * @cache: previously initialized #MemoryRegionCache to be accessed ++ * @addr: address within the address space ++ * @val: data value, for stores ++ * @attrs: memory transaction attributes ++ * @result: location to write the success/failure of the transaction; ++ * if NULL, this information is discarded ++ */ ++ ++#define SUFFIX _cached ++#define ARG1 cache ++#define ARG1_DECL MemoryRegionCache *cache ++#include "exec/memory_ldst.inc.h" ++ ++#define SUFFIX _cached ++#define ARG1 cache ++#define ARG1_DECL MemoryRegionCache *cache ++#include "exec/memory_ldst_phys.inc.h" ++ + /* address_space_cache_init: prepare for repeated access to a physical + * memory region + * +@@ -1799,72 +1800,6 @@ void address_space_cache_invalidate(MemoryRegionCache *cache, + */ + void address_space_cache_destroy(MemoryRegionCache *cache); + +-/* address_space_ld*_cached: load from a cached #MemoryRegion +- * address_space_st*_cached: store into a cached #MemoryRegion +- * +- * These functions perform a load or store of the byte, word, +- * longword or quad to the specified address. The address is +- * a physical address in the AddressSpace, but it must lie within +- * a #MemoryRegion that was mapped with address_space_cache_init. +- * +- * The _le suffixed functions treat the data as little endian; +- * _be indicates big endian; no suffix indicates "same endianness +- * as guest CPU". +- * +- * The "guest CPU endianness" accessors are deprecated for use outside +- * target-* code; devices should be CPU-agnostic and use either the LE +- * or the BE accessors. +- * +- * @cache: previously initialized #MemoryRegionCache to be accessed +- * @addr: address within the address space +- * @val: data value, for stores +- * @attrs: memory transaction attributes +- * @result: location to write the success/failure of the transaction; +- * if NULL, this information is discarded +- */ +-uint32_t address_space_ldub_cached(MemoryRegionCache *cache, hwaddr addr, +- MemTxAttrs attrs, MemTxResult *result); +-uint32_t address_space_lduw_le_cached(MemoryRegionCache *cache, hwaddr addr, +- MemTxAttrs attrs, MemTxResult *result); +-uint32_t address_space_lduw_be_cached(MemoryRegionCache *cache, hwaddr addr, +- MemTxAttrs attrs, MemTxResult *result); +-uint32_t address_space_ldl_le_cached(MemoryRegionCache *cache, hwaddr addr, +- MemTxAttrs attrs, MemTxResult *result); +-uint32_t address_space_ldl_be_cached(MemoryRegionCache *cache, hwaddr addr, +- MemTxAttrs attrs, MemTxResult *result); +-uint64_t address_space_ldq_le_cached(MemoryRegionCache *cache, hwaddr addr, +- MemTxAttrs attrs, MemTxResult *result); +-uint64_t address_space_ldq_be_cached(MemoryRegionCache *cache, hwaddr addr, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stb_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stw_le_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stw_be_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stl_le_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stl_be_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stq_le_cached(MemoryRegionCache *cache, hwaddr addr, uint64_t val, +- MemTxAttrs attrs, MemTxResult *result); +-void address_space_stq_be_cached(MemoryRegionCache *cache, hwaddr addr, uint64_t val, +- MemTxAttrs attrs, MemTxResult *result); +- +-uint32_t ldub_phys_cached(MemoryRegionCache *cache, hwaddr addr); +-uint32_t lduw_le_phys_cached(MemoryRegionCache *cache, hwaddr addr); +-uint32_t lduw_be_phys_cached(MemoryRegionCache *cache, hwaddr addr); +-uint32_t ldl_le_phys_cached(MemoryRegionCache *cache, hwaddr addr); +-uint32_t ldl_be_phys_cached(MemoryRegionCache *cache, hwaddr addr); +-uint64_t ldq_le_phys_cached(MemoryRegionCache *cache, hwaddr addr); +-uint64_t ldq_be_phys_cached(MemoryRegionCache *cache, hwaddr addr); +-void stb_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); +-void stw_le_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); +-void stw_be_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); +-void stl_le_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); +-void stl_be_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); +-void stq_le_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint64_t val); +-void stq_be_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint64_t val); + /* address_space_get_iotlb_entry: translate an address into an IOTLB + * entry. Should be called from an RCU critical section. + */ +diff --git a/include/exec/memory_ldst.inc.h b/include/exec/memory_ldst.inc.h +new file mode 100644 +index 0000000..272c20f +--- /dev/null ++++ b/include/exec/memory_ldst.inc.h +@@ -0,0 +1,71 @@ ++/* ++ * Physical memory access templates ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * Copyright (c) 2015 Linaro, Inc. ++ * 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 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 . ++ */ ++ ++#ifdef TARGET_ENDIANNESS ++extern uint32_t glue(address_space_lduw, SUFFIX)(ARG1_DECL, ++ hwaddr addr, MemTxAttrs attrs, MemTxResult *result); ++extern uint32_t glue(address_space_ldl, SUFFIX)(ARG1_DECL, ++ hwaddr addr, MemTxAttrs attrs, MemTxResult *result); ++extern uint64_t glue(address_space_ldq, SUFFIX)(ARG1_DECL, ++ hwaddr addr, MemTxAttrs attrs, MemTxResult *result); ++extern void glue(address_space_stl_notdirty, SUFFIX)(ARG1_DECL, ++ hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); ++extern void glue(address_space_stw, SUFFIX)(ARG1_DECL, ++ hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); ++extern void glue(address_space_stl, SUFFIX)(ARG1_DECL, ++ hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); ++extern void glue(address_space_stq, SUFFIX)(ARG1_DECL, ++ hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result); ++#else ++extern uint32_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, ++ hwaddr addr, MemTxAttrs attrs, MemTxResult *result); ++extern uint32_t glue(address_space_lduw_le, SUFFIX)(ARG1_DECL, ++ hwaddr addr, MemTxAttrs attrs, MemTxResult *result); ++extern uint32_t glue(address_space_lduw_be, SUFFIX)(ARG1_DECL, ++ hwaddr addr, MemTxAttrs attrs, MemTxResult *result); ++extern uint32_t glue(address_space_ldl_le, SUFFIX)(ARG1_DECL, ++ hwaddr addr, MemTxAttrs attrs, MemTxResult *result); ++extern uint32_t glue(address_space_ldl_be, SUFFIX)(ARG1_DECL, ++ hwaddr addr, MemTxAttrs attrs, MemTxResult *result); ++extern uint64_t glue(address_space_ldq_le, SUFFIX)(ARG1_DECL, ++ hwaddr addr, MemTxAttrs attrs, MemTxResult *result); ++extern uint64_t glue(address_space_ldq_be, SUFFIX)(ARG1_DECL, ++ hwaddr addr, MemTxAttrs attrs, MemTxResult *result); ++extern void glue(address_space_stb, SUFFIX)(ARG1_DECL, ++ hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); ++extern void glue(address_space_stw_le, SUFFIX)(ARG1_DECL, ++ hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); ++extern void glue(address_space_stw_be, SUFFIX)(ARG1_DECL, ++ hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); ++extern void glue(address_space_stl_le, SUFFIX)(ARG1_DECL, ++ hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); ++extern void glue(address_space_stl_be, SUFFIX)(ARG1_DECL, ++ hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); ++extern void glue(address_space_stq_le, SUFFIX)(ARG1_DECL, ++ hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result); ++extern void glue(address_space_stq_be, SUFFIX)(ARG1_DECL, ++ hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result); ++#endif ++ ++#undef ARG1_DECL ++#undef ARG1 ++#undef SUFFIX ++#undef TARGET_ENDIANNESS +diff --git a/include/exec/memory_ldst_phys.inc.h b/include/exec/memory_ldst_phys.inc.h +new file mode 100644 +index 0000000..91f7297 +--- /dev/null ++++ b/include/exec/memory_ldst_phys.inc.h +@@ -0,0 +1,147 @@ ++/* ++ * Physical memory access templates ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * Copyright (c) 2015 Linaro, Inc. ++ * 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 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 . ++ */ ++ ++#ifdef TARGET_ENDIANNESS ++static inline uint32_t glue(ldl_phys, SUFFIX)(ARG1_DECL, hwaddr addr) ++{ ++ return glue(address_space_ldl, SUFFIX)(ARG1, addr, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++ ++static inline uint64_t glue(ldq_phys, SUFFIX)(ARG1_DECL, hwaddr addr) ++{ ++ return glue(address_space_ldq, SUFFIX)(ARG1, addr, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++ ++static inline uint32_t glue(lduw_phys, SUFFIX)(ARG1_DECL, hwaddr addr) ++{ ++ return glue(address_space_lduw, SUFFIX)(ARG1, addr, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++ ++static inline void glue(stl_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) ++{ ++ glue(address_space_stl, SUFFIX)(ARG1, addr, val, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++ ++static inline void glue(stw_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) ++{ ++ glue(address_space_stw, SUFFIX)(ARG1, addr, val, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++ ++static inline void glue(stq_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val) ++{ ++ glue(address_space_stq, SUFFIX)(ARG1, addr, val, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++#else ++static inline uint32_t glue(ldl_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr) ++{ ++ return glue(address_space_ldl_le, SUFFIX)(ARG1, addr, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++ ++static inline uint32_t glue(ldl_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr) ++{ ++ return glue(address_space_ldl_be, SUFFIX)(ARG1, addr, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++ ++static inline uint64_t glue(ldq_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr) ++{ ++ return glue(address_space_ldq_le, SUFFIX)(ARG1, addr, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++ ++static inline uint64_t glue(ldq_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr) ++{ ++ return glue(address_space_ldq_be, SUFFIX)(ARG1, addr, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++ ++static inline uint32_t glue(ldub_phys, SUFFIX)(ARG1_DECL, hwaddr addr) ++{ ++ return glue(address_space_ldub, SUFFIX)(ARG1, addr, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++ ++static inline uint32_t glue(lduw_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr) ++{ ++ return glue(address_space_lduw_le, SUFFIX)(ARG1, addr, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++ ++static inline uint32_t glue(lduw_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr) ++{ ++ return glue(address_space_lduw_be, SUFFIX)(ARG1, addr, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++ ++static inline void glue(stl_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) ++{ ++ glue(address_space_stl_le, SUFFIX)(ARG1, addr, val, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++ ++static inline void glue(stl_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) ++{ ++ glue(address_space_stl_be, SUFFIX)(ARG1, addr, val, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++ ++static inline void glue(stb_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) ++{ ++ glue(address_space_stb, SUFFIX)(ARG1, addr, val, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++ ++static inline void glue(stw_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) ++{ ++ glue(address_space_stw_le, SUFFIX)(ARG1, addr, val, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++ ++static inline void glue(stw_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) ++{ ++ glue(address_space_stw_be, SUFFIX)(ARG1, addr, val, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++ ++static inline void glue(stq_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val) ++{ ++ glue(address_space_stq_le, SUFFIX)(ARG1, addr, val, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++ ++static inline void glue(stq_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val) ++{ ++ glue(address_space_stq_be, SUFFIX)(ARG1, addr, val, ++ MEMTXATTRS_UNSPECIFIED, NULL); ++} ++#endif ++ ++#undef ARG1_DECL ++#undef ARG1 ++#undef SUFFIX ++#undef TARGET_ENDIANNESS +diff --git a/memory_ldst.inc.c b/memory_ldst.inc.c +index 5dbff9c..25d6125 100644 +--- a/memory_ldst.inc.c ++++ b/memory_ldst.inc.c +@@ -95,24 +95,6 @@ uint32_t glue(address_space_ldl_be, SUFFIX)(ARG1_DECL, + DEVICE_BIG_ENDIAN); + } + +-uint32_t glue(ldl_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +-{ +- return glue(address_space_ldl, SUFFIX)(ARG1, addr, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- +-uint32_t glue(ldl_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +-{ +- return glue(address_space_ldl_le, SUFFIX)(ARG1, addr, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- +-uint32_t glue(ldl_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +-{ +- return glue(address_space_ldl_be, SUFFIX)(ARG1, addr, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- + /* warning: addr must be aligned */ + static inline uint64_t glue(address_space_ldq_internal, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result, +@@ -189,24 +171,6 @@ uint64_t glue(address_space_ldq_be, SUFFIX)(ARG1_DECL, + DEVICE_BIG_ENDIAN); + } + +-uint64_t glue(ldq_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +-{ +- return glue(address_space_ldq, SUFFIX)(ARG1, addr, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- +-uint64_t glue(ldq_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +-{ +- return glue(address_space_ldq_le, SUFFIX)(ARG1, addr, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- +-uint64_t glue(ldq_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +-{ +- return glue(address_space_ldq_be, SUFFIX)(ARG1, addr, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- + uint32_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result) + { +@@ -241,12 +205,6 @@ uint32_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, + return val; + } + +-uint32_t glue(ldub_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +-{ +- return glue(address_space_ldub, SUFFIX)(ARG1, addr, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- + /* warning: addr must be aligned */ + static inline uint32_t glue(address_space_lduw_internal, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result, +@@ -323,24 +281,6 @@ uint32_t glue(address_space_lduw_be, SUFFIX)(ARG1_DECL, + DEVICE_BIG_ENDIAN); + } + +-uint32_t glue(lduw_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +-{ +- return glue(address_space_lduw, SUFFIX)(ARG1, addr, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- +-uint32_t glue(lduw_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +-{ +- return glue(address_space_lduw_le, SUFFIX)(ARG1, addr, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- +-uint32_t glue(lduw_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +-{ +- return glue(address_space_lduw_be, SUFFIX)(ARG1, addr, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- + /* warning: addr must be aligned. The ram page is not masked as dirty + and the code inside is not invalidated. It is useful if the dirty + bits are used to track modified PTEs */ +@@ -380,12 +320,6 @@ void glue(address_space_stl_notdirty, SUFFIX)(ARG1_DECL, + RCU_READ_UNLOCK(); + } + +-void glue(stl_phys_notdirty, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +-{ +- glue(address_space_stl_notdirty, SUFFIX)(ARG1, addr, val, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- + /* warning: addr must be aligned */ + static inline void glue(address_space_stl_internal, SUFFIX)(ARG1_DECL, + hwaddr addr, uint32_t val, MemTxAttrs attrs, +@@ -460,24 +394,6 @@ void glue(address_space_stl_be, SUFFIX)(ARG1_DECL, + result, DEVICE_BIG_ENDIAN); + } + +-void glue(stl_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +-{ +- glue(address_space_stl, SUFFIX)(ARG1, addr, val, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- +-void glue(stl_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +-{ +- glue(address_space_stl_le, SUFFIX)(ARG1, addr, val, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- +-void glue(stl_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +-{ +- glue(address_space_stl_be, SUFFIX)(ARG1, addr, val, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- + void glue(address_space_stb, SUFFIX)(ARG1_DECL, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result) + { +@@ -509,12 +425,6 @@ void glue(address_space_stb, SUFFIX)(ARG1_DECL, + RCU_READ_UNLOCK(); + } + +-void glue(stb_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +-{ +- glue(address_space_stb, SUFFIX)(ARG1, addr, val, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- + /* warning: addr must be aligned */ + static inline void glue(address_space_stw_internal, SUFFIX)(ARG1_DECL, + hwaddr addr, uint32_t val, MemTxAttrs attrs, +@@ -589,24 +499,6 @@ void glue(address_space_stw_be, SUFFIX)(ARG1_DECL, + DEVICE_BIG_ENDIAN); + } + +-void glue(stw_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +-{ +- glue(address_space_stw, SUFFIX)(ARG1, addr, val, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- +-void glue(stw_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +-{ +- glue(address_space_stw_le, SUFFIX)(ARG1, addr, val, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- +-void glue(stw_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +-{ +- glue(address_space_stw_be, SUFFIX)(ARG1, addr, val, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- + static void glue(address_space_stq_internal, SUFFIX)(ARG1_DECL, + hwaddr addr, uint64_t val, MemTxAttrs attrs, + MemTxResult *result, enum device_endian endian) +@@ -680,24 +572,6 @@ void glue(address_space_stq_be, SUFFIX)(ARG1_DECL, + DEVICE_BIG_ENDIAN); + } + +-void glue(stq_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val) +-{ +- glue(address_space_stq, SUFFIX)(ARG1, addr, val, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- +-void glue(stq_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val) +-{ +- glue(address_space_stq_le, SUFFIX)(ARG1, addr, val, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- +-void glue(stq_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val) +-{ +- glue(address_space_stq_be, SUFFIX)(ARG1, addr, val, +- MEMTXATTRS_UNSPECIFIED, NULL); +-} +- + #undef ARG1_DECL + #undef ARG1 + #undef SUFFIX +-- +1.8.3.1 + diff --git a/SOURCES/kvm-exec-reintroduce-MemoryRegion-caching.patch b/SOURCES/kvm-exec-reintroduce-MemoryRegion-caching.patch new file mode 100644 index 0000000..d108bb5 --- /dev/null +++ b/SOURCES/kvm-exec-reintroduce-MemoryRegion-caching.patch @@ -0,0 +1,473 @@ +From c879b300ff1852e5102a787372cd949f6755f5ce Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 25 Jan 2019 22:50:04 +0100 +Subject: [PATCH 04/23] exec: reintroduce MemoryRegion caching + +RH-Author: John Snow +Message-id: <20190125225007.8197-5-jsnow@redhat.com> +Patchwork-id: 84120 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 4/7] exec: reintroduce MemoryRegion caching +Bugzilla: 1597482 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Peter Xu +RH-Acked-by: Stefano Garzarella + +From: Paolo Bonzini + +MemoryRegionCache was reverted to "normal" address_space_* operations +for 2.9, due to lack of support for IOMMUs. Reinstate the +optimizations, caching only the IOMMU translation at address_cache_init +but not the IOMMU lookup and target AddressSpace translation are not +cached; now that MemoryRegionCache supports IOMMUs, it becomes more widely +applicable too. + +The inlined fast path is defined in memory_ldst_cached.inc.h, while the +slow path uses memory_ldst.inc.c as before. The smaller fast path causes +a little code size reduction in MemoryRegionCache users: + + hw/virtio/virtio.o text size before: 32373 + hw/virtio/virtio.o text size after: 31941 + +Signed-off-by: Paolo Bonzini +(cherry picked from commit 48564041a73adbbff52834f9edbe3806fceefab7) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + exec.c | 121 ++++++++++++++++++++++++++++++---- + include/exec/cpu-all.h | 6 +- + include/exec/memory-internal.h | 3 + + include/exec/memory.h | 58 ++++++++++++++-- + include/exec/memory_ldst_cached.inc.h | 108 ++++++++++++++++++++++++++++++ + memory.c | 4 +- + 6 files changed, 280 insertions(+), 20 deletions(-) + create mode 100644 include/exec/memory_ldst_cached.inc.h + +diff --git a/exec.c b/exec.c +index 1bd0e6c..805a2d4 100644 +--- a/exec.c ++++ b/exec.c +@@ -3656,33 +3656,130 @@ int64_t address_space_cache_init(MemoryRegionCache *cache, + hwaddr len, + bool is_write) + { +- cache->len = len; +- cache->as = as; +- cache->xlat = addr; +- return len; ++ AddressSpaceDispatch *d; ++ hwaddr l; ++ MemoryRegion *mr; ++ ++ assert(len > 0); ++ ++ l = len; ++ cache->fv = address_space_get_flatview(as); ++ d = flatview_to_dispatch(cache->fv); ++ cache->mrs = *address_space_translate_internal(d, addr, &cache->xlat, &l, true); ++ ++ mr = cache->mrs.mr; ++ memory_region_ref(mr); ++ if (memory_access_is_direct(mr, is_write)) { ++ l = flatview_extend_translation(cache->fv, addr, len, mr, ++ cache->xlat, l, is_write); ++ cache->ptr = qemu_ram_ptr_length(mr->ram_block, cache->xlat, &l, true); ++ } else { ++ cache->ptr = NULL; ++ } ++ ++ cache->len = l; ++ cache->is_write = is_write; ++ return l; + } + + void address_space_cache_invalidate(MemoryRegionCache *cache, + hwaddr addr, + hwaddr access_len) + { ++ assert(cache->is_write); ++ if (likely(cache->ptr)) { ++ invalidate_and_set_dirty(cache->mrs.mr, addr + cache->xlat, access_len); ++ } + } + + void address_space_cache_destroy(MemoryRegionCache *cache) + { +- cache->as = NULL; ++ if (!cache->mrs.mr) { ++ return; ++ } ++ ++ if (xen_enabled()) { ++ xen_invalidate_map_cache_entry(cache->ptr); ++ } ++ memory_region_unref(cache->mrs.mr); ++ flatview_unref(cache->fv); ++ cache->mrs.mr = NULL; ++ cache->fv = NULL; ++} ++ ++/* Called from RCU critical section. This function has the same ++ * semantics as address_space_translate, but it only works on a ++ * predefined range of a MemoryRegion that was mapped with ++ * address_space_cache_init. ++ */ ++static inline MemoryRegion *address_space_translate_cached( ++ MemoryRegionCache *cache, hwaddr addr, hwaddr *xlat, ++ hwaddr *plen, bool is_write) ++{ ++ MemoryRegionSection section; ++ MemoryRegion *mr; ++ IOMMUMemoryRegion *iommu_mr; ++ AddressSpace *target_as; ++ ++ assert(!cache->ptr); ++ *xlat = addr + cache->xlat; ++ ++ mr = cache->mrs.mr; ++ iommu_mr = memory_region_get_iommu(mr); ++ if (!iommu_mr) { ++ /* MMIO region. */ ++ return mr; ++ } ++ ++ section = address_space_translate_iommu(iommu_mr, xlat, plen, ++ NULL, is_write, true, ++ &target_as); ++ return section.mr; ++} ++ ++/* Called from RCU critical section. address_space_read_cached uses this ++ * out of line function when the target is an MMIO or IOMMU region. ++ */ ++void ++address_space_read_cached_slow(MemoryRegionCache *cache, hwaddr addr, ++ void *buf, int len) ++{ ++ hwaddr addr1, l; ++ MemoryRegion *mr; ++ ++ l = len; ++ mr = address_space_translate_cached(cache, addr, &addr1, &l, false); ++ flatview_read_continue(cache->fv, ++ addr, MEMTXATTRS_UNSPECIFIED, buf, len, ++ addr1, l, mr); ++} ++ ++/* Called from RCU critical section. address_space_write_cached uses this ++ * out of line function when the target is an MMIO or IOMMU region. ++ */ ++void ++address_space_write_cached_slow(MemoryRegionCache *cache, hwaddr addr, ++ const void *buf, int len) ++{ ++ hwaddr addr1, l; ++ MemoryRegion *mr; ++ ++ l = len; ++ mr = address_space_translate_cached(cache, addr, &addr1, &l, true); ++ flatview_write_continue(cache->fv, ++ addr, MEMTXATTRS_UNSPECIFIED, buf, len, ++ addr1, l, mr); + } + + #define ARG1_DECL MemoryRegionCache *cache + #define ARG1 cache +-#define SUFFIX _cached +-#define TRANSLATE(addr, ...) \ +- address_space_translate(cache->as, cache->xlat + (addr), __VA_ARGS__) +-#define IS_DIRECT(mr, is_write) true +-#define MAP_RAM(mr, ofs) qemu_map_ram_ptr((mr)->ram_block, ofs) ++#define SUFFIX _cached_slow ++#define TRANSLATE(...) address_space_translate_cached(cache, __VA_ARGS__) ++#define IS_DIRECT(mr, is_write) memory_access_is_direct(mr, is_write) ++#define MAP_RAM(mr, ofs) (cache->ptr + (ofs - cache->xlat)) + #define INVALIDATE(mr, ofs, len) invalidate_and_set_dirty(mr, ofs, len) +-#define RCU_READ_LOCK() rcu_read_lock() +-#define RCU_READ_UNLOCK() rcu_read_unlock() ++#define RCU_READ_LOCK() ((void)0) ++#define RCU_READ_UNLOCK() ((void)0) + #include "memory_ldst.inc.c" + + /* virtual memory access for debug (includes writing to ROM) */ +diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h +index 173edd1..a635f53 100644 +--- a/include/exec/cpu-all.h ++++ b/include/exec/cpu-all.h +@@ -175,7 +175,7 @@ extern unsigned long reserved_va; + #define TARGET_ENDIANNESS + #include "exec/memory_ldst.inc.h" + +-#define SUFFIX _cached ++#define SUFFIX _cached_slow + #define ARG1 cache + #define ARG1_DECL MemoryRegionCache *cache + #define TARGET_ENDIANNESS +@@ -193,6 +193,10 @@ static inline void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val + #define TARGET_ENDIANNESS + #include "exec/memory_ldst_phys.inc.h" + ++/* Inline fast path for direct RAM access. */ ++#define ENDIANNESS ++#include "exec/memory_ldst_cached.inc.h" ++ + #define SUFFIX _cached + #define ARG1 cache + #define ARG1_DECL MemoryRegionCache *cache +diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h +index 6a5ee42..58399b9 100644 +--- a/include/exec/memory-internal.h ++++ b/include/exec/memory-internal.h +@@ -31,6 +31,9 @@ static inline AddressSpaceDispatch *address_space_to_dispatch(AddressSpace *as) + return flatview_to_dispatch(address_space_to_flatview(as)); + } + ++FlatView *address_space_get_flatview(AddressSpace *as); ++void flatview_unref(FlatView *view); ++ + extern const MemoryRegionOps unassigned_mem_ops; + + bool memory_region_access_valid(MemoryRegion *mr, hwaddr addr, +diff --git a/include/exec/memory.h b/include/exec/memory.h +index 84de0d4..96f1fd8 100644 +--- a/include/exec/memory.h ++++ b/include/exec/memory.h +@@ -1715,12 +1715,16 @@ MemTxResult address_space_write(AddressSpace *as, hwaddr addr, + #include "exec/memory_ldst_phys.inc.h" + + struct MemoryRegionCache { ++ void *ptr; + hwaddr xlat; + hwaddr len; +- AddressSpace *as; ++ FlatView *fv; ++ MemoryRegionSection mrs; ++ bool is_write; + }; + +-#define MEMORY_REGION_CACHE_INVALID ((MemoryRegionCache) { .as = NULL }) ++#define MEMORY_REGION_CACHE_INVALID ((MemoryRegionCache) { .mrs.mr = NULL }) ++ + + /* address_space_ld*_cached: load from a cached #MemoryRegion + * address_space_st*_cached: store into a cached #MemoryRegion +@@ -1746,11 +1750,40 @@ struct MemoryRegionCache { + * if NULL, this information is discarded + */ + +-#define SUFFIX _cached ++#define SUFFIX _cached_slow + #define ARG1 cache + #define ARG1_DECL MemoryRegionCache *cache + #include "exec/memory_ldst.inc.h" + ++/* Inline fast path for direct RAM access. */ ++static inline uint8_t address_space_ldub_cached(MemoryRegionCache *cache, ++ hwaddr addr, MemTxAttrs attrs, MemTxResult *result) ++{ ++ assert(addr < cache->len); ++ if (likely(cache->ptr)) { ++ return ldub_p(cache->ptr + addr); ++ } else { ++ return address_space_ldub_cached_slow(cache, addr, attrs, result); ++ } ++} ++ ++static inline void address_space_stb_cached(MemoryRegionCache *cache, ++ hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result) ++{ ++ assert(addr < cache->len); ++ if (likely(cache->ptr)) { ++ stb_p(cache->ptr + addr, val); ++ } else { ++ address_space_stb_cached_slow(cache, addr, val, attrs, result); ++ } ++} ++ ++#define ENDIANNESS _le ++#include "exec/memory_ldst_cached.inc.h" ++ ++#define ENDIANNESS _be ++#include "exec/memory_ldst_cached.inc.h" ++ + #define SUFFIX _cached + #define ARG1 cache + #define ARG1_DECL MemoryRegionCache *cache +@@ -1887,6 +1920,13 @@ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, + MemoryRegion *mr); + void *qemu_map_ram_ptr(RAMBlock *ram_block, ram_addr_t addr); + ++/* Internal functions, part of the implementation of address_space_read_cached ++ * and address_space_write_cached. */ ++void address_space_read_cached_slow(MemoryRegionCache *cache, ++ hwaddr addr, void *buf, int len); ++void address_space_write_cached_slow(MemoryRegionCache *cache, ++ hwaddr addr, const void *buf, int len); ++ + static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) + { + if (is_write) { +@@ -1955,7 +1995,11 @@ address_space_read_cached(MemoryRegionCache *cache, hwaddr addr, + void *buf, int len) + { + assert(addr < cache->len && len <= cache->len - addr); +- address_space_read(cache->as, cache->xlat + addr, MEMTXATTRS_UNSPECIFIED, buf, len); ++ if (likely(cache->ptr)) { ++ memcpy(buf, cache->ptr + addr, len); ++ } else { ++ address_space_read_cached_slow(cache, addr, buf, len); ++ } + } + + /** +@@ -1971,7 +2015,11 @@ address_space_write_cached(MemoryRegionCache *cache, hwaddr addr, + void *buf, int len) + { + assert(addr < cache->len && len <= cache->len - addr); +- address_space_write(cache->as, cache->xlat + addr, MEMTXATTRS_UNSPECIFIED, buf, len); ++ if (likely(cache->ptr)) { ++ memcpy(cache->ptr + addr, buf, len); ++ } else { ++ address_space_write_cached_slow(cache, addr, buf, len); ++ } + } + + #endif +diff --git a/include/exec/memory_ldst_cached.inc.h b/include/exec/memory_ldst_cached.inc.h +new file mode 100644 +index 0000000..fd4bbb4 +--- /dev/null ++++ b/include/exec/memory_ldst_cached.inc.h +@@ -0,0 +1,108 @@ ++/* ++ * Memory access templates for MemoryRegionCache ++ * ++ * 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 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 . ++ */ ++ ++#define ADDRESS_SPACE_LD_CACHED(size) \ ++ glue(glue(address_space_ld, size), glue(ENDIANNESS, _cached)) ++#define ADDRESS_SPACE_LD_CACHED_SLOW(size) \ ++ glue(glue(address_space_ld, size), glue(ENDIANNESS, _cached_slow)) ++#define LD_P(size) \ ++ glue(glue(ld, size), glue(ENDIANNESS, _p)) ++ ++static inline uint32_t ADDRESS_SPACE_LD_CACHED(l)(MemoryRegionCache *cache, ++ hwaddr addr, MemTxAttrs attrs, MemTxResult *result) ++{ ++ assert(addr < cache->len && 4 <= cache->len - addr); ++ if (likely(cache->ptr)) { ++ return LD_P(l)(cache->ptr + addr); ++ } else { ++ return ADDRESS_SPACE_LD_CACHED_SLOW(l)(cache, addr, attrs, result); ++ } ++} ++ ++static inline uint64_t ADDRESS_SPACE_LD_CACHED(q)(MemoryRegionCache *cache, ++ hwaddr addr, MemTxAttrs attrs, MemTxResult *result) ++{ ++ assert(addr < cache->len && 8 <= cache->len - addr); ++ if (likely(cache->ptr)) { ++ return LD_P(q)(cache->ptr + addr); ++ } else { ++ return ADDRESS_SPACE_LD_CACHED_SLOW(q)(cache, addr, attrs, result); ++ } ++} ++ ++static inline uint32_t ADDRESS_SPACE_LD_CACHED(uw)(MemoryRegionCache *cache, ++ hwaddr addr, MemTxAttrs attrs, MemTxResult *result) ++{ ++ assert(addr < cache->len && 2 <= cache->len - addr); ++ if (likely(cache->ptr)) { ++ return LD_P(uw)(cache->ptr + addr); ++ } else { ++ return ADDRESS_SPACE_LD_CACHED_SLOW(uw)(cache, addr, attrs, result); ++ } ++} ++ ++#undef ADDRESS_SPACE_LD_CACHED ++#undef ADDRESS_SPACE_LD_CACHED_SLOW ++#undef LD_P ++ ++#define ADDRESS_SPACE_ST_CACHED(size) \ ++ glue(glue(address_space_st, size), glue(ENDIANNESS, _cached)) ++#define ADDRESS_SPACE_ST_CACHED_SLOW(size) \ ++ glue(glue(address_space_st, size), glue(ENDIANNESS, _cached_slow)) ++#define ST_P(size) \ ++ glue(glue(st, size), glue(ENDIANNESS, _p)) ++ ++static inline void ADDRESS_SPACE_ST_CACHED(l)(MemoryRegionCache *cache, ++ hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result) ++{ ++ assert(addr < cache->len && 4 <= cache->len - addr); ++ if (likely(cache->ptr)) { ++ ST_P(l)(cache->ptr + addr, val); ++ } else { ++ ADDRESS_SPACE_ST_CACHED_SLOW(l)(cache, addr, val, attrs, result); ++ } ++} ++ ++static inline void ADDRESS_SPACE_ST_CACHED(w)(MemoryRegionCache *cache, ++ hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result) ++{ ++ assert(addr < cache->len && 2 <= cache->len - addr); ++ if (likely(cache->ptr)) { ++ ST_P(w)(cache->ptr + addr, val); ++ } else { ++ ADDRESS_SPACE_ST_CACHED_SLOW(w)(cache, addr, val, attrs, result); ++ } ++} ++ ++static inline void ADDRESS_SPACE_ST_CACHED(q)(MemoryRegionCache *cache, ++ hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result) ++{ ++ assert(addr < cache->len && 8 <= cache->len - addr); ++ if (likely(cache->ptr)) { ++ ST_P(q)(cache->ptr + addr, val); ++ } else { ++ ADDRESS_SPACE_ST_CACHED_SLOW(q)(cache, addr, val, attrs, result); ++ } ++} ++ ++#undef ADDRESS_SPACE_ST_CACHED ++#undef ADDRESS_SPACE_ST_CACHED_SLOW ++#undef ST_P ++ ++#undef ENDIANNESS +diff --git a/memory.c b/memory.c +index 4974f97..1e90912 100644 +--- a/memory.c ++++ b/memory.c +@@ -298,7 +298,7 @@ static bool flatview_ref(FlatView *view) + return atomic_fetch_inc_nonzero(&view->ref) > 0; + } + +-static void flatview_unref(FlatView *view) ++void flatview_unref(FlatView *view) + { + if (atomic_fetch_dec(&view->ref) == 1) { + trace_flatview_destroy_rcu(view, view->root); +@@ -822,7 +822,7 @@ static void address_space_add_del_ioeventfds(AddressSpace *as, + } + } + +-static FlatView *address_space_get_flatview(AddressSpace *as) ++FlatView *address_space_get_flatview(AddressSpace *as) + { + FlatView *view; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-exec-small-changes-to-flatview_do_translate.patch b/SOURCES/kvm-exec-small-changes-to-flatview_do_translate.patch new file mode 100644 index 0000000..e6eeeab --- /dev/null +++ b/SOURCES/kvm-exec-small-changes-to-flatview_do_translate.patch @@ -0,0 +1,113 @@ +From 035c7c0a1c88284d2628532a70f43c04aa0aba56 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 25 Jan 2019 22:50:02 +0100 +Subject: [PATCH 02/23] exec: small changes to flatview_do_translate +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190125225007.8197-3-jsnow@redhat.com> +Patchwork-id: 84117 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 2/7] exec: small changes to flatview_do_translate +Bugzilla: 1597482 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Peter Xu +RH-Acked-by: Stefano Garzarella + +From: Paolo Bonzini + +Prepare for extracting the IOMMU part to a separate function. Mostly +cosmetic; the only semantic change is that, if there is more than one +cascaded IOMMU and the second one fails to translate, *plen_out is now +adjusted according to the page mask of the first IOMMU. + +Reviewed-by: Peter Xu +Signed-off-by: Paolo Bonzini +(cherry picked from commit ad2804d9e47df2dab642a253502b5ceef233f450) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + exec.c | 33 ++++++++++++++------------------- + 1 file changed, 14 insertions(+), 19 deletions(-) + +diff --git a/exec.c b/exec.c +index 9028700..c6aeded 100644 +--- a/exec.c ++++ b/exec.c +@@ -459,6 +459,7 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x + * would tell. It can be @NULL if we don't care about it. + * @is_write: whether the translation operation is for write + * @is_mmio: whether this can be MMIO, set true if it can ++ * @target_as: the address space targeted by the IOMMU + * + * This function is called from RCU critical section + */ +@@ -478,14 +479,14 @@ static MemoryRegionSection flatview_do_translate(FlatView *fv, + hwaddr page_mask = (hwaddr)(-1); + hwaddr plen = (hwaddr)(-1); + +- if (plen_out) { +- plen = *plen_out; ++ if (!plen_out) { ++ plen_out = &plen; + } + + for (;;) { + section = address_space_translate_internal( +- flatview_to_dispatch(fv), addr, &addr, +- &plen, is_mmio); ++ flatview_to_dispatch(fv), addr, xlat, ++ plen_out, is_mmio); + + iommu_mr = memory_region_get_iommu(section->mr); + if (!iommu_mr) { +@@ -493,35 +494,29 @@ static MemoryRegionSection flatview_do_translate(FlatView *fv, + } + imrc = memory_region_get_iommu_class_nocheck(iommu_mr); + ++ addr = *xlat; + iotlb = imrc->translate(iommu_mr, addr, is_write ? + IOMMU_WO : IOMMU_RO); +- addr = ((iotlb.translated_addr & ~iotlb.addr_mask) +- | (addr & iotlb.addr_mask)); +- page_mask &= iotlb.addr_mask; +- plen = MIN(plen, (addr | iotlb.addr_mask) - addr + 1); + if (!(iotlb.perm & (1 << is_write))) { + goto translate_fail; + } + ++ addr = ((iotlb.translated_addr & ~iotlb.addr_mask) ++ | (addr & iotlb.addr_mask)); ++ page_mask &= iotlb.addr_mask; ++ *plen_out = MIN(*plen_out, (addr | iotlb.addr_mask) - addr + 1); + fv = address_space_to_flatview(iotlb.target_as); + *target_as = iotlb.target_as; + } + +- *xlat = addr; +- +- if (page_mask == (hwaddr)(-1)) { +- /* Not behind an IOMMU, use default page size. */ +- page_mask = ~TARGET_PAGE_MASK; +- } +- + if (page_mask_out) { ++ if (page_mask == (hwaddr)(-1)) { ++ /* Not behind an IOMMU, use default page size. */ ++ page_mask = ~TARGET_PAGE_MASK; ++ } + *page_mask_out = page_mask; + } + +- if (plen_out) { +- *plen_out = plen; +- } +- + return *section; + + translate_fail: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Avoid-aio_worker-for-QEMU_AIO_WRITE_ZEROE.patch b/SOURCES/kvm-file-posix-Avoid-aio_worker-for-QEMU_AIO_WRITE_ZEROE.patch new file mode 100644 index 0000000..48fed01 --- /dev/null +++ b/SOURCES/kvm-file-posix-Avoid-aio_worker-for-QEMU_AIO_WRITE_ZEROE.patch @@ -0,0 +1,147 @@ +From 207dc8620ffb1a1bdbfaddba5208a82f93058b2c Mon Sep 17 00:00:00 2001 +From: Maxim Levitsky +Date: Wed, 5 Jun 2019 13:57:02 +0200 +Subject: [PATCH 14/23] file-posix: Avoid aio_worker() for + QEMU_AIO_WRITE_ZEROES + +RH-Author: Maxim Levitsky +Message-id: <20190605135705.24526-7-mlevitsk@redhat.com> +Patchwork-id: 88561 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 6/9] file-posix: Avoid aio_worker() for QEMU_AIO_WRITE_ZEROES +Bugzilla: 1648622 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow + +From: Kevin Wolf + +aio_worker() doesn't add anything interesting, it's only a useless +indirection. Call the handler function directly instead. + +As we know that this handler function is only called from coroutine +context and the coroutine stays around until the worker thread finishes, +we can keep RawPosixAIOData on the stack. + +Signed-off-by: Kevin Wolf + +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1648622 + +Signed-off-by: Maxim Levitsky +(Cherry picked from 7154d8ae66c75c97b08c8f1c80dd6f46f0dbffca) + +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 53 ++++++++++++++++++++++++++++++++++------------------- + 1 file changed, 34 insertions(+), 19 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index 74da336..90c719f 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -1465,8 +1465,9 @@ static ssize_t handle_aiocb_write_zeroes_block(RawPosixAIOData *aiocb) + return ret; + } + +-static ssize_t handle_aiocb_write_zeroes(RawPosixAIOData *aiocb) ++static int handle_aiocb_write_zeroes(void *opaque) + { ++ RawPosixAIOData *aiocb = opaque; + #if defined(CONFIG_FALLOCATE) || defined(CONFIG_XFS) + BDRVRawState *s = aiocb->bs->opaque; + #endif +@@ -1530,8 +1531,9 @@ static ssize_t handle_aiocb_write_zeroes(RawPosixAIOData *aiocb) + return -ENOTSUP; + } + +-static ssize_t handle_aiocb_write_zeroes_unmap(RawPosixAIOData *aiocb) ++static int handle_aiocb_write_zeroes_unmap(void *opaque) + { ++ RawPosixAIOData *aiocb = opaque; + BDRVRawState *s G_GNUC_UNUSED = aiocb->bs->opaque; + int ret; + +@@ -1797,11 +1799,7 @@ static int aio_worker(void *arg) + ret = handle_aiocb_discard(aiocb); + break; + case QEMU_AIO_WRITE_ZEROES: +- ret = handle_aiocb_write_zeroes(aiocb); +- break; + case QEMU_AIO_WRITE_ZEROES | QEMU_AIO_DISCARD: +- ret = handle_aiocb_write_zeroes_unmap(aiocb); +- break; + case QEMU_AIO_COPY_RANGE: + ret = handle_aiocb_copy_range(aiocb); + break; +@@ -2518,18 +2516,41 @@ static coroutine_fn BlockAIOCB *raw_aio_pdiscard(BlockDriverState *bs, + cb, opaque, QEMU_AIO_DISCARD); + } + +-static int coroutine_fn raw_co_pwrite_zeroes( +- BlockDriverState *bs, int64_t offset, +- int bytes, BdrvRequestFlags flags) ++static int coroutine_fn ++raw_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, ++ BdrvRequestFlags flags, bool blkdev) + { + BDRVRawState *s = bs->opaque; +- int operation = QEMU_AIO_WRITE_ZEROES; ++ RawPosixAIOData acb; ++ ThreadPoolFunc *handler; ++ ++ 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_MAY_UNMAP) { +- operation |= QEMU_AIO_DISCARD; ++ acb.aio_type |= QEMU_AIO_DISCARD; ++ handler = handle_aiocb_write_zeroes_unmap; ++ } else { ++ handler = handle_aiocb_write_zeroes; + } + +- return paio_submit_co(bs, s->fd, offset, NULL, bytes, operation); ++ return raw_thread_pool_submit(bs, handler, &acb); ++} ++ ++static int coroutine_fn raw_co_pwrite_zeroes( ++ BlockDriverState *bs, int64_t offset, ++ int bytes, BdrvRequestFlags flags) ++{ ++ return raw_do_pwrite_zeroes(bs, offset, bytes, flags, false); + } + + static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +@@ -3093,8 +3114,6 @@ static coroutine_fn BlockAIOCB *hdev_aio_pdiscard(BlockDriverState *bs, + static coroutine_fn int hdev_co_pwrite_zeroes(BlockDriverState *bs, + int64_t offset, int bytes, BdrvRequestFlags flags) + { +- BDRVRawState *s = bs->opaque; +- int operation = QEMU_AIO_WRITE_ZEROES | QEMU_AIO_BLKDEV; + int rc; + + rc = fd_open(bs); +@@ -3102,11 +3121,7 @@ static coroutine_fn int hdev_co_pwrite_zeroes(BlockDriverState *bs, + return rc; + } + +- if (flags & BDRV_REQ_MAY_UNMAP) { +- operation |= QEMU_AIO_DISCARD; +- } +- +- return paio_submit_co(bs, s->fd, offset, NULL, bytes, operation); ++ return raw_do_pwrite_zeroes(bs, offset, bytes, flags, true); + } + + static int coroutine_fn hdev_co_create_opts(const char *filename, QemuOpts *opts, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Drop-s-lock_fd.patch b/SOURCES/kvm-file-posix-Drop-s-lock_fd.patch new file mode 100644 index 0000000..4701c5c --- /dev/null +++ b/SOURCES/kvm-file-posix-Drop-s-lock_fd.patch @@ -0,0 +1,138 @@ +From e56693b6f482bf594b39f751d17f371301e280b5 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 4 Feb 2019 20:42:04 +0100 +Subject: [PATCH 04/33] file-posix: Drop s->lock_fd + +RH-Author: Max Reitz +Message-id: <20190204204207.18079-5-mreitz@redhat.com> +Patchwork-id: 84223 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 4/7] file-posix: Drop s->lock_fd +Bugzilla: 1551486 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Kevin Wolf + +From: Fam Zheng + +The lock_fd field is not strictly necessary because transferring locked +bytes from old fd to the new one shouldn't fail anyway. This spares the +user one fd per image. + +Signed-off-by: Fam Zheng +Reviewed-by: Max Reitz +Signed-off-by: Kevin Wolf +(cherry picked from commit f2e3af29b70624659a50903bd075e1663b64c9da) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 37 +++++++++++++------------------------ + 1 file changed, 13 insertions(+), 24 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index 2a05193..97e7ff2 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -142,7 +142,6 @@ do { \ + + typedef struct BDRVRawState { + int fd; +- int lock_fd; + bool use_lock; + int type; + int open_flags; +@@ -153,7 +152,7 @@ typedef struct BDRVRawState { + uint64_t shared_perm; + + /* The perms bits whose corresponding bytes are already locked in +- * s->lock_fd. */ ++ * s->fd. */ + uint64_t locked_perm; + uint64_t locked_shared_perm; + +@@ -545,18 +544,6 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, + } + s->fd = fd; + +- s->lock_fd = -1; +- if (s->use_lock) { +- fd = qemu_open(filename, s->open_flags); +- if (fd < 0) { +- ret = -errno; +- error_setg_errno(errp, errno, "Could not open '%s' for locking", +- filename); +- qemu_close(s->fd); +- goto fail; +- } +- s->lock_fd = fd; +- } + s->perm = 0; + s->shared_perm = BLK_PERM_ALL; + +@@ -811,15 +798,13 @@ static int raw_handle_perm_lock(BlockDriverState *bs, + return 0; + } + +- assert(s->lock_fd > 0); +- + switch (op) { + case RAW_PL_PREPARE: +- ret = raw_apply_lock_bytes(s, s->lock_fd, s->perm | new_perm, ++ 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->lock_fd, new_perm, new_shared, errp); ++ ret = raw_check_lock_bytes(s->fd, new_perm, new_shared, errp); + if (!ret) { + return 0; + } +@@ -830,7 +815,7 @@ static int raw_handle_perm_lock(BlockDriverState *bs, + op = RAW_PL_ABORT; + /* fall through to unlock bytes. */ + case RAW_PL_ABORT: +- raw_apply_lock_bytes(s, s->lock_fd, s->perm, ~s->shared_perm, ++ 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 +@@ -840,7 +825,7 @@ static int raw_handle_perm_lock(BlockDriverState *bs, + } + break; + case RAW_PL_COMMIT: +- raw_apply_lock_bytes(s, s->lock_fd, new_perm, ~new_shared, ++ 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 +@@ -938,9 +923,17 @@ static void raw_reopen_commit(BDRVReopenState *state) + { + BDRVRawReopenState *rs = state->opaque; + BDRVRawState *s = state->bs->opaque; ++ Error *local_err = NULL; + + s->open_flags = rs->open_flags; + ++ /* Copy locks to the new fd before closing the old one. */ ++ raw_apply_lock_bytes(NULL, rs->fd, s->locked_perm, ++ ~s->locked_shared_perm, false, &local_err); ++ if (local_err) { ++ /* shouldn't fail in a sane host, but report it just in case. */ ++ error_report_err(local_err); ++ } + qemu_close(s->fd); + s->fd = rs->fd; + +@@ -1903,10 +1896,6 @@ static void raw_close(BlockDriverState *bs) + qemu_close(s->fd); + s->fd = -1; + } +- if (s->lock_fd >= 0) { +- qemu_close(s->lock_fd); +- s->lock_fd = -1; +- } + } + + /** +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Factor-out-raw_reconfigure_getfd.patch b/SOURCES/kvm-file-posix-Factor-out-raw_reconfigure_getfd.patch new file mode 100644 index 0000000..652fc88 --- /dev/null +++ b/SOURCES/kvm-file-posix-Factor-out-raw_reconfigure_getfd.patch @@ -0,0 +1,157 @@ +From 1787cd88f968426a3f8a447408ed03f7778dc18a Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 15 Mar 2019 18:10:06 +0100 +Subject: [PATCH 010/163] file-posix: Factor out raw_reconfigure_getfd() + +RH-Author: Kevin Wolf +Message-id: <20190315181010.14964-11-kwolf@redhat.com> +Patchwork-id: 84887 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 10/14] file-posix: Factor out raw_reconfigure_getfd() +Bugzilla: 1685989 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +Signed-off-by: Kevin Wolf +(cherry picked from commit 5cec28702587d8dc9792f8274bfc6bb91f07d672) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 87 ++++++++++++++++++++++++++++++++---------------------- + 1 file changed, 52 insertions(+), 35 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index 419781c..e50eb0e 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -834,35 +834,24 @@ static int raw_handle_perm_lock(BlockDriverState *bs, + return ret; + } + +-static int raw_reopen_prepare(BDRVReopenState *state, +- BlockReopenQueue *queue, Error **errp) ++static int raw_reconfigure_getfd(BlockDriverState *bs, int flags, ++ int *open_flags, Error **errp) + { +- BDRVRawState *s; +- BDRVRawReopenState *rs; +- int ret = 0; +- Error *local_err = NULL; +- +- assert(state != NULL); +- assert(state->bs != NULL); +- +- s = state->bs->opaque; +- +- state->opaque = g_new0(BDRVRawReopenState, 1); +- rs = state->opaque; +- +- if (s->type == FTYPE_CD) { +- rs->open_flags |= O_NONBLOCK; +- } +- +- raw_parse_flags(state->flags, &rs->open_flags); +- +- rs->fd = -1; +- ++ BDRVRawState *s = bs->opaque; ++ int fd = -1; ++ int ret; + 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); ++ + #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 +@@ -872,32 +861,59 @@ static int raw_reopen_prepare(BDRVReopenState *state, + assert((s->open_flags & O_ASYNC) == 0); + #endif + +- if ((rs->open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) { ++ if ((*open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) { + /* dup the original fd */ +- rs->fd = qemu_dup(s->fd); +- if (rs->fd >= 0) { +- ret = fcntl_setfl(rs->fd, rs->open_flags); ++ fd = qemu_dup(s->fd); ++ if (fd >= 0) { ++ ret = fcntl_setfl(fd, *open_flags); + if (ret) { +- qemu_close(rs->fd); +- rs->fd = -1; ++ qemu_close(fd); ++ fd = -1; + } + } + } + + /* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */ +- if (rs->fd == -1) { +- const char *normalized_filename = state->bs->filename; ++ if (fd == -1) { ++ const char *normalized_filename = bs->filename; + ret = raw_normalize_devicepath(&normalized_filename, errp); + if (ret >= 0) { +- assert(!(rs->open_flags & O_CREAT)); +- rs->fd = qemu_open(normalized_filename, rs->open_flags); +- if (rs->fd == -1) { ++ assert(!(*open_flags & O_CREAT)); ++ fd = qemu_open(normalized_filename, *open_flags); ++ if (fd == -1) { + error_setg_errno(errp, errno, "Could not reopen file"); +- ret = -1; ++ return -1; + } + } + } + ++ return fd; ++} ++ ++static int raw_reopen_prepare(BDRVReopenState *state, ++ BlockReopenQueue *queue, Error **errp) ++{ ++ BDRVRawState *s; ++ BDRVRawReopenState *rs; ++ int ret = 0; ++ Error *local_err = NULL; ++ ++ assert(state != NULL); ++ assert(state->bs != NULL); ++ ++ s = state->bs->opaque; ++ ++ state->opaque = g_new0(BDRVRawReopenState, 1); ++ rs = state->opaque; ++ ++ rs->fd = raw_reconfigure_getfd(state->bs, state->flags, &rs->open_flags, ++ &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ ret = -1; ++ goto out; ++ } ++ + /* Fail already reopen_prepare() if we can't get a working O_DIRECT + * alignment with the new fd. */ + if (rs->fd != -1) { +@@ -910,6 +926,7 @@ static int raw_reopen_prepare(BDRVReopenState *state, + } + } + ++out: + return ret; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Factor-out-raw_thread_pool_submit.patch b/SOURCES/kvm-file-posix-Factor-out-raw_thread_pool_submit.patch new file mode 100644 index 0000000..2c2c981 --- /dev/null +++ b/SOURCES/kvm-file-posix-Factor-out-raw_thread_pool_submit.patch @@ -0,0 +1,90 @@ +From aa184b19787e6f9701c6b273394d502e03d8b0c1 Mon Sep 17 00:00:00 2001 +From: Maxim Levitsky +Date: Wed, 5 Jun 2019 13:57:01 +0200 +Subject: [PATCH 13/23] file-posix: Factor out raw_thread_pool_submit() + +RH-Author: Maxim Levitsky +Message-id: <20190605135705.24526-6-mlevitsk@redhat.com> +Patchwork-id: 88556 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 5/9] file-posix: Factor out raw_thread_pool_submit() +Bugzilla: 1648622 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow + +From: Kevin Wolf + +Getting the thread pool of the AioContext of a block node and scheduling +some work in it is an operation that is already done twice, and we'll +get more instances. Factor it out into a separate function. + +Signed-off-by: Kevin Wolf + +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1648622 + +Signed-off-by: Maxim Levitsky +(Cherry picked from 5d5de250056b0972cde2e88133db702960a32b72 with some conflicts) + +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 17 ++++++++++------- + 1 file changed, 10 insertions(+), 7 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index 5b93d06..74da336 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -1818,13 +1818,20 @@ static int aio_worker(void *arg) + return ret; + } + ++static int coroutine_fn raw_thread_pool_submit(BlockDriverState *bs, ++ ThreadPoolFunc func, void *arg) ++{ ++ /* @bs can be NULL, bdrv_get_aio_context() returns the main context then */ ++ ThreadPool *pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); ++ return thread_pool_submit_co(pool, func, arg); ++} ++ + static int paio_submit_co_full(BlockDriverState *bs, int fd, + int64_t offset, int fd2, int64_t offset2, + QEMUIOVector *qiov, + int bytes, int type) + { + RawPosixAIOData *acb = g_new(RawPosixAIOData, 1); +- ThreadPool *pool; + + acb->bs = bs; + acb->aio_type = type; +@@ -1842,8 +1849,7 @@ static int paio_submit_co_full(BlockDriverState *bs, int fd, + } + + trace_paio_submit_co(offset, bytes, type); +- pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); +- return thread_pool_submit_co(pool, aio_worker, acb); ++ return raw_thread_pool_submit(bs, aio_worker, acb); + } + + static inline int paio_submit_co(BlockDriverState *bs, int fd, +@@ -1976,7 +1982,6 @@ raw_regular_truncate(BlockDriverState *bs, int fd, int64_t offset, + PreallocMode prealloc, Error **errp) + { + RawPosixAIOData *acb = g_new(RawPosixAIOData, 1); +- ThreadPool *pool; + + *acb = (RawPosixAIOData) { + .bs = bs, +@@ -1987,9 +1992,7 @@ raw_regular_truncate(BlockDriverState *bs, int fd, int64_t offset, + .errp = errp, + }; + +- /* @bs can be NULL, bdrv_get_aio_context() returns the main context then */ +- pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); +- return thread_pool_submit_co(pool, aio_worker, acb); ++ return raw_thread_pool_submit(bs, aio_worker, acb); + } + + static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Fix-EINTR-handling.patch b/SOURCES/kvm-file-posix-Fix-EINTR-handling.patch new file mode 100644 index 0000000..e1a85c2 --- /dev/null +++ b/SOURCES/kvm-file-posix-Fix-EINTR-handling.patch @@ -0,0 +1,65 @@ +From 9075cb2c3c074b395192ce166fe17664b0d9543c Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Fri, 29 Jun 2018 06:11:53 +0200 +Subject: [PATCH 49/57] file-posix: Fix EINTR handling + +RH-Author: Fam Zheng +Message-id: <20180629061153.12687-14-famz@redhat.com> +Patchwork-id: 81164 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 13/13] file-posix: Fix EINTR handling +Bugzilla: 1482537 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Kevin Wolf + +EINTR should be checked against errno, not ret. While fixing the bug, +collect the branches with a switch block. + +Also, change the return value from -ENOSTUP to -ENOSPC when the actual +issue is request range passes EOF, which should be distinguishable from +the case of error == ENOSYS by the caller, so that it could still retry +with other byte ranges, whereas it shouldn't retry anymore upon ENOSYS. + +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 17 +++++++++-------- + 1 file changed, 9 insertions(+), 8 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index 29ff699..0a9df5b 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -1449,20 +1449,21 @@ static ssize_t handle_aiocb_copy_range(RawPosixAIOData *aiocb) + ssize_t ret = copy_file_range(aiocb->aio_fildes, &in_off, + aiocb->aio_fd2, &out_off, + bytes, 0); +- if (ret == -EINTR) { +- continue; ++ if (ret == 0) { ++ /* No progress (e.g. when beyond EOF), let the caller fall back to ++ * buffer I/O. */ ++ return -ENOSPC; + } + if (ret < 0) { +- if (errno == ENOSYS) { ++ switch (errno) { ++ case ENOSYS: + return -ENOTSUP; +- } else { ++ case EINTR: ++ continue; ++ default: + return -errno; + } + } +- if (!ret) { +- /* No progress (e.g. when beyond EOF), fall back to buffer I/O. */ +- return -ENOTSUP; +- } + bytes -= ret; + } + return 0; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Fix-bdrv_open_flags-for-snapshot-on.patch b/SOURCES/kvm-file-posix-Fix-bdrv_open_flags-for-snapshot-on.patch new file mode 100644 index 0000000..a1a3b71 --- /dev/null +++ b/SOURCES/kvm-file-posix-Fix-bdrv_open_flags-for-snapshot-on.patch @@ -0,0 +1,114 @@ +From 043d71d0340799feafee434f8eec0360840ec777 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 15 Mar 2019 18:10:04 +0100 +Subject: [PATCH 008/163] file-posix: Fix bdrv_open_flags() for snapshot=on + +RH-Author: Kevin Wolf +Message-id: <20190315181010.14964-9-kwolf@redhat.com> +Patchwork-id: 84885 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 08/14] file-posix: Fix bdrv_open_flags() for snapshot=on +Bugzilla: 1685989 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +Using a different read-only setting for bs->open_flags than for the +flags to the driver's open function is just inconsistent and a bad idea. +After this patch, the temporary snapshot keeps being opened read-only if +read-only=on,snapshot=on is passed. + +If we wanted to change this behaviour to make only the orginal image +file read-only, but the temporary overlay read-write (as the comment in +the removed code suggests), that change would have to be made in +bdrv_temp_snapshot_options() (where the comment suggests otherwise). + +Addressing this inconsistency before introducing dynamic auto-read-only +is important because otherwise we would immediately try to reopen the +temporary overlay even though the file is already unlinked. + +Signed-off-by: Kevin Wolf +(cherry picked from commit 30855137783c0c762007044821a6f11e14e6af33) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 7 ------- + tests/qemu-iotests/051 | 7 +++++++ + tests/qemu-iotests/051.out | 9 +++++++++ + tests/qemu-iotests/051.pc.out | 9 +++++++++ + 4 files changed, 25 insertions(+), 7 deletions(-) + +diff --git a/block.c b/block.c +index 25b3fe5..bcf277d 100644 +--- a/block.c ++++ b/block.c +@@ -1100,13 +1100,6 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags) + */ + open_flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_PROTOCOL); + +- /* +- * Snapshots should be writable. +- */ +- if (flags & BDRV_O_TEMPORARY) { +- open_flags |= BDRV_O_RDWR; +- } +- + return open_flags; + } + +diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 +index 4899f84..23f9678 100755 +--- a/tests/qemu-iotests/051 ++++ b/tests/qemu-iotests/051 +@@ -357,6 +357,13 @@ $QEMU_IO -c "read -P 0x33 0 4k" "$TEST_IMG" | _filter_qemu_io + # Using snapshot=on with a non-existent TMPDIR + TMPDIR=/nonexistent run_qemu -drive driver=null-co,snapshot=on + ++# Using snapshot=on together with read-only=on ++echo "info block" | ++ run_qemu -drive file="$TEST_IMG",snapshot=on,read-only=on,if=none,id=$device_id | ++ _filter_qemu_io | ++ sed -e 's#/[^"]*/vl\.[A-Za-z0-9]\{6\}#SNAPSHOT_PATH#g' ++ ++ + # success, all done + echo "*** done" + rm -f $seq.full +diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out +index 793af2a..e2f91fc 100644 +--- a/tests/qemu-iotests/051.out ++++ b/tests/qemu-iotests/051.out +@@ -458,4 +458,13 @@ read 4096/4096 bytes at offset 0 + Testing: -drive driver=null-co,snapshot=on + QEMU_PROG: -drive driver=null-co,snapshot=on: Could not get temporary filename: No such file or directory + ++Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,read-only=on,if=none,id=drive0 ++QEMU X.Y.Z monitor - type 'help' for more information ++(qemu) info block ++drive0 (NODE_NAME): SNAPSHOT_PATH (qcow2, read-only) ++ Removable device: not locked, tray closed ++ Cache mode: writeback, ignore flushes ++ Backing file: TEST_DIR/t.qcow2 (chain depth: 1) ++(qemu) quit ++ + *** done +diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out +index ca64eda..3f71f9e 100644 +--- a/tests/qemu-iotests/051.pc.out ++++ b/tests/qemu-iotests/051.pc.out +@@ -530,4 +530,13 @@ read 4096/4096 bytes at offset 0 + Testing: -drive driver=null-co,snapshot=on + QEMU_PROG: -drive driver=null-co,snapshot=on: Could not get temporary filename: No such file or directory + ++Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,read-only=on,if=none,id=drive0 ++QEMU X.Y.Z monitor - type 'help' for more information ++(qemu) info block ++drive0 (NODE_NAME): SNAPSHOT_PATH (qcow2, read-only) ++ Removable device: not locked, tray closed ++ Cache mode: writeback, ignore flushes ++ Backing file: TEST_DIR/t.qcow2 (chain depth: 1) ++(qemu) quit ++ + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Fix-creation-locking.patch b/SOURCES/kvm-file-posix-Fix-creation-locking.patch new file mode 100644 index 0000000..f95e53f --- /dev/null +++ b/SOURCES/kvm-file-posix-Fix-creation-locking.patch @@ -0,0 +1,56 @@ +From 19452491c1ec03ce4eef24388ec24076eb306377 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 9 Jul 2018 15:11:21 +0200 +Subject: [PATCH 29/89] file-posix: Fix creation locking + +RH-Author: Max Reitz +Message-id: <20180709151122.27541-2-mreitz@redhat.com> +Patchwork-id: 81271 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/2] file-posix: Fix creation locking +Bugzilla: 1599335 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Kevin Wolf +RH-Acked-by: John Snow + +raw_apply_lock_bytes() takes a bit mask of "permissions that are NOT +shared". + +Also, make the "perm" and "shared" variables uint64_t, because I do not +particularly like using ~ on signed integers (and other permission masks +are usually uint64_t, too). + +Reported-by: Kevin Wolf +Signed-off-by: Max Reitz +Signed-off-by: Kevin Wolf +(cherry picked from commit d815efcaf01b1698e2fdf0f3e125201025c53191) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index 0a9df5b..e876770 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -2052,7 +2052,7 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp) + { + BlockdevCreateOptionsFile *file_opts; + int fd; +- int perm, shared; ++ uint64_t perm, shared; + int result = 0; + + /* Validate options and set default values */ +@@ -2088,7 +2088,7 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp) + shared = BLK_PERM_ALL & ~BLK_PERM_RESIZE; + + /* Step one: Take locks */ +- result = raw_apply_lock_bytes(fd, perm, shared, false, errp); ++ result = raw_apply_lock_bytes(fd, perm, ~shared, false, errp); + if (result < 0) { + goto out_close; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Fix-shared-locks-on-reopen-commit.patch b/SOURCES/kvm-file-posix-Fix-shared-locks-on-reopen-commit.patch new file mode 100644 index 0000000..2c1a9ac --- /dev/null +++ b/SOURCES/kvm-file-posix-Fix-shared-locks-on-reopen-commit.patch @@ -0,0 +1,45 @@ +From ef27c0164bee4e910d0f2e20928688f873d924a3 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 4 Feb 2019 20:42:06 +0100 +Subject: [PATCH 06/33] file-posix: Fix shared locks on reopen commit + +RH-Author: Max Reitz +Message-id: <20190204204207.18079-7-mreitz@redhat.com> +Patchwork-id: 84225 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 6/7] file-posix: Fix shared locks on reopen commit +Bugzilla: 1551486 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Kevin Wolf + +s->locked_shared_perm is the set of bits locked in the file, which is +the inverse of the permissions actually shared. So we need to pass them +as they are to raw_apply_lock_bytes() instead of inverting them again. + +Reported-by: Alberto Garcia +Signed-off-by: Max Reitz +Reviewed-by: Alberto Garcia +Signed-off-by: Kevin Wolf +(cherry picked from commit 577a133988c76e4ebf01d050d0d758d207a1baf7) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index 97e7ff2..deecf58 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -929,7 +929,7 @@ static void raw_reopen_commit(BDRVReopenState *state) + + /* Copy locks to the new fd before closing the old one. */ + raw_apply_lock_bytes(NULL, rs->fd, s->locked_perm, +- ~s->locked_shared_perm, false, &local_err); ++ s->locked_shared_perm, false, &local_err); + if (local_err) { + /* shouldn't fail in a sane host, but report it just in case. */ + error_report_err(local_err); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Fix-write_zeroes-with-unmap-on-block-devi.patch b/SOURCES/kvm-file-posix-Fix-write_zeroes-with-unmap-on-block-devi.patch new file mode 100644 index 0000000..450eee6 --- /dev/null +++ b/SOURCES/kvm-file-posix-Fix-write_zeroes-with-unmap-on-block-devi.patch @@ -0,0 +1,153 @@ +From 4704bbaba38bfeb1710b109f0b4b32a839b24734 Mon Sep 17 00:00:00 2001 +From: Maxim Levitsky +Date: Wed, 5 Jun 2019 13:57:00 +0200 +Subject: [PATCH 12/23] file-posix: Fix write_zeroes with unmap on block + devices + +RH-Author: Maxim Levitsky +Message-id: <20190605135705.24526-5-mlevitsk@redhat.com> +Patchwork-id: 88562 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 4/9] file-posix: Fix write_zeroes with unmap on block devices +Bugzilla: 1648622 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow + +From: Kevin Wolf + +The BLKDISCARD ioctl doesn't guarantee that the discarded blocks read as +all-zero afterwards, so don't try to abuse it for zero writing. We try +to only use this if BLKDISCARDZEROES tells us that it is safe, but this +is unreliable on older kernels and a constant 0 in newer kernels. In +other words, this code path is never actually used with newer kernels, +so we don't even try to unmap while writing zeros. + +This patch removes the abuse of discard for writing zeroes from +file-posix and instead adds a new function that uses interfaces that are +actually meant to deallocate and zero out at the same time. Only if +those fail, it falls back to zeroing out without unmap. We never fall +back to a discard operation any more that may or may not result in +zeros. + +Signed-off-by: Kevin Wolf + +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1648622 + +Signed-off-by: Maxim Levitsky +(Cherry picked from 34fa110e424e9a6a9b7e0274c3d4bfee766eb7ed) + +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 59 ++++++++++++++++++++++++++++++++++++++++-------------- + 1 file changed, 44 insertions(+), 15 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index 518f16b..5b93d06 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -632,7 +632,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, + } + #endif + +- bs->supported_zero_flags = s->discard_zeroes ? BDRV_REQ_MAY_UNMAP : 0; ++ bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP; + ret = 0; + fail: + if (filename && (bdrv_flags & BDRV_O_TEMPORARY)) { +@@ -1530,6 +1530,35 @@ static ssize_t handle_aiocb_write_zeroes(RawPosixAIOData *aiocb) + return -ENOTSUP; + } + ++static ssize_t handle_aiocb_write_zeroes_unmap(RawPosixAIOData *aiocb) ++{ ++ BDRVRawState *s G_GNUC_UNUSED = aiocb->bs->opaque; ++ int ret; ++ ++ /* First try to write zeros and unmap at the same time */ ++ ++#ifdef CONFIG_FALLOCATE_PUNCH_HOLE ++ ret = do_fallocate(s->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, ++ aiocb->aio_offset, aiocb->aio_nbytes); ++ if (ret != -ENOTSUP) { ++ return ret; ++ } ++#endif ++ ++#ifdef CONFIG_XFS ++ if (s->is_xfs) { ++ /* xfs_discard() guarantees that the discarded area reads as all-zero ++ * afterwards, so we can use it here. */ ++ return xfs_discard(s, aiocb->aio_offset, aiocb->aio_nbytes); ++ } ++#endif ++ ++ /* If we couldn't manage to unmap while guaranteed that the area reads as ++ * all-zero afterwards, just write zeroes without unmapping */ ++ ret = handle_aiocb_write_zeroes(aiocb); ++ return ret; ++} ++ + #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) +@@ -1770,6 +1799,9 @@ static int aio_worker(void *arg) + case QEMU_AIO_WRITE_ZEROES: + ret = handle_aiocb_write_zeroes(aiocb); + break; ++ case QEMU_AIO_WRITE_ZEROES | QEMU_AIO_DISCARD: ++ ret = handle_aiocb_write_zeroes_unmap(aiocb); ++ break; + case QEMU_AIO_COPY_RANGE: + ret = handle_aiocb_copy_range(aiocb); + break; +@@ -2488,15 +2520,13 @@ static int coroutine_fn raw_co_pwrite_zeroes( + int bytes, BdrvRequestFlags flags) + { + BDRVRawState *s = bs->opaque; ++ int operation = QEMU_AIO_WRITE_ZEROES; + +- if (!(flags & BDRV_REQ_MAY_UNMAP)) { +- return paio_submit_co(bs, s->fd, offset, NULL, bytes, +- QEMU_AIO_WRITE_ZEROES); +- } else if (s->discard_zeroes) { +- return paio_submit_co(bs, s->fd, offset, NULL, bytes, +- QEMU_AIO_DISCARD); ++ if (flags & BDRV_REQ_MAY_UNMAP) { ++ operation |= QEMU_AIO_DISCARD; + } +- return -ENOTSUP; ++ ++ return paio_submit_co(bs, s->fd, offset, NULL, bytes, operation); + } + + static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +@@ -3061,20 +3091,19 @@ static coroutine_fn int hdev_co_pwrite_zeroes(BlockDriverState *bs, + int64_t offset, int bytes, BdrvRequestFlags flags) + { + BDRVRawState *s = bs->opaque; ++ int operation = QEMU_AIO_WRITE_ZEROES | QEMU_AIO_BLKDEV; + int rc; + + rc = fd_open(bs); + if (rc < 0) { + return rc; + } +- if (!(flags & BDRV_REQ_MAY_UNMAP)) { +- return paio_submit_co(bs, s->fd, offset, NULL, bytes, +- QEMU_AIO_WRITE_ZEROES|QEMU_AIO_BLKDEV); +- } else if (s->discard_zeroes) { +- return paio_submit_co(bs, s->fd, offset, NULL, bytes, +- QEMU_AIO_DISCARD|QEMU_AIO_BLKDEV); ++ ++ if (flags & BDRV_REQ_MAY_UNMAP) { ++ operation |= QEMU_AIO_DISCARD; + } +- return -ENOTSUP; ++ ++ return paio_submit_co(bs, s->fd, offset, NULL, bytes, operation); + } + + static int coroutine_fn hdev_co_create_opts(const char *filename, QemuOpts *opts, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Handle-undetectable-alignment.patch b/SOURCES/kvm-file-posix-Handle-undetectable-alignment.patch new file mode 100644 index 0000000..f860d80 --- /dev/null +++ b/SOURCES/kvm-file-posix-Handle-undetectable-alignment.patch @@ -0,0 +1,129 @@ +From 29c28722af50333db06c4c2497852896cf15ca23 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Fri, 23 Aug 2019 15:11:10 +0200 +Subject: [PATCH 3/4] file-posix: Handle undetectable alignment + +RH-Author: Max Reitz +Message-id: <20190823151110.17322-2-mreitz@redhat.com> +Patchwork-id: 90139 +O-Subject: [RHEL-7.7.z qemu-kvm-rhev PATCH 1/1] file-posix: Handle undetectable alignment +Bugzilla: 1743365 +RH-Acked-by: Maxim Levitsky +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Stefano Garzarella + +From: Nir Soffer + +In some cases buf_align or request_alignment cannot be detected: + +1. With Gluster, buf_align cannot be detected since the actual I/O is + done on Gluster server, and qemu buffer alignment does not matter. + Since we don't have alignment requirement, buf_align=1 is the best + value. + +2. With local XFS filesystem, buf_align cannot be detected if reading + from unallocated area. In this we must align the buffer, but we don't + know what is the correct size. Using the wrong alignment results in + I/O error. + +3. With Gluster backed by XFS, request_alignment cannot be detected if + reading from unallocated area. In this case we need to use the + correct alignment, and failing to do so results in I/O errors. + +4. With NFS, the server does not use direct I/O, so both buf_align cannot + be detected. In this case we don't need any alignment so we can use + buf_align=1 and request_alignment=1. + +These cases seems to work when storage sector size is 512 bytes, because +the current code starts checking align=512. If the check succeeds +because alignment cannot be detected we use 512. But this does not work +for storage with 4k sector size. + +To determine if we can detect the alignment, we probe first with +align=1. If probing succeeds, maybe there are no alignment requirement +(cases 1, 4) or we are probing unallocated area (cases 2, 3). Since we +don't have any way to tell, we treat this as undetectable alignment. If +probing with align=1 fails with EINVAL, but probing with one of the +expected alignments succeeds, we know that we found a working alignment. + +Practically the alignment requirements are the same for buffer +alignment, buffer length, and offset in file. So in case we cannot +detect buf_align, we can use request alignment. If we cannot detect +request alignment, we can fallback to a safe value. To use this logic, +we probe first request alignment instead of buf_align. + +Here is a table showing the behaviour with current code (the value in +parenthesis is the optimal value). + +Case Sector buf_align (opt) request_alignment (opt) result + +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 36 +++++++++++++++++++++++++----------- + 1 file changed, 25 insertions(+), 11 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index d1926b3..548424d 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -325,6 +325,7 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) + BDRVRawState *s = bs->opaque; + char *buf; + size_t max_align = MAX(MAX_BLOCKSIZE, getpagesize()); ++ 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. */ +@@ -351,25 +352,38 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) + } + #endif + +- /* If we could not get the sizes so far, we can only guess them */ +- if (!s->buf_align) { ++ /* ++ * 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, 2 * max_align); +- for (align = 512; align <= max_align; align <<= 1) { +- if (raw_is_io_aligned(fd, buf + align, max_align)) { +- s->buf_align = 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 (!bs->bl.request_alignment) { ++ if (!s->buf_align) { ++ int i; + size_t align; +- buf = qemu_memalign(s->buf_align, max_align); +- for (align = 512; align <= max_align; align <<= 1) { +- if (raw_is_io_aligned(fd, buf, align)) { +- bs->bl.request_alignment = 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_aligment. */ ++ s->buf_align = (align != 1) ? align : bs->bl.request_alignment; + break; + } + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Implement-bdrv_co_copy_range.patch b/SOURCES/kvm-file-posix-Implement-bdrv_co_copy_range.patch new file mode 100644 index 0000000..8872926 --- /dev/null +++ b/SOURCES/kvm-file-posix-Implement-bdrv_co_copy_range.patch @@ -0,0 +1,261 @@ +From 29d5339959b539767f6482adc00e02f0bb6083f9 Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Fri, 29 Jun 2018 06:11:45 +0200 +Subject: [PATCH 41/57] file-posix: Implement bdrv_co_copy_range + +RH-Author: Fam Zheng +Message-id: <20180629061153.12687-6-famz@redhat.com> +Patchwork-id: 81156 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 05/13] file-posix: Implement bdrv_co_copy_range +Bugzilla: 1482537 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Kevin Wolf + +With copy_file_range(2), we can implement the bdrv_co_copy_range +semantics. + +Signed-off-by: Fam Zheng +Message-id: 20180601092648.24614-6-famz@redhat.com +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit 1efad060d7e131dd52ecd1e038a6ddd37a3940c8) +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++-- + configure | 17 +++++++++ + include/block/raw-aio.h | 10 ++++- + 3 files changed, 120 insertions(+), 5 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index 370a483..29ff699 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -59,6 +59,7 @@ + #ifdef __linux__ + #include + #include ++#include + #include + #include + #include +@@ -185,6 +186,8 @@ typedef struct RawPosixAIOData { + #define aio_ioctl_cmd aio_nbytes /* for QEMU_AIO_IOCTL */ + off_t aio_offset; + int aio_type; ++ int aio_fd2; ++ off_t aio_offset2; + } RawPosixAIOData; + + #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +@@ -1422,6 +1425,49 @@ static ssize_t handle_aiocb_write_zeroes(RawPosixAIOData *aiocb) + return -ENOTSUP; + } + ++#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 ++ ++static ssize_t handle_aiocb_copy_range(RawPosixAIOData *aiocb) ++{ ++ uint64_t bytes = aiocb->aio_nbytes; ++ off_t in_off = aiocb->aio_offset; ++ off_t out_off = aiocb->aio_offset2; ++ ++ while (bytes) { ++ ssize_t ret = copy_file_range(aiocb->aio_fildes, &in_off, ++ aiocb->aio_fd2, &out_off, ++ bytes, 0); ++ if (ret == -EINTR) { ++ continue; ++ } ++ if (ret < 0) { ++ if (errno == ENOSYS) { ++ return -ENOTSUP; ++ } else { ++ return -errno; ++ } ++ } ++ if (!ret) { ++ /* No progress (e.g. when beyond EOF), fall back to buffer I/O. */ ++ return -ENOTSUP; ++ } ++ bytes -= ret; ++ } ++ return 0; ++} ++ + static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb) + { + int ret = -EOPNOTSUPP; +@@ -1502,6 +1548,9 @@ static int aio_worker(void *arg) + case QEMU_AIO_WRITE_ZEROES: + ret = handle_aiocb_write_zeroes(aiocb); + break; ++ case QEMU_AIO_COPY_RANGE: ++ ret = handle_aiocb_copy_range(aiocb); ++ break; + default: + fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type); + ret = -EINVAL; +@@ -1512,9 +1561,10 @@ static int aio_worker(void *arg) + return ret; + } + +-static int paio_submit_co(BlockDriverState *bs, int fd, +- int64_t offset, QEMUIOVector *qiov, +- int bytes, int type) ++static int paio_submit_co_full(BlockDriverState *bs, int fd, ++ int64_t offset, int fd2, int64_t offset2, ++ QEMUIOVector *qiov, ++ int bytes, int type) + { + RawPosixAIOData *acb = g_new(RawPosixAIOData, 1); + ThreadPool *pool; +@@ -1522,6 +1572,8 @@ static int paio_submit_co(BlockDriverState *bs, int fd, + acb->bs = bs; + acb->aio_type = type; + acb->aio_fildes = fd; ++ acb->aio_fd2 = fd2; ++ acb->aio_offset2 = offset2; + + acb->aio_nbytes = bytes; + acb->aio_offset = offset; +@@ -1537,6 +1589,13 @@ static int paio_submit_co(BlockDriverState *bs, int fd, + return thread_pool_submit_co(pool, aio_worker, acb); + } + ++static inline int paio_submit_co(BlockDriverState *bs, int fd, ++ int64_t offset, QEMUIOVector *qiov, ++ int bytes, int type) ++{ ++ return paio_submit_co_full(bs, fd, offset, -1, 0, qiov, bytes, type); ++} ++ + static BlockAIOCB *paio_submit(BlockDriverState *bs, int fd, + int64_t offset, QEMUIOVector *qiov, int bytes, + BlockCompletionFunc *cb, void *opaque, int type) +@@ -2346,6 +2405,35 @@ static void raw_abort_perm_update(BlockDriverState *bs) + raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL); + } + ++static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs, ++ BdrvChild *src, uint64_t src_offset, ++ BdrvChild *dst, uint64_t dst_offset, ++ uint64_t bytes, BdrvRequestFlags flags) ++{ ++ return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, flags); ++} ++ ++static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, ++ BdrvChild *src, uint64_t src_offset, ++ BdrvChild *dst, uint64_t dst_offset, ++ uint64_t bytes, BdrvRequestFlags flags) ++{ ++ 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(bs) < 0 || fd_open(bs) < 0) { ++ return -EIO; ++ } ++ return paio_submit_co_full(bs, src_s->fd, src_offset, s->fd, dst_offset, ++ NULL, bytes, QEMU_AIO_COPY_RANGE); ++} ++ + BlockDriver bdrv_file = { + .format_name = "file", + .protocol_name = "file", +@@ -2368,6 +2456,8 @@ BlockDriver bdrv_file = { + .bdrv_co_pwritev = raw_co_pwritev, + .bdrv_aio_flush = raw_aio_flush, + .bdrv_aio_pdiscard = raw_aio_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_io_plug = raw_aio_plug, + .bdrv_io_unplug = raw_aio_unplug, +@@ -2845,6 +2935,8 @@ static BlockDriver bdrv_host_device = { + .bdrv_co_pwritev = raw_co_pwritev, + .bdrv_aio_flush = raw_aio_flush, + .bdrv_aio_pdiscard = hdev_aio_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_io_plug = raw_aio_plug, + .bdrv_io_unplug = raw_aio_unplug, +diff --git a/configure b/configure +index d2367f1..a869f19 100755 +--- a/configure ++++ b/configure +@@ -5156,6 +5156,20 @@ if test "$fortify_source" != "no"; then + fi + fi + ++############################################### ++# Check if copy_file_range is provided by glibc ++have_copy_file_range=no ++cat > $TMPC << EOF ++#include ++int main(void) { ++ copy_file_range(0, NULL, 0, NULL, 0, 0); ++ return 0; ++} ++EOF ++if compile_prog "" "" ; then ++ have_copy_file_range=yes ++fi ++ + ########################################## + # check if struct fsxattr is available via linux/fs.h + +@@ -6233,6 +6247,9 @@ fi + if test "$have_fsxattr" = "yes" ; then + echo "HAVE_FSXATTR=y" >> $config_host_mak + fi ++if test "$have_copy_file_range" = "yes" ; then ++ echo "HAVE_COPY_FILE_RANGE=y" >> $config_host_mak ++fi + if test "$vte" = "yes" ; then + echo "CONFIG_VTE=y" >> $config_host_mak + echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak +diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h +index a4cdbbf..3240530 100644 +--- a/include/block/raw-aio.h ++++ b/include/block/raw-aio.h +@@ -25,9 +25,15 @@ + #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_TYPE_MASK \ +- (QEMU_AIO_READ|QEMU_AIO_WRITE|QEMU_AIO_IOCTL|QEMU_AIO_FLUSH| \ +- QEMU_AIO_DISCARD|QEMU_AIO_WRITE_ZEROES) ++ (QEMU_AIO_READ | \ ++ QEMU_AIO_WRITE | \ ++ QEMU_AIO_IOCTL | \ ++ QEMU_AIO_FLUSH | \ ++ QEMU_AIO_DISCARD | \ ++ QEMU_AIO_WRITE_ZEROES | \ ++ QEMU_AIO_COPY_RANGE) + + /* AIO flags */ + #define QEMU_AIO_MISALIGNED 0x1000 +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Include-filename-in-locking-error-message.patch b/SOURCES/kvm-file-posix-Include-filename-in-locking-error-message.patch new file mode 100644 index 0000000..8b29b08 --- /dev/null +++ b/SOURCES/kvm-file-posix-Include-filename-in-locking-error-message.patch @@ -0,0 +1,345 @@ +From 53e0ff897da0cac47f08e1cb49ab98823301fe3b Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 4 Feb 2019 20:42:02 +0100 +Subject: [PATCH 02/33] file-posix: Include filename in locking error message + +RH-Author: Max Reitz +Message-id: <20190204204207.18079-3-mreitz@redhat.com> +Patchwork-id: 84221 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 2/7] file-posix: Include filename in locking error message +Bugzilla: 1551486 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Kevin Wolf + +From: Fam Zheng + +Image locking errors happening at device initialization time doesn't say +which file cannot be locked, for instance, + + -device scsi-disk,drive=drive-1: Failed to get shared "write" lock + Is another process using the image? + +could refer to either the overlay image or its backing image. + +Hoist the error_append_hint to the caller of raw_check_lock_bytes where +file name is known, and include it in the error hint. + +Signed-off-by: Fam Zheng +Reviewed-by: Eric Blake +Signed-off-by: Kevin Wolf +(cherry picked from commit b857431d2abe3945b672b41f33690e9943a8752a) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 10 +++--- + tests/qemu-iotests/153.out | 76 +++++++++++++++++++++++----------------------- + tests/qemu-iotests/182.out | 2 +- + 3 files changed, 45 insertions(+), 43 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index 7e6869d..c2403ba 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -738,8 +738,6 @@ static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm, + "Failed to get \"%s\" lock", + perm_name); + g_free(perm_name); +- error_append_hint(errp, +- "Is another process using the image?\n"); + return ret; + } + } +@@ -755,8 +753,6 @@ static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm, + "Failed to get shared \"%s\" lock", + perm_name); + g_free(perm_name); +- error_append_hint(errp, +- "Is another process using the image?\n"); + return ret; + } + } +@@ -793,6 +789,9 @@ static int raw_handle_perm_lock(BlockDriverState *bs, + if (!ret) { + return 0; + } ++ error_append_hint(errp, ++ "Is another process using the image [%s]?\n", ++ bs->filename); + } + op = RAW_PL_ABORT; + /* fall through to unlock bytes. */ +@@ -2169,6 +2168,9 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) + /* 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; + } + +diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out +index 93eaf10..8842548 100644 +--- a/tests/qemu-iotests/153.out ++++ b/tests/qemu-iotests/153.out +@@ -12,11 +12,11 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t + + == Launching another QEMU, opts: '' == + QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=none,: Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + == Launching another QEMU, opts: 'read-only=on' == + QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=none,read-only=on: Failed to get shared "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + == Launching another QEMU, opts: 'read-only=on,force-share=on' == + +@@ -24,77 +24,77 @@ Is another process using the image? + + _qemu_io_wrapper -c read 0 512 TEST_DIR/t.qcow2 + can't open device TEST_DIR/t.qcow2: Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_io_wrapper -r -c read 0 512 TEST_DIR/t.qcow2 + can't open device TEST_DIR/t.qcow2: Failed to get shared "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_io_wrapper -c open TEST_DIR/t.qcow2 -c read 0 512 + can't open device TEST_DIR/t.qcow2: Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + no file open, try 'help open' + + _qemu_io_wrapper -c open -r TEST_DIR/t.qcow2 -c read 0 512 + can't open device TEST_DIR/t.qcow2: Failed to get shared "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + no file open, try 'help open' + + _qemu_img_wrapper info TEST_DIR/t.qcow2 + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_img_wrapper check TEST_DIR/t.qcow2 + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_img_wrapper compare TEST_DIR/t.qcow2 TEST_DIR/t.qcow2 + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_img_wrapper map TEST_DIR/t.qcow2 + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_img_wrapper amend -o TEST_DIR/t.qcow2 + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_img_wrapper commit TEST_DIR/t.qcow2 + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_img_wrapper resize TEST_DIR/t.qcow2 32M + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_img_wrapper rebase TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_img_wrapper snapshot -l TEST_DIR/t.qcow2 + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_img_wrapper convert TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.convert + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_img_wrapper dd if=TEST_DIR/t.qcow2 of=TEST_DIR/t.qcow2.convert bs=512 count=1 + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_img_wrapper bench -c 1 TEST_DIR/t.qcow2 + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2 + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base + qemu-img: TEST_DIR/t.qcow2: Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + file format: IMGFMT + + == Running utility commands -U == +@@ -132,7 +132,7 @@ Try 'qemu-img --help' for more information + + _qemu_img_wrapper rebase -U TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_img_wrapper snapshot -l -U TEST_DIR/t.qcow2 + +@@ -157,7 +157,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t + + == Launching another QEMU, opts: '' == + QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=none,: Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + == Launching another QEMU, opts: 'read-only=on' == + +@@ -167,13 +167,13 @@ Is another process using the image? + + _qemu_io_wrapper -c read 0 512 TEST_DIR/t.qcow2 + can't open device TEST_DIR/t.qcow2: Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_io_wrapper -r -c read 0 512 TEST_DIR/t.qcow2 + + _qemu_io_wrapper -c open TEST_DIR/t.qcow2 -c read 0 512 + can't open device TEST_DIR/t.qcow2: Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + no file open, try 'help open' + + _qemu_io_wrapper -c open -r TEST_DIR/t.qcow2 -c read 0 512 +@@ -188,19 +188,19 @@ _qemu_img_wrapper map TEST_DIR/t.qcow2 + + _qemu_img_wrapper amend -o TEST_DIR/t.qcow2 + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_img_wrapper commit TEST_DIR/t.qcow2 + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_img_wrapper resize TEST_DIR/t.qcow2 32M + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_img_wrapper rebase TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_img_wrapper snapshot -l TEST_DIR/t.qcow2 + +@@ -212,11 +212,11 @@ _qemu_img_wrapper bench -c 1 TEST_DIR/t.qcow2 + + _qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2 + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base + qemu-img: TEST_DIR/t.qcow2: Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + file format: IMGFMT + + == Running utility commands -U == +@@ -254,7 +254,7 @@ Try 'qemu-img --help' for more information + + _qemu_img_wrapper rebase -U TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + _qemu_img_wrapper snapshot -l -U TEST_DIR/t.qcow2 + +@@ -372,17 +372,17 @@ Round done + + == Two devices with the same image (read-only=off - read-only=off) == + QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,read-only=off: Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + == Two devices with the same image (read-only=off - read-only=on) == + QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,read-only=on: Failed to get shared "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + == Two devices with the same image (read-only=off - read-only=on,force-share=on) == + + == Two devices with the same image (read-only=on - read-only=off) == + QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,read-only=off: Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + == Two devices with the same image (read-only=on - read-only=on) == + +@@ -403,13 +403,13 @@ Formatting 'TEST_DIR/t.IMGFMT.c', fmt=IMGFMT size=33554432 backing_file=TEST_DIR + + == Backing image also as an active device == + QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2: Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + == Backing image also as an active device (ro) == + + == Symbolic link == + QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2: Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + + == Active commit to intermediate layer should work when base in use == + {"return": {}} +@@ -420,7 +420,7 @@ Adding drive + + _qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512 + can't open device TEST_DIR/t.qcow2: Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + Creating overlay with qemu-img when the guest is running should be allowed + + _qemu_img_wrapper create -f qcow2 -b TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.overlay +@@ -433,7 +433,7 @@ _qemu_img_wrapper info TEST_DIR/t.qcow2 + + _qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512 + can't open device TEST_DIR/t.qcow2: Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + Closing the other + + _qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512 +diff --git a/tests/qemu-iotests/182.out b/tests/qemu-iotests/182.out +index 23a4dbf..f1463c8 100644 +--- a/tests/qemu-iotests/182.out ++++ b/tests/qemu-iotests/182.out +@@ -4,5 +4,5 @@ Starting QEMU + + Starting a second QEMU using the same image should fail + QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=none,id=drive0,file.locking=on: Failed to get "write" lock +-Is another process using the image? ++Is another process using the image [TEST_DIR/t.qcow2]? + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Lock-new-fd-in-raw_reopen_prepare.patch b/SOURCES/kvm-file-posix-Lock-new-fd-in-raw_reopen_prepare.patch new file mode 100644 index 0000000..bb54012 --- /dev/null +++ b/SOURCES/kvm-file-posix-Lock-new-fd-in-raw_reopen_prepare.patch @@ -0,0 +1,90 @@ +From ffe4f32d741439547577bc87e1f08df8f6f2a151 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 15 Mar 2019 18:10:08 +0100 +Subject: [PATCH 012/163] file-posix: Lock new fd in raw_reopen_prepare() + +RH-Author: Kevin Wolf +Message-id: <20190315181010.14964-13-kwolf@redhat.com> +Patchwork-id: 84889 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 12/14] file-posix: Lock new fd in raw_reopen_prepare() +Bugzilla: 1685989 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +There is no reason why we can take locks on the new file descriptor only +in raw_reopen_commit() where error handling isn't possible any more. +Instead, we can already do this in raw_reopen_prepare(). + +Signed-off-by: Kevin Wolf +(cherry picked from commit a6aeca0ca530f104b5a5dd6704fca22b2c5edefa) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 27 ++++++++++++++++----------- + 1 file changed, 16 insertions(+), 11 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index b577d88..ae16f2f 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -897,7 +897,7 @@ static int raw_reopen_prepare(BDRVReopenState *state, + { + BDRVRawState *s; + BDRVRawReopenState *rs; +- int ret = 0; ++ int ret; + Error *local_err = NULL; + + assert(state != NULL); +@@ -921,14 +921,27 @@ static int raw_reopen_prepare(BDRVReopenState *state, + if (rs->fd != -1) { + raw_probe_alignment(state->bs, rs->fd, &local_err); + if (local_err) { +- qemu_close(rs->fd); +- rs->fd = -1; + error_propagate(errp, local_err); + ret = -EINVAL; ++ goto out_fd; ++ } ++ ++ /* Copy locks to the new fd */ ++ ret = raw_apply_lock_bytes(NULL, rs->fd, s->locked_perm, ++ s->locked_shared_perm, false, errp); ++ if (ret < 0) { ++ ret = -EINVAL; ++ goto out_fd; + } + } + + s->reopen_state = state; ++ ret = 0; ++out_fd: ++ if (ret < 0) { ++ qemu_close(rs->fd); ++ rs->fd = -1; ++ } + out: + return ret; + } +@@ -937,17 +950,9 @@ static void raw_reopen_commit(BDRVReopenState *state) + { + BDRVRawReopenState *rs = state->opaque; + BDRVRawState *s = state->bs->opaque; +- Error *local_err = NULL; + + s->open_flags = rs->open_flags; + +- /* Copy locks to the new fd before closing the old one. */ +- raw_apply_lock_bytes(NULL, rs->fd, s->locked_perm, +- s->locked_shared_perm, false, &local_err); +- if (local_err) { +- /* shouldn't fail in a sane host, but report it just in case. */ +- error_report_err(local_err); +- } + qemu_close(s->fd); + s->fd = rs->fd; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Make-.bdrv_co_truncate-asynchronous.patch b/SOURCES/kvm-file-posix-Make-.bdrv_co_truncate-asynchronous.patch new file mode 100644 index 0000000..b6a0528 --- /dev/null +++ b/SOURCES/kvm-file-posix-Make-.bdrv_co_truncate-asynchronous.patch @@ -0,0 +1,384 @@ +From 3bcd3a26a88ebeafa17dcf7e17d589f78e17500b Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 24 Jul 2018 09:23:26 +0200 +Subject: [PATCH 39/89] file-posix: Make .bdrv_co_truncate asynchronous + +RH-Author: Kevin Wolf +Message-id: <20180712144258.17303-7-kwolf@redhat.com> +Patchwork-id: 81324 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 6/6] file-posix: Make .bdrv_co_truncate asynchronous +Bugzilla: 1595173 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow + +This moves the code to resize an image file to the thread pool to avoid +blocking. + +Creating large images with preallocation with blockdev-create is now +actually a background job instead of blocking the monitor (and most +other things) until the preallocation has completed. + +Signed-off-by: Kevin Wolf +Reviewed-by: Stefan Hajnoczi +(cherry picked from commit 93f4e2ff4b31205d8bab0856631a52ed442b8b1c) +Signed-off-by: Kevin Wolf +--- + block/file-posix.c | 266 +++++++++++++++++++++++++++--------------------- + include/block/raw-aio.h | 4 +- + 2 files changed, 154 insertions(+), 116 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index f8488ec..24c2367 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -186,8 +186,16 @@ typedef struct RawPosixAIOData { + #define aio_ioctl_cmd aio_nbytes /* for QEMU_AIO_IOCTL */ + off_t aio_offset; + int aio_type; +- int aio_fd2; +- off_t aio_offset2; ++ union { ++ struct { ++ int aio_fd2; ++ off_t aio_offset2; ++ }; ++ struct { ++ PreallocMode prealloc; ++ Error **errp; ++ }; ++ }; + } RawPosixAIOData; + + #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +@@ -1509,6 +1517,122 @@ static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb) + return ret; + } + ++static int handle_aiocb_truncate(RawPosixAIOData *aiocb) ++{ ++ 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; ++ Error **errp = aiocb->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 && aiocb->prealloc != PREALLOC_MODE_OFF) { ++ error_setg(errp, "Cannot use preallocation for shrinking files"); ++ return -ENOTSUP; ++ } ++ ++ switch (aiocb->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 { ++ 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) { ++ 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"); ++ } ++ return result; ++ default: ++ result = -ENOTSUP; ++ error_setg(errp, "Unsupported preallocation mode: %s", ++ PreallocMode_str(aiocb->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 aio_worker(void *arg) + { + RawPosixAIOData *aiocb = arg; +@@ -1552,6 +1676,9 @@ static int aio_worker(void *arg) + case QEMU_AIO_COPY_RANGE: + ret = handle_aiocb_copy_range(aiocb); + break; ++ case QEMU_AIO_TRUNCATE: ++ ret = handle_aiocb_truncate(aiocb); ++ break; + default: + fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type); + ret = -EINVAL; +@@ -1719,117 +1846,25 @@ static void raw_close(BlockDriverState *bs) + * + * Returns: 0 on success, -errno on failure. + */ +-static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc, +- Error **errp) ++static int coroutine_fn ++raw_regular_truncate(BlockDriverState *bs, int fd, int64_t offset, ++ PreallocMode prealloc, Error **errp) + { +- int result = 0; +- int64_t current_length = 0; +- char *buf = NULL; +- struct stat st; +- +- 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 { +- 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) { +- 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"); +- } +- return result; +- default: +- result = -ENOTSUP; +- error_setg(errp, "Unsupported preallocation mode: %s", +- PreallocMode_str(prealloc)); +- return result; +- } ++ RawPosixAIOData *acb = g_new(RawPosixAIOData, 1); ++ ThreadPool *pool; + +-out: +- if (result < 0) { +- if (ftruncate(fd, current_length) < 0) { +- error_report("Failed to restore old file length: %s", +- strerror(errno)); +- } +- } ++ *acb = (RawPosixAIOData) { ++ .bs = bs, ++ .aio_fildes = fd, ++ .aio_type = QEMU_AIO_TRUNCATE, ++ .aio_offset = offset, ++ .prealloc = prealloc, ++ .errp = errp, ++ }; + +- g_free(buf); +- return result; ++ /* @bs can be NULL, bdrv_get_aio_context() returns the main context then */ ++ pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); ++ return thread_pool_submit_co(pool, aio_worker, acb); + } + + static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, +@@ -1846,7 +1881,7 @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, + } + + if (S_ISREG(st.st_mode)) { +- return raw_regular_truncate(s->fd, offset, prealloc, errp); ++ return raw_regular_truncate(bs, s->fd, offset, prealloc, errp); + } + + if (prealloc != PREALLOC_MODE_OFF) { +@@ -2048,7 +2083,8 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs) + return (int64_t)st.st_blocks * 512; + } + +-static int raw_co_create(BlockdevCreateOptions *options, Error **errp) ++static int coroutine_fn ++raw_co_create(BlockdevCreateOptions *options, Error **errp) + { + BlockdevCreateOptionsFile *file_opts; + Error *local_err = NULL; +@@ -2101,7 +2137,7 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp) + } + + /* Clear the file by truncating it to 0 */ +- result = raw_regular_truncate(fd, 0, PREALLOC_MODE_OFF, errp); ++ result = raw_regular_truncate(NULL, fd, 0, PREALLOC_MODE_OFF, errp); + if (result < 0) { + goto out_unlock; + } +@@ -2123,8 +2159,8 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp) + + /* Resize and potentially preallocate the file to the desired + * final size */ +- result = raw_regular_truncate(fd, file_opts->size, file_opts->preallocation, +- errp); ++ result = raw_regular_truncate(NULL, fd, file_opts->size, ++ file_opts->preallocation, errp); + if (result < 0) { + goto out_unlock; + } +diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h +index 3240530..2ffcd9d 100644 +--- a/include/block/raw-aio.h ++++ b/include/block/raw-aio.h +@@ -26,6 +26,7 @@ + #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_TYPE_MASK \ + (QEMU_AIO_READ | \ + QEMU_AIO_WRITE | \ +@@ -33,7 +34,8 @@ + QEMU_AIO_FLUSH | \ + QEMU_AIO_DISCARD | \ + QEMU_AIO_WRITE_ZEROES | \ +- QEMU_AIO_COPY_RANGE) ++ QEMU_AIO_COPY_RANGE | \ ++ QEMU_AIO_TRUNCATE) + + /* AIO flags */ + #define QEMU_AIO_MISALIGNED 0x1000 +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Make-auto-read-only-dynamic.patch b/SOURCES/kvm-file-posix-Make-auto-read-only-dynamic.patch new file mode 100644 index 0000000..4a4f0de --- /dev/null +++ b/SOURCES/kvm-file-posix-Make-auto-read-only-dynamic.patch @@ -0,0 +1,174 @@ +From ee549d8b1c8cd482bb84d49e7535e174fd89b9ea Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 15 Mar 2019 18:10:10 +0100 +Subject: [PATCH 014/163] file-posix: Make auto-read-only dynamic + +RH-Author: Kevin Wolf +Message-id: <20190315181010.14964-15-kwolf@redhat.com> +Patchwork-id: 84891 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 14/14] file-posix: Make auto-read-only dynamic +Bugzilla: 1685989 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +Until now, with auto-read-only=on we tried to open the file read-write +first and if that failed, read-only was tried. This is actually not good +enough for libvirt, which gives QEMU SELinux permissions for read-write +only as soon as it actually intends to write to the image. So we need to +be able to switch between read-only and read-write at runtime. + +This patch makes auto-read-only dynamic, i.e. the file is opened +read-only as long as no user of the node has requested write +permissions, but it is automatically reopened read-write as soon as the +first writer is attached. Conversely, if the last writer goes away, the +file is reopened read-only again. + +bs->read_only is no longer set for auto-read-only=on files even if the +file descriptor is opened read-only because it will be transparently +upgraded as soon as a writer is attached. This changes the output of +qemu-iotests 232. + +Signed-off-by: Kevin Wolf +(cherry picked from commit 23dece19da41724349809873923e20a48b619cb7) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 36 +++++++++++++++++------------------- + tests/qemu-iotests/232.out | 12 ++++++------ + 2 files changed, 23 insertions(+), 25 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index f0f8eaf..0cf7261 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -382,13 +382,21 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) + } + } + +-static void raw_parse_flags(int bdrv_flags, int *open_flags) ++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_RDWR) { ++ ++ 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; +@@ -516,24 +524,12 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, + } + + s->open_flags = open_flags; +- raw_parse_flags(bdrv_flags, &s->open_flags); ++ raw_parse_flags(bdrv_flags, &s->open_flags, false); + + s->fd = -1; + fd = qemu_open(filename, s->open_flags, 0644); + ret = fd < 0 ? -errno : 0; + +- if (ret == -EACCES || ret == -EROFS) { +- /* Try to degrade to read-only, but if it doesn't work, still use the +- * normal error message. */ +- if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) { +- bdrv_flags &= ~BDRV_O_RDWR; +- raw_parse_flags(bdrv_flags, &s->open_flags); +- assert(!(s->open_flags & O_CREAT)); +- fd = qemu_open(filename, s->open_flags); +- ret = fd < 0 ? -errno : 0; +- } +- } +- + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not open '%s'", filename); + if (ret == -EROFS) { +@@ -838,12 +834,14 @@ static int raw_handle_perm_lock(BlockDriverState *bs, + } + + static int raw_reconfigure_getfd(BlockDriverState *bs, int flags, +- int *open_flags, bool force_dup, ++ int *open_flags, uint64_t perm, bool force_dup, + 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; +@@ -854,7 +852,7 @@ static int raw_reconfigure_getfd(BlockDriverState *bs, int flags, + *open_flags |= O_NONBLOCK; + } + +- raw_parse_flags(flags, open_flags); ++ raw_parse_flags(flags, open_flags, has_writers); + + #ifdef O_ASYNC + /* Not all operating systems have O_ASYNC, and those that don't +@@ -916,7 +914,7 @@ static int raw_reopen_prepare(BDRVReopenState *state, + rs = state->opaque; + + rs->fd = raw_reconfigure_getfd(state->bs, state->flags, &rs->open_flags, +- true, &local_err); ++ state->perm, true, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -1; +@@ -2548,7 +2546,7 @@ static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared, + s->perm_change_fd = rs->fd; + } else { + /* We may need a new fd if auto-read-only switches the mode */ +- ret = raw_reconfigure_getfd(bs, bs->open_flags, &open_flags, ++ ret = raw_reconfigure_getfd(bs, bs->open_flags, &open_flags, perm, + false, errp); + if (ret < 0) { + return ret; +diff --git a/tests/qemu-iotests/232.out b/tests/qemu-iotests/232.out +index dcb683a..3bd1a92 100644 +--- a/tests/qemu-iotests/232.out ++++ b/tests/qemu-iotests/232.out +@@ -22,12 +22,12 @@ NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) + NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) + + QEMU_PROG: -drive driver=file,file=TEST_DIR/t.IMGFMT,if=none,read-only=off,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied +-NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) +-NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) ++NODE_NAME: TEST_DIR/t.IMGFMT (file) ++NODE_NAME: TEST_DIR/t.IMGFMT (file) + + QEMU_PROG: -drive driver=file,file=TEST_DIR/t.IMGFMT,if=none,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied +-NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) +-NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) ++NODE_NAME: TEST_DIR/t.IMGFMT (file) ++NODE_NAME: TEST_DIR/t.IMGFMT (file) + + === -blockdev with read-write image: read-only/auto-read-only combinations === + +@@ -50,10 +50,10 @@ node0: TEST_DIR/t.IMGFMT (file, read-only) + node0: TEST_DIR/t.IMGFMT (file, read-only) + + QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,read-only=off,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied +-node0: TEST_DIR/t.IMGFMT (file, read-only) ++node0: TEST_DIR/t.IMGFMT (file) + QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied + + QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied +-node0: TEST_DIR/t.IMGFMT (file, read-only) ++node0: TEST_DIR/t.IMGFMT (file) + QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0: Could not open 'TEST_DIR/t.IMGFMT': Permission denied + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Prepare-permission-code-for-fd-switching.patch b/SOURCES/kvm-file-posix-Prepare-permission-code-for-fd-switching.patch new file mode 100644 index 0000000..bca91ee --- /dev/null +++ b/SOURCES/kvm-file-posix-Prepare-permission-code-for-fd-switching.patch @@ -0,0 +1,191 @@ +From a1cc5d5a181559a6f68c459cbae8488303112660 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 15 Mar 2019 18:10:09 +0100 +Subject: [PATCH 013/163] file-posix: Prepare permission code for fd switching + +RH-Author: Kevin Wolf +Message-id: <20190315181010.14964-14-kwolf@redhat.com> +Patchwork-id: 84890 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 13/14] file-posix: Prepare permission code for fd switching +Bugzilla: 1685989 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +In order to be able to dynamically reopen the file read-only or +read-write, depending on the users that are attached, we need to be able +to switch to a different file descriptor during the permission change. + +This interacts with reopen, which also creates a new file descriptor and +performs permission changes internally. In this case, the permission +change code must reuse the reopen file descriptor instead of creating a +third one. + +In turn, reopen can drop its code to copy file locks to the new file +descriptor because that is now done when applying the new permissions. + +Signed-off-by: Kevin Wolf +(cherry picked from commit 6ceabe6f77e4ae5ac2fa3d2ac1be11dc95021941) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++------- + 1 file changed, 85 insertions(+), 11 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index ae16f2f..f0f8eaf 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -156,6 +156,7 @@ typedef struct BDRVRawState { + uint64_t locked_perm; + uint64_t locked_shared_perm; + ++ int perm_change_fd; + BDRVReopenState *reopen_state; + + #ifdef CONFIG_XFS +@@ -837,7 +838,8 @@ static int raw_handle_perm_lock(BlockDriverState *bs, + } + + static int raw_reconfigure_getfd(BlockDriverState *bs, int flags, +- int *open_flags, Error **errp) ++ int *open_flags, bool force_dup, ++ Error **errp) + { + BDRVRawState *s = bs->opaque; + int fd = -1; +@@ -863,6 +865,11 @@ static int raw_reconfigure_getfd(BlockDriverState *bs, int flags, + assert((s->open_flags & O_ASYNC) == 0); + #endif + ++ if (!force_dup && *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); +@@ -909,7 +916,7 @@ static int raw_reopen_prepare(BDRVReopenState *state, + rs = state->opaque; + + rs->fd = raw_reconfigure_getfd(state->bs, state->flags, &rs->open_flags, +- &local_err); ++ true, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -1; +@@ -925,14 +932,6 @@ static int raw_reopen_prepare(BDRVReopenState *state, + ret = -EINVAL; + goto out_fd; + } +- +- /* Copy locks to the new fd */ +- ret = raw_apply_lock_bytes(NULL, rs->fd, s->locked_perm, +- s->locked_shared_perm, false, errp); +- if (ret < 0) { +- ret = -EINVAL; +- goto out_fd; +- } + } + + s->reopen_state = state; +@@ -2524,12 +2523,78 @@ static QemuOptsList raw_create_opts = { + static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared, + Error **errp) + { +- return raw_handle_perm_lock(bs, RAW_PL_PREPARE, perm, shared, errp); ++ BDRVRawState *s = bs->opaque; ++ BDRVRawReopenState *rs = NULL; ++ int open_flags; ++ int ret; ++ ++ if (s->perm_change_fd) { ++ /* ++ * In the context of reopen, this function may be called several times ++ * (directly and recursively while change permissions of the parent). ++ * This is even true for children that don't inherit from the original ++ * reopen node, so s->reopen_state is not set. ++ * ++ * Ignore all but the first call. ++ */ ++ return 0; ++ } ++ ++ if (s->reopen_state) { ++ /* We already have a new file descriptor to set permissions for */ ++ assert(s->reopen_state->perm == perm); ++ assert(s->reopen_state->shared_perm == shared); ++ rs = s->reopen_state->opaque; ++ s->perm_change_fd = rs->fd; ++ } else { ++ /* We may need a new fd if auto-read-only switches the mode */ ++ ret = raw_reconfigure_getfd(bs, bs->open_flags, &open_flags, ++ false, errp); ++ if (ret < 0) { ++ return ret; ++ } else if (ret != s->fd) { ++ s->perm_change_fd = ret; ++ } ++ } ++ ++ /* 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) { ++ 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 && !s->reopen_state) { ++ 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->perm_change_fd = 0; ++ + raw_handle_perm_lock(bs, RAW_PL_COMMIT, perm, shared, NULL); + s->perm = perm; + s->shared_perm = shared; +@@ -2537,6 +2602,15 @@ static void raw_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t 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 && !s->reopen_state) { ++ qemu_close(s->perm_change_fd); ++ } ++ s->perm_change_fd = 0; ++ + raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL); + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Skip-effectiveless-OFD-lock-operations.patch b/SOURCES/kvm-file-posix-Skip-effectiveless-OFD-lock-operations.patch new file mode 100644 index 0000000..ecadc42 --- /dev/null +++ b/SOURCES/kvm-file-posix-Skip-effectiveless-OFD-lock-operations.patch @@ -0,0 +1,195 @@ +From 63ef1f91221aabc910fea57486ecc54b1958c7a8 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 4 Feb 2019 20:42:03 +0100 +Subject: [PATCH 03/33] file-posix: Skip effectiveless OFD lock operations + +RH-Author: Max Reitz +Message-id: <20190204204207.18079-4-mreitz@redhat.com> +Patchwork-id: 84220 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 3/7] file-posix: Skip effectiveless OFD lock operations +Bugzilla: 1551486 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Kevin Wolf + +From: Fam Zheng + +If we know we've already locked the bytes, don't do it again; similarly +don't unlock a byte if we haven't locked it. This doesn't change the +behavior, but fixes a corner case explained below. + +Libvirt had an error handling bug that an image can get its (ownership, +file mode, SELinux) permissions changed (RHBZ 1584982) by mistake behind +QEMU. Specifically, an image in use by Libvirt VM has: + + $ ls -lhZ b.img + -rw-r--r--. qemu qemu system_u:object_r:svirt_image_t:s0:c600,c690 b.img + +Trying to attach it a second time won't work because of image locking. +And after the error, it becomes: + + $ ls -lhZ b.img + -rw-r--r--. root root system_u:object_r:virt_image_t:s0 b.img + +Then, we won't be able to do OFD lock operations with the existing fd. +In other words, the code such as in blk_detach_dev: + + blk_set_perm(blk, 0, BLK_PERM_ALL, &error_abort); + +can abort() QEMU, out of environmental changes. + +This patch is an easy fix to this and the change is regardlessly +reasonable, so do it. + +Signed-off-by: Fam Zheng +Reviewed-by: Max Reitz +Signed-off-by: Kevin Wolf +(cherry picked from commit 2996ffad3acabe890fbb4f84a069cdc325a68108) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 54 ++++++++++++++++++++++++++++++++++++++++++++---------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index c2403ba..2a05193 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -152,6 +152,11 @@ typedef struct BDRVRawState { + uint64_t perm; + uint64_t shared_perm; + ++ /* The perms bits whose corresponding bytes are already locked in ++ * s->lock_fd. */ ++ uint64_t locked_perm; ++ uint64_t locked_shared_perm; ++ + #ifdef CONFIG_XFS + bool is_xfs:1; + #endif +@@ -677,43 +682,72 @@ typedef enum { + * 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(int fd, ++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; +- if (perm_lock_bits & (1ULL << i)) { ++ uint64_t bit = (1ULL << i); ++ if ((perm_lock_bits & bit) && !(locked_perm & bit)) { + ret = qemu_lock_fd(fd, off, 1, false); + if (ret) { + error_setg(errp, "Failed to lock byte %d", off); + return ret; ++ } else if (s) { ++ s->locked_perm |= bit; + } +- } else if (unlock) { ++ } else if (unlock && (locked_perm & bit) && !(perm_lock_bits & bit)) { + ret = qemu_unlock_fd(fd, off, 1); + if (ret) { + error_setg(errp, "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; +- if (shared_perm_lock_bits & (1ULL << 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) { + error_setg(errp, "Failed to lock byte %d", off); + return ret; ++ } else if (s) { ++ s->locked_shared_perm |= bit; + } +- } else if (unlock) { ++ } else if (unlock && (locked_shared_perm & bit) && ++ !(shared_perm_lock_bits & bit)) { + ret = qemu_unlock_fd(fd, off, 1); + if (ret) { + error_setg(errp, "Failed to unlock byte %d", off); + return ret; ++ } else if (s) { ++ s->locked_shared_perm &= ~bit; + } + } + } +@@ -781,7 +815,7 @@ static int raw_handle_perm_lock(BlockDriverState *bs, + + switch (op) { + case RAW_PL_PREPARE: +- ret = raw_apply_lock_bytes(s->lock_fd, s->perm | new_perm, ++ ret = raw_apply_lock_bytes(s, s->lock_fd, s->perm | new_perm, + ~s->shared_perm | ~new_shared, + false, errp); + if (!ret) { +@@ -796,7 +830,7 @@ static int raw_handle_perm_lock(BlockDriverState *bs, + op = RAW_PL_ABORT; + /* fall through to unlock bytes. */ + case RAW_PL_ABORT: +- raw_apply_lock_bytes(s->lock_fd, s->perm, ~s->shared_perm, ++ raw_apply_lock_bytes(s, s->lock_fd, s->perm, ~s->shared_perm, + true, &local_err); + if (local_err) { + /* Theoretically the above call only unlocks bytes and it cannot +@@ -806,7 +840,7 @@ static int raw_handle_perm_lock(BlockDriverState *bs, + } + break; + case RAW_PL_COMMIT: +- raw_apply_lock_bytes(s->lock_fd, new_perm, ~new_shared, ++ raw_apply_lock_bytes(s, s->lock_fd, new_perm, ~new_shared, + true, &local_err); + if (local_err) { + /* Theoretically the above call only unlocks bytes and it cannot +@@ -2160,7 +2194,7 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) + shared = BLK_PERM_ALL & ~BLK_PERM_RESIZE; + + /* Step one: Take locks */ +- result = raw_apply_lock_bytes(fd, perm, ~shared, false, errp); ++ result = raw_apply_lock_bytes(NULL, fd, perm, ~shared, false, errp); + if (result < 0) { + goto out_close; + } +@@ -2204,7 +2238,7 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) + } + + out_unlock: +- raw_apply_lock_bytes(fd, 0, 0, true, &local_err); ++ 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 +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Store-BDRVRawState.reopen_state-during-re.patch b/SOURCES/kvm-file-posix-Store-BDRVRawState.reopen_state-during-re.patch new file mode 100644 index 0000000..18233fa --- /dev/null +++ b/SOURCES/kvm-file-posix-Store-BDRVRawState.reopen_state-during-re.patch @@ -0,0 +1,77 @@ +From c5a20feec46deb3f25037f29e7ae4823fe46afdf Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 15 Mar 2019 18:10:07 +0100 +Subject: [PATCH 011/163] file-posix: Store BDRVRawState.reopen_state during + reopen + +RH-Author: Kevin Wolf +Message-id: <20190315181010.14964-12-kwolf@redhat.com> +Patchwork-id: 84888 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 11/14] file-posix: Store BDRVRawState.reopen_state during reopen +Bugzilla: 1685989 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +We'll want to access the file descriptor in the reopen_state while +processing permission changes in the context of the repoen. + +Signed-off-by: Kevin Wolf +(cherry picked from commit e0c9cf3a484beb746996c0cd63e5585fecb3fd25) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/block/file-posix.c b/block/file-posix.c +index e50eb0e..b577d88 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -156,6 +156,8 @@ typedef struct BDRVRawState { + uint64_t locked_perm; + uint64_t locked_shared_perm; + ++ BDRVReopenState *reopen_state; ++ + #ifdef CONFIG_XFS + bool is_xfs:1; + #endif +@@ -926,6 +928,7 @@ static int raw_reopen_prepare(BDRVReopenState *state, + } + } + ++ s->reopen_state = state; + out: + return ret; + } +@@ -950,12 +953,16 @@ static void raw_reopen_commit(BDRVReopenState *state) + + 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) { +@@ -968,6 +975,9 @@ static void raw_reopen_abort(BDRVReopenState *state) + } + g_free(state->opaque); + state->opaque = NULL; ++ ++ assert(s->reopen_state == state); ++ s->reopen_state = NULL; + } + + static int hdev_get_max_transfer_length(BlockDriverState *bs, int fd) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Support-BDRV_REQ_NO_FALLBACK-for-zero-wri.patch b/SOURCES/kvm-file-posix-Support-BDRV_REQ_NO_FALLBACK-for-zero-wri.patch new file mode 100644 index 0000000..17cb59d --- /dev/null +++ b/SOURCES/kvm-file-posix-Support-BDRV_REQ_NO_FALLBACK-for-zero-wri.patch @@ -0,0 +1,103 @@ +From 9a02c0bcafdbf681e76f816ad3f60dfb7dea13fb Mon Sep 17 00:00:00 2001 +From: Maxim Levitsky +Date: Wed, 5 Jun 2019 13:57:03 +0200 +Subject: [PATCH 15/23] file-posix: Support BDRV_REQ_NO_FALLBACK for zero + writes + +RH-Author: Maxim Levitsky +Message-id: <20190605135705.24526-8-mlevitsk@redhat.com> +Patchwork-id: 88560 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 7/9] file-posix: Support BDRV_REQ_NO_FALLBACK for zero writes +Bugzilla: 1648622 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow + +From: Kevin Wolf + +We know that the kernel implements a slow fallback code path for +BLKZEROOUT, so if BDRV_REQ_NO_FALLBACK is given, we shouldn't call it. +The other operations we call in the context of .bdrv_co_pwrite_zeroes +should usually be quick, so no modification should be needed for them. +If we ever notice that there are additional problematic cases, we can +still make these conditional as well. + +Signed-off-by: Kevin Wolf +Acked-by: Eric Blake + +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1648622 + +Signed-off-by: Maxim Levitsky +(Cherry picked from 738301e11758171defaa5a5237d584f8226af89f) + +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 24 ++++++++++++++++-------- + include/block/raw-aio.h | 1 + + 2 files changed, 17 insertions(+), 8 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index 90c719f..d1926b3 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -632,7 +632,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, + } + #endif + +- bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP; ++ bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK; + ret = 0; + fail: + if (filename && (bdrv_flags & BDRV_O_TEMPORARY)) { +@@ -1449,14 +1449,19 @@ static ssize_t handle_aiocb_write_zeroes_block(RawPosixAIOData *aiocb) + } + + #ifdef BLKZEROOUT +- do { +- uint64_t range[2] = { aiocb->aio_offset, aiocb->aio_nbytes }; +- if (ioctl(aiocb->aio_fildes, BLKZEROOUT, range) == 0) { +- return 0; +- } +- } while (errno == EINTR); ++ /* 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); ++ ret = translate_err(-errno); ++ } + #endif + + if (ret == -ENOTSUP) { +@@ -2535,6 +2540,9 @@ raw_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int 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; +diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h +index 2ffcd9d..5a926a3 100644 +--- a/include/block/raw-aio.h ++++ b/include/block/raw-aio.h +@@ -40,6 +40,7 @@ + /* AIO flags */ + #define QEMU_AIO_MISALIGNED 0x1000 + #define QEMU_AIO_BLKDEV 0x2000 ++#define QEMU_AIO_NO_FALLBACK 0x4000 + + + /* linux-aio.c - Linux native implementation */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Support-auto-read-only-option.patch b/SOURCES/kvm-file-posix-Support-auto-read-only-option.patch new file mode 100644 index 0000000..f7e4b5d --- /dev/null +++ b/SOURCES/kvm-file-posix-Support-auto-read-only-option.patch @@ -0,0 +1,61 @@ +From e342033247c37090e1b8e0f99abc849e36521701 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 23 Nov 2018 10:41:48 +0100 +Subject: [PATCH 07/34] file-posix: Support auto-read-only option + +RH-Author: Kevin Wolf +Message-id: <20181123104154.13541-7-kwolf@redhat.com> +Patchwork-id: 83116 +O-Subject: [RHEL-7.7/7.6.z qemu-kvm-rhev PATCH v2 06/12] file-posix: Support auto-read-only option +Bugzilla: 1623986 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina +RH-Acked-by: John Snow + +If read-only=off, but auto-read-only=on is given, open the file +read-write if we have the permissions, but instead of erroring out for +read-only files, just degrade to read-only. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +(cherry picked from commit 64107dc044a54ebe46348ac0fe87584be2eb3e81) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 19 ++++++++++++++++--- + 1 file changed, 16 insertions(+), 3 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index c12cdb7..7e6869d 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -517,9 +517,22 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, + + s->fd = -1; + fd = qemu_open(filename, s->open_flags, 0644); +- if (fd < 0) { +- ret = -errno; +- error_setg_errno(errp, errno, "Could not open '%s'", filename); ++ ret = fd < 0 ? -errno : 0; ++ ++ if (ret == -EACCES || ret == -EROFS) { ++ /* Try to degrade to read-only, but if it doesn't work, still use the ++ * normal error message. */ ++ if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) { ++ bdrv_flags &= ~BDRV_O_RDWR; ++ raw_parse_flags(bdrv_flags, &s->open_flags); ++ assert(!(s->open_flags & O_CREAT)); ++ fd = qemu_open(filename, s->open_flags); ++ ret = fd < 0 ? -errno : 0; ++ } ++ } ++ ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not open '%s'", filename); + if (ret == -EROFS) { + ret = -EACCES; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Unlock-FD-after-creation.patch b/SOURCES/kvm-file-posix-Unlock-FD-after-creation.patch new file mode 100644 index 0000000..c463fca --- /dev/null +++ b/SOURCES/kvm-file-posix-Unlock-FD-after-creation.patch @@ -0,0 +1,77 @@ +From 1375d18f600b29111c7d8e6e5a2c73d01afe994b Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 9 Jul 2018 15:11:22 +0200 +Subject: [PATCH 30/89] file-posix: Unlock FD after creation + +RH-Author: Max Reitz +Message-id: <20180709151122.27541-3-mreitz@redhat.com> +Patchwork-id: 81269 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 2/2] file-posix: Unlock FD after creation +Bugzilla: 1599335 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Kevin Wolf +RH-Acked-by: John Snow + +Closing the FD does not necessarily mean that it is unlocked. Fix this +by relinquishing all permission locks before qemu_close(). + +Reported-by: Kevin Wolf +Signed-off-by: Max Reitz +Signed-off-by: Kevin Wolf +(cherry picked from commit 7c20c808a5cbf5d244735bc78fc3138c739c1946) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 17 ++++++++++++++--- + 1 file changed, 14 insertions(+), 3 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index e876770..cbf7c11 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -2051,6 +2051,7 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs) + static int raw_co_create(BlockdevCreateOptions *options, Error **errp) + { + BlockdevCreateOptionsFile *file_opts; ++ Error *local_err = NULL; + int fd; + uint64_t perm, shared; + int result = 0; +@@ -2096,13 +2097,13 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp) + /* Step two: Check that nobody else has taken conflicting locks */ + result = raw_check_lock_bytes(fd, perm, shared, errp); + if (result < 0) { +- goto out_close; ++ goto out_unlock; + } + + /* Clear the file by truncating it to 0 */ + result = raw_regular_truncate(fd, 0, PREALLOC_MODE_OFF, errp); + if (result < 0) { +- goto out_close; ++ goto out_unlock; + } + + if (file_opts->nocow) { +@@ -2125,7 +2126,17 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp) + result = raw_regular_truncate(fd, file_opts->size, file_opts->preallocation, + errp); + if (result < 0) { +- goto out_close; ++ goto out_unlock; ++ } ++ ++out_unlock: ++ raw_apply_lock_bytes(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. */ ++ error_report_err(local_err); + } + + out_close: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-Use-error-API-properly.patch b/SOURCES/kvm-file-posix-Use-error-API-properly.patch new file mode 100644 index 0000000..1543afd --- /dev/null +++ b/SOURCES/kvm-file-posix-Use-error-API-properly.patch @@ -0,0 +1,163 @@ +From a36c6beb5f5a2a02f805bcbf6303e3c6ab908c95 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 15 Mar 2019 18:10:05 +0100 +Subject: [PATCH 009/163] file-posix: Use error API properly + +RH-Author: Kevin Wolf +Message-id: <20190315181010.14964-10-kwolf@redhat.com> +Patchwork-id: 84886 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 09/14] file-posix: Use error API properly +Bugzilla: 1685989 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Fam Zheng + +Use error_report for situations that affect user operation (i.e. we're +actually returning error), and warn_report/warn_report_err when some +less critical error happened but the user operation can still carry on. + +For raw_normalize_devicepath, add Error parameter to propagate to +its callers. + +Suggested-by: Markus Armbruster +Signed-off-by: Fam Zheng +Signed-off-by: Kevin Wolf +(cherry picked from commit db0754df88e3ca4797539c1edbde596d871b64b6) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 39 ++++++++++++++++----------------------- + 1 file changed, 16 insertions(+), 23 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index deecf58..419781c 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -207,7 +207,7 @@ static int cdrom_reopen(BlockDriverState *bs); + #endif + + #if defined(__NetBSD__) +-static int raw_normalize_devicepath(const char **filename) ++static int raw_normalize_devicepath(const char **filename, Error **errp) + { + static char namebuf[PATH_MAX]; + const char *dp, *fname; +@@ -216,8 +216,7 @@ static int raw_normalize_devicepath(const char **filename) + fname = *filename; + dp = strrchr(fname, '/'); + if (lstat(fname, &sb) < 0) { +- fprintf(stderr, "%s: stat failed: %s\n", +- fname, strerror(errno)); ++ error_setg_errno(errp, errno, "%s: stat failed", fname); + return -errno; + } + +@@ -231,14 +230,13 @@ static int raw_normalize_devicepath(const char **filename) + snprintf(namebuf, PATH_MAX, "%.*s/r%s", + (int)(dp - fname), fname, dp + 1); + } +- fprintf(stderr, "%s is a block device", fname); + *filename = namebuf; +- fprintf(stderr, ", using %s\n", *filename); ++ warn_report("%s is a block device, using %s", fname, *filename); + + return 0; + } + #else +-static int raw_normalize_devicepath(const char **filename) ++static int raw_normalize_devicepath(const char **filename, Error **errp) + { + return 0; + } +@@ -458,9 +456,8 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, + + filename = qemu_opt_get(opts, "filename"); + +- ret = raw_normalize_devicepath(&filename); ++ ret = raw_normalize_devicepath(&filename, errp); + if (ret != 0) { +- error_setg_errno(errp, -ret, "Could not normalize device path"); + goto fail; + } + +@@ -489,11 +486,10 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, + case ON_OFF_AUTO_ON: + s->use_lock = true; + if (!qemu_has_ofd_lock()) { +- fprintf(stderr, +- "File lock requested but OFD locking syscall is " +- "unavailable, falling back to POSIX file locks.\n" +- "Due to the implementation, locks can be lost " +- "unexpectedly.\n"); ++ 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: +@@ -821,7 +817,7 @@ static int raw_handle_perm_lock(BlockDriverState *bs, + /* Theoretically the above call only unlocks bytes and it cannot + * fail. Something weird happened, report it. + */ +- error_report_err(local_err); ++ warn_report_err(local_err); + } + break; + case RAW_PL_COMMIT: +@@ -831,7 +827,7 @@ static int raw_handle_perm_lock(BlockDriverState *bs, + /* Theoretically the above call only unlocks bytes and it cannot + * fail. Something weird happened, report it. + */ +- error_report_err(local_err); ++ warn_report_err(local_err); + } + break; + } +@@ -891,10 +887,8 @@ static int raw_reopen_prepare(BDRVReopenState *state, + /* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */ + if (rs->fd == -1) { + const char *normalized_filename = state->bs->filename; +- ret = raw_normalize_devicepath(&normalized_filename); +- if (ret < 0) { +- error_setg_errno(errp, -ret, "Could not normalize device path"); +- } else { ++ ret = raw_normalize_devicepath(&normalized_filename, errp); ++ if (ret >= 0) { + assert(!(rs->open_flags & O_CREAT)); + rs->fd = qemu_open(normalized_filename, rs->open_flags); + if (rs->fd == -1) { +@@ -1742,7 +1736,7 @@ static int aio_worker(void *arg) + ret = handle_aiocb_truncate(aiocb); + break; + default: +- fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type); ++ error_report("invalid aio request (0x%x)", aiocb->aio_type); + ret = -EINVAL; + break; + } +@@ -2233,7 +2227,7 @@ out_unlock: + * not mean the whole creation operation has failed. So + * report it the user for their convenience, but do not report + * it to the caller. */ +- error_report_err(local_err); ++ warn_report_err(local_err); + } + + out_close: +@@ -2986,9 +2980,8 @@ static int coroutine_fn hdev_co_create_opts(const char *filename, QemuOpts *opts + + (void)has_prefix; + +- ret = raw_normalize_devicepath(&filename); ++ ret = raw_normalize_devicepath(&filename, errp); + if (ret < 0) { +- error_setg_errno(errp, -ret, "Could not normalize device path"); + return ret; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-file-posix-specify-expected-filetypes.patch b/SOURCES/kvm-file-posix-specify-expected-filetypes.patch new file mode 100644 index 0000000..8318184 --- /dev/null +++ b/SOURCES/kvm-file-posix-specify-expected-filetypes.patch @@ -0,0 +1,151 @@ +From 75d7ceaa7c842deeae5cd7c09bbfd982c3dd0f2e Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 13 Jul 2018 14:50:01 +0200 +Subject: [PATCH 43/89] file-posix: specify expected filetypes + +RH-Author: Kevin Wolf +Message-id: <20180713145002.20953-2-kwolf@redhat.com> +Patchwork-id: 81350 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/2] file-posix: specify expected filetypes +Bugzilla: 1525829 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +From: John Snow + +Adjust each caller of raw_open_common to specify if they are expecting +host and character devices or not. Tighten expectations of file types upon +open in the common code and refuse types that are not expected. + +This has two effects: + +(1) Character and block devices are now considered deprecated for the + 'file' driver, which expects only S_IFREG, and +(2) no file-posix driver (file, host_cdrom, or host_device) can open + directories now. + +I don't think there's a legitimate reason to open directories as if +they were files. This prevents QEMU from opening and attempting to probe +a directory inode, which can break in exciting ways. One of those ways +is lseek on ext4/xfs, which will return 0x7fffffffffffffff as the file +size instead of EISDIR. This can coax QEMU into responding with a +confusing "file too big" instead of "Hey, that's not a file". + +See: https://bugs.launchpad.net/qemu/+bug/1739304/ +Signed-off-by: John Snow +Signed-off-by: Kevin Wolf +(cherry picked from commit 230ff73904e72dde2d7718c2da407786a1c72e57) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/file-posix.c | 39 +++++++++++++++++++++++++++++++-------- + qemu-doc.texi | 6 ++++++ + 2 files changed, 37 insertions(+), 8 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index 24c2367..06ec67d 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -431,7 +431,8 @@ static QemuOptsList raw_runtime_opts = { + }; + + static int raw_open_common(BlockDriverState *bs, QDict *options, +- int bdrv_flags, int open_flags, Error **errp) ++ int bdrv_flags, int open_flags, ++ bool device, Error **errp) + { + BDRVRawState *s = bs->opaque; + QemuOpts *opts; +@@ -569,10 +570,32 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, + error_setg_errno(errp, errno, "Could not stat file"); + goto fail; + } +- if (S_ISREG(st.st_mode)) { +- s->discard_zeroes = true; +- s->has_fallocate = true; ++ ++ if (!device) { ++ if (S_ISBLK(st.st_mode)) { ++ warn_report("Opening a block device as a file using the '%s' " ++ "driver is deprecated", bs->drv->format_name); ++ } else if (S_ISCHR(st.st_mode)) { ++ warn_report("Opening a character device as a file using the '%s' " ++ "driver is deprecated", bs->drv->format_name); ++ } else if (!S_ISREG(st.st_mode)) { ++ error_setg(errp, "A regular file was expected by the '%s' driver, " ++ "but something else was given", bs->drv->format_name); ++ ret = -EINVAL; ++ goto fail; ++ } else { ++ s->discard_zeroes = true; ++ s->has_fallocate = true; ++ } ++ } else { ++ if (!(S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))) { ++ error_setg(errp, "'%s' driver expects either " ++ "a character or block device", bs->drv->format_name); ++ ret = -EINVAL; ++ goto fail; ++ } + } ++ + if (S_ISBLK(st.st_mode)) { + #ifdef BLKDISCARDZEROES + unsigned int arg; +@@ -625,7 +648,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, + BDRVRawState *s = bs->opaque; + + s->type = FTYPE_FILE; +- return raw_open_common(bs, options, flags, 0, errp); ++ return raw_open_common(bs, options, flags, 0, false, errp); + } + + typedef enum { +@@ -2794,7 +2817,7 @@ hdev_open_Mac_error: + + s->type = FTYPE_FILE; + +- ret = raw_open_common(bs, options, flags, 0, &local_err); ++ ret = raw_open_common(bs, options, flags, 0, true, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + #if defined(__APPLE__) && defined(__MACH__) +@@ -3023,7 +3046,7 @@ static int cdrom_open(BlockDriverState *bs, QDict *options, int flags, + 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, errp); ++ return raw_open_common(bs, options, flags, O_NONBLOCK, true, errp); + } + + static int cdrom_probe_device(const char *filename) +@@ -3136,7 +3159,7 @@ static int cdrom_open(BlockDriverState *bs, QDict *options, int flags, + + s->type = FTYPE_CD; + +- ret = raw_open_common(bs, options, flags, 0, &local_err); ++ ret = raw_open_common(bs, options, flags, 0, true, &local_err); + if (ret) { + error_propagate(errp, local_err); + return ret; +diff --git a/qemu-doc.texi b/qemu-doc.texi +index de5097a..985e0f2 100644 +--- a/qemu-doc.texi ++++ b/qemu-doc.texi +@@ -2938,6 +2938,12 @@ The @code{-startdate} option has been replaced by @code{-rtc base=@var{date}}. + The ``convert -s snapshot_id_or_name'' argument is obsoleted + by the ``convert -l snapshot_param'' argument instead. + ++@subsection -drive file=json:@{...@{'driver':'file'@}@} (since 3.0) ++ ++The 'file' driver for drives is no longer appropriate for character or host ++devices and will only accept regular files (S_IFREG). The correct driver ++for these file types is 'host_cdrom' or 'host_device' as appropriate. ++ + @section QEMU Machine Protocol (QMP) commands + + @subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-gluster-Support-auto-read-only-option.patch b/SOURCES/kvm-gluster-Support-auto-read-only-option.patch new file mode 100644 index 0000000..b3b8a54 --- /dev/null +++ b/SOURCES/kvm-gluster-Support-auto-read-only-option.patch @@ -0,0 +1,54 @@ +From 8c49a93129fc9faf8cdb0eb7599f625a2069a0fd Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 23 Nov 2018 10:41:50 +0100 +Subject: [PATCH 09/34] gluster: Support auto-read-only option + +RH-Author: Kevin Wolf +Message-id: <20181123104154.13541-9-kwolf@redhat.com> +Patchwork-id: 83118 +O-Subject: [RHEL-7.7/7.6.z qemu-kvm-rhev PATCH v2 08/12] gluster: Support auto-read-only option +Bugzilla: 1623986 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina +RH-Acked-by: John Snow + +If read-only=off, but auto-read-only=on is given, open the file +read-write if we have the permissions, but instead of erroring out for +read-only files, just degrade to read-only. + +Signed-off-by: Kevin Wolf +Reviewed-by: Niels de Vos +(cherry picked from commit 54ea21bd16202c4a3e43c67b573b5d1aa2ec1c0c) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/gluster.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/block/gluster.c b/block/gluster.c +index cecfe09..8c13002 100644 +--- a/block/gluster.c ++++ b/block/gluster.c +@@ -849,8 +849,16 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options, + qemu_gluster_parse_flags(bdrv_flags, &open_flags); + + s->fd = glfs_open(s->glfs, gconf->path, open_flags); +- if (!s->fd) { +- ret = -errno; ++ ret = s->fd ? 0 : -errno; ++ ++ if (ret == -EACCES || ret == -EROFS) { ++ /* Try to degrade to read-only, but if it doesn't work, still use the ++ * normal error message. */ ++ if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) { ++ open_flags = (open_flags & ~O_RDWR) | O_RDONLY; ++ s->fd = glfs_open(s->glfs, gconf->path, open_flags); ++ ret = s->fd ? 0 : -errno; ++ } + } + + s->supports_seek_data = qemu_gluster_test_seek(s->fd); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-hbitmap-Add-advance-param-to-hbitmap_iter_next.patch b/SOURCES/kvm-hbitmap-Add-advance-param-to-hbitmap_iter_next.patch new file mode 100644 index 0000000..8c01257 --- /dev/null +++ b/SOURCES/kvm-hbitmap-Add-advance-param-to-hbitmap_iter_next.patch @@ -0,0 +1,182 @@ +From 300ff68e30f51846d0a226ee33a5878364303158 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:23 +0100 +Subject: [PATCH 13/33] hbitmap: Add @advance param to hbitmap_iter_next() + +RH-Author: John Snow +Message-id: <20190206221243.7407-4-jsnow@redhat.com> +Patchwork-id: 84260 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 03/23] hbitmap: Add @advance param to hbitmap_iter_next() +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +From: Max Reitz + +This new parameter allows the caller to just query the next dirty +position without moving the iterator. + +Signed-off-by: Max Reitz +Reviewed-by: Fam Zheng +Reviewed-by: John Snow +Message-id: 20180613181823.13618-8-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit a33fbb4f8b64226becf502a123733776ce319b24) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 2 +- + block/dirty-bitmap.c | 2 +- + include/qemu/hbitmap.h | 5 ++++- + tests/test-hbitmap.c | 26 +++++++++++++------------- + util/hbitmap.c | 10 +++++++--- + 5 files changed, 26 insertions(+), 19 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index 524e0ff..ac17db6 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -409,7 +409,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) + HBitmapIter hbi; + + hbitmap_iter_init(&hbi, job->copy_bitmap, 0); +- while ((cluster = hbitmap_iter_next(&hbi)) != -1) { ++ while ((cluster = hbitmap_iter_next(&hbi, true)) != -1) { + do { + if (yield_and_check(job)) { + return 0; +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index cd39afd..f4a4cb7 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -525,7 +525,7 @@ void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter) + + int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) + { +- return hbitmap_iter_next(&iter->hbi); ++ return hbitmap_iter_next(&iter->hbi, true); + } + + /* Called within bdrv_dirty_bitmap_lock..unlock */ +diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h +index 6b6490e..ddca52c 100644 +--- a/include/qemu/hbitmap.h ++++ b/include/qemu/hbitmap.h +@@ -324,11 +324,14 @@ void hbitmap_free_meta(HBitmap *hb); + /** + * hbitmap_iter_next: + * @hbi: HBitmapIter to operate on. ++ * @advance: If true, advance the iterator. Otherwise, the next call ++ * of this function will return the same result (if that ++ * position is still dirty). + * + * 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); ++int64_t hbitmap_iter_next(HBitmapIter *hbi, bool advance); + + /** + * hbitmap_iter_next_word: +diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c +index f29631f..f2158f7 100644 +--- a/tests/test-hbitmap.c ++++ b/tests/test-hbitmap.c +@@ -46,7 +46,7 @@ static void hbitmap_test_check(TestHBitmapData *data, + + i = first; + for (;;) { +- next = hbitmap_iter_next(&hbi); ++ next = hbitmap_iter_next(&hbi, true); + if (next < 0) { + next = data->size; + } +@@ -435,25 +435,25 @@ static void test_hbitmap_iter_granularity(TestHBitmapData *data, + /* Note that hbitmap_test_check has to be invoked manually in this test. */ + hbitmap_test_init(data, 131072 << 7, 7); + hbitmap_iter_init(&hbi, data->hb, 0); +- g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); ++ g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); + + hbitmap_test_set(data, ((L2 + L1 + 1) << 7) + 8, 8); + hbitmap_iter_init(&hbi, data->hb, 0); +- g_assert_cmpint(hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); +- g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); ++ g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, (L2 + L1 + 1) << 7); ++ g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); + + hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); +- g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); ++ g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); + + hbitmap_test_set(data, (131072 << 7) - 8, 8); + hbitmap_iter_init(&hbi, data->hb, 0); +- g_assert_cmpint(hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); +- g_assert_cmpint(hbitmap_iter_next(&hbi), ==, 131071 << 7); +- g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); ++ g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, (L2 + L1 + 1) << 7); ++ g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, 131071 << 7); ++ g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); + + hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); +- g_assert_cmpint(hbitmap_iter_next(&hbi), ==, 131071 << 7); +- g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); ++ g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, 131071 << 7); ++ g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); + } + + static void hbitmap_test_set_boundary_bits(TestHBitmapData *data, ssize_t diff) +@@ -893,7 +893,7 @@ static void test_hbitmap_serialize_zeroes(TestHBitmapData *data, + for (i = 0; i < num_positions; i++) { + hbitmap_deserialize_zeroes(data->hb, positions[i], min_l1, true); + hbitmap_iter_init(&iter, data->hb, 0); +- next = hbitmap_iter_next(&iter); ++ next = hbitmap_iter_next(&iter, true); + if (i == num_positions - 1) { + g_assert_cmpint(next, ==, -1); + } else { +@@ -919,10 +919,10 @@ static void test_hbitmap_iter_and_reset(TestHBitmapData *data, + + hbitmap_iter_init(&hbi, data->hb, BITS_PER_LONG - 1); + +- hbitmap_iter_next(&hbi); ++ hbitmap_iter_next(&hbi, true); + + hbitmap_reset_all(data->hb); +- hbitmap_iter_next(&hbi); ++ hbitmap_iter_next(&hbi, true); + } + + static void test_hbitmap_next_zero_check(TestHBitmapData *data, int64_t start) +diff --git a/util/hbitmap.c b/util/hbitmap.c +index 58a2c93..bcd3040 100644 +--- a/util/hbitmap.c ++++ b/util/hbitmap.c +@@ -141,7 +141,7 @@ unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi) + return cur; + } + +-int64_t hbitmap_iter_next(HBitmapIter *hbi) ++int64_t hbitmap_iter_next(HBitmapIter *hbi, bool advance) + { + unsigned long cur = hbi->cur[HBITMAP_LEVELS - 1] & + hbi->hb->levels[HBITMAP_LEVELS - 1][hbi->pos]; +@@ -154,8 +154,12 @@ int64_t hbitmap_iter_next(HBitmapIter *hbi) + } + } + +- /* The next call will resume work from the next bit. */ +- hbi->cur[HBITMAP_LEVELS - 1] = cur & (cur - 1); ++ if (advance) { ++ /* The next call will resume work from the next bit. */ ++ hbi->cur[HBITMAP_LEVELS - 1] = cur & (cur - 1); ++ } else { ++ hbi->cur[HBITMAP_LEVELS - 1] = cur; ++ } + item = ((uint64_t)hbi->pos << BITS_PER_LEVEL) + ctzl(cur); + + return item << hbi->granularity; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-hostmem-drop-error-variable-from-host_memory_backend.patch b/SOURCES/kvm-hostmem-drop-error-variable-from-host_memory_backend.patch new file mode 100644 index 0000000..ba00f3b --- /dev/null +++ b/SOURCES/kvm-hostmem-drop-error-variable-from-host_memory_backend.patch @@ -0,0 +1,139 @@ +From 09b663f38767ce0d84b2301c97efec2d37e71a49 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 29 Oct 2018 07:01:36 +0100 +Subject: [PATCH 06/22] hostmem: drop error variable from + host_memory_backend_get_memory() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20181029070137.21196-3-armbru@redhat.com> +Patchwork-id: 82902 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 2/3] hostmem: drop error variable from host_memory_backend_get_memory() +Bugzilla: 1620373 +RH-Acked-by: David Hildenbrand +RH-Acked-by: Igor Mammedov +RH-Acked-by: Laurent Vivier +RH-Acked-by: Marc-André Lureau + +From: David Hildenbrand + +Unused, so let's remove it. + +Reviewed-by: David Gibson +Reviewed-by: Igor Mammedov +Signed-off-by: David Hildenbrand +Message-Id: <20180619134141.29478-8-david@redhat.com> +Signed-off-by: Paolo Bonzini +(cherry picked from commit 7943e97b858e64eddf0f3395427e58c5cc00a7d9) +Signed-off-by: Miroslav Rezanina +--- + backends/hostmem.c | 3 +-- + hw/mem/nvdimm.c | 4 ++-- + hw/mem/pc-dimm.c | 4 ++-- + hw/misc/ivshmem.c | 3 +-- + include/sysemu/hostmem.h | 3 +-- + numa.c | 3 +-- + 6 files changed, 8 insertions(+), 12 deletions(-) + +diff --git a/backends/hostmem.c b/backends/hostmem.c +index f610936..799ec69 100644 +--- a/backends/hostmem.c ++++ b/backends/hostmem.c +@@ -246,8 +246,7 @@ bool host_memory_backend_mr_inited(HostMemoryBackend *backend) + return memory_region_size(&backend->mr) != 0; + } + +-MemoryRegion * +-host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp) ++MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend) + { + return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL; + } +diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c +index 5f1813d..da2b989 100644 +--- a/hw/mem/nvdimm.c ++++ b/hw/mem/nvdimm.c +@@ -106,7 +106,7 @@ static MemoryRegion *nvdimm_get_memory_region(PCDIMMDevice *dimm, Error **errp) + + static void nvdimm_realize(PCDIMMDevice *dimm, Error **errp) + { +- MemoryRegion *mr = host_memory_backend_get_memory(dimm->hostmem, errp); ++ MemoryRegion *mr = host_memory_backend_get_memory(dimm->hostmem); + NVDIMMDevice *nvdimm = NVDIMM(dimm); + uint64_t align, pmem_size, size = memory_region_size(mr); + +@@ -168,7 +168,7 @@ static void nvdimm_write_label_data(NVDIMMDevice *nvdimm, const void *buf, + pmem_memcpy_persist(nvdimm->label_data + offset, buf, size); + } + +- mr = host_memory_backend_get_memory(dimm->hostmem, &error_abort); ++ mr = host_memory_backend_get_memory(dimm->hostmem); + backend_offset = memory_region_size(mr) - nvdimm->label_size + offset; + memory_region_set_dirty(mr, backend_offset, size); + } +diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c +index a9d7c51..c4a6551 100644 +--- a/hw/mem/pc-dimm.c ++++ b/hw/mem/pc-dimm.c +@@ -442,12 +442,12 @@ static MemoryRegion *pc_dimm_get_memory_region(PCDIMMDevice *dimm, Error **errp) + return NULL; + } + +- return host_memory_backend_get_memory(dimm->hostmem, errp); ++ return host_memory_backend_get_memory(dimm->hostmem); + } + + static MemoryRegion *pc_dimm_get_vmstate_memory_region(PCDIMMDevice *dimm) + { +- return host_memory_backend_get_memory(dimm->hostmem, &error_abort); ++ return host_memory_backend_get_memory(dimm->hostmem); + } + + static void pc_dimm_class_init(ObjectClass *oc, void *data) +diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c +index bfbfc0e..47456b8 100644 +--- a/hw/misc/ivshmem.c ++++ b/hw/misc/ivshmem.c +@@ -916,8 +916,7 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp) + if (s->hostmem != NULL) { + IVSHMEM_DPRINTF("using hostmem\n"); + +- s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem, +- &error_abort); ++ s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem); + } else { + Chardev *chr = qemu_chr_fe_get_driver(&s->server_chr); + assert(chr); +diff --git a/include/sysemu/hostmem.h b/include/sysemu/hostmem.h +index 47bc984..69ba393 100644 +--- a/include/sysemu/hostmem.h ++++ b/include/sysemu/hostmem.h +@@ -63,8 +63,7 @@ struct HostMemoryBackend { + }; + + bool host_memory_backend_mr_inited(HostMemoryBackend *backend); +-MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend, +- Error **errp); ++MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend); + + void host_memory_backend_set_mapped(HostMemoryBackend *backend, bool mapped); + bool host_memory_backend_is_mapped(HostMemoryBackend *backend); +diff --git a/numa.c b/numa.c +index e78fba5..6e3d679 100644 +--- a/numa.c ++++ b/numa.c +@@ -512,8 +512,7 @@ void memory_region_allocate_system_memory(MemoryRegion *mr, Object *owner, + if (!backend) { + continue; + } +- MemoryRegion *seg = host_memory_backend_get_memory(backend, +- &error_fatal); ++ MemoryRegion *seg = host_memory_backend_get_memory(backend); + + if (memory_region_is_mapped(seg)) { + char *path = object_get_canonical_path_component(OBJECT(backend)); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-hostmem-file-add-the-pmem-option.patch b/SOURCES/kvm-hostmem-file-add-the-pmem-option.patch new file mode 100644 index 0000000..f495bf4 --- /dev/null +++ b/SOURCES/kvm-hostmem-file-add-the-pmem-option.patch @@ -0,0 +1,274 @@ +From 0b7c71b8b4afcbc92a9ef549d485c54da92204b3 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Fri, 31 Aug 2018 16:25:56 +0200 +Subject: [PATCH 14/29] hostmem-file: add the 'pmem' option + +RH-Author: plai@redhat.com +Message-id: <1535732759-22481-7-git-send-email-plai@redhat.com> +Patchwork-id: 82007 +O-Subject: [RHEL7.6 PATCH BZ 1539280 6/9] hostmem-file: add the 'pmem' option +Bugzilla: 1539280 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Pankaj Gupta +RH-Acked-by: Miroslav Rezanina + +From: Junyan He + +When QEMU emulates vNVDIMM labels and migrates vNVDIMM devices, it +needs to know whether the backend storage is a real persistent memory, +in order to decide whether special operations should be performed to +ensure the data persistence. + +This boolean option 'pmem' allows users to specify whether the backend +storage of memory-backend-file is a real persistent memory. If +'pmem=on', QEMU will set the flag RAM_PMEM in the RAM block of the +corresponding memory region. If 'pmem' is set while lack of libpmem +support, a error is generated. + +Signed-off-by: Junyan He +Signed-off-by: Haozhong Zhang +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Igor Mammedov +Reviewed-by: Richard Henderson +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit a4de8552b2580adf6fa4874439217b65d3bdd88b) +Signed-off-by: Paul Lai + +Resolved Conflicts: + docs/nvdimm.txt + +Signed-off-by: Miroslav Rezanina +--- + backends/hostmem-file.c | 43 +++++++++++++++++++++++++++++++++++++++++-- + docs/nvdimm.txt | 42 ++++++++++++++++++++++++++++++++++++++++++ + exec.c | 8 ++++++++ + include/exec/memory.h | 4 ++++ + include/exec/ram_addr.h | 3 +++ + qemu-options.hx | 7 +++++++ + 6 files changed, 105 insertions(+), 2 deletions(-) + +diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c +index 34c68bb..2476dcb 100644 +--- a/backends/hostmem-file.c ++++ b/backends/hostmem-file.c +@@ -12,6 +12,7 @@ + #include "qemu/osdep.h" + #include "qapi/error.h" + #include "qemu-common.h" ++#include "qemu/error-report.h" + #include "sysemu/hostmem.h" + #include "sysemu/sysemu.h" + #include "qom/object_interfaces.h" +@@ -31,9 +32,10 @@ typedef struct HostMemoryBackendFile HostMemoryBackendFile; + struct HostMemoryBackendFile { + HostMemoryBackend parent_obj; + +- bool discard_data; + char *mem_path; + uint64_t align; ++ bool discard_data; ++ bool is_pmem; + }; + + static void +@@ -59,7 +61,8 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) + memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), + path, + backend->size, fb->align, +- backend->share ? RAM_SHARED : 0, ++ (backend->share ? RAM_SHARED : 0) | ++ (fb->is_pmem ? RAM_PMEM : 0), + fb->mem_path, errp); + g_free(path); + } +@@ -131,6 +134,39 @@ static void file_memory_backend_set_align(Object *o, Visitor *v, + error_propagate(errp, local_err); + } + ++static bool file_memory_backend_get_pmem(Object *o, Error **errp) ++{ ++ return MEMORY_BACKEND_FILE(o)->is_pmem; ++} ++ ++static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp) ++{ ++ HostMemoryBackend *backend = MEMORY_BACKEND(o); ++ HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); ++ ++ if (host_memory_backend_mr_inited(backend)) { ++ error_setg(errp, "cannot change property 'pmem' of %s '%s'", ++ object_get_typename(o), ++ object_get_canonical_path_component(o)); ++ return; ++ } ++ ++#ifndef CONFIG_LIBPMEM ++ if (value) { ++ Error *local_err = NULL; ++ error_setg(&local_err, ++ "Lack of libpmem support while setting the 'pmem=on'" ++ " of %s '%s'. We can't ensure data persistence.", ++ object_get_typename(o), ++ object_get_canonical_path_component(o)); ++ error_propagate(errp, local_err); ++ return; ++ } ++#endif ++ ++ fb->is_pmem = value; ++} ++ + static void file_backend_unparent(Object *obj) + { + HostMemoryBackend *backend = MEMORY_BACKEND(obj); +@@ -162,6 +198,9 @@ file_backend_class_init(ObjectClass *oc, void *data) + file_memory_backend_get_align, + file_memory_backend_set_align, + NULL, NULL, &error_abort); ++ object_class_property_add_bool(oc, "pmem", ++ file_memory_backend_get_pmem, file_memory_backend_set_pmem, ++ &error_abort); + } + + static void file_backend_instance_finalize(Object *o) +diff --git a/docs/nvdimm.txt b/docs/nvdimm.txt +index e903d8b..5f158a6 100644 +--- a/docs/nvdimm.txt ++++ b/docs/nvdimm.txt +@@ -153,3 +153,45 @@ guest NVDIMM region mapping structure. This unarmed flag indicates + guest software that this vNVDIMM device contains a region that cannot + accept persistent writes. In result, for example, the guest Linux + NVDIMM driver, marks such vNVDIMM device as read-only. ++ ++NVDIMM Persistence ++------------------ ++ ++ACPI 6.2 Errata A added support for a new Platform Capabilities Structure ++which allows the platform to communicate what features it supports related to ++NVDIMM data persistence. Users can provide a persistence value to a guest via ++the optional "nvdimm-persistence" machine command line option: ++ ++ -machine pc,accel=kvm,nvdimm,nvdimm-persistence=cpu ++ ++There are currently two valid values for this option: ++ ++"mem-ctrl" - The platform supports flushing dirty data from the memory ++ controller to the NVDIMMs in the event of power loss. ++ ++"cpu" - The platform supports flushing dirty data from the CPU cache to ++ the NVDIMMs in the event of power loss. This implies that the ++ platform also supports flushing dirty data through the memory ++ controller on power loss. ++ ++If the vNVDIMM backend is in host persistent memory that can be accessed in ++SNIA NVM Programming Model [1] (e.g., Intel NVDIMM), it's suggested to set ++the 'pmem' option of memory-backend-file to 'on'. When 'pmem' is 'on' and QEMU ++is built with libpmem [2] support (configured with --enable-libpmem), QEMU ++will take necessary operations to guarantee the persistence of its own writes ++to the vNVDIMM backend(e.g., in vNVDIMM label emulation and live migration). ++If 'pmem' is 'on' while there is no libpmem support, qemu will exit and report ++a "lack of libpmem support" message to ensure the persistence is available. ++For example, if we want to ensure the persistence for some backend file, ++use the QEMU command line: ++ ++ -object memory-backend-file,id=nv_mem,mem-path=/XXX/yyy,size=4G,pmem=on ++ ++References ++---------- ++ ++[1] NVM Programming Model (NPM) ++ Version 1.2 ++ https://www.snia.org/sites/default/files/technical_work/final/NVMProgrammingModel_v1.2.pdf ++[2] Persistent Memory Development Kit (PMDK), formerly known as NVML project, home page: ++ http://pmem.io/pmdk/ +diff --git a/exec.c b/exec.c +index 295142b..c670185 100644 +--- a/exec.c ++++ b/exec.c +@@ -2045,6 +2045,9 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, + Error *local_err = NULL; + int64_t file_size; + ++ /* Just support these ram flags by now. */ ++ assert((ram_flags & ~(RAM_SHARED | RAM_PMEM)) == 0); ++ + if (xen_enabled()) { + error_setg(errp, "-mem-path not supported with Xen"); + return NULL; +@@ -3863,6 +3866,11 @@ err: + return ret; + } + ++bool ramblock_is_pmem(RAMBlock *rb) ++{ ++ return rb->flags & RAM_PMEM; ++} ++ + #endif + + void page_size_init(void) +diff --git a/include/exec/memory.h b/include/exec/memory.h +index b3abe61..fd2c574 100644 +--- a/include/exec/memory.h ++++ b/include/exec/memory.h +@@ -122,6 +122,9 @@ typedef struct IOMMUNotifier IOMMUNotifier; + /* RAM can be migrated */ + #define RAM_MIGRATABLE (1 << 4) + ++/* RAM is a persistent kind memory */ ++#define RAM_PMEM (1 << 5) ++ + static inline void iommu_notifier_init(IOMMUNotifier *n, IOMMUNotify fn, + IOMMUNotifierFlag flags, + hwaddr start, hwaddr end) +@@ -541,6 +544,7 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr, + * (getpagesize()) will be used. + * @ram_flags: Memory region features: + * - RAM_SHARED: memory must be mmaped with the MAP_SHARED flag ++ * - RAM_PMEM: the memory is persistent memory + * Other bits are ignored now. + * @path: the path in which to allocate the RAM. + * @errp: pointer to Error*, to store an error if it happens. +diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h +index 67e163e..922305d 100644 +--- a/include/exec/ram_addr.h ++++ b/include/exec/ram_addr.h +@@ -70,6 +70,8 @@ static inline unsigned long int ramblock_recv_bitmap_offset(void *host_addr, + return host_addr_offset >> TARGET_PAGE_BITS; + } + ++bool ramblock_is_pmem(RAMBlock *rb); ++ + long qemu_getrampagesize(void); + unsigned long last_ram_page(void); + +@@ -84,6 +86,7 @@ unsigned long last_ram_page(void); + * @ram_flags: specify the properties of the ram block, which can be one + * or bit-or of following values + * - RAM_SHARED: mmap the backing file or device with MAP_SHARED ++ * - RAM_PMEM: the backend @mem_path or @fd is persistent memory + * Other bits are ignored. + * @mem_path or @fd: specify the backing file or device + * @errp: pointer to Error*, to store an error if it happens +diff --git a/qemu-options.hx b/qemu-options.hx +index 4271cd3..5c58760 100644 +--- a/qemu-options.hx ++++ b/qemu-options.hx +@@ -4045,6 +4045,13 @@ requires an alignment different than the default one used by QEMU, eg + the device DAX /dev/dax0.0 requires 2M alignment rather than 4K. In + such cases, users can specify the required alignment via this option. + ++The @option{pmem} option specifies whether the backing file specified ++by @option{mem-path} is in host persistent memory that can be accessed ++using the SNIA NVM programming model (e.g. Intel NVDIMM). ++If @option{pmem} is set to 'on', QEMU will take necessary operations to ++guarantee the persistence of its own writes to @option{mem-path} ++(e.g. in vNVDIMM label emulation and live migration). ++ + @item -object memory-backend-ram,id=@var{id},merge=@var{on|off},dump=@var{on|off},share=@var{on|off},prealloc=@var{on|off},size=@var{size},host-nodes=@var{host-nodes},policy=@var{default|preferred|bind|interleave} + + Creates a memory backend object, which can be used to back the guest RAM. +-- +1.8.3.1 + diff --git a/SOURCES/kvm-hostmem-file-remove-object-id-from-pmem-error-messag.patch b/SOURCES/kvm-hostmem-file-remove-object-id-from-pmem-error-messag.patch new file mode 100644 index 0000000..3c66717 --- /dev/null +++ b/SOURCES/kvm-hostmem-file-remove-object-id-from-pmem-error-messag.patch @@ -0,0 +1,78 @@ +From 111769e4a152fdb52f2b10fb957cdfec2fe7d4d2 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Tue, 8 Jan 2019 21:33:09 +0100 +Subject: [PATCH 3/8] hostmem-file: remove object id from pmem error message + +RH-Author: plai@redhat.com +Message-id: <1546983189-26340-1-git-send-email-plai@redhat.com> +Patchwork-id: 83911 +O-Subject: [RHEL7.7 qemu-kvm-rhev PATCH v2] hostmem-file: remove object id from pmem error message +Bugzilla: 1628098 +RH-Acked-by: Pankaj Gupta +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Eduardo Habkost + +From: Zhang Yi + +BZ: https://bugzilla.redhat.com/show_bug.cgi?id=1628098 +Brew: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=19688087 +Branch: rhv7/master-2.12.0 + +Tested by Intel OTC virtualization. + +--- +v2 + fixing subject line to use qemu-kvm-rhev instead of qemu-kvm +--- +We will never get the canonical path from the object +before object_property_add_child. + +Signed-off-by: Zhang Yi +Message-Id: +[ehabkost: reword commit message] +Signed-off-by: Eduardo Habkost + +(cherry picked from commit 87dc3ce60a8a16b47aeb6c5f4dbc14ee975563df) +Signed-off-by: Paul Lai + +Resolved Conflicts: + backends/hostmem-file.c + +Signed-off-by: Miroslav Rezanina +--- + backends/hostmem-file.c | 11 +++++------ + 1 file changed, 5 insertions(+), 6 deletions(-) + +diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c +index 2476dcb..5b519c2 100644 +--- a/backends/hostmem-file.c ++++ b/backends/hostmem-file.c +@@ -145,20 +145,19 @@ static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp) + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); + + if (host_memory_backend_mr_inited(backend)) { +- error_setg(errp, "cannot change property 'pmem' of %s '%s'", +- object_get_typename(o), +- object_get_canonical_path_component(o)); ++ error_setg(errp, "cannot change property 'pmem' of %s.", ++ object_get_typename(o)); + return; + } + + #ifndef CONFIG_LIBPMEM + if (value) { + Error *local_err = NULL; ++ + error_setg(&local_err, + "Lack of libpmem support while setting the 'pmem=on'" +- " of %s '%s'. We can't ensure data persistence.", +- object_get_typename(o), +- object_get_canonical_path_component(o)); ++ " of %s. We can't ensure data persistence.", ++ object_get_typename(o)); + error_propagate(errp, local_err); + return; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-hw-Use-PFLASH_CFI0-1-2-and-TYPE_PFLASH_CFI0-1-2.patch b/SOURCES/kvm-hw-Use-PFLASH_CFI0-1-2-and-TYPE_PFLASH_CFI0-1-2.patch new file mode 100644 index 0000000..e2ff4b2 --- /dev/null +++ b/SOURCES/kvm-hw-Use-PFLASH_CFI0-1-2-and-TYPE_PFLASH_CFI0-1-2.patch @@ -0,0 +1,159 @@ +From 8bad602712aa9d875e1826b6cc17baba97deb4c8 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:50:56 +0200 +Subject: [PATCH 29/53] hw: Use PFLASH_CFI0{1, 2} and TYPE_PFLASH_CFI0{1, 2} +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-8-armbru@redhat.com> +Patchwork-id: 87997 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 07/31] hw: Use PFLASH_CFI0{1, 2} and TYPE_PFLASH_CFI0{1, 2} +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +We have two open-coded copies of macro PFLASH_CFI01(). Move the macro +to the header, so we can ditch the copies. Move PFLASH_CFI02() to the +header for symmetry. + +We define macros TYPE_PFLASH_CFI01 and TYPE_PFLASH_CFI02 for type name +strings, then mostly use the strings. If the macros are worth +defining, they are worth using. Replace the strings by the macros. + +Signed-off-by: Markus Armbruster +Reviewed-by: Laszlo Ersek +Reviewed-by: Philippe Mathieu-Daudé +Reviewed-by: Alex Bennée +Message-Id: <20190308094610.21210-6-armbru@redhat.com> +(cherry picked from commit 81c7db723ebd0c677784a728020c7e8845868daf) +Signed-off-by: Miroslav Rezanina +--- + hw/arm/vexpress.c | 4 ++-- + hw/arm/virt.c | 3 ++- + hw/block/pflash_cfi01.c | 3 --- + hw/block/pflash_cfi02.c | 3 --- + hw/xtensa/xtfpga.c | 4 ++-- + include/hw/block/flash.h | 4 ++++ + 6 files changed, 10 insertions(+), 11 deletions(-) + +diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c +index 5cca371..cf87379 100644 +--- a/hw/arm/vexpress.c ++++ b/hw/arm/vexpress.c +@@ -504,7 +504,7 @@ static void vexpress_modify_dtb(const struct arm_boot_info *info, void *fdt) + static PFlashCFI01 *ve_pflash_cfi01_register(hwaddr base, const char *name, + DriveInfo *di) + { +- DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); ++ DeviceState *dev = qdev_create(NULL, TYPE_PFLASH_CFI01); + + if (di) { + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(di), +@@ -525,7 +525,7 @@ static PFlashCFI01 *ve_pflash_cfi01_register(hwaddr base, const char *name, + qdev_init_nofail(dev); + + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); +- return OBJECT_CHECK(PFlashCFI01, (dev), "cfi.pflash01"); ++ return PFLASH_CFI01(dev); + } + + static void vexpress_common_init(MachineState *machine) +diff --git a/hw/arm/virt.c b/hw/arm/virt.c +index 751a93c..4c652de 100644 +--- a/hw/arm/virt.c ++++ b/hw/arm/virt.c +@@ -34,6 +34,7 @@ + #include "hw/arm/arm.h" + #include "hw/arm/primecell.h" + #include "hw/arm/virt.h" ++#include "hw/block/flash.h" + #include "hw/vfio/vfio-calxeda-xgmac.h" + #include "hw/vfio/vfio-amd-xgbe.h" + #include "hw/devices.h" +@@ -812,7 +813,7 @@ static void create_one_flash(const char *name, hwaddr flashbase, + * parameters as the flash devices on the Versatile Express board. + */ + DriveInfo *dinfo = drive_get_next(IF_PFLASH); +- DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); ++ DeviceState *dev = qdev_create(NULL, TYPE_PFLASH_CFI01); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + const uint64_t sectorlength = 256 * 1024; + +diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c +index 1ff4d25..dbd3b9d 100644 +--- a/hw/block/pflash_cfi01.c ++++ b/hw/block/pflash_cfi01.c +@@ -59,9 +59,6 @@ do { \ + #define DPRINTF(fmt, ...) do { } while (0) + #endif + +-#define PFLASH_CFI01(obj) \ +- OBJECT_CHECK(PFlashCFI01, (obj), TYPE_PFLASH_CFI01) +- + #define PFLASH_BE 0 + #define PFLASH_SECURE 1 + +diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c +index 2c0cbde..b9e0448 100644 +--- a/hw/block/pflash_cfi02.c ++++ b/hw/block/pflash_cfi02.c +@@ -57,9 +57,6 @@ do { \ + + #define PFLASH_LAZY_ROMD_THRESHOLD 42 + +-#define PFLASH_CFI02(obj) \ +- OBJECT_CHECK(PFlashCFI02, (obj), TYPE_PFLASH_CFI02) +- + struct PFlashCFI02 { + /*< private >*/ + SysBusDevice parent_obj; +diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c +index 8f8c0b9..56deadc 100644 +--- a/hw/xtensa/xtfpga.c ++++ b/hw/xtensa/xtfpga.c +@@ -164,7 +164,7 @@ static PFlashCFI01 *xtfpga_flash_init(MemoryRegion *address_space, + DriveInfo *dinfo, int be) + { + SysBusDevice *s; +- DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); ++ DeviceState *dev = qdev_create(NULL, TYPE_PFLASH_CFI01); + + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo), + &error_abort); +@@ -178,7 +178,7 @@ static PFlashCFI01 *xtfpga_flash_init(MemoryRegion *address_space, + s = SYS_BUS_DEVICE(dev); + memory_region_add_subregion(address_space, board->flash->base, + sysbus_mmio_get_region(s, 0)); +- return OBJECT_CHECK(PFlashCFI01, (dev), "cfi.pflash01"); ++ return PFLASH_CFI01(dev); + } + + static uint64_t translate_phys_addr(void *opaque, uint64_t addr) +diff --git a/include/hw/block/flash.h b/include/hw/block/flash.h +index 333005d..aeea3ca 100644 +--- a/include/hw/block/flash.h ++++ b/include/hw/block/flash.h +@@ -8,6 +8,8 @@ + /* pflash_cfi01.c */ + + #define TYPE_PFLASH_CFI01 "cfi.pflash01" ++#define PFLASH_CFI01(obj) \ ++ OBJECT_CHECK(PFlashCFI01, (obj), TYPE_PFLASH_CFI01) + + typedef struct PFlashCFI01 PFlashCFI01; + +@@ -25,6 +27,8 @@ MemoryRegion *pflash_cfi01_get_memory(PFlashCFI01 *fl); + /* pflash_cfi02.c */ + + #define TYPE_PFLASH_CFI02 "cfi.pflash02" ++#define PFLASH_CFI02(obj) \ ++ OBJECT_CHECK(PFlashCFI02, (obj), TYPE_PFLASH_CFI02) + + typedef struct PFlashCFI02 PFlashCFI02; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-hw-char-serial-Only-retry-if-qemu_chr_fe_write-retur.patch b/SOURCES/kvm-hw-char-serial-Only-retry-if-qemu_chr_fe_write-retur.patch new file mode 100644 index 0000000..2ae2575 --- /dev/null +++ b/SOURCES/kvm-hw-char-serial-Only-retry-if-qemu_chr_fe_write-retur.patch @@ -0,0 +1,59 @@ +From a7a6995e2a24e9e4c89084acf3241e5640f30f3e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= +Date: Fri, 20 Jul 2018 12:17:59 +0200 +Subject: [PATCH 87/89] hw/char/serial: Only retry if qemu_chr_fe_write returns + 0 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Marc-André Lureau +Message-id: <20180720121800.18952-2-marcandre.lureau@redhat.com> +Patchwork-id: 81454 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 1/2] hw/char/serial: Only retry if qemu_chr_fe_write returns 0 +Bugzilla: 1592817 +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Markus Armbruster +RH-Acked-by: John Snow + +From: Sergio Lopez + +Only retry on serial_xmit if qemu_chr_fe_write returns 0, as this is the +only recoverable error. + +Retrying with any other scenario, in addition to being a waste of CPU +cycles, can compromise the Guest stability if by the vCPU issuing the +write and the main loop thread are, by chance or explicit pinning, +running on the same pCPU. + +Previous discussion: + +https://lists.nongnu.org/archive/html/qemu-devel/2018-05/msg06998.html + +Signed-off-by: Sergio Lopez +Message-Id: <1528185295-14199-1-git-send-email-slp@redhat.com> +Signed-off-by: Paolo Bonzini + +(cherry picked from commit 019288bf137183bf3407c9824655b753bfafc99f) +Signed-off-by: Marc-André Lureau +Signed-off-by: Miroslav Rezanina +--- + hw/char/serial.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/char/serial.c b/hw/char/serial.c +index d6d9b18..d4057bf 100644 +--- a/hw/char/serial.c ++++ b/hw/char/serial.c +@@ -262,7 +262,7 @@ static void serial_xmit(SerialState *s) + if (s->mcr & UART_MCR_LOOP) { + /* in loopback mode, say that we just received a char */ + serial_receive1(s, &s->tsr, 1); +- } else if (qemu_chr_fe_write(&s->chr, &s->tsr, 1) != 1 && ++ } else if (qemu_chr_fe_write(&s->chr, &s->tsr, 1) == 0 && + s->tsr_retry < MAX_XMIT_RETRY) { + assert(s->watch_tag == 0); + s->watch_tag = +-- +1.8.3.1 + diff --git a/SOURCES/kvm-hw-char-serial-retry-write-if-EAGAIN.patch b/SOURCES/kvm-hw-char-serial-retry-write-if-EAGAIN.patch new file mode 100644 index 0000000..222ee98 --- /dev/null +++ b/SOURCES/kvm-hw-char-serial-retry-write-if-EAGAIN.patch @@ -0,0 +1,72 @@ +From d357e5f3a56416c6f5a7a9308b8c30f825d89cfa Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= +Date: Fri, 20 Jul 2018 12:18:00 +0200 +Subject: [PATCH 88/89] hw/char/serial: retry write if EAGAIN +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Marc-André Lureau +Message-id: <20180720121800.18952-3-marcandre.lureau@redhat.com> +Patchwork-id: 81455 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 2/2] hw/char/serial: retry write if EAGAIN +Bugzilla: 1592817 +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Markus Armbruster +RH-Acked-by: John Snow + +If the chardev returns -1 with EAGAIN errno on write(), it should try +to send it again (EINTR is handled by the chardev itself). + +This fixes commit 019288bf137183bf3407c9824655b753bfafc99f +"hw/char/serial: Only retry if qemu_chr_fe_write returns 0" + +Tested-by: Igor Mammedov +Signed-off-by: Marc-André Lureau +Message-Id: <20180716110755.12499-1-marcandre.lureau@redhat.com> +Signed-off-by: Paolo Bonzini + +(cherry picked from commit f3575af130c700cea060b51a89008a76dae22259) +Signed-off-by: Marc-André Lureau +Signed-off-by: Miroslav Rezanina +--- + hw/char/serial.c | 23 ++++++++++++++--------- + 1 file changed, 14 insertions(+), 9 deletions(-) + +diff --git a/hw/char/serial.c b/hw/char/serial.c +index d4057bf..478f9a8 100644 +--- a/hw/char/serial.c ++++ b/hw/char/serial.c +@@ -262,15 +262,20 @@ static void serial_xmit(SerialState *s) + if (s->mcr & UART_MCR_LOOP) { + /* in loopback mode, say that we just received a char */ + serial_receive1(s, &s->tsr, 1); +- } else if (qemu_chr_fe_write(&s->chr, &s->tsr, 1) == 0 && +- s->tsr_retry < MAX_XMIT_RETRY) { +- assert(s->watch_tag == 0); +- s->watch_tag = +- qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, +- serial_watch_cb, s); +- if (s->watch_tag > 0) { +- s->tsr_retry++; +- return; ++ } else { ++ int rc = qemu_chr_fe_write(&s->chr, &s->tsr, 1); ++ ++ if ((rc == 0 || ++ (rc == -1 && errno == EAGAIN)) && ++ s->tsr_retry < MAX_XMIT_RETRY) { ++ assert(s->watch_tag == 0); ++ s->watch_tag = ++ qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, ++ serial_watch_cb, s); ++ if (s->watch_tag > 0) { ++ s->tsr_retry++; ++ return; ++ } + } + } + s->tsr_retry = 0; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-hw-mips-malta-Remove-fl_sectors-variable.patch b/SOURCES/kvm-hw-mips-malta-Remove-fl_sectors-variable.patch new file mode 100644 index 0000000..55ce03a --- /dev/null +++ b/SOURCES/kvm-hw-mips-malta-Remove-fl_sectors-variable.patch @@ -0,0 +1,57 @@ +From 1170f872af13f294082e33e172e3c6e3a8995895 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:02 +0200 +Subject: [PATCH 35/53] hw/mips/malta: Remove fl_sectors variable +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-14-armbru@redhat.com> +Patchwork-id: 87996 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 13/31] hw/mips/malta: Remove fl_sectors variable +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +From: Philippe Mathieu-Daudé + +Variable fl_sectors is used just once. Since +fl_sectors = bios_size >> 16 and bios_size = FLASH_SIZE there, +we can simply use FLASH_SIZE >> 16, and eliminate variable. + +Signed-off-by: Philippe Mathieu-Daudé +Reviewed-by: Richard Henderson +Signed-off-by: Markus Armbruster +Message-Id: <20190308094610.21210-12-armbru@redhat.com> +(cherry picked from commit 5207c595eb8910eee3d329214e65d64e348985d1) +Signed-off-by: Miroslav Rezanina +--- + hw/mips/mips_malta.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c +index 7f19bdc..0566d18 100644 +--- a/hw/mips/mips_malta.c ++++ b/hw/mips/mips_malta.c +@@ -1003,7 +1003,6 @@ void mips_malta_init(MachineState *machine) + DriveInfo *dinfo; + DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + int fl_idx = 0; +- int fl_sectors = bios_size >> 16; + int be; + + DeviceState *dev = qdev_create(NULL, TYPE_MIPS_MALTA); +@@ -1067,7 +1066,7 @@ void mips_malta_init(MachineState *machine) + fl = pflash_cfi01_register(FLASH_ADDRESS, NULL, "mips_malta.bios", + BIOS_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +- 65536, fl_sectors, ++ 65536, FLASH_SIZE >> 16, + 4, 0x0000, 0x0000, 0x0000, 0x0000, be); + bios = pflash_cfi01_get_memory(fl); + fl_idx++; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-hw-mips-malta-Restrict-bios_size-variable-scope.patch b/SOURCES/kvm-hw-mips-malta-Restrict-bios_size-variable-scope.patch new file mode 100644 index 0000000..4a25736 --- /dev/null +++ b/SOURCES/kvm-hw-mips-malta-Restrict-bios_size-variable-scope.patch @@ -0,0 +1,60 @@ +From 874453e6bed4da39f9860236148efdb09d285203 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:03 +0200 +Subject: [PATCH 36/53] hw/mips/malta: Restrict 'bios_size' variable scope +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-15-armbru@redhat.com> +Patchwork-id: 88003 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 14/31] hw/mips/malta: Restrict 'bios_size' variable scope +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +From: Philippe Mathieu-Daudé + +The 'bios_size' variable is only used in the 'if (!kernel_filename && +!dinfo)' clause. This is the case when we don't provide -pflash command +line option, and also don't provide a -kernel option. In this case we +will check for the -bios option, or use the default BIOS_FILENAME file. + +The 'bios' term is valid in this if statement, but is confuse in the +whole mips_malta_init() scope. Restrict his scope. + +Signed-off-by: Philippe Mathieu-Daudé +Reviewed-by: Richard Henderson +Signed-off-by: Markus Armbruster +Message-Id: <20190308094610.21210-13-armbru@redhat.com> +(cherry picked from commit 74c02ebd80fa331361c431d8dbfcba45a2a36e85) +Signed-off-by: Miroslav Rezanina +--- + hw/mips/mips_malta.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c +index 0566d18..857bdb8 100644 +--- a/hw/mips/mips_malta.c ++++ b/hw/mips/mips_malta.c +@@ -990,7 +990,6 @@ void mips_malta_init(MachineState *machine) + MemoryRegion *ram_low_preio = g_new(MemoryRegion, 1); + MemoryRegion *ram_low_postio; + MemoryRegion *bios, *bios_copy = g_new(MemoryRegion, 1); +- target_long bios_size = FLASH_SIZE; + const size_t smbus_eeprom_size = 8 * 256; + uint8_t *smbus_eeprom_buf = g_malloc0(smbus_eeprom_size); + int64_t kernel_entry, bootloader_run_addr; +@@ -1097,6 +1096,7 @@ void mips_malta_init(MachineState *machine) + bootloader_run_addr, kernel_entry); + } + } else { ++ target_long bios_size = FLASH_SIZE; + /* The flash region isn't executable from a KVM guest */ + if (kvm_enabled()) { + error_report("KVM enabled but no -kernel argument was specified. " +-- +1.8.3.1 + diff --git a/SOURCES/kvm-hw-scsi-add-VPD-Block-Limits-emulation.patch b/SOURCES/kvm-hw-scsi-add-VPD-Block-Limits-emulation.patch new file mode 100644 index 0000000..996176f --- /dev/null +++ b/SOURCES/kvm-hw-scsi-add-VPD-Block-Limits-emulation.patch @@ -0,0 +1,338 @@ +From 2f019168e70aa391d110a09dbae9ac937091ddc2 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Wed, 7 Nov 2018 18:00:02 +0100 +Subject: [PATCH 28/34] hw/scsi: add VPD Block Limits emulation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Paolo Bonzini +Message-id: <20181107180007.22954-5-pbonzini@redhat.com> +Patchwork-id: 82943 +O-Subject: [RHEL7.6.z qemu-kvm-rhev PATCH 4/9] hw/scsi: add VPD Block Limits emulation +Bugzilla: 1566195 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Philippe Mathieu-Daudé + +From: Daniel Henrique Barboza + +The VPD Block Limits Inquiry page is optional, allowing SCSI devices +to not implement it. This is the case for devices like the MegaRAID +SAS 9361-8i and Microsemi PM8069. + +In case of SCSI passthrough, the response of this request is used by +the QEMU SCSI layer to set the max_io_sectors that the guest +device will support, based on the value of the max_sectors_kb that +the device has set in the host at that time. Without this response, +the guest kernel is free to assume any value of max_io_sectors +for the SCSI device. If this value is greater than the value from +the host, SCSI Sense errors will occur because the guest will send +read/write requests that are larger than the underlying host device +is configured to support. An example of this behavior can be seen +in [1]. + +A workaround is to set the max_sectors_kb host value back in the guest +kernel (a process that can be automated using rc.local startup scripts +and the like), but this has several drawbacks: + +- it can be troublesome if the guest has many passthrough devices that +needs this tuning; + +- if a change in max_sectors_kb is made in the host side, manual change +in the guests will also be required; + +- during an OS install it is difficult, and sometimes not possible, to +go to a terminal and change the max_sectors_kb prior to the installation. +This means that the disk can't be used during the install process. The +easiest alternative here is to roll back to scsi-hd, install the guest +and then go back to SCSI passthrough when the installation is done and +max_sectors_kb can be set. + +An easier way would be to QEMU handle the absence of the Block Limits +VPD device response, setting max_io_sectors accordingly and allowing +the guest to use the device without the hassle. + +This patch adds emulation of the Block Limits VPD response for +SCSI passthrough devices of type TYPE_DISK that doesn't support +it. The following changes were made: + +- scsi_handle_inquiry_reply will now check the available VPD +pages from the Inquiry EVPD reply. In case the device does not + +- a new function called scsi_generic_set_vpd_bl_emulation, +that is called during device realize, was created to set a +new flag 'needs_vpd_bl_emulation' of the device. This function +retrieves the Inquiry EVPD response of the device to check for +VPD BL support. + +- scsi_handle_inquiry_reply will now check the available VPD +pages from the Inquiry EVPD reply in case the device needs +VPD BL emulation, adding the Block Limits page (0xb0) to +the list. This will make the guest kernel aware of the +support that we're now providing by emulation. + +- a new function scsi_emulate_block_limits creates the +emulated Block Limits response. This function is called +inside scsi_read_complete in case the device requires +Block Limits VPD emulation and we detected a SCSI Sense +error in the VPD Block Limits reply that was issued +from the guest kernel to the device. This error is +expected: we're reporting support from our side, but +the device isn't aware of it. + +With this patch, the guest now queries the Block Limits +page during the device configuration because it is being +advertised in the Supported Pages response. It will either +receive the Block Limits page from the hardware, if it supports +it, or will receive an emulated response from QEMU. At any rate, +the guest now has the information to set the max_sectors_kb +parameter accordingly, sparing the user of SCSI sense errors +that would happen without the emulated response and in the +absence of Block Limits support from the hardware. + +[1] https://bugzilla.redhat.com/show_bug.cgi?id=1566195 + +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1566195 +Reported-by: Dac Nguyen +Signed-off-by: Daniel Henrique Barboza +Message-Id: <20180627172432.11120-4-danielhb413@gmail.com> +Signed-off-by: Paolo Bonzini +(cherry picked from commit a71c775b24ebc664129eb1d9b4c360590353efd5) +Signed-off-by: Miroslav Rezanina +--- + hw/scsi/scsi-disk.c | 2 +- + hw/scsi/scsi-generic.c | 132 +++++++++++++++++++++++++++++++++++++++++++++---- + include/hw/scsi/scsi.h | 3 +- + 3 files changed, 125 insertions(+), 12 deletions(-) + +diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c +index ea86849..b3d53ec 100644 +--- a/hw/scsi/scsi-disk.c ++++ b/hw/scsi/scsi-disk.c +@@ -2646,7 +2646,7 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp) + s->features |= (1 << SCSI_DISK_F_NO_REMOVABLE_DEVOPS); + + scsi_realize(&s->qdev, errp); +- scsi_generic_read_device_identification(&s->qdev); ++ scsi_generic_read_device_inquiry(&s->qdev); + } + + typedef struct SCSIBlockReq { +diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c +index c6307a8..4266003 100644 +--- a/hw/scsi/scsi-generic.c ++++ b/hw/scsi/scsi-generic.c +@@ -145,6 +145,8 @@ static int execute_command(BlockBackend *blk, + + static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s) + { ++ uint8_t page, page_len; ++ + /* + * EVPD set to zero returns the standard INQUIRY data. + * +@@ -168,22 +170,57 @@ static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s) + s->scsi_version = r->buf[2]; + } + } +- if (s->type == TYPE_DISK && r->req.cmd.buf[2] == 0xb0) { +- uint32_t max_transfer = +- blk_get_max_transfer(s->conf.blk) / s->blocksize; + +- assert(max_transfer); +- stl_be_p(&r->buf[8], max_transfer); +- /* Also take care of the opt xfer len. */ +- stl_be_p(&r->buf[12], +- MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12]))); ++ if (s->type == TYPE_DISK && (r->req.cmd.buf[1] & 0x01)) { ++ page = r->req.cmd.buf[2]; ++ if (page == 0xb0) { ++ uint32_t max_transfer = ++ blk_get_max_transfer(s->conf.blk) / s->blocksize; ++ ++ assert(max_transfer); ++ stl_be_p(&r->buf[8], max_transfer); ++ /* Also take care of the opt xfer len. */ ++ stl_be_p(&r->buf[12], ++ MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12]))); ++ } else if (page == 0x00 && s->needs_vpd_bl_emulation) { ++ /* ++ * Now we're capable of supplying the VPD Block Limits ++ * response if the hardware can't. Add it in the INQUIRY ++ * Supported VPD pages response in case we are using the ++ * emulation for this device. ++ * ++ * This way, the guest kernel will be aware of the support ++ * and will use it to proper setup the SCSI device. ++ */ ++ page_len = r->buf[3]; ++ r->buf[page_len + 4] = 0xb0; ++ r->buf[3] = ++page_len; ++ } + } + } + ++static int scsi_emulate_block_limits(SCSIGenericReq *r) ++{ ++ r->buflen = scsi_disk_emulate_vpd_page(&r->req, r->buf); ++ r->io_header.sb_len_wr = 0; ++ ++ /* ++ * We have valid contents in the reply buffer but the ++ * io_header can report a sense error coming from ++ * the hardware in scsi_command_complete_noio. Clean ++ * up the io_header to avoid reporting it. ++ */ ++ r->io_header.driver_status = 0; ++ r->io_header.status = 0; ++ ++ return r->buflen; ++} ++ + static void scsi_read_complete(void * opaque, int ret) + { + SCSIGenericReq *r = (SCSIGenericReq *)opaque; + SCSIDevice *s = r->req.dev; ++ SCSISense sense; + int len; + + assert(r->req.aiocb != NULL); +@@ -200,6 +237,27 @@ static void scsi_read_complete(void * opaque, int ret) + DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len); + + r->len = -1; ++ ++ /* ++ * Check if this is a VPD Block Limits request that ++ * resulted in sense error but would need emulation. ++ * In this case, emulate a valid VPD response. ++ */ ++ if (s->needs_vpd_bl_emulation) { ++ int is_vpd_bl = r->req.cmd.buf[0] == INQUIRY && ++ r->req.cmd.buf[1] & 0x01 && ++ r->req.cmd.buf[2] == 0xb0; ++ ++ if (is_vpd_bl && sg_io_sense_from_errno(-ret, &r->io_header, &sense)) { ++ len = scsi_emulate_block_limits(r); ++ /* ++ * No need to let scsi_read_complete go on and handle an ++ * INQUIRY VPD BL request we created manually. ++ */ ++ goto req_complete; ++ } ++ } ++ + if (len == 0) { + scsi_command_complete_noio(r, 0); + goto done; +@@ -234,6 +292,8 @@ static void scsi_read_complete(void * opaque, int ret) + if (r->req.cmd.buf[0] == INQUIRY) { + scsi_handle_inquiry_reply(r, s); + } ++ ++req_complete: + scsi_req_data(&r->req, len); + scsi_req_unref(&r->req); + +@@ -435,7 +495,49 @@ int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size, + return 0; + } + +-void scsi_generic_read_device_identification(SCSIDevice *s) ++/* ++ * Executes an INQUIRY request with EVPD set to retrieve the ++ * available VPD pages of the device. If the device does ++ * not support the Block Limits page (page 0xb0), set ++ * the needs_vpd_bl_emulation flag for future use. ++ */ ++static void scsi_generic_set_vpd_bl_emulation(SCSIDevice *s) ++{ ++ uint8_t cmd[6]; ++ uint8_t buf[250]; ++ uint8_t page_len; ++ int ret, i; ++ ++ memset(cmd, 0, sizeof(cmd)); ++ memset(buf, 0, sizeof(buf)); ++ cmd[0] = INQUIRY; ++ cmd[1] = 1; ++ cmd[2] = 0x00; ++ cmd[4] = sizeof(buf); ++ ++ ret = scsi_SG_IO_FROM_DEV(s->conf.blk, cmd, sizeof(cmd), ++ buf, sizeof(buf)); ++ if (ret < 0) { ++ /* ++ * Do not assume anything if we can't retrieve the ++ * INQUIRY response to assert the VPD Block Limits ++ * support. ++ */ ++ s->needs_vpd_bl_emulation = false; ++ return; ++ } ++ ++ page_len = buf[3]; ++ for (i = 4; i < page_len + 4; i++) { ++ if (buf[i] == 0xb0) { ++ s->needs_vpd_bl_emulation = false; ++ return; ++ } ++ } ++ s->needs_vpd_bl_emulation = true; ++} ++ ++static void scsi_generic_read_device_identification(SCSIDevice *s) + { + uint8_t cmd[6]; + uint8_t buf[250]; +@@ -480,6 +582,16 @@ void scsi_generic_read_device_identification(SCSIDevice *s) + } + } + ++void scsi_generic_read_device_inquiry(SCSIDevice *s) ++{ ++ scsi_generic_read_device_identification(s); ++ if (s->type == TYPE_DISK) { ++ scsi_generic_set_vpd_bl_emulation(s); ++ } else { ++ s->needs_vpd_bl_emulation = false; ++ } ++} ++ + static int get_stream_blocksize(BlockBackend *blk) + { + uint8_t cmd[6]; +@@ -581,7 +693,7 @@ static void scsi_generic_realize(SCSIDevice *s, Error **errp) + + /* Only used by scsi-block, but initialize it nevertheless to be clean. */ + s->default_scsi_version = -1; +- scsi_generic_read_device_identification(s); ++ scsi_generic_read_device_inquiry(s); + } + + const SCSIReqOps scsi_generic_req_ops = { +diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h +index b6e05c4..ee3a411 100644 +--- a/include/hw/scsi/scsi.h ++++ b/include/hw/scsi/scsi.h +@@ -87,6 +87,7 @@ struct SCSIDevice + uint64_t port_wwn; + int scsi_version; + int default_scsi_version; ++ bool needs_vpd_bl_emulation; + }; + + extern const VMStateDescription vmstate_scsi_device; +@@ -186,7 +187,7 @@ void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense); + void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense); + void scsi_device_report_change(SCSIDevice *dev, SCSISense sense); + void scsi_device_unit_attention_reported(SCSIDevice *dev); +-void scsi_generic_read_device_identification(SCSIDevice *dev); ++void scsi_generic_read_device_inquiry(SCSIDevice *dev); + int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed); + int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf); + int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-hw-scsi-centralize-SG_IO-calls-into-single-function.patch b/SOURCES/kvm-hw-scsi-centralize-SG_IO-calls-into-single-function.patch new file mode 100644 index 0000000..0f623d9 --- /dev/null +++ b/SOURCES/kvm-hw-scsi-centralize-SG_IO-calls-into-single-function.patch @@ -0,0 +1,204 @@ +From bf544897c8e513b68b9262f6f9315b8a49421429 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Wed, 7 Nov 2018 18:00:01 +0100 +Subject: [PATCH 27/34] hw/scsi: centralize SG_IO calls into single function +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Paolo Bonzini +Message-id: <20181107180007.22954-4-pbonzini@redhat.com> +Patchwork-id: 82947 +O-Subject: [RHEL7.6.z qemu-kvm-rhev PATCH 3/9] hw/scsi: centralize SG_IO calls into single function +Bugzilla: 1566195 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Philippe Mathieu-Daudé + +From: Daniel Henrique Barboza + +For the VPD Block Limits emulation with SCSI passthrough, +we'll issue an Inquiry request with EVPD set to retrieve +the available VPD pages of the device. This would be done in +a way similar of what scsi_generic_read_device_identification +does: create a SCSI command and a reply buffer, fill in the +sg_io_hdr_t structure, call blk_ioctl, check if an error +occurred, process the response. + +This same process is done in other 2 functions, get_device_type +and get_stream_blocksize. They differ in the command/reply +buffer and post-processing, everything else is almost a +copy/paste. + +Instead of adding a forth copy/pasted-ish code when adding +the passthrough VPD BL emulation, this patch extirpates +this repetition of those 3 functions and put it into +a new one called scsi_SG_IO_FROM_DEV. Any future code that +wants to execute an SG_DXFER_FROM_DEV to the device can +use it, avoiding filling sg_io_hdr_t again and et cetera. + +Signed-off-by: Daniel Henrique Barboza +Message-Id: <20180627172432.11120-3-danielhb413@gmail.com> +Signed-off-by: Paolo Bonzini +(cherry picked from commit a0c7e35b17b3d2cade8a5fc8e57904e02fb91fe4) +Signed-off-by: Miroslav Rezanina +--- + hw/scsi/scsi-disk.c | 18 +++------------ + hw/scsi/scsi-generic.c | 61 +++++++++++++++++++++++++------------------------- + include/hw/scsi/scsi.h | 2 ++ + 3 files changed, 36 insertions(+), 45 deletions(-) + +diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c +index ae5b4c0..ea86849 100644 +--- a/hw/scsi/scsi-disk.c ++++ b/hw/scsi/scsi-disk.c +@@ -2579,8 +2579,6 @@ static int get_device_type(SCSIDiskState *s) + { + uint8_t cmd[16]; + uint8_t buf[36]; +- uint8_t sensebuf[8]; +- sg_io_hdr_t io_header; + int ret; + + memset(cmd, 0, sizeof(cmd)); +@@ -2588,19 +2586,9 @@ static int get_device_type(SCSIDiskState *s) + cmd[0] = INQUIRY; + cmd[4] = sizeof(buf); + +- memset(&io_header, 0, sizeof(io_header)); +- io_header.interface_id = 'S'; +- io_header.dxfer_direction = SG_DXFER_FROM_DEV; +- io_header.dxfer_len = sizeof(buf); +- io_header.dxferp = buf; +- io_header.cmdp = cmd; +- io_header.cmd_len = sizeof(cmd); +- io_header.mx_sb_len = sizeof(sensebuf); +- io_header.sbp = sensebuf; +- io_header.timeout = 6000; /* XXX */ +- +- ret = blk_ioctl(s->qdev.conf.blk, SG_IO, &io_header); +- if (ret < 0 || io_header.driver_status || io_header.host_status) { ++ ret = scsi_SG_IO_FROM_DEV(s->qdev.conf.blk, cmd, sizeof(cmd), ++ buf, sizeof(buf)); ++ if (ret < 0) { + return -1; + } + s->qdev.type = buf[0]; +diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c +index 796162c..c6307a8 100644 +--- a/hw/scsi/scsi-generic.c ++++ b/hw/scsi/scsi-generic.c +@@ -410,35 +410,48 @@ static int read_naa_id(const uint8_t *p, uint64_t *p_wwn) + return -EINVAL; + } + +-void scsi_generic_read_device_identification(SCSIDevice *s) ++int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size, ++ uint8_t *buf, uint8_t buf_size) + { +- uint8_t cmd[6]; +- uint8_t buf[250]; +- uint8_t sensebuf[8]; + sg_io_hdr_t io_header; ++ uint8_t sensebuf[8]; + int ret; +- int i, len; +- +- memset(cmd, 0, sizeof(cmd)); +- memset(buf, 0, sizeof(buf)); +- cmd[0] = INQUIRY; +- cmd[1] = 1; +- cmd[2] = 0x83; +- cmd[4] = sizeof(buf); + + memset(&io_header, 0, sizeof(io_header)); + io_header.interface_id = 'S'; + io_header.dxfer_direction = SG_DXFER_FROM_DEV; +- io_header.dxfer_len = sizeof(buf); ++ io_header.dxfer_len = buf_size; + io_header.dxferp = buf; + io_header.cmdp = cmd; +- io_header.cmd_len = sizeof(cmd); ++ io_header.cmd_len = cmd_size; + io_header.mx_sb_len = sizeof(sensebuf); + io_header.sbp = sensebuf; + io_header.timeout = 6000; /* XXX */ + +- ret = blk_ioctl(s->conf.blk, SG_IO, &io_header); ++ ret = blk_ioctl(blk, SG_IO, &io_header); + if (ret < 0 || io_header.driver_status || io_header.host_status) { ++ return -1; ++ } ++ return 0; ++} ++ ++void scsi_generic_read_device_identification(SCSIDevice *s) ++{ ++ uint8_t cmd[6]; ++ uint8_t buf[250]; ++ int ret; ++ int i, len; ++ ++ memset(cmd, 0, sizeof(cmd)); ++ memset(buf, 0, sizeof(buf)); ++ cmd[0] = INQUIRY; ++ cmd[1] = 1; ++ cmd[2] = 0x83; ++ cmd[4] = sizeof(buf); ++ ++ ret = scsi_SG_IO_FROM_DEV(s->conf.blk, cmd, sizeof(cmd), ++ buf, sizeof(buf)); ++ if (ret < 0) { + return; + } + +@@ -471,8 +484,6 @@ static int get_stream_blocksize(BlockBackend *blk) + { + uint8_t cmd[6]; + uint8_t buf[12]; +- uint8_t sensebuf[8]; +- sg_io_hdr_t io_header; + int ret; + + memset(cmd, 0, sizeof(cmd)); +@@ -480,21 +491,11 @@ static int get_stream_blocksize(BlockBackend *blk) + cmd[0] = MODE_SENSE; + cmd[4] = sizeof(buf); + +- memset(&io_header, 0, sizeof(io_header)); +- io_header.interface_id = 'S'; +- io_header.dxfer_direction = SG_DXFER_FROM_DEV; +- io_header.dxfer_len = sizeof(buf); +- io_header.dxferp = buf; +- io_header.cmdp = cmd; +- io_header.cmd_len = sizeof(cmd); +- io_header.mx_sb_len = sizeof(sensebuf); +- io_header.sbp = sensebuf; +- io_header.timeout = 6000; /* XXX */ +- +- ret = blk_ioctl(blk, SG_IO, &io_header); +- if (ret < 0 || io_header.driver_status || io_header.host_status) { ++ ret = scsi_SG_IO_FROM_DEV(blk, cmd, sizeof(cmd), buf, sizeof(buf)); ++ if (ret < 0) { + return -1; + } ++ + return (buf[9] << 16) | (buf[10] << 8) | buf[11]; + } + +diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h +index 5930a43..b6e05c4 100644 +--- a/include/hw/scsi/scsi.h ++++ b/include/hw/scsi/scsi.h +@@ -189,6 +189,8 @@ void scsi_device_unit_attention_reported(SCSIDevice *dev); + void scsi_generic_read_device_identification(SCSIDevice *dev); + int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed); + int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf); ++int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size, ++ uint8_t *buf, uint8_t buf_size); + SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun); + + /* scsi-generic.c. */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-hw-scsi-cleanups-before-VPD-BL-emulation.patch b/SOURCES/kvm-hw-scsi-cleanups-before-VPD-BL-emulation.patch new file mode 100644 index 0000000..f2278f3 --- /dev/null +++ b/SOURCES/kvm-hw-scsi-cleanups-before-VPD-BL-emulation.patch @@ -0,0 +1,597 @@ +From 3597e0269b24fe17de30e1554f33b1770852211b Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Wed, 7 Nov 2018 18:00:00 +0100 +Subject: [PATCH 26/34] hw/scsi: cleanups before VPD BL emulation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Paolo Bonzini +Message-id: <20181107180007.22954-3-pbonzini@redhat.com> +Patchwork-id: 82948 +O-Subject: [RHEL7.6.z qemu-kvm-rhev PATCH 2/9] hw/scsi: cleanups before VPD BL emulation +Bugzilla: 1566195 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Philippe Mathieu-Daudé + +From: Daniel Henrique Barboza + +To add support for the emulation of Block Limits VPD page +for passthrough devices, a few adjustments in the current code +base is required to avoid repetition and improve clarity. + +In scsi-generic.c, detach the Inquiry handling from +scsi_read_complete and put it into a new function called +scsi_handle_inquiry_reply. This change aims to avoid +cluttering of scsi_read_complete when we more logic in the +Inquiry response handling is added in the next patches, +centralizing the changes in the new function. + +In scsi-disk.c, take the build of all emulated VPD pages +from scsi_disk_emulate_inquiry and make it available to +other files into a non-static function called +scsi_disk_emulate_vpd_page. Making it public will allow +the future VPD BL emulation code for passthrough devices +to use it from scsi-generic.c, avoiding copy/pasting this +code solely for that purpose. It also has the advantage of +providing emulation of all VPD pages in case we need to +emulate other pages in other scenarios. As a bonus, +scsi_disk_emulate_inquiry got tidier. + +Signed-off-by: Daniel Henrique Barboza +Message-Id: <20180627172432.11120-2-danielhb413@gmail.com> +Signed-off-by: Paolo Bonzini +(cherry picked from commit 0a96ca2437646bad197b0108c5f4a93e7ead05a9) +Signed-off-by: Miroslav Rezanina +--- + hw/scsi/scsi-disk.c | 407 +++++++++++++++++++++++++------------------------ + hw/scsi/scsi-generic.c | 71 +++++---- + include/hw/scsi/scsi.h | 1 + + 3 files changed, 249 insertions(+), 230 deletions(-) + +diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c +index ded23d3..ae5b4c0 100644 +--- a/hw/scsi/scsi-disk.c ++++ b/hw/scsi/scsi-disk.c +@@ -585,219 +585,228 @@ static uint8_t *scsi_get_buf(SCSIRequest *req) + return (uint8_t *)r->iov.iov_base; + } + +-static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) ++int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) + { + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); +- int buflen = 0; +- int start; +- +- if (req->cmd.buf[1] & 0x1) { +- /* Vital product data */ +- uint8_t page_code = req->cmd.buf[2]; +- +- outbuf[buflen++] = s->qdev.type & 0x1f; +- outbuf[buflen++] = page_code ; // this page +- outbuf[buflen++] = 0x00; +- outbuf[buflen++] = 0x00; +- start = buflen; +- +- switch (page_code) { +- case 0x00: /* Supported page codes, mandatory */ +- { +- DPRINTF("Inquiry EVPD[Supported pages] " +- "buffer size %zd\n", req->cmd.xfer); +- outbuf[buflen++] = 0x00; // list of supported pages (this page) +- if (s->serial) { +- outbuf[buflen++] = 0x80; // unit serial number +- } +- outbuf[buflen++] = 0x83; // device identification +- if (s->qdev.type == TYPE_DISK) { +- outbuf[buflen++] = 0xb0; // block limits +- outbuf[buflen++] = 0xb1; /* block device characteristics */ +- outbuf[buflen++] = 0xb2; // thin provisioning +- } +- break; +- } +- case 0x80: /* Device serial number, optional */ +- { +- int l; ++ uint8_t page_code = req->cmd.buf[2]; ++ int start, buflen = 0; + +- if (!s->serial) { +- DPRINTF("Inquiry (EVPD[Serial number] not supported\n"); +- return -1; +- } ++ outbuf[buflen++] = s->qdev.type & 0x1f; ++ outbuf[buflen++] = page_code; ++ outbuf[buflen++] = 0x00; ++ outbuf[buflen++] = 0x00; ++ start = buflen; + +- l = strlen(s->serial); +- if (l > 36) { +- l = 36; +- } ++ switch (page_code) { ++ case 0x00: /* Supported page codes, mandatory */ ++ { ++ DPRINTF("Inquiry EVPD[Supported pages] " ++ "buffer size %zd\n", req->cmd.xfer); ++ outbuf[buflen++] = 0x00; /* list of supported pages (this page) */ ++ if (s->serial) { ++ outbuf[buflen++] = 0x80; /* unit serial number */ ++ } ++ outbuf[buflen++] = 0x83; /* device identification */ ++ if (s->qdev.type == TYPE_DISK) { ++ outbuf[buflen++] = 0xb0; /* block limits */ ++ outbuf[buflen++] = 0xb1; /* block device characteristics */ ++ outbuf[buflen++] = 0xb2; /* thin provisioning */ ++ } ++ break; ++ } ++ case 0x80: /* Device serial number, optional */ ++ { ++ int l; + +- DPRINTF("Inquiry EVPD[Serial number] " +- "buffer size %zd\n", req->cmd.xfer); +- memcpy(outbuf+buflen, s->serial, l); +- buflen += l; +- break; ++ if (!s->serial) { ++ DPRINTF("Inquiry (EVPD[Serial number] not supported\n"); ++ return -1; + } + +- case 0x83: /* Device identification page, mandatory */ +- { +- const char *str = s->serial ?: blk_name(s->qdev.conf.blk); +- int max_len = s->serial ? 20 : 255 - 8; +- int id_len = strlen(str); ++ l = strlen(s->serial); ++ if (l > 36) { ++ l = 36; ++ } + +- if (id_len > max_len) { +- id_len = max_len; +- } +- DPRINTF("Inquiry EVPD[Device identification] " +- "buffer size %zd\n", req->cmd.xfer); +- +- outbuf[buflen++] = 0x2; // ASCII +- outbuf[buflen++] = 0; // not officially assigned +- outbuf[buflen++] = 0; // reserved +- outbuf[buflen++] = id_len; // length of data following +- memcpy(outbuf+buflen, str, id_len); +- buflen += id_len; +- +- if (s->qdev.wwn) { +- outbuf[buflen++] = 0x1; // Binary +- outbuf[buflen++] = 0x3; // NAA +- outbuf[buflen++] = 0; // reserved +- outbuf[buflen++] = 8; +- stq_be_p(&outbuf[buflen], s->qdev.wwn); +- buflen += 8; +- } ++ DPRINTF("Inquiry EVPD[Serial number] " ++ "buffer size %zd\n", req->cmd.xfer); ++ memcpy(outbuf + buflen, s->serial, l); ++ buflen += l; ++ break; ++ } + +- if (s->qdev.port_wwn) { +- outbuf[buflen++] = 0x61; // SAS / Binary +- outbuf[buflen++] = 0x93; // PIV / Target port / NAA +- outbuf[buflen++] = 0; // reserved +- outbuf[buflen++] = 8; +- stq_be_p(&outbuf[buflen], s->qdev.port_wwn); +- buflen += 8; +- } ++ case 0x83: /* Device identification page, mandatory */ ++ { ++ const char *str = s->serial ?: blk_name(s->qdev.conf.blk); ++ int max_len = s->serial ? 20 : 255 - 8; ++ int id_len = strlen(str); + +- if (s->port_index) { +- outbuf[buflen++] = 0x61; // SAS / Binary +- outbuf[buflen++] = 0x94; // PIV / Target port / relative target port +- outbuf[buflen++] = 0; // reserved +- outbuf[buflen++] = 4; +- stw_be_p(&outbuf[buflen + 2], s->port_index); +- buflen += 4; +- } +- break; ++ if (id_len > max_len) { ++ id_len = max_len; + } +- case 0xb0: /* block limits */ +- { +- unsigned int unmap_sectors = +- s->qdev.conf.discard_granularity / s->qdev.blocksize; +- unsigned int min_io_size = +- s->qdev.conf.min_io_size / s->qdev.blocksize; +- unsigned int opt_io_size = +- s->qdev.conf.opt_io_size / s->qdev.blocksize; +- unsigned int max_unmap_sectors = +- s->max_unmap_size / s->qdev.blocksize; +- unsigned int max_io_sectors = +- s->max_io_size / s->qdev.blocksize; +- +- if (s->qdev.type == TYPE_ROM) { +- DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n", +- page_code); +- return -1; +- } +- if (s->qdev.type == TYPE_DISK) { +- int max_transfer_blk = blk_get_max_transfer(s->qdev.conf.blk); +- int max_io_sectors_blk = +- max_transfer_blk / s->qdev.blocksize; +- +- max_io_sectors = +- MIN_NON_ZERO(max_io_sectors_blk, max_io_sectors); +- +- /* min_io_size and opt_io_size can't be greater than +- * max_io_sectors */ +- if (min_io_size) { +- min_io_size = MIN(min_io_size, max_io_sectors); +- } +- if (opt_io_size) { +- opt_io_size = MIN(opt_io_size, max_io_sectors); +- } +- } +- /* required VPD size with unmap support */ +- buflen = 0x40; +- memset(outbuf + 4, 0, buflen - 4); +- +- outbuf[4] = 0x1; /* wsnz */ +- +- /* optimal transfer length granularity */ +- outbuf[6] = (min_io_size >> 8) & 0xff; +- outbuf[7] = min_io_size & 0xff; +- +- /* maximum transfer length */ +- outbuf[8] = (max_io_sectors >> 24) & 0xff; +- outbuf[9] = (max_io_sectors >> 16) & 0xff; +- outbuf[10] = (max_io_sectors >> 8) & 0xff; +- outbuf[11] = max_io_sectors & 0xff; +- +- /* optimal transfer length */ +- outbuf[12] = (opt_io_size >> 24) & 0xff; +- outbuf[13] = (opt_io_size >> 16) & 0xff; +- outbuf[14] = (opt_io_size >> 8) & 0xff; +- outbuf[15] = opt_io_size & 0xff; +- +- /* max unmap LBA count, default is 1GB */ +- outbuf[20] = (max_unmap_sectors >> 24) & 0xff; +- outbuf[21] = (max_unmap_sectors >> 16) & 0xff; +- outbuf[22] = (max_unmap_sectors >> 8) & 0xff; +- outbuf[23] = max_unmap_sectors & 0xff; +- +- /* max unmap descriptors, 255 fit in 4 kb with an 8-byte header. */ +- outbuf[24] = 0; +- outbuf[25] = 0; +- outbuf[26] = 0; +- outbuf[27] = 255; +- +- /* optimal unmap granularity */ +- outbuf[28] = (unmap_sectors >> 24) & 0xff; +- outbuf[29] = (unmap_sectors >> 16) & 0xff; +- outbuf[30] = (unmap_sectors >> 8) & 0xff; +- outbuf[31] = unmap_sectors & 0xff; +- +- /* max write same size */ +- outbuf[36] = 0; +- outbuf[37] = 0; +- outbuf[38] = 0; +- outbuf[39] = 0; +- +- outbuf[40] = (max_io_sectors >> 24) & 0xff; +- outbuf[41] = (max_io_sectors >> 16) & 0xff; +- outbuf[42] = (max_io_sectors >> 8) & 0xff; +- outbuf[43] = max_io_sectors & 0xff; +- break; ++ DPRINTF("Inquiry EVPD[Device identification] " ++ "buffer size %zd\n", req->cmd.xfer); ++ ++ outbuf[buflen++] = 0x2; /* ASCII */ ++ outbuf[buflen++] = 0; /* not officially assigned */ ++ outbuf[buflen++] = 0; /* reserved */ ++ outbuf[buflen++] = id_len; /* length of data following */ ++ memcpy(outbuf + buflen, str, id_len); ++ buflen += id_len; ++ ++ if (s->qdev.wwn) { ++ outbuf[buflen++] = 0x1; /* Binary */ ++ outbuf[buflen++] = 0x3; /* NAA */ ++ outbuf[buflen++] = 0; /* reserved */ ++ outbuf[buflen++] = 8; ++ stq_be_p(&outbuf[buflen], s->qdev.wwn); ++ buflen += 8; + } +- case 0xb1: /* block device characteristics */ +- { +- buflen = 8; +- outbuf[4] = (s->rotation_rate >> 8) & 0xff; +- outbuf[5] = s->rotation_rate & 0xff; +- outbuf[6] = 0; +- outbuf[7] = 0; +- break; ++ ++ if (s->qdev.port_wwn) { ++ outbuf[buflen++] = 0x61; /* SAS / Binary */ ++ outbuf[buflen++] = 0x93; /* PIV / Target port / NAA */ ++ outbuf[buflen++] = 0; /* reserved */ ++ outbuf[buflen++] = 8; ++ stq_be_p(&outbuf[buflen], s->qdev.port_wwn); ++ buflen += 8; + } +- case 0xb2: /* thin provisioning */ +- { +- buflen = 8; +- outbuf[4] = 0; +- outbuf[5] = 0xe0; /* unmap & write_same 10/16 all supported */ +- outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1; +- outbuf[7] = 0; +- break; ++ ++ if (s->port_index) { ++ outbuf[buflen++] = 0x61; /* SAS / Binary */ ++ ++ /* PIV/Target port/relative target port */ ++ outbuf[buflen++] = 0x94; ++ ++ outbuf[buflen++] = 0; /* reserved */ ++ outbuf[buflen++] = 4; ++ stw_be_p(&outbuf[buflen + 2], s->port_index); ++ buflen += 4; + } +- default: ++ break; ++ } ++ case 0xb0: /* block limits */ ++ { ++ unsigned int unmap_sectors = ++ s->qdev.conf.discard_granularity / s->qdev.blocksize; ++ unsigned int min_io_size = ++ s->qdev.conf.min_io_size / s->qdev.blocksize; ++ unsigned int opt_io_size = ++ s->qdev.conf.opt_io_size / s->qdev.blocksize; ++ unsigned int max_unmap_sectors = ++ s->max_unmap_size / s->qdev.blocksize; ++ unsigned int max_io_sectors = ++ s->max_io_size / s->qdev.blocksize; ++ ++ if (s->qdev.type == TYPE_ROM) { ++ DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n", ++ page_code); + return -1; + } +- /* done with EVPD */ +- assert(buflen - start <= 255); +- outbuf[start - 1] = buflen - start; +- return buflen; ++ if (s->qdev.type == TYPE_DISK) { ++ int max_transfer_blk = blk_get_max_transfer(s->qdev.conf.blk); ++ int max_io_sectors_blk = ++ max_transfer_blk / s->qdev.blocksize; ++ ++ max_io_sectors = ++ MIN_NON_ZERO(max_io_sectors_blk, max_io_sectors); ++ ++ /* min_io_size and opt_io_size can't be greater than ++ * max_io_sectors */ ++ if (min_io_size) { ++ min_io_size = MIN(min_io_size, max_io_sectors); ++ } ++ if (opt_io_size) { ++ opt_io_size = MIN(opt_io_size, max_io_sectors); ++ } ++ } ++ /* required VPD size with unmap support */ ++ buflen = 0x40; ++ memset(outbuf + 4, 0, buflen - 4); ++ ++ outbuf[4] = 0x1; /* wsnz */ ++ ++ /* optimal transfer length granularity */ ++ outbuf[6] = (min_io_size >> 8) & 0xff; ++ outbuf[7] = min_io_size & 0xff; ++ ++ /* maximum transfer length */ ++ outbuf[8] = (max_io_sectors >> 24) & 0xff; ++ outbuf[9] = (max_io_sectors >> 16) & 0xff; ++ outbuf[10] = (max_io_sectors >> 8) & 0xff; ++ outbuf[11] = max_io_sectors & 0xff; ++ ++ /* optimal transfer length */ ++ outbuf[12] = (opt_io_size >> 24) & 0xff; ++ outbuf[13] = (opt_io_size >> 16) & 0xff; ++ outbuf[14] = (opt_io_size >> 8) & 0xff; ++ outbuf[15] = opt_io_size & 0xff; ++ ++ /* max unmap LBA count, default is 1GB */ ++ outbuf[20] = (max_unmap_sectors >> 24) & 0xff; ++ outbuf[21] = (max_unmap_sectors >> 16) & 0xff; ++ outbuf[22] = (max_unmap_sectors >> 8) & 0xff; ++ outbuf[23] = max_unmap_sectors & 0xff; ++ ++ /* max unmap descriptors, 255 fit in 4 kb with an 8-byte header */ ++ outbuf[24] = 0; ++ outbuf[25] = 0; ++ outbuf[26] = 0; ++ outbuf[27] = 255; ++ ++ /* optimal unmap granularity */ ++ outbuf[28] = (unmap_sectors >> 24) & 0xff; ++ outbuf[29] = (unmap_sectors >> 16) & 0xff; ++ outbuf[30] = (unmap_sectors >> 8) & 0xff; ++ outbuf[31] = unmap_sectors & 0xff; ++ ++ /* max write same size */ ++ outbuf[36] = 0; ++ outbuf[37] = 0; ++ outbuf[38] = 0; ++ outbuf[39] = 0; ++ ++ outbuf[40] = (max_io_sectors >> 24) & 0xff; ++ outbuf[41] = (max_io_sectors >> 16) & 0xff; ++ outbuf[42] = (max_io_sectors >> 8) & 0xff; ++ outbuf[43] = max_io_sectors & 0xff; ++ break; ++ } ++ case 0xb1: /* block device characteristics */ ++ { ++ buflen = 8; ++ outbuf[4] = (s->rotation_rate >> 8) & 0xff; ++ outbuf[5] = s->rotation_rate & 0xff; ++ outbuf[6] = 0; ++ outbuf[7] = 0; ++ break; ++ } ++ case 0xb2: /* thin provisioning */ ++ { ++ buflen = 8; ++ outbuf[4] = 0; ++ outbuf[5] = 0xe0; /* unmap & write_same 10/16 all supported */ ++ outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1; ++ outbuf[7] = 0; ++ break; ++ } ++ default: ++ return -1; ++ } ++ /* done with EVPD */ ++ assert(buflen - start <= 255); ++ outbuf[start - 1] = buflen - start; ++ return buflen; ++} ++ ++static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) ++{ ++ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); ++ int buflen = 0; ++ ++ if (req->cmd.buf[1] & 0x1) { ++ /* Vital product data */ ++ return scsi_disk_emulate_vpd_page(req, outbuf); + } + + /* Standard INQUIRY data */ +@@ -3040,6 +3049,10 @@ static Property scsi_block_properties[] = { + DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.blk), + DEFINE_PROP_BOOL("share-rw", SCSIDiskState, qdev.conf.share_rw, false), + DEFINE_PROP_UINT16("rotation_rate", SCSIDiskState, rotation_rate, 0), ++ DEFINE_PROP_UINT64("max_unmap_size", SCSIDiskState, max_unmap_size, ++ DEFAULT_MAX_UNMAP_SIZE), ++ DEFINE_PROP_UINT64("max_io_size", SCSIDiskState, max_io_size, ++ DEFAULT_MAX_IO_SIZE), + DEFINE_PROP_INT32("scsi_version", SCSIDiskState, qdev.default_scsi_version, + -1), + DEFINE_PROP_END_OF_LIST(), +diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c +index 381f04e..796162c 100644 +--- a/hw/scsi/scsi-generic.c ++++ b/hw/scsi/scsi-generic.c +@@ -143,6 +143,43 @@ static int execute_command(BlockBackend *blk, + return 0; + } + ++static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s) ++{ ++ /* ++ * EVPD set to zero returns the standard INQUIRY data. ++ * ++ * Check if scsi_version is unset (-1) to avoid re-defining it ++ * each time an INQUIRY with standard data is received. ++ * scsi_version is initialized with -1 in scsi_generic_reset ++ * and scsi_disk_reset, making sure that we'll set the ++ * scsi_version after a reset. If the version field of the ++ * INQUIRY response somehow changes after a guest reboot, ++ * we'll be able to keep track of it. ++ * ++ * On SCSI-2 and older, first 3 bits of byte 2 is the ++ * ANSI-approved version, while on later versions the ++ * whole byte 2 contains the version. Check if we're dealing ++ * with a newer version and, in that case, assign the ++ * whole byte. ++ */ ++ if (s->scsi_version == -1 && !(r->req.cmd.buf[1] & 0x01)) { ++ s->scsi_version = r->buf[2] & 0x07; ++ if (s->scsi_version > 2) { ++ s->scsi_version = r->buf[2]; ++ } ++ } ++ if (s->type == TYPE_DISK && r->req.cmd.buf[2] == 0xb0) { ++ uint32_t max_transfer = ++ blk_get_max_transfer(s->conf.blk) / s->blocksize; ++ ++ assert(max_transfer); ++ stl_be_p(&r->buf[8], max_transfer); ++ /* Also take care of the opt xfer len. */ ++ stl_be_p(&r->buf[12], ++ MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12]))); ++ } ++} ++ + static void scsi_read_complete(void * opaque, int ret) + { + SCSIGenericReq *r = (SCSIGenericReq *)opaque; +@@ -195,39 +232,7 @@ static void scsi_read_complete(void * opaque, int ret) + } + } + if (r->req.cmd.buf[0] == INQUIRY) { +- /* +- * EVPD set to zero returns the standard INQUIRY data. +- * +- * Check if scsi_version is unset (-1) to avoid re-defining it +- * each time an INQUIRY with standard data is received. +- * scsi_version is initialized with -1 in scsi_generic_reset +- * and scsi_disk_reset, making sure that we'll set the +- * scsi_version after a reset. If the version field of the +- * INQUIRY response somehow changes after a guest reboot, +- * we'll be able to keep track of it. +- * +- * On SCSI-2 and older, first 3 bits of byte 2 is the +- * ANSI-approved version, while on later versions the +- * whole byte 2 contains the version. Check if we're dealing +- * with a newer version and, in that case, assign the +- * whole byte. +- */ +- if (s->scsi_version == -1 && !(r->req.cmd.buf[1] & 0x01)) { +- s->scsi_version = r->buf[2] & 0x07; +- if (s->scsi_version > 2) { +- s->scsi_version = r->buf[2]; +- } +- } +- if (s->type == TYPE_DISK && r->req.cmd.buf[2] == 0xb0) { +- uint32_t max_transfer = +- blk_get_max_transfer(s->conf.blk) / s->blocksize; +- +- assert(max_transfer); +- stl_be_p(&r->buf[8], max_transfer); +- /* Also take care of the opt xfer len. */ +- stl_be_p(&r->buf[12], +- MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12]))); +- } ++ scsi_handle_inquiry_reply(r, s); + } + scsi_req_data(&r->req, len); + scsi_req_unref(&r->req); +diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h +index 1a7290d..5930a43 100644 +--- a/include/hw/scsi/scsi.h ++++ b/include/hw/scsi/scsi.h +@@ -188,6 +188,7 @@ void scsi_device_report_change(SCSIDevice *dev, SCSISense sense); + void scsi_device_unit_attention_reported(SCSIDevice *dev); + void scsi_generic_read_device_identification(SCSIDevice *dev); + int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed); ++int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf); + SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun); + + /* scsi-generic.c. */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-hw-usb-dev-smartcard-reader-Handle-64-B-USB-packets.patch b/SOURCES/kvm-hw-usb-dev-smartcard-reader-Handle-64-B-USB-packets.patch new file mode 100644 index 0000000..0e288b3 --- /dev/null +++ b/SOURCES/kvm-hw-usb-dev-smartcard-reader-Handle-64-B-USB-packets.patch @@ -0,0 +1,97 @@ +From a05d849be393110a4efacb148197434c095e06e2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= +Date: Thu, 16 Aug 2018 10:41:54 +0200 +Subject: [PATCH 3/5] hw/usb/dev-smartcard-reader: Handle 64 B USB packets +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Marc-André Lureau +Message-id: <20180816104154.18782-1-marcandre.lureau@redhat.com> +Patchwork-id: 81855 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH] hw/usb/dev-smartcard-reader: Handle 64 B USB packets +Bugzilla: 1589147 +RH-Acked-by: Gerd Hoffmann +RH-Acked-by: Igor Mammedov +RH-Acked-by: Miroslav Rezanina + +From: Jakub Jelen + +The current code was not correctly handling 64 B (Max USB 1.1 payload size) +packets and therefore preventing some of the messages from smart card to +pass through to the guest. + +If the smart card in host responded with 34 B of data in APDU layer, the +CCID headers added up to 64 B. The packet was send, but not correctly +committed per USB specification (8.5.3.2 Variable-length Data Stage): + +> When all of the data structure is returned to the host, the function +> should indicate that the Data stage is ended by returning a packet +> that is shorter than the MaxPacketSize for the pipe. If the data +> structure is an exact multiple of wMaxPacketSize for the pipe, the +> function will return a zero-length packet to indicate the end of the +> Data stage. + +This lead the guest applications to timeout while waiting for the rest +of data (the emulation layer is answering with NAK until the timeout). + +This patch is checking the current maximum packet size and if the +payload of this size is detected, the message buffer is not yet released. +With the next call, the empty buffer is sent and the message buffer +is finally released. + +Signed-off-by: Jakub Jelen +Message-id: 20180516115544.3897-2-jjelen@redhat.com +Signed-off-by: Gerd Hoffmann + +(cherry picked from commit 8030dca376fa1bc4d8a6be7628196578f8783ab3) + +Signed-off-by: Marc-André Lureau +Signed-off-by: Miroslav Rezanina +--- + hw/usb/dev-smartcard-reader.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c +index e646805..214d3e9 100644 +--- a/hw/usb/dev-smartcard-reader.c ++++ b/hw/usb/dev-smartcard-reader.c +@@ -1064,7 +1064,8 @@ err: + return; + } + +-static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p) ++static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p, ++ unsigned int max_packet_size) + { + int len = 0; + +@@ -1072,10 +1073,13 @@ static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p) + if (s->current_bulk_in != NULL) { + len = MIN(s->current_bulk_in->len - s->current_bulk_in->pos, + p->iov.size); +- usb_packet_copy(p, s->current_bulk_in->data + +- s->current_bulk_in->pos, len); ++ if (len) { ++ usb_packet_copy(p, s->current_bulk_in->data + ++ s->current_bulk_in->pos, len); ++ } + s->current_bulk_in->pos += len; +- if (s->current_bulk_in->pos == s->current_bulk_in->len) { ++ if (s->current_bulk_in->pos == s->current_bulk_in->len ++ && len != max_packet_size) { + ccid_bulk_in_release(s); + } + } else { +@@ -1107,7 +1111,7 @@ static void ccid_handle_data(USBDevice *dev, USBPacket *p) + case USB_TOKEN_IN: + switch (p->ep->nr) { + case CCID_BULK_IN_EP: +- ccid_bulk_in_copy_to_guest(s, p); ++ ccid_bulk_in_copy_to_guest(s, p, dev->ep_ctl.max_packet_size); + break; + case CCID_INT_IN_EP: + if (s->notify_slot_change) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-Add-CPUID-bit-and-feature-words-for-IA32_ARCH_C.patch b/SOURCES/kvm-i386-Add-CPUID-bit-and-feature-words-for-IA32_ARCH_C.patch new file mode 100644 index 0000000..1c02644 --- /dev/null +++ b/SOURCES/kvm-i386-Add-CPUID-bit-and-feature-words-for-IA32_ARCH_C.patch @@ -0,0 +1,68 @@ +From f326041af5bd8ebebee83dbc296ab853955cd806 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Tue, 4 Jun 2019 21:47:21 +0200 +Subject: [PATCH 02/23] i386: Add CPUID bit and feature words for + IA32_ARCH_CAPABILITIES MSR + +RH-Author: plai@redhat.com +Message-id: <1559684847-10889-3-git-send-email-plai@redhat.com> +Patchwork-id: 88533 +O-Subject: [RHEL7.7 qemu-kvm-rhev PATCH v4 2/8] i386: Add CPUID bit and feature words for IA32_ARCH_CAPABILITIES MSR +Bugzilla: 1709972 +RH-Acked-by: Eduardo Habkost +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Miroslav Rezanina + +From: Robert Hoo + +Support of IA32_PRED_CMD MSR already be enumerated by same CPUID bit as +SPEC_CTRL. + +At present, mark CPUID_7_0_EDX_ARCH_CAPABILITIES unmigratable, per Paolo's +comment. + +Signed-off-by: Robert Hoo +Message-Id: <1530781798-183214-3-git-send-email-robert.hu@linux.intel.com> +Signed-off-by: Eduardo Habkost +(cherry picked from commit 3fc7c73139d2d38ae80c3b0bc963b1ac1555924c) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 3 ++- + target/i386/cpu.h | 1 + + 2 files changed, 3 insertions(+), 1 deletion(-) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 4558b1a..4c7364b 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -1008,12 +1008,13 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, "spec-ctrl", "stibp", +- NULL, NULL, NULL, "ssbd", ++ NULL, "arch-capabilities", NULL, "ssbd", + }, + .cpuid_eax = 7, + .cpuid_needs_ecx = true, .cpuid_ecx = 0, + .cpuid_reg = R_EDX, + .tcg_features = TCG_7_0_EDX_FEATURES, ++ .unmigratable_flags = CPUID_7_0_EDX_ARCH_CAPABILITIES, + }, + [FEAT_8000_0007_EDX] = { + .feat_names = { +diff --git a/target/i386/cpu.h b/target/i386/cpu.h +index aabb6c8..eb39724 100644 +--- a/target/i386/cpu.h ++++ b/target/i386/cpu.h +@@ -689,6 +689,7 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS]; + #define CPUID_7_0_EDX_AVX512_4VNNIW (1U << 2) /* AVX512 Neural Network Instructions */ + #define CPUID_7_0_EDX_AVX512_4FMAPS (1U << 3) /* AVX512 Multiply Accumulation Single Precision */ + #define CPUID_7_0_EDX_SPEC_CTRL (1U << 26) /* Speculation Control */ ++#define CPUID_7_0_EDX_ARCH_CAPABILITIES (1U << 29) /*Arch Capabilities*/ + #define CPUID_7_0_EDX_SPEC_CTRL_SSBD (1U << 31) /* Speculative Store Bypass Disable */ + + #define KVM_HINTS_DEDICATED (1U << 0) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-Add-cache-information-in-X86CPUDefinition.patch b/SOURCES/kvm-i386-Add-cache-information-in-X86CPUDefinition.patch new file mode 100644 index 0000000..8c56680 --- /dev/null +++ b/SOURCES/kvm-i386-Add-cache-information-in-X86CPUDefinition.patch @@ -0,0 +1,71 @@ +From 0636bd2be78ee1df4a6a661e568ef02175f23127 Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Tue, 3 Jul 2018 17:23:48 +0200 +Subject: [PATCH 03/89] i386: Add cache information in X86CPUDefinition + +RH-Author: Eduardo Habkost +Message-id: <20180703172356.21038-3-ehabkost@redhat.com> +Patchwork-id: 81211 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v3 02/10] i386: Add cache information in X86CPUDefinition +Bugzilla: 1481253 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Igor Mammedov +RH-Acked-by: Miroslav Rezanina + +From: Babu Moger + +Add cache information in X86CPUDefinition and CPUX86State. + +Signed-off-by: Babu Moger +Tested-by: Geoffrey McRae +Reviewed-by: Eduardo Habkost +Message-Id: <20180510204148.11687-3-babu.moger@amd.com> +Signed-off-by: Eduardo Habkost +(cherry picked from commit 6aaeb05492ef668f415324f43e7d875c0f1e90b3) +Signed-off-by: Eduardo Habkost +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 1 + + target/i386/cpu.h | 7 +++++++ + 2 files changed, 8 insertions(+) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 9a5a164..7e81e08 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -1105,6 +1105,7 @@ struct X86CPUDefinition { + int stepping; + FeatureWordArray features; + const char *model_id; ++ CPUCaches *cache_info; + }; + + static X86CPUDefinition builtin_x86_defs[] = { +diff --git a/target/i386/cpu.h b/target/i386/cpu.h +index a0e4eeb..912aa34 100644 +--- a/target/i386/cpu.h ++++ b/target/i386/cpu.h +@@ -1098,6 +1098,12 @@ typedef struct CPUCacheInfo { + } CPUCacheInfo; + + ++typedef struct CPUCaches { ++ CPUCacheInfo l1d_cache; ++ CPUCacheInfo l1i_cache; ++ CPUCacheInfo l2_cache; ++ CPUCacheInfo l3_cache; ++} CPUCaches; + + typedef struct CPUX86State { + /* standard registers */ +@@ -1285,6 +1291,7 @@ typedef struct CPUX86State { + /* Features that were explicitly enabled/disabled */ + FeatureWordArray user_features; + uint32_t cpuid_model[12]; ++ CPUCaches *cache_info; + + /* MTRRs */ + uint64_t mtrr_fixed[11]; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-Add-new-MSR-indices-for-IA32_PRED_CMD-and-IA32_.patch b/SOURCES/kvm-i386-Add-new-MSR-indices-for-IA32_PRED_CMD-and-IA32_.patch new file mode 100644 index 0000000..adb01fe --- /dev/null +++ b/SOURCES/kvm-i386-Add-new-MSR-indices-for-IA32_PRED_CMD-and-IA32_.patch @@ -0,0 +1,50 @@ +From 1261bc0eac292f500640c7ca96e381a044650717 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Tue, 4 Jun 2019 21:47:20 +0200 +Subject: [PATCH 01/23] i386: Add new MSR indices for IA32_PRED_CMD and + IA32_ARCH_CAPABILITIES + +RH-Author: plai@redhat.com +Message-id: <1559684847-10889-2-git-send-email-plai@redhat.com> +Patchwork-id: 88528 +O-Subject: [RHEL7.7 qemu-kvm-rhev PATCH v4 1/8] i386: Add new MSR indices for IA32_PRED_CMD and IA32_ARCH_CAPABILITIES +Bugzilla: 1709972 +RH-Acked-by: Eduardo Habkost +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Miroslav Rezanina + +From: Robert Hoo + +IA32_PRED_CMD MSR gives software a way to issue commands that affect the state +of indirect branch predictors. Enumerated by CPUID.(EAX=7H,ECX=0):EDX[26]. +IA32_ARCH_CAPABILITIES MSR enumerates architectural features of RDCL_NO and +IBRS_ALL. Enumerated by CPUID.(EAX=07H, ECX=0):EDX[29]. + +https://software.intel.com/sites/default/files/managed/c5/63/336996-Speculative-Execution-Side-Channel-Mitigations.pdf + +Signed-off-by: Robert Hoo +Message-Id: <1530781798-183214-2-git-send-email-robert.hu@linux.intel.com> +Signed-off-by: Eduardo Habkost +(cherry picked from commit 8c80c99fcceabd0708a5a83f08577e778c9419f5) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.h | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/target/i386/cpu.h b/target/i386/cpu.h +index ea8c355..aabb6c8 100644 +--- a/target/i386/cpu.h ++++ b/target/i386/cpu.h +@@ -352,6 +352,8 @@ typedef enum X86Seg { + #define MSR_TSC_ADJUST 0x0000003b + #define MSR_IA32_SPEC_CTRL 0x48 + #define MSR_VIRT_SSBD 0xc001011f ++#define MSR_IA32_PRED_CMD 0x49 ++#define MSR_IA32_ARCH_CAPABILITIES 0x10a + #define MSR_IA32_TSCDEADLINE 0x6e0 + + #define FEATURE_CONTROL_LOCKED (1<<0) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-Add-new-property-to-control-cache-info.patch b/SOURCES/kvm-i386-Add-new-property-to-control-cache-info.patch new file mode 100644 index 0000000..24308e3 --- /dev/null +++ b/SOURCES/kvm-i386-Add-new-property-to-control-cache-info.patch @@ -0,0 +1,287 @@ +From 64b860ac7db707ef2a29d957b794c831637315a6 Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Tue, 3 Jul 2018 17:23:50 +0200 +Subject: [PATCH 05/89] i386: Add new property to control cache info + +RH-Author: Eduardo Habkost +Message-id: <20180703172356.21038-5-ehabkost@redhat.com> +Patchwork-id: 81212 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v3 04/10] i386: Add new property to control cache info +Bugzilla: 1481253 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Igor Mammedov +RH-Acked-by: Miroslav Rezanina + +From: Babu Moger + +The property legacy-cache will be used to control the cache information. +If user passes "-cpu legacy-cache" then older information will +be displayed even if the hardware supports new information. Otherwise +use the statically loaded cache definitions if available. + +Renamed the previous cache structures to legacy_*. If there is any change in +the cache information, then it needs to be initialized in builtin_x86_defs. + +Signed-off-by: Babu Moger +Tested-by: Geoffrey McRae +Message-Id: <20180514164156.27034-3-babu.moger@amd.com> +Reviewed-by: Eduardo Habkost +Signed-off-by: Eduardo Habkost +(cherry picked from commit ab8f992e3e63e91be257e4e343d386dae7be4bcb) +Signed-off-by: Eduardo Habkost +Signed-off-by: Miroslav Rezanina +--- + include/hw/i386/pc.h | 4 +++ + target/i386/cpu.c | 97 ++++++++++++++++++++++++++++++++++++++-------------- + target/i386/cpu.h | 5 +++ + 3 files changed, 80 insertions(+), 26 deletions(-) + +diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h +index d5a0827..3ff55c6 100644 +--- a/include/hw/i386/pc.h ++++ b/include/hw/i386/pc.h +@@ -979,6 +979,10 @@ extern void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id); + .driver = "Skylake-Server" "-" TYPE_X86_CPU,\ + .property = "clflushopt",\ + .value = "off",\ ++ },{ /* PC_RHEL7_5_COMPAT from PC_COMPAT_2_12 */ \ ++ .driver = TYPE_X86_CPU,\ ++ .property = "legacy-cache",\ ++ .value = "on",\ + }, + + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 23eb47d..3426130 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -336,10 +336,14 @@ static void encode_cache_cpuid80000006(CPUCacheInfo *l2, + } + } + +-/* Definitions of the hardcoded cache entries we expose: */ ++/* ++ * Definitions of the hardcoded cache entries we expose: ++ * These are legacy cache values. If there is a need to change any ++ * of these values please use builtin_x86_defs ++ */ + + /* L1 data cache: */ +-static CPUCacheInfo l1d_cache = { ++static CPUCacheInfo legacy_l1d_cache = { + .type = DCACHE, + .level = 1, + .size = 32 * KiB, +@@ -352,7 +356,7 @@ static CPUCacheInfo l1d_cache = { + }; + + /*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ +-static CPUCacheInfo l1d_cache_amd = { ++static CPUCacheInfo legacy_l1d_cache_amd = { + .type = DCACHE, + .level = 1, + .size = 64 * KiB, +@@ -366,7 +370,7 @@ static CPUCacheInfo l1d_cache_amd = { + }; + + /* L1 instruction cache: */ +-static CPUCacheInfo l1i_cache = { ++static CPUCacheInfo legacy_l1i_cache = { + .type = ICACHE, + .level = 1, + .size = 32 * KiB, +@@ -379,7 +383,7 @@ static CPUCacheInfo l1i_cache = { + }; + + /*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ +-static CPUCacheInfo l1i_cache_amd = { ++static CPUCacheInfo legacy_l1i_cache_amd = { + .type = ICACHE, + .level = 1, + .size = 64 * KiB, +@@ -393,7 +397,7 @@ static CPUCacheInfo l1i_cache_amd = { + }; + + /* Level 2 unified cache: */ +-static CPUCacheInfo l2_cache = { ++static CPUCacheInfo legacy_l2_cache = { + .type = UNIFIED_CACHE, + .level = 2, + .size = 4 * MiB, +@@ -406,7 +410,7 @@ static CPUCacheInfo l2_cache = { + }; + + /*FIXME: CPUID leaf 2 descriptor is inconsistent with CPUID leaf 4 */ +-static CPUCacheInfo l2_cache_cpuid2 = { ++static CPUCacheInfo legacy_l2_cache_cpuid2 = { + .type = UNIFIED_CACHE, + .level = 2, + .size = 2 * MiB, +@@ -416,7 +420,7 @@ static CPUCacheInfo l2_cache_cpuid2 = { + + + /*FIXME: CPUID leaf 0x80000006 is inconsistent with leaves 2 & 4 */ +-static CPUCacheInfo l2_cache_amd = { ++static CPUCacheInfo legacy_l2_cache_amd = { + .type = UNIFIED_CACHE, + .level = 2, + .size = 512 * KiB, +@@ -428,7 +432,7 @@ static CPUCacheInfo l2_cache_amd = { + }; + + /* Level 3 unified cache: */ +-static CPUCacheInfo l3_cache = { ++static CPUCacheInfo legacy_l3_cache = { + .type = UNIFIED_CACHE, + .level = 3, + .size = 16 * MiB, +@@ -3321,6 +3325,10 @@ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp) + env->features[w] = def->features[w]; + } + ++ /* Store Cache information from the X86CPUDefinition if available */ ++ env->cache_info = def->cache_info; ++ cpu->legacy_cache = def->cache_info ? 0 : 1; ++ + /* Special cases not set in the X86CPUDefinition structs: */ + /* TODO: in-kernel irqchip for hvf */ + if (kvm_enabled()) { +@@ -3670,11 +3678,21 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, + if (!cpu->enable_l3_cache) { + *ecx = 0; + } else { +- *ecx = cpuid2_cache_descriptor(&l3_cache); ++ if (env->cache_info && !cpu->legacy_cache) { ++ *ecx = cpuid2_cache_descriptor(&env->cache_info->l3_cache); ++ } else { ++ *ecx = cpuid2_cache_descriptor(&legacy_l3_cache); ++ } ++ } ++ if (env->cache_info && !cpu->legacy_cache) { ++ *edx = (cpuid2_cache_descriptor(&env->cache_info->l1d_cache) << 16) | ++ (cpuid2_cache_descriptor(&env->cache_info->l1i_cache) << 8) | ++ (cpuid2_cache_descriptor(&env->cache_info->l2_cache)); ++ } else { ++ *edx = (cpuid2_cache_descriptor(&legacy_l1d_cache) << 16) | ++ (cpuid2_cache_descriptor(&legacy_l1i_cache) << 8) | ++ (cpuid2_cache_descriptor(&legacy_l2_cache_cpuid2)); + } +- *edx = (cpuid2_cache_descriptor(&l1d_cache) << 16) | +- (cpuid2_cache_descriptor(&l1i_cache) << 8) | +- (cpuid2_cache_descriptor(&l2_cache_cpuid2)); + break; + case 4: + /* cache info: needed for Core compatibility */ +@@ -3687,27 +3705,35 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, + } + } else { + *eax = 0; ++ CPUCacheInfo *l1d, *l1i, *l2, *l3; ++ if (env->cache_info && !cpu->legacy_cache) { ++ l1d = &env->cache_info->l1d_cache; ++ l1i = &env->cache_info->l1i_cache; ++ l2 = &env->cache_info->l2_cache; ++ l3 = &env->cache_info->l3_cache; ++ } else { ++ l1d = &legacy_l1d_cache; ++ l1i = &legacy_l1i_cache; ++ l2 = &legacy_l2_cache; ++ l3 = &legacy_l3_cache; ++ } + switch (count) { + case 0: /* L1 dcache info */ +- encode_cache_cpuid4(&l1d_cache, +- 1, cs->nr_cores, ++ encode_cache_cpuid4(l1d, 1, cs->nr_cores, + eax, ebx, ecx, edx); + break; + case 1: /* L1 icache info */ +- encode_cache_cpuid4(&l1i_cache, +- 1, cs->nr_cores, ++ encode_cache_cpuid4(l1i, 1, cs->nr_cores, + eax, ebx, ecx, edx); + break; + case 2: /* L2 cache info */ +- encode_cache_cpuid4(&l2_cache, +- cs->nr_threads, cs->nr_cores, ++ encode_cache_cpuid4(l2, cs->nr_threads, cs->nr_cores, + eax, ebx, ecx, edx); + break; + case 3: /* L3 cache info */ + pkg_offset = apicid_pkg_offset(cs->nr_cores, cs->nr_threads); + if (cpu->enable_l3_cache) { +- encode_cache_cpuid4(&l3_cache, +- (1 << pkg_offset), cs->nr_cores, ++ encode_cache_cpuid4(l3, (1 << pkg_offset), cs->nr_cores, + eax, ebx, ecx, edx); + break; + } +@@ -3920,8 +3946,13 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, + (L1_ITLB_2M_ASSOC << 8) | (L1_ITLB_2M_ENTRIES); + *ebx = (L1_DTLB_4K_ASSOC << 24) | (L1_DTLB_4K_ENTRIES << 16) | \ + (L1_ITLB_4K_ASSOC << 8) | (L1_ITLB_4K_ENTRIES); +- *ecx = encode_cache_cpuid80000005(&l1d_cache_amd); +- *edx = encode_cache_cpuid80000005(&l1i_cache_amd); ++ if (env->cache_info && !cpu->legacy_cache) { ++ *ecx = encode_cache_cpuid80000005(&env->cache_info->l1d_cache); ++ *edx = encode_cache_cpuid80000005(&env->cache_info->l1i_cache); ++ } else { ++ *ecx = encode_cache_cpuid80000005(&legacy_l1d_cache_amd); ++ *edx = encode_cache_cpuid80000005(&legacy_l1i_cache_amd); ++ } + break; + case 0x80000006: + /* cache info (L2 cache) */ +@@ -3937,9 +3968,17 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, + (L2_DTLB_4K_ENTRIES << 16) | \ + (AMD_ENC_ASSOC(L2_ITLB_4K_ASSOC) << 12) | \ + (L2_ITLB_4K_ENTRIES); +- encode_cache_cpuid80000006(&l2_cache_amd, +- cpu->enable_l3_cache ? &l3_cache : NULL, +- ecx, edx); ++ if (env->cache_info && !cpu->legacy_cache) { ++ encode_cache_cpuid80000006(&env->cache_info->l2_cache, ++ cpu->enable_l3_cache ? ++ &env->cache_info->l3_cache : NULL, ++ ecx, edx); ++ } else { ++ encode_cache_cpuid80000006(&legacy_l2_cache_amd, ++ cpu->enable_l3_cache ? ++ &legacy_l3_cache : NULL, ++ ecx, edx); ++ } + break; + case 0x80000007: + *eax = 0; +@@ -5119,6 +5158,12 @@ static Property x86_cpu_properties[] = { + false), + DEFINE_PROP_BOOL("vmware-cpuid-freq", X86CPU, vmware_cpuid_freq, true), + DEFINE_PROP_BOOL("tcg-cpuid", X86CPU, expose_tcg, true), ++ /* ++ * lecacy_cache defaults to CPU model being chosen. This is set in ++ * x86_cpu_load_def based on cache_info which is initialized in ++ * builtin_x86_defs ++ */ ++ DEFINE_PROP_BOOL("legacy-cache", X86CPU, legacy_cache, false), + + /* + * From "Requirements for Implementing the Microsoft +diff --git a/target/i386/cpu.h b/target/i386/cpu.h +index 912aa34..b01b0c1 100644 +--- a/target/i386/cpu.h ++++ b/target/i386/cpu.h +@@ -1397,6 +1397,11 @@ struct X86CPU { + */ + bool enable_l3_cache; + ++ /* Compatibility bits for old machine types. ++ * If true present the old cache topology information ++ */ ++ bool legacy_cache; ++ + /* Compatibility bits for old machine types: */ + bool enable_cpuid_0xb; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-Add-support-for-CPUID_8000_001E-for-AMD.patch b/SOURCES/kvm-i386-Add-support-for-CPUID_8000_001E-for-AMD.patch new file mode 100644 index 0000000..4aa1886 --- /dev/null +++ b/SOURCES/kvm-i386-Add-support-for-CPUID_8000_001E-for-AMD.patch @@ -0,0 +1,137 @@ +From 31d4822f7d25e9438de3dc7d689cfe3dd5544cc7 Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Tue, 3 Jul 2018 17:23:53 +0200 +Subject: [PATCH 08/89] i386: Add support for CPUID_8000_001E for AMD + +RH-Author: Eduardo Habkost +Message-id: <20180703172356.21038-8-ehabkost@redhat.com> +Patchwork-id: 81217 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v3 07/10] i386: Add support for CPUID_8000_001E for AMD +Bugzilla: 1481253 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Igor Mammedov +RH-Acked-by: Miroslav Rezanina + +From: Babu Moger + +Add support for cpuid leaf CPUID_8000_001E. Build the config that closely +match the underlying hardware. Please refer to the Processor Programming +Reference (PPR) for AMD Family 17h Model for more details. + +Signed-off-by: Babu Moger +Message-Id: <1528498581-131037-2-git-send-email-babu.moger@amd.com> +Signed-off-by: Eduardo Habkost +(cherry picked from commit ed78467a214595a63af7800a073a03ffe37cd7db) +Signed-off-by: Eduardo Habkost +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 86 insertions(+) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 57f74c6..643d3b1 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -429,6 +429,87 @@ static void encode_cache_cpuid8000001d(CPUCacheInfo *cache, CPUState *cs, + (cache->complex_indexing ? CACHE_COMPLEX_IDX : 0); + } + ++/* Data structure to hold the configuration info for a given core index */ ++struct core_topology { ++ /* core complex id of the current core index */ ++ int ccx_id; ++ /* ++ * Adjusted core index for this core in the topology ++ * This can be 0,1,2,3 with max 4 cores in a core complex ++ */ ++ int core_id; ++ /* Node id for this core index */ ++ int node_id; ++ /* Number of nodes in this config */ ++ int num_nodes; ++}; ++ ++/* ++ * Build the configuration closely match the EPYC hardware. Using the EPYC ++ * hardware configuration values (MAX_CCX, MAX_CORES_IN_CCX, MAX_CORES_IN_NODE) ++ * right now. This could change in future. ++ * nr_cores : Total number of cores in the config ++ * core_id : Core index of the current CPU ++ * topo : Data structure to hold all the config info for this core index ++ */ ++static void build_core_topology(int nr_cores, int core_id, ++ struct core_topology *topo) ++{ ++ int nodes, cores_in_ccx; ++ ++ /* First get the number of nodes required */ ++ nodes = nodes_in_socket(nr_cores); ++ ++ cores_in_ccx = cores_in_core_complex(nr_cores); ++ ++ topo->node_id = core_id / (cores_in_ccx * MAX_CCX); ++ topo->ccx_id = (core_id % (cores_in_ccx * MAX_CCX)) / cores_in_ccx; ++ topo->core_id = core_id % cores_in_ccx; ++ topo->num_nodes = nodes; ++} ++ ++/* Encode cache info for CPUID[8000001E] */ ++static void encode_topo_cpuid8000001e(CPUState *cs, X86CPU *cpu, ++ uint32_t *eax, uint32_t *ebx, ++ uint32_t *ecx, uint32_t *edx) ++{ ++ struct core_topology topo = {0}; ++ ++ build_core_topology(cs->nr_cores, cpu->core_id, &topo); ++ *eax = cpu->apic_id; ++ /* ++ * CPUID_Fn8000001E_EBX ++ * 31:16 Reserved ++ * 15:8 Threads per core (The number of threads per core is ++ * Threads per core + 1) ++ * 7:0 Core id (see bit decoding below) ++ * SMT: ++ * 4:3 node id ++ * 2 Core complex id ++ * 1:0 Core id ++ * Non SMT: ++ * 5:4 node id ++ * 3 Core complex id ++ * 1:0 Core id ++ */ ++ if (cs->nr_threads - 1) { ++ *ebx = ((cs->nr_threads - 1) << 8) | (topo.node_id << 3) | ++ (topo.ccx_id << 2) | topo.core_id; ++ } else { ++ *ebx = (topo.node_id << 4) | (topo.ccx_id << 3) | topo.core_id; ++ } ++ /* ++ * CPUID_Fn8000001E_ECX ++ * 31:11 Reserved ++ * 10:8 Nodes per processor (Nodes per processor is number of nodes + 1) ++ * 7:0 Node id (see bit decoding below) ++ * 2 Socket id ++ * 1:0 Node id ++ */ ++ *ecx = ((topo.num_nodes - 1) << 8) | (cpu->socket_id << 2) | topo.node_id; ++ *edx = 0; ++} ++ + /* + * Definitions of the hardcoded cache entries we expose: + * These are legacy cache values. If there is a need to change any +@@ -4105,6 +4186,11 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, + break; + } + break; ++ case 0x8000001E: ++ assert(cpu->core_id <= 255); ++ encode_topo_cpuid8000001e(cs, cpu, ++ eax, ebx, ecx, edx); ++ break; + case 0xC0000000: + *eax = env->cpuid_xlevel2; + *ebx = 0; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-Allow-TOPOEXT-to-be-enabled-on-older-kernels.patch b/SOURCES/kvm-i386-Allow-TOPOEXT-to-be-enabled-on-older-kernels.patch new file mode 100644 index 0000000..17b0876 --- /dev/null +++ b/SOURCES/kvm-i386-Allow-TOPOEXT-to-be-enabled-on-older-kernels.patch @@ -0,0 +1,53 @@ +From 499c2933a848699a80edd44308e1c4f7497a8a66 Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Thu, 26 Jul 2018 15:25:35 +0200 +Subject: [PATCH 10/15] i386: Allow TOPOEXT to be enabled on older kernels + +RH-Author: Eduardo Habkost +Message-id: <20180726152535.4493-2-ehabkost@redhat.com> +Patchwork-id: 81513 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/1] i386: Allow TOPOEXT to be enabled on older kernels +Bugzilla: 1608698 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Igor Mammedov + +From: Babu Moger + +Enabling TOPOEXT feature might cause compatibility issues if +older kernels does not set this feature. Lets set this feature +unconditionally. + +Signed-off-by: Babu Moger +Message-Id: <1528939107-17193-2-git-send-email-babu.moger@amd.com> +[ehabkost: rewrite comment and commit message] +Signed-off-by: Eduardo Habkost +(cherry picked from commit f98bbd8304112187cafc3e636c31b2a3865d2717) +Signed-off-by: Eduardo Habkost + +Signed-off-by: Miroslav Rezanina +--- + target/i386/kvm.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/target/i386/kvm.c b/target/i386/kvm.c +index 447215b..00f2141 100644 +--- a/target/i386/kvm.c ++++ b/target/i386/kvm.c +@@ -372,6 +372,13 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, + if (host_tsx_blacklisted()) { + ret &= ~(CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_HLE); + } ++ } else if (function == 0x80000001 && reg == R_ECX) { ++ /* ++ * It's safe to enable TOPOEXT even if it's not returned by ++ * GET_SUPPORTED_CPUID. Unconditionally enabling TOPOEXT here allows ++ * us to keep CPU models including TOPOEXT runnable on older kernels. ++ */ ++ ret |= CPUID_EXT3_TOPOEXT; + } else if (function == 0x80000001 && reg == R_EDX) { + /* On Intel, kvm returns cpuid according to the Intel spec, + * so add missing bits according to the AMD spec: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-Clean-up-cache-CPUID-code.patch b/SOURCES/kvm-i386-Clean-up-cache-CPUID-code.patch new file mode 100644 index 0000000..93e20b1 --- /dev/null +++ b/SOURCES/kvm-i386-Clean-up-cache-CPUID-code.patch @@ -0,0 +1,280 @@ +From 98205e71073c36234048b60feedc1ce142902572 Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Tue, 3 Jul 2018 17:23:51 +0200 +Subject: [PATCH 06/89] i386: Clean up cache CPUID code + +RH-Author: Eduardo Habkost +Message-id: <20180703172356.21038-6-ehabkost@redhat.com> +Patchwork-id: 81215 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v3 05/10] i386: Clean up cache CPUID code +Bugzilla: 1481253 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Igor Mammedov +RH-Acked-by: Miroslav Rezanina + +Always initialize CPUCaches structs with cache information, even +if legacy_cache=true. Use different CPUCaches struct for +CPUID[2], CPUID[4], and the AMD CPUID leaves. + +This will simplify a lot the logic inside cpu_x86_cpuid(). + +Signed-off-by: Eduardo Habkost +Signed-off-by: Babu Moger +Message-Id: <1527176614-26271-2-git-send-email-babu.moger@amd.com> +Signed-off-by: Eduardo Habkost +(cherry picked from commit a9f27ea9adc8c695197bd08f2e938ef7b4183f07) +Signed-off-by: Eduardo Habkost +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 117 +++++++++++++++++++++++++++--------------------------- + target/i386/cpu.h | 14 ++++--- + 2 files changed, 67 insertions(+), 64 deletions(-) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 3426130..41d0b72 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -1113,7 +1113,7 @@ struct X86CPUDefinition { + }; + + static CPUCaches epyc_cache_info = { +- .l1d_cache = { ++ .l1d_cache = &(CPUCacheInfo) { + .type = DCACHE, + .level = 1, + .size = 32 * KiB, +@@ -1125,7 +1125,7 @@ static CPUCaches epyc_cache_info = { + .self_init = 1, + .no_invd_sharing = true, + }, +- .l1i_cache = { ++ .l1i_cache = &(CPUCacheInfo) { + .type = ICACHE, + .level = 1, + .size = 64 * KiB, +@@ -1137,7 +1137,7 @@ static CPUCaches epyc_cache_info = { + .self_init = 1, + .no_invd_sharing = true, + }, +- .l2_cache = { ++ .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 512 * KiB, +@@ -1147,7 +1147,7 @@ static CPUCaches epyc_cache_info = { + .sets = 1024, + .lines_per_tag = 1, + }, +- .l3_cache = { ++ .l3_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 3, + .size = 8 * MiB, +@@ -3325,9 +3325,8 @@ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp) + env->features[w] = def->features[w]; + } + +- /* Store Cache information from the X86CPUDefinition if available */ +- env->cache_info = def->cache_info; +- cpu->legacy_cache = def->cache_info ? 0 : 1; ++ /* legacy-cache defaults to 'off' if CPU model provides cache info */ ++ cpu->legacy_cache = !def->cache_info; + + /* Special cases not set in the X86CPUDefinition structs: */ + /* TODO: in-kernel irqchip for hvf */ +@@ -3678,21 +3677,11 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, + if (!cpu->enable_l3_cache) { + *ecx = 0; + } else { +- if (env->cache_info && !cpu->legacy_cache) { +- *ecx = cpuid2_cache_descriptor(&env->cache_info->l3_cache); +- } else { +- *ecx = cpuid2_cache_descriptor(&legacy_l3_cache); +- } +- } +- if (env->cache_info && !cpu->legacy_cache) { +- *edx = (cpuid2_cache_descriptor(&env->cache_info->l1d_cache) << 16) | +- (cpuid2_cache_descriptor(&env->cache_info->l1i_cache) << 8) | +- (cpuid2_cache_descriptor(&env->cache_info->l2_cache)); +- } else { +- *edx = (cpuid2_cache_descriptor(&legacy_l1d_cache) << 16) | +- (cpuid2_cache_descriptor(&legacy_l1i_cache) << 8) | +- (cpuid2_cache_descriptor(&legacy_l2_cache_cpuid2)); ++ *ecx = cpuid2_cache_descriptor(env->cache_info_cpuid2.l3_cache); + } ++ *edx = (cpuid2_cache_descriptor(env->cache_info_cpuid2.l1d_cache) << 16) | ++ (cpuid2_cache_descriptor(env->cache_info_cpuid2.l1i_cache) << 8) | ++ (cpuid2_cache_descriptor(env->cache_info_cpuid2.l2_cache)); + break; + case 4: + /* cache info: needed for Core compatibility */ +@@ -3705,35 +3694,27 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, + } + } else { + *eax = 0; +- CPUCacheInfo *l1d, *l1i, *l2, *l3; +- if (env->cache_info && !cpu->legacy_cache) { +- l1d = &env->cache_info->l1d_cache; +- l1i = &env->cache_info->l1i_cache; +- l2 = &env->cache_info->l2_cache; +- l3 = &env->cache_info->l3_cache; +- } else { +- l1d = &legacy_l1d_cache; +- l1i = &legacy_l1i_cache; +- l2 = &legacy_l2_cache; +- l3 = &legacy_l3_cache; +- } + switch (count) { + case 0: /* L1 dcache info */ +- encode_cache_cpuid4(l1d, 1, cs->nr_cores, ++ encode_cache_cpuid4(env->cache_info_cpuid4.l1d_cache, ++ 1, cs->nr_cores, + eax, ebx, ecx, edx); + break; + case 1: /* L1 icache info */ +- encode_cache_cpuid4(l1i, 1, cs->nr_cores, ++ encode_cache_cpuid4(env->cache_info_cpuid4.l1i_cache, ++ 1, cs->nr_cores, + eax, ebx, ecx, edx); + break; + case 2: /* L2 cache info */ +- encode_cache_cpuid4(l2, cs->nr_threads, cs->nr_cores, ++ encode_cache_cpuid4(env->cache_info_cpuid4.l2_cache, ++ cs->nr_threads, cs->nr_cores, + eax, ebx, ecx, edx); + break; + case 3: /* L3 cache info */ + pkg_offset = apicid_pkg_offset(cs->nr_cores, cs->nr_threads); + if (cpu->enable_l3_cache) { +- encode_cache_cpuid4(l3, (1 << pkg_offset), cs->nr_cores, ++ encode_cache_cpuid4(env->cache_info_cpuid4.l3_cache, ++ (1 << pkg_offset), cs->nr_cores, + eax, ebx, ecx, edx); + break; + } +@@ -3946,13 +3927,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, + (L1_ITLB_2M_ASSOC << 8) | (L1_ITLB_2M_ENTRIES); + *ebx = (L1_DTLB_4K_ASSOC << 24) | (L1_DTLB_4K_ENTRIES << 16) | \ + (L1_ITLB_4K_ASSOC << 8) | (L1_ITLB_4K_ENTRIES); +- if (env->cache_info && !cpu->legacy_cache) { +- *ecx = encode_cache_cpuid80000005(&env->cache_info->l1d_cache); +- *edx = encode_cache_cpuid80000005(&env->cache_info->l1i_cache); +- } else { +- *ecx = encode_cache_cpuid80000005(&legacy_l1d_cache_amd); +- *edx = encode_cache_cpuid80000005(&legacy_l1i_cache_amd); +- } ++ *ecx = encode_cache_cpuid80000005(env->cache_info_amd.l1d_cache); ++ *edx = encode_cache_cpuid80000005(env->cache_info_amd.l1i_cache); + break; + case 0x80000006: + /* cache info (L2 cache) */ +@@ -3968,17 +3944,10 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, + (L2_DTLB_4K_ENTRIES << 16) | \ + (AMD_ENC_ASSOC(L2_ITLB_4K_ASSOC) << 12) | \ + (L2_ITLB_4K_ENTRIES); +- if (env->cache_info && !cpu->legacy_cache) { +- encode_cache_cpuid80000006(&env->cache_info->l2_cache, +- cpu->enable_l3_cache ? +- &env->cache_info->l3_cache : NULL, +- ecx, edx); +- } else { +- encode_cache_cpuid80000006(&legacy_l2_cache_amd, +- cpu->enable_l3_cache ? +- &legacy_l3_cache : NULL, +- ecx, edx); +- } ++ encode_cache_cpuid80000006(env->cache_info_amd.l2_cache, ++ cpu->enable_l3_cache ? ++ env->cache_info_amd.l3_cache : NULL, ++ ecx, edx); + break; + case 0x80000007: + *eax = 0; +@@ -4675,6 +4644,37 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) + cpu->phys_bits = 32; + } + } ++ ++ /* Cache information initialization */ ++ if (!cpu->legacy_cache) { ++ if (!xcc->cpu_def || !xcc->cpu_def->cache_info) { ++ char *name = x86_cpu_class_get_model_name(xcc); ++ error_setg(errp, ++ "CPU model '%s' doesn't support legacy-cache=off", name); ++ g_free(name); ++ return; ++ } ++ env->cache_info_cpuid2 = env->cache_info_cpuid4 = env->cache_info_amd = ++ *xcc->cpu_def->cache_info; ++ } else { ++ /* Build legacy cache information */ ++ env->cache_info_cpuid2.l1d_cache = &legacy_l1d_cache; ++ env->cache_info_cpuid2.l1i_cache = &legacy_l1i_cache; ++ env->cache_info_cpuid2.l2_cache = &legacy_l2_cache_cpuid2; ++ env->cache_info_cpuid2.l3_cache = &legacy_l3_cache; ++ ++ env->cache_info_cpuid4.l1d_cache = &legacy_l1d_cache; ++ env->cache_info_cpuid4.l1i_cache = &legacy_l1i_cache; ++ env->cache_info_cpuid4.l2_cache = &legacy_l2_cache; ++ env->cache_info_cpuid4.l3_cache = &legacy_l3_cache; ++ ++ env->cache_info_amd.l1d_cache = &legacy_l1d_cache_amd; ++ env->cache_info_amd.l1i_cache = &legacy_l1i_cache_amd; ++ env->cache_info_amd.l2_cache = &legacy_l2_cache_amd; ++ env->cache_info_amd.l3_cache = &legacy_l3_cache; ++ } ++ ++ + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); +@@ -5159,11 +5159,10 @@ static Property x86_cpu_properties[] = { + DEFINE_PROP_BOOL("vmware-cpuid-freq", X86CPU, vmware_cpuid_freq, true), + DEFINE_PROP_BOOL("tcg-cpuid", X86CPU, expose_tcg, true), + /* +- * lecacy_cache defaults to CPU model being chosen. This is set in +- * x86_cpu_load_def based on cache_info which is initialized in +- * builtin_x86_defs ++ * lecacy_cache defaults to true unless the CPU model provides its ++ * own cache information (see x86_cpu_load_def()). + */ +- DEFINE_PROP_BOOL("legacy-cache", X86CPU, legacy_cache, false), ++ DEFINE_PROP_BOOL("legacy-cache", X86CPU, legacy_cache, true), + + /* + * From "Requirements for Implementing the Microsoft +diff --git a/target/i386/cpu.h b/target/i386/cpu.h +index b01b0c1..c80c461 100644 +--- a/target/i386/cpu.h ++++ b/target/i386/cpu.h +@@ -1099,10 +1099,10 @@ typedef struct CPUCacheInfo { + + + typedef struct CPUCaches { +- CPUCacheInfo l1d_cache; +- CPUCacheInfo l1i_cache; +- CPUCacheInfo l2_cache; +- CPUCacheInfo l3_cache; ++ CPUCacheInfo *l1d_cache; ++ CPUCacheInfo *l1i_cache; ++ CPUCacheInfo *l2_cache; ++ CPUCacheInfo *l3_cache; + } CPUCaches; + + typedef struct CPUX86State { +@@ -1291,7 +1291,11 @@ typedef struct CPUX86State { + /* Features that were explicitly enabled/disabled */ + FeatureWordArray user_features; + uint32_t cpuid_model[12]; +- CPUCaches *cache_info; ++ /* Cache information for CPUID. When legacy-cache=on, the cache data ++ * on each CPUID leaf will be different, because we keep compatibility ++ * with old QEMU versions. ++ */ ++ CPUCaches cache_info_cpuid2, cache_info_cpuid4, cache_info_amd; + + /* MTRRs */ + uint64_t mtrr_fixed[11]; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-Define-the-Virt-SSBD-MSR-and-handling-of-it-CVE.patch b/SOURCES/kvm-i386-Define-the-Virt-SSBD-MSR-and-handling-of-it-CVE.patch new file mode 100644 index 0000000..86e1324 --- /dev/null +++ b/SOURCES/kvm-i386-Define-the-Virt-SSBD-MSR-and-handling-of-it-CVE.patch @@ -0,0 +1,164 @@ +From 72fa5081eba5db48161c5edfdc75b5246a5cf455 Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Wed, 13 Jun 2018 18:08:11 +0200 +Subject: [PATCH 04/57] i386: Define the Virt SSBD MSR and handling of it + (CVE-2018-3639) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Eduardo Habkost +Message-id: <20180613180812.28169-2-ehabkost@redhat.com> +Patchwork-id: 80677 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/2] i386: Define the Virt SSBD MSR and handling of it (CVE-2018-3639) +Bugzilla: 1574216 +RH-Acked-by: Igor Mammedov +RH-Acked-by: Miroslav Rezanina +RH-Acked-by: Laurent Vivier + +From: Konrad Rzeszutek Wilk + +"Some AMD processors only support a non-architectural means of enabling +speculative store bypass disable (SSBD). To allow a simplified view of +this to a guest, an architectural definition has been created through a new +CPUID bit, 0x80000008_EBX[25], and a new MSR, 0xc001011f. With this, a +hypervisor can virtualize the existence of this definition and provide an +architectural method for using SSBD to a guest. + +Add the new CPUID feature, the new MSR and update the existing SSBD +support to use this MSR when present." (from x86/speculation: Add virtualized +speculative store bypass disable support in Linux). + +Backport conflicts: + * target-i386/machine.c: trivial conflict with vmstate_xsave section + +Signed-off-by: Konrad Rzeszutek Wilk +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Daniel P. Berrangé +Message-Id: <20180521215424.13520-4-berrange@redhat.com> +Signed-off-by: Eduardo Habkost +(cherry picked from commit cfeea0c021db6234c154dbc723730e81553924ff) +Signed-off-by: Eduardo Habkost +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.h | 2 ++ + target/i386/kvm.c | 16 ++++++++++++++-- + target/i386/machine.c | 20 ++++++++++++++++++++ + 3 files changed, 36 insertions(+), 2 deletions(-) + +diff --git a/target/i386/cpu.h b/target/i386/cpu.h +index 0c7a3d6..9aebe64 100644 +--- a/target/i386/cpu.h ++++ b/target/i386/cpu.h +@@ -351,6 +351,7 @@ typedef enum X86Seg { + #define MSR_IA32_FEATURE_CONTROL 0x0000003a + #define MSR_TSC_ADJUST 0x0000003b + #define MSR_IA32_SPEC_CTRL 0x48 ++#define MSR_VIRT_SSBD 0xc001011f + #define MSR_IA32_TSCDEADLINE 0x6e0 + + #define FEATURE_CONTROL_LOCKED (1<<0) +@@ -1150,6 +1151,7 @@ typedef struct CPUX86State { + uint32_t pkru; + + uint64_t spec_ctrl; ++ uint64_t virt_ssbd; + + /* End of state preserved by INIT (dummy marker). */ + struct {} end_init_save; +diff --git a/target/i386/kvm.c b/target/i386/kvm.c +index 6c49954..19e6aa3 100644 +--- a/target/i386/kvm.c ++++ b/target/i386/kvm.c +@@ -92,6 +92,7 @@ static bool has_msr_hv_stimer; + static bool has_msr_hv_frequencies; + static bool has_msr_xss; + static bool has_msr_spec_ctrl; ++static bool has_msr_virt_ssbd; + static bool has_msr_smi_count; + + static uint32_t has_architectural_pmu_version; +@@ -1218,6 +1219,9 @@ static int kvm_get_supported_msrs(KVMState *s) + case MSR_IA32_SPEC_CTRL: + has_msr_spec_ctrl = true; + break; ++ case MSR_VIRT_SSBD: ++ has_msr_virt_ssbd = true; ++ break; + } + } + } +@@ -1706,6 +1710,10 @@ static int kvm_put_msrs(X86CPU *cpu, int level) + if (has_msr_spec_ctrl) { + kvm_msr_entry_add(cpu, MSR_IA32_SPEC_CTRL, env->spec_ctrl); + } ++ if (has_msr_virt_ssbd) { ++ kvm_msr_entry_add(cpu, MSR_VIRT_SSBD, env->virt_ssbd); ++ } ++ + #ifdef TARGET_X86_64 + if (lm_capable_kernel) { + kvm_msr_entry_add(cpu, MSR_CSTAR, env->cstar); +@@ -2077,8 +2085,9 @@ static int kvm_get_msrs(X86CPU *cpu) + if (has_msr_spec_ctrl) { + kvm_msr_entry_add(cpu, MSR_IA32_SPEC_CTRL, 0); + } +- +- ++ if (has_msr_virt_ssbd) { ++ kvm_msr_entry_add(cpu, MSR_VIRT_SSBD, 0); ++ } + if (!env->tsc_valid) { + kvm_msr_entry_add(cpu, MSR_IA32_TSC, 0); + env->tsc_valid = !runstate_is_running(); +@@ -2444,6 +2453,9 @@ static int kvm_get_msrs(X86CPU *cpu) + case MSR_IA32_SPEC_CTRL: + env->spec_ctrl = msrs[i].data; + break; ++ case MSR_VIRT_SSBD: ++ env->virt_ssbd = msrs[i].data; ++ break; + case MSR_IA32_RTIT_CTL: + env->msr_rtit_ctrl = msrs[i].data; + break; +diff --git a/target/i386/machine.c b/target/i386/machine.c +index f86abe7..9e7256a 100644 +--- a/target/i386/machine.c ++++ b/target/i386/machine.c +@@ -916,6 +916,25 @@ static const VMStateDescription vmstate_xsave ={ + } + }; + ++static bool virt_ssbd_needed(void *opaque) ++{ ++ X86CPU *cpu = opaque; ++ CPUX86State *env = &cpu->env; ++ ++ return env->virt_ssbd != 0; ++} ++ ++static const VMStateDescription vmstate_msr_virt_ssbd = { ++ .name = "cpu/virt_ssbd", ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .needed = virt_ssbd_needed, ++ .fields = (VMStateField[]){ ++ VMSTATE_UINT64(env.virt_ssbd, X86CPU), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ + VMStateDescription vmstate_x86_cpu = { + .name = "cpu", + .version_id = 12, +@@ -1039,6 +1058,7 @@ VMStateDescription vmstate_x86_cpu = { + &vmstate_mcg_ext_ctl, + &vmstate_msr_intel_pt, + &vmstate_xsave, ++ &vmstate_msr_virt_ssbd, + NULL + } + }; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-Disable-TOPOEXT-by-default-on-cpu-host.patch b/SOURCES/kvm-i386-Disable-TOPOEXT-by-default-on-cpu-host.patch new file mode 100644 index 0000000..3352318 --- /dev/null +++ b/SOURCES/kvm-i386-Disable-TOPOEXT-by-default-on-cpu-host.patch @@ -0,0 +1,57 @@ +From ed04f30927017781f0aa6aa5f9bd734320f4a330 Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Tue, 21 Aug 2018 18:53:11 +0200 +Subject: [PATCH 4/5] i386: Disable TOPOEXT by default on "-cpu host" + +RH-Author: Eduardo Habkost +Message-id: <20180821185311.27865-1-ehabkost@redhat.com> +Patchwork-id: 81903 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH] i386: Disable TOPOEXT by default on "-cpu host" +Bugzilla: 1613277 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Igor Mammedov +RH-Acked-by: Miroslav Rezanina + +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1613277 +Brew: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=17980585 + +Enabling TOPOEXT is always allowed, but it can't be enabled +blindly by "-cpu host" because it may make guests crash if the +rest of the cache topology information isn't provided or isn't +consistent. + +This addresses the bug reported at: +https://bugzilla.redhat.com/show_bug.cgi?id=1613277 + +Signed-off-by: Eduardo Habkost +Message-Id: <20180809221852.15285-1-ehabkost@redhat.com> +Tested-by: Richard W.M. Jones +Reviewed-by: Babu Moger +Signed-off-by: Eduardo Habkost +(cherry picked from commit 7210a02c58572b2686a3a8d610c6628f87864aed) +Signed-off-by: Eduardo Habkost +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index eabe4ea..e6ad66c 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -852,6 +852,12 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + }, + .cpuid_eax = 0x80000001, .cpuid_reg = R_ECX, + .tcg_features = TCG_EXT3_FEATURES, ++ /* ++ * TOPOEXT is always allowed but can't be enabled blindly by ++ * "-cpu host", as it requires consistent cache topology info ++ * to be provided so it doesn't confuse guests. ++ */ ++ .no_autoenable_flags = CPUID_EXT3_TOPOEXT, + }, + [FEAT_C000_0001_EDX] = { + .feat_names = { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-Enable-TOPOEXT-feature-on-AMD-EPYC-CPU.patch b/SOURCES/kvm-i386-Enable-TOPOEXT-feature-on-AMD-EPYC-CPU.patch new file mode 100644 index 0000000..ef70673 --- /dev/null +++ b/SOURCES/kvm-i386-Enable-TOPOEXT-feature-on-AMD-EPYC-CPU.patch @@ -0,0 +1,111 @@ +From 2f039646554d29873f39b867cfe80d044f2c56b4 Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Tue, 3 Jul 2018 17:23:55 +0200 +Subject: [PATCH 10/89] i386: Enable TOPOEXT feature on AMD EPYC CPU + +RH-Author: Eduardo Habkost +Message-id: <20180703172356.21038-10-ehabkost@redhat.com> +Patchwork-id: 81219 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v3 09/10] i386: Enable TOPOEXT feature on AMD EPYC CPU +Bugzilla: 1481253 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Igor Mammedov +RH-Acked-by: Miroslav Rezanina + +From: Babu Moger + +Enable TOPOEXT feature on EPYC CPU. This is required to support +hyperthreading on VM guests. Also extend xlevel to 0x8000001E. + +Disable topoext on PC_COMPAT_2_12 and keep xlevel 0x8000000a. + +Signed-off-by: Babu Moger +Message-Id: <1529443919-67509-3-git-send-email-babu.moger@amd.com> +[ehabkost: Added EPYC-IBPB.xlevel to PC_COMPAT_2_12] +Signed-off-by: Eduardo Habkost +(cherry picked from commit e00516475c270dcb6705753da96063f95699abf2) +[ehabkost: moved compat code to PC_RHEL75_COMPAT] +[ehabkost: fixed typo on EPYC-IBPB compat entry] +Signed-off-by: Eduardo Habkost +--- +Changes v2 -> v3: +* Removed duplicate TYPE_X86_CPU.legacy-cache=on entry from + PC_RHEL7_5_COMPAT (caught by Laurent Vivier) + +Changes v1 -> v2: +* Fixed typo on EPYC-IBPB compat entry + +Signed-off-by: Miroslav Rezanina +--- + include/hw/i386/pc.h | 12 ++++++++++++ + target/i386/cpu.c | 10 ++++++---- + 2 files changed, 18 insertions(+), 4 deletions(-) + +diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h +index 3ff55c6..88e5a92 100644 +--- a/include/hw/i386/pc.h ++++ b/include/hw/i386/pc.h +@@ -983,6 +983,18 @@ extern void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id); + .driver = TYPE_X86_CPU,\ + .property = "legacy-cache",\ + .value = "on",\ ++ },{ /* PC_RHEL7_5_COMPAT from PC_COMPAT_2_12 */ \ ++ .driver = TYPE_X86_CPU,\ ++ .property = "topoext",\ ++ .value = "off",\ ++ },{ /* PC_RHEL7_5_COMPAT from PC_COMPAT_2_12 */ \ ++ .driver = "EPYC-" TYPE_X86_CPU,\ ++ .property = "xlevel",\ ++ .value = stringify(0x8000000a),\ ++ },{ /* PC_RHEL7_5_COMPAT from PC_COMPAT_2_12 */ \ ++ .driver = "EPYC-IBPB-" TYPE_X86_CPU,\ ++ .property = "xlevel",\ ++ .value = stringify(0x8000000a),\ + }, + + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index ef43ea0..5d5d7e6 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -2562,7 +2562,8 @@ static X86CPUDefinition builtin_x86_defs[] = { + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_OSVW | CPUID_EXT3_3DNOWPREFETCH | + CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | +- CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM, ++ CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM | ++ CPUID_EXT3_TOPOEXT, + .features[FEAT_7_0_EBX] = + CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 | + CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_RDSEED | +@@ -2577,7 +2578,7 @@ static X86CPUDefinition builtin_x86_defs[] = { + CPUID_XSAVE_XGETBV1, + .features[FEAT_6_EAX] = + CPUID_6_EAX_ARAT, +- .xlevel = 0x8000000A, ++ .xlevel = 0x8000001E, + .model_id = "AMD EPYC Processor", + .cache_info = &epyc_cache_info, + }, +@@ -2607,7 +2608,8 @@ static X86CPUDefinition builtin_x86_defs[] = { + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_OSVW | CPUID_EXT3_3DNOWPREFETCH | + CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | +- CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM, ++ CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM | ++ CPUID_EXT3_TOPOEXT, + .features[FEAT_8000_0008_EBX] = + CPUID_8000_0008_EBX_IBPB, + .features[FEAT_7_0_EBX] = +@@ -2624,7 +2626,7 @@ static X86CPUDefinition builtin_x86_defs[] = { + CPUID_XSAVE_XGETBV1, + .features[FEAT_6_EAX] = + CPUID_6_EAX_ARAT, +- .xlevel = 0x8000000A, ++ .xlevel = 0x8000001E, + .model_id = "AMD EPYC Processor (with IBPB)", + .cache_info = &epyc_cache_info, + }, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-Fix-arch_query_cpu_model_expansion-leak.patch b/SOURCES/kvm-i386-Fix-arch_query_cpu_model_expansion-leak.patch new file mode 100644 index 0000000..238f27e --- /dev/null +++ b/SOURCES/kvm-i386-Fix-arch_query_cpu_model_expansion-leak.patch @@ -0,0 +1,87 @@ +From c8edc3f99b203feb6795d2d727c9b46058bcf3bd Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 31 Aug 2018 14:24:59 +0200 +Subject: [PATCH 08/29] i386: Fix arch_query_cpu_model_expansion() leak + +RH-Author: Markus Armbruster +Message-id: <20180831142459.18567-3-armbru@redhat.com> +Patchwork-id: 81985 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 2/2] i386: Fix arch_query_cpu_model_expansion() leak +Bugzilla: 1624390 +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Miroslav Rezanina +RH-Acked-by: Auger Eric + +From: Eduardo Habkost + +Reported by Coverity: + +Error: RESOURCE_LEAK (CWE-772): [#def439] +qemu-2.12.0/target/i386/cpu.c:3179: alloc_fn: Storage is returned from allocation function "qdict_new". +qemu-2.12.0/qobject/qdict.c:34:5: alloc_fn: Storage is returned from allocation function "g_malloc0". +qemu-2.12.0/qobject/qdict.c:34:5: var_assign: Assigning: "qdict" = "g_malloc0(4120UL)". +qemu-2.12.0/qobject/qdict.c:37:5: return_alloc: Returning allocated memory "qdict". +qemu-2.12.0/target/i386/cpu.c:3179: var_assign: Assigning: "props" = storage returned from "qdict_new()". +qemu-2.12.0/target/i386/cpu.c:3217: leaked_storage: Variable "props" going out of scope leaks the storage it points to. + +This was introduced by commit b8097deb359b ("i386: Improve +query-cpu-model-expansion full mode"). + +The leak is only theoretical: if ret->model->props is set to +props, the qapi_free_CpuModelExpansionInfo() call will free props +too in case of errors. The only way for this to not happen is if +we enter the default branch of the switch statement, which would +never happen because all CpuModelExpansionType values are being +handled. + +It's still worth to change this to make the allocation logic +easier to follow and make the Coverity error go away. To make +everything simpler, initialize ret->model and ret->model->props +earlier in the function. + +While at it, remove redundant check for !prop because prop is +always initialized at the beginning of the function. + +Fixes: b8097deb359bbbd92592b9670adfe9e245b2d0bd +Signed-off-by: Eduardo Habkost +Message-Id: <20180816183509.8231-1-ehabkost@redhat.com> +Signed-off-by: Paolo Bonzini +(cherry picked from commit e38bf612477fca62b205ebd909b1372a7e45a8c0) +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 9 +++------ + 1 file changed, 3 insertions(+), 6 deletions(-) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index e317aaf..6b5acdf 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -3733,6 +3733,9 @@ arch_query_cpu_model_expansion(CpuModelExpansionType type, + } + + props = qdict_new(); ++ ret->model = g_new0(CpuModelInfo, 1); ++ ret->model->props = QOBJECT(props); ++ ret->model->has_props = true; + + switch (type) { + case CPU_MODEL_EXPANSION_TYPE_STATIC: +@@ -3753,15 +3756,9 @@ arch_query_cpu_model_expansion(CpuModelExpansionType type, + goto out; + } + +- if (!props) { +- props = qdict_new(); +- } + x86_cpu_to_dict(xc, props); + +- ret->model = g_new0(CpuModelInfo, 1); + ret->model->name = g_strdup(base_name); +- ret->model->props = QOBJECT(props); +- ret->model->has_props = true; + + out: + object_unref(OBJECT(xc)); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-Fix-up-the-Node-id-for-CPUID_8000_001E.patch b/SOURCES/kvm-i386-Fix-up-the-Node-id-for-CPUID_8000_001E.patch new file mode 100644 index 0000000..03c2003 --- /dev/null +++ b/SOURCES/kvm-i386-Fix-up-the-Node-id-for-CPUID_8000_001E.patch @@ -0,0 +1,88 @@ +From 7d5a7d6926805c069903bef845d43912cf718410 Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Tue, 3 Jul 2018 17:23:54 +0200 +Subject: [PATCH 09/89] i386: Fix up the Node id for CPUID_8000_001E + +RH-Author: Eduardo Habkost +Message-id: <20180703172356.21038-9-ehabkost@redhat.com> +Patchwork-id: 81218 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v3 08/10] i386: Fix up the Node id for CPUID_8000_001E +Bugzilla: 1481253 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Igor Mammedov +RH-Acked-by: Miroslav Rezanina + +From: Babu Moger + +This is part of topoext support. To keep the compatibility, it is better +we support all the combination of nr_cores and nr_threads currently +supported. By allowing more nr_cores and nr_threads, we might end up with +more nodes than we can actually support with the real hardware. We need to +fix up the node id to make this work. We can achieve this by shifting the +socket_id bits left to address more nodes. + +Signed-off-by: Babu Moger +Message-Id: <1529443919-67509-2-git-send-email-babu.moger@amd.com> +Reviewed-by: Eduardo Habkost +Signed-off-by: Eduardo Habkost +(cherry picked from commit 631be32155dbafa1fe886f2488127956c9120ba6) +Signed-off-by: Eduardo Habkost +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 26 +++++++++++++++++++++++++- + 1 file changed, 25 insertions(+), 1 deletion(-) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 643d3b1..ef43ea0 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -19,6 +19,7 @@ + + #include "qemu/osdep.h" + #include "qemu/cutils.h" ++#include "qemu/bitops.h" + + #include "cpu.h" + #include "exec/exec-all.h" +@@ -474,6 +475,8 @@ static void encode_topo_cpuid8000001e(CPUState *cs, X86CPU *cpu, + uint32_t *ecx, uint32_t *edx) + { + struct core_topology topo = {0}; ++ unsigned long nodes; ++ int shift; + + build_core_topology(cs->nr_cores, cpu->core_id, &topo); + *eax = cpu->apic_id; +@@ -506,7 +509,28 @@ static void encode_topo_cpuid8000001e(CPUState *cs, X86CPU *cpu, + * 2 Socket id + * 1:0 Node id + */ +- *ecx = ((topo.num_nodes - 1) << 8) | (cpu->socket_id << 2) | topo.node_id; ++ if (topo.num_nodes <= 4) { ++ *ecx = ((topo.num_nodes - 1) << 8) | (cpu->socket_id << 2) | ++ topo.node_id; ++ } else { ++ /* ++ * Node id fix up. Actual hardware supports up to 4 nodes. But with ++ * more than 32 cores, we may end up with more than 4 nodes. ++ * Node id is a combination of socket id and node id. Only requirement ++ * here is that this number should be unique accross the system. ++ * Shift the socket id to accommodate more nodes. We dont expect both ++ * socket id and node id to be big number at the same time. This is not ++ * an ideal config but we need to to support it. Max nodes we can have ++ * is 32 (255/8) with 8 cores per node and 255 max cores. We only need ++ * 5 bits for nodes. Find the left most set bit to represent the total ++ * number of nodes. find_last_bit returns last set bit(0 based). Left ++ * shift(+1) the socket id to represent all the nodes. ++ */ ++ nodes = topo.num_nodes - 1; ++ shift = find_last_bit(&nodes, 8); ++ *ecx = ((topo.num_nodes - 1) << 8) | (cpu->socket_id << (shift + 1)) | ++ topo.node_id; ++ } + *edx = 0; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-Helpers-to-encode-cache-information-consistentl.patch b/SOURCES/kvm-i386-Helpers-to-encode-cache-information-consistentl.patch new file mode 100644 index 0000000..0a369cf --- /dev/null +++ b/SOURCES/kvm-i386-Helpers-to-encode-cache-information-consistentl.patch @@ -0,0 +1,687 @@ +From 4e90b977aecdc26df8cda57ee38e2a8159685b1f Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Tue, 3 Jul 2018 17:23:47 +0200 +Subject: [PATCH 02/89] i386: Helpers to encode cache information consistently + +RH-Author: Eduardo Habkost +Message-id: <20180703172356.21038-2-ehabkost@redhat.com> +Patchwork-id: 81210 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v3 01/10] i386: Helpers to encode cache information consistently +Bugzilla: 1481253 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Igor Mammedov +RH-Acked-by: Miroslav Rezanina + +Instead of having a collection of macros that need to be used in +complex expressions to build CPUID data, define a CPUCacheInfo +struct that can hold information about a given cache. Helper +functions will take a CPUCacheInfo struct as input to encode +CPUID leaves for a cache. + +This will help us ensure consistency between cache information +CPUID leaves, and make the existing inconsistencies in CPUID info +more visible. + +Signed-off-by: Eduardo Habkost +Signed-off-by: Babu Moger +Tested-by: Geoffrey McRae +Message-Id: <20180510204148.11687-2-babu.moger@amd.com> +Signed-off-by: Eduardo Habkost +(cherry picked from commit 7e3482f824809e1f6ffeb5bb8103ba27a7d1a52a) +Signed-off-by: Eduardo Habkost +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 495 ++++++++++++++++++++++++++++++++++++++++-------------- + target/i386/cpu.h | 53 ++++++ + 2 files changed, 424 insertions(+), 124 deletions(-) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 5d60d76..9a5a164 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -56,33 +56,240 @@ + + #include "disas/capstone.h" + ++/* Helpers for building CPUID[2] descriptors: */ ++ ++struct CPUID2CacheDescriptorInfo { ++ enum CacheType type; ++ int level; ++ int size; ++ int line_size; ++ int associativity; ++}; + +-/* Cache topology CPUID constants: */ ++#define KiB 1024 ++#define MiB (1024 * 1024) + +-/* CPUID Leaf 2 Descriptors */ ++/* ++ * Known CPUID 2 cache descriptors. ++ * From Intel SDM Volume 2A, CPUID instruction ++ */ ++struct CPUID2CacheDescriptorInfo cpuid2_cache_descriptors[] = { ++ [0x06] = { .level = 1, .type = ICACHE, .size = 8 * KiB, ++ .associativity = 4, .line_size = 32, }, ++ [0x08] = { .level = 1, .type = ICACHE, .size = 16 * KiB, ++ .associativity = 4, .line_size = 32, }, ++ [0x09] = { .level = 1, .type = ICACHE, .size = 32 * KiB, ++ .associativity = 4, .line_size = 64, }, ++ [0x0A] = { .level = 1, .type = DCACHE, .size = 8 * KiB, ++ .associativity = 2, .line_size = 32, }, ++ [0x0C] = { .level = 1, .type = DCACHE, .size = 16 * KiB, ++ .associativity = 4, .line_size = 32, }, ++ [0x0D] = { .level = 1, .type = DCACHE, .size = 16 * KiB, ++ .associativity = 4, .line_size = 64, }, ++ [0x0E] = { .level = 1, .type = DCACHE, .size = 24 * KiB, ++ .associativity = 6, .line_size = 64, }, ++ [0x1D] = { .level = 2, .type = UNIFIED_CACHE, .size = 128 * KiB, ++ .associativity = 2, .line_size = 64, }, ++ [0x21] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB, ++ .associativity = 8, .line_size = 64, }, ++ /* lines per sector is not supported cpuid2_cache_descriptor(), ++ * so descriptors 0x22, 0x23 are not included ++ */ ++ [0x24] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, ++ .associativity = 16, .line_size = 64, }, ++ /* lines per sector is not supported cpuid2_cache_descriptor(), ++ * so descriptors 0x25, 0x20 are not included ++ */ ++ [0x2C] = { .level = 1, .type = DCACHE, .size = 32 * KiB, ++ .associativity = 8, .line_size = 64, }, ++ [0x30] = { .level = 1, .type = ICACHE, .size = 32 * KiB, ++ .associativity = 8, .line_size = 64, }, ++ [0x41] = { .level = 2, .type = UNIFIED_CACHE, .size = 128 * KiB, ++ .associativity = 4, .line_size = 32, }, ++ [0x42] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB, ++ .associativity = 4, .line_size = 32, }, ++ [0x43] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, ++ .associativity = 4, .line_size = 32, }, ++ [0x44] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, ++ .associativity = 4, .line_size = 32, }, ++ [0x45] = { .level = 2, .type = UNIFIED_CACHE, .size = 2 * MiB, ++ .associativity = 4, .line_size = 32, }, ++ [0x46] = { .level = 3, .type = UNIFIED_CACHE, .size = 4 * MiB, ++ .associativity = 4, .line_size = 64, }, ++ [0x47] = { .level = 3, .type = UNIFIED_CACHE, .size = 8 * MiB, ++ .associativity = 8, .line_size = 64, }, ++ [0x48] = { .level = 2, .type = UNIFIED_CACHE, .size = 3 * MiB, ++ .associativity = 12, .line_size = 64, }, ++ /* Descriptor 0x49 depends on CPU family/model, so it is not included */ ++ [0x4A] = { .level = 3, .type = UNIFIED_CACHE, .size = 6 * MiB, ++ .associativity = 12, .line_size = 64, }, ++ [0x4B] = { .level = 3, .type = UNIFIED_CACHE, .size = 8 * MiB, ++ .associativity = 16, .line_size = 64, }, ++ [0x4C] = { .level = 3, .type = UNIFIED_CACHE, .size = 12 * MiB, ++ .associativity = 12, .line_size = 64, }, ++ [0x4D] = { .level = 3, .type = UNIFIED_CACHE, .size = 16 * MiB, ++ .associativity = 16, .line_size = 64, }, ++ [0x4E] = { .level = 2, .type = UNIFIED_CACHE, .size = 6 * MiB, ++ .associativity = 24, .line_size = 64, }, ++ [0x60] = { .level = 1, .type = DCACHE, .size = 16 * KiB, ++ .associativity = 8, .line_size = 64, }, ++ [0x66] = { .level = 1, .type = DCACHE, .size = 8 * KiB, ++ .associativity = 4, .line_size = 64, }, ++ [0x67] = { .level = 1, .type = DCACHE, .size = 16 * KiB, ++ .associativity = 4, .line_size = 64, }, ++ [0x68] = { .level = 1, .type = DCACHE, .size = 32 * KiB, ++ .associativity = 4, .line_size = 64, }, ++ [0x78] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, ++ .associativity = 4, .line_size = 64, }, ++ /* lines per sector is not supported cpuid2_cache_descriptor(), ++ * so descriptors 0x79, 0x7A, 0x7B, 0x7C are not included. ++ */ ++ [0x7D] = { .level = 2, .type = UNIFIED_CACHE, .size = 2 * MiB, ++ .associativity = 8, .line_size = 64, }, ++ [0x7F] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, ++ .associativity = 2, .line_size = 64, }, ++ [0x80] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, ++ .associativity = 8, .line_size = 64, }, ++ [0x82] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB, ++ .associativity = 8, .line_size = 32, }, ++ [0x83] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, ++ .associativity = 8, .line_size = 32, }, ++ [0x84] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, ++ .associativity = 8, .line_size = 32, }, ++ [0x85] = { .level = 2, .type = UNIFIED_CACHE, .size = 2 * MiB, ++ .associativity = 8, .line_size = 32, }, ++ [0x86] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, ++ .associativity = 4, .line_size = 64, }, ++ [0x87] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, ++ .associativity = 8, .line_size = 64, }, ++ [0xD0] = { .level = 3, .type = UNIFIED_CACHE, .size = 512 * KiB, ++ .associativity = 4, .line_size = 64, }, ++ [0xD1] = { .level = 3, .type = UNIFIED_CACHE, .size = 1 * MiB, ++ .associativity = 4, .line_size = 64, }, ++ [0xD2] = { .level = 3, .type = UNIFIED_CACHE, .size = 2 * MiB, ++ .associativity = 4, .line_size = 64, }, ++ [0xD6] = { .level = 3, .type = UNIFIED_CACHE, .size = 1 * MiB, ++ .associativity = 8, .line_size = 64, }, ++ [0xD7] = { .level = 3, .type = UNIFIED_CACHE, .size = 2 * MiB, ++ .associativity = 8, .line_size = 64, }, ++ [0xD8] = { .level = 3, .type = UNIFIED_CACHE, .size = 4 * MiB, ++ .associativity = 8, .line_size = 64, }, ++ [0xDC] = { .level = 3, .type = UNIFIED_CACHE, .size = 1.5 * MiB, ++ .associativity = 12, .line_size = 64, }, ++ [0xDD] = { .level = 3, .type = UNIFIED_CACHE, .size = 3 * MiB, ++ .associativity = 12, .line_size = 64, }, ++ [0xDE] = { .level = 3, .type = UNIFIED_CACHE, .size = 6 * MiB, ++ .associativity = 12, .line_size = 64, }, ++ [0xE2] = { .level = 3, .type = UNIFIED_CACHE, .size = 2 * MiB, ++ .associativity = 16, .line_size = 64, }, ++ [0xE3] = { .level = 3, .type = UNIFIED_CACHE, .size = 4 * MiB, ++ .associativity = 16, .line_size = 64, }, ++ [0xE4] = { .level = 3, .type = UNIFIED_CACHE, .size = 8 * MiB, ++ .associativity = 16, .line_size = 64, }, ++ [0xEA] = { .level = 3, .type = UNIFIED_CACHE, .size = 12 * MiB, ++ .associativity = 24, .line_size = 64, }, ++ [0xEB] = { .level = 3, .type = UNIFIED_CACHE, .size = 18 * MiB, ++ .associativity = 24, .line_size = 64, }, ++ [0xEC] = { .level = 3, .type = UNIFIED_CACHE, .size = 24 * MiB, ++ .associativity = 24, .line_size = 64, }, ++}; ++ ++/* ++ * "CPUID leaf 2 does not report cache descriptor information, ++ * use CPUID leaf 4 to query cache parameters" ++ */ ++#define CACHE_DESCRIPTOR_UNAVAILABLE 0xFF + +-#define CPUID_2_L1D_32KB_8WAY_64B 0x2c +-#define CPUID_2_L1I_32KB_8WAY_64B 0x30 +-#define CPUID_2_L2_2MB_8WAY_64B 0x7d +-#define CPUID_2_L3_16MB_16WAY_64B 0x4d ++/* ++ * Return a CPUID 2 cache descriptor for a given cache. ++ * If no known descriptor is found, return CACHE_DESCRIPTOR_UNAVAILABLE ++ */ ++static uint8_t cpuid2_cache_descriptor(CPUCacheInfo *cache) ++{ ++ int i; ++ ++ assert(cache->size > 0); ++ assert(cache->level > 0); ++ assert(cache->line_size > 0); ++ assert(cache->associativity > 0); ++ for (i = 0; i < ARRAY_SIZE(cpuid2_cache_descriptors); i++) { ++ struct CPUID2CacheDescriptorInfo *d = &cpuid2_cache_descriptors[i]; ++ if (d->level == cache->level && d->type == cache->type && ++ d->size == cache->size && d->line_size == cache->line_size && ++ d->associativity == cache->associativity) { ++ return i; ++ } ++ } + ++ return CACHE_DESCRIPTOR_UNAVAILABLE; ++} + + /* CPUID Leaf 4 constants: */ + + /* EAX: */ +-#define CPUID_4_TYPE_DCACHE 1 +-#define CPUID_4_TYPE_ICACHE 2 +-#define CPUID_4_TYPE_UNIFIED 3 ++#define CACHE_TYPE_D 1 ++#define CACHE_TYPE_I 2 ++#define CACHE_TYPE_UNIFIED 3 + +-#define CPUID_4_LEVEL(l) ((l) << 5) ++#define CACHE_LEVEL(l) (l << 5) + +-#define CPUID_4_SELF_INIT_LEVEL (1 << 8) +-#define CPUID_4_FULLY_ASSOC (1 << 9) ++#define CACHE_SELF_INIT_LEVEL (1 << 8) + + /* EDX: */ +-#define CPUID_4_NO_INVD_SHARING (1 << 0) +-#define CPUID_4_INCLUSIVE (1 << 1) +-#define CPUID_4_COMPLEX_IDX (1 << 2) ++#define CACHE_NO_INVD_SHARING (1 << 0) ++#define CACHE_INCLUSIVE (1 << 1) ++#define CACHE_COMPLEX_IDX (1 << 2) ++ ++/* Encode CacheType for CPUID[4].EAX */ ++#define CACHE_TYPE(t) (((t) == DCACHE) ? CACHE_TYPE_D : \ ++ ((t) == ICACHE) ? CACHE_TYPE_I : \ ++ ((t) == UNIFIED_CACHE) ? CACHE_TYPE_UNIFIED : \ ++ 0 /* Invalid value */) ++ ++ ++/* Encode cache info for CPUID[4] */ ++static void encode_cache_cpuid4(CPUCacheInfo *cache, ++ int num_apic_ids, int num_cores, ++ uint32_t *eax, uint32_t *ebx, ++ uint32_t *ecx, uint32_t *edx) ++{ ++ assert(cache->size == cache->line_size * cache->associativity * ++ cache->partitions * cache->sets); ++ ++ assert(num_apic_ids > 0); ++ *eax = CACHE_TYPE(cache->type) | ++ CACHE_LEVEL(cache->level) | ++ (cache->self_init ? CACHE_SELF_INIT_LEVEL : 0) | ++ ((num_cores - 1) << 26) | ++ ((num_apic_ids - 1) << 14); ++ ++ assert(cache->line_size > 0); ++ assert(cache->partitions > 0); ++ assert(cache->associativity > 0); ++ /* We don't implement fully-associative caches */ ++ assert(cache->associativity < cache->sets); ++ *ebx = (cache->line_size - 1) | ++ ((cache->partitions - 1) << 12) | ++ ((cache->associativity - 1) << 22); ++ ++ assert(cache->sets > 0); ++ *ecx = cache->sets - 1; ++ ++ *edx = (cache->no_invd_sharing ? CACHE_NO_INVD_SHARING : 0) | ++ (cache->inclusive ? CACHE_INCLUSIVE : 0) | ++ (cache->complex_indexing ? CACHE_COMPLEX_IDX : 0); ++} ++ ++/* Encode cache info for CPUID[0x80000005].ECX or CPUID[0x80000005].EDX */ ++static uint32_t encode_cache_cpuid80000005(CPUCacheInfo *cache) ++{ ++ assert(cache->size % 1024 == 0); ++ assert(cache->lines_per_tag > 0); ++ assert(cache->associativity > 0); ++ assert(cache->line_size > 0); ++ return ((cache->size / 1024) << 24) | (cache->associativity << 16) | ++ (cache->lines_per_tag << 8) | (cache->line_size); ++} + + #define ASSOC_FULL 0xFF + +@@ -100,57 +307,140 @@ + a == ASSOC_FULL ? 0xF : \ + 0 /* invalid value */) + ++/* ++ * Encode cache info for CPUID[0x80000006].ECX and CPUID[0x80000006].EDX ++ * @l3 can be NULL. ++ */ ++static void encode_cache_cpuid80000006(CPUCacheInfo *l2, ++ CPUCacheInfo *l3, ++ uint32_t *ecx, uint32_t *edx) ++{ ++ assert(l2->size % 1024 == 0); ++ assert(l2->associativity > 0); ++ assert(l2->lines_per_tag > 0); ++ assert(l2->line_size > 0); ++ *ecx = ((l2->size / 1024) << 16) | ++ (AMD_ENC_ASSOC(l2->associativity) << 12) | ++ (l2->lines_per_tag << 8) | (l2->line_size); ++ ++ if (l3) { ++ assert(l3->size % (512 * 1024) == 0); ++ assert(l3->associativity > 0); ++ assert(l3->lines_per_tag > 0); ++ assert(l3->line_size > 0); ++ *edx = ((l3->size / (512 * 1024)) << 18) | ++ (AMD_ENC_ASSOC(l3->associativity) << 12) | ++ (l3->lines_per_tag << 8) | (l3->line_size); ++ } else { ++ *edx = 0; ++ } ++} + + /* Definitions of the hardcoded cache entries we expose: */ + + /* L1 data cache: */ +-#define L1D_LINE_SIZE 64 +-#define L1D_ASSOCIATIVITY 8 +-#define L1D_SETS 64 +-#define L1D_PARTITIONS 1 +-/* Size = LINE_SIZE*ASSOCIATIVITY*SETS*PARTITIONS = 32KiB */ +-#define L1D_DESCRIPTOR CPUID_2_L1D_32KB_8WAY_64B ++static CPUCacheInfo l1d_cache = { ++ .type = DCACHE, ++ .level = 1, ++ .size = 32 * KiB, ++ .self_init = 1, ++ .line_size = 64, ++ .associativity = 8, ++ .sets = 64, ++ .partitions = 1, ++ .no_invd_sharing = true, ++}; ++ + /*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ +-#define L1D_LINES_PER_TAG 1 +-#define L1D_SIZE_KB_AMD 64 +-#define L1D_ASSOCIATIVITY_AMD 2 ++static CPUCacheInfo l1d_cache_amd = { ++ .type = DCACHE, ++ .level = 1, ++ .size = 64 * KiB, ++ .self_init = 1, ++ .line_size = 64, ++ .associativity = 2, ++ .sets = 512, ++ .partitions = 1, ++ .lines_per_tag = 1, ++ .no_invd_sharing = true, ++}; + + /* L1 instruction cache: */ +-#define L1I_LINE_SIZE 64 +-#define L1I_ASSOCIATIVITY 8 +-#define L1I_SETS 64 +-#define L1I_PARTITIONS 1 +-/* Size = LINE_SIZE*ASSOCIATIVITY*SETS*PARTITIONS = 32KiB */ +-#define L1I_DESCRIPTOR CPUID_2_L1I_32KB_8WAY_64B ++static CPUCacheInfo l1i_cache = { ++ .type = ICACHE, ++ .level = 1, ++ .size = 32 * KiB, ++ .self_init = 1, ++ .line_size = 64, ++ .associativity = 8, ++ .sets = 64, ++ .partitions = 1, ++ .no_invd_sharing = true, ++}; ++ + /*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ +-#define L1I_LINES_PER_TAG 1 +-#define L1I_SIZE_KB_AMD 64 +-#define L1I_ASSOCIATIVITY_AMD 2 ++static CPUCacheInfo l1i_cache_amd = { ++ .type = ICACHE, ++ .level = 1, ++ .size = 64 * KiB, ++ .self_init = 1, ++ .line_size = 64, ++ .associativity = 2, ++ .sets = 512, ++ .partitions = 1, ++ .lines_per_tag = 1, ++ .no_invd_sharing = true, ++}; + + /* Level 2 unified cache: */ +-#define L2_LINE_SIZE 64 +-#define L2_ASSOCIATIVITY 16 +-#define L2_SETS 4096 +-#define L2_PARTITIONS 1 +-/* Size = LINE_SIZE*ASSOCIATIVITY*SETS*PARTITIONS = 4MiB */ ++static CPUCacheInfo l2_cache = { ++ .type = UNIFIED_CACHE, ++ .level = 2, ++ .size = 4 * MiB, ++ .self_init = 1, ++ .line_size = 64, ++ .associativity = 16, ++ .sets = 4096, ++ .partitions = 1, ++ .no_invd_sharing = true, ++}; ++ + /*FIXME: CPUID leaf 2 descriptor is inconsistent with CPUID leaf 4 */ +-#define L2_DESCRIPTOR CPUID_2_L2_2MB_8WAY_64B ++static CPUCacheInfo l2_cache_cpuid2 = { ++ .type = UNIFIED_CACHE, ++ .level = 2, ++ .size = 2 * MiB, ++ .line_size = 64, ++ .associativity = 8, ++}; ++ ++ + /*FIXME: CPUID leaf 0x80000006 is inconsistent with leaves 2 & 4 */ +-#define L2_LINES_PER_TAG 1 +-#define L2_SIZE_KB_AMD 512 ++static CPUCacheInfo l2_cache_amd = { ++ .type = UNIFIED_CACHE, ++ .level = 2, ++ .size = 512 * KiB, ++ .line_size = 64, ++ .lines_per_tag = 1, ++ .associativity = 16, ++ .sets = 512, ++ .partitions = 1, ++}; + + /* Level 3 unified cache: */ +-#define L3_SIZE_KB 0 /* disabled */ +-#define L3_ASSOCIATIVITY 0 /* disabled */ +-#define L3_LINES_PER_TAG 0 /* disabled */ +-#define L3_LINE_SIZE 0 /* disabled */ +-#define L3_N_LINE_SIZE 64 +-#define L3_N_ASSOCIATIVITY 16 +-#define L3_N_SETS 16384 +-#define L3_N_PARTITIONS 1 +-#define L3_N_DESCRIPTOR CPUID_2_L3_16MB_16WAY_64B +-#define L3_N_LINES_PER_TAG 1 +-#define L3_N_SIZE_KB_AMD 16384 ++static CPUCacheInfo l3_cache = { ++ .type = UNIFIED_CACHE, ++ .level = 3, ++ .size = 16 * MiB, ++ .line_size = 64, ++ .associativity = 16, ++ .sets = 16384, ++ .partitions = 1, ++ .lines_per_tag = 1, ++ .self_init = true, ++ .inclusive = true, ++ .complex_indexing = true, ++}; + + /* TLB definitions: */ + +@@ -3327,85 +3617,53 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, + if (!cpu->enable_l3_cache) { + *ecx = 0; + } else { +- *ecx = L3_N_DESCRIPTOR; ++ *ecx = cpuid2_cache_descriptor(&l3_cache); + } +- *edx = (L1D_DESCRIPTOR << 16) | \ +- (L1I_DESCRIPTOR << 8) | \ +- (L2_DESCRIPTOR); ++ *edx = (cpuid2_cache_descriptor(&l1d_cache) << 16) | ++ (cpuid2_cache_descriptor(&l1i_cache) << 8) | ++ (cpuid2_cache_descriptor(&l2_cache_cpuid2)); + break; + case 4: + /* cache info: needed for Core compatibility */ + if (cpu->cache_info_passthrough) { + host_cpuid(index, count, eax, ebx, ecx, edx); ++ /* QEMU gives out its own APIC IDs, never pass down bits 31..26. */ + *eax &= ~0xFC000000; ++ if ((*eax & 31) && cs->nr_cores > 1) { ++ *eax |= (cs->nr_cores - 1) << 26; ++ } + } else { + *eax = 0; + switch (count) { + case 0: /* L1 dcache info */ +- *eax |= CPUID_4_TYPE_DCACHE | \ +- CPUID_4_LEVEL(1) | \ +- CPUID_4_SELF_INIT_LEVEL; +- *ebx = (L1D_LINE_SIZE - 1) | \ +- ((L1D_PARTITIONS - 1) << 12) | \ +- ((L1D_ASSOCIATIVITY - 1) << 22); +- *ecx = L1D_SETS - 1; +- *edx = CPUID_4_NO_INVD_SHARING; ++ encode_cache_cpuid4(&l1d_cache, ++ 1, cs->nr_cores, ++ eax, ebx, ecx, edx); + break; + case 1: /* L1 icache info */ +- *eax |= CPUID_4_TYPE_ICACHE | \ +- CPUID_4_LEVEL(1) | \ +- CPUID_4_SELF_INIT_LEVEL; +- *ebx = (L1I_LINE_SIZE - 1) | \ +- ((L1I_PARTITIONS - 1) << 12) | \ +- ((L1I_ASSOCIATIVITY - 1) << 22); +- *ecx = L1I_SETS - 1; +- *edx = CPUID_4_NO_INVD_SHARING; ++ encode_cache_cpuid4(&l1i_cache, ++ 1, cs->nr_cores, ++ eax, ebx, ecx, edx); + break; + case 2: /* L2 cache info */ +- *eax |= CPUID_4_TYPE_UNIFIED | \ +- CPUID_4_LEVEL(2) | \ +- CPUID_4_SELF_INIT_LEVEL; +- if (cs->nr_threads > 1) { +- *eax |= (cs->nr_threads - 1) << 14; +- } +- *ebx = (L2_LINE_SIZE - 1) | \ +- ((L2_PARTITIONS - 1) << 12) | \ +- ((L2_ASSOCIATIVITY - 1) << 22); +- *ecx = L2_SETS - 1; +- *edx = CPUID_4_NO_INVD_SHARING; ++ encode_cache_cpuid4(&l2_cache, ++ cs->nr_threads, cs->nr_cores, ++ eax, ebx, ecx, edx); + break; + case 3: /* L3 cache info */ +- if (!cpu->enable_l3_cache) { +- *eax = 0; +- *ebx = 0; +- *ecx = 0; +- *edx = 0; ++ pkg_offset = apicid_pkg_offset(cs->nr_cores, cs->nr_threads); ++ if (cpu->enable_l3_cache) { ++ encode_cache_cpuid4(&l3_cache, ++ (1 << pkg_offset), cs->nr_cores, ++ eax, ebx, ecx, edx); + break; + } +- *eax |= CPUID_4_TYPE_UNIFIED | \ +- CPUID_4_LEVEL(3) | \ +- CPUID_4_SELF_INIT_LEVEL; +- pkg_offset = apicid_pkg_offset(cs->nr_cores, cs->nr_threads); +- *eax |= ((1 << pkg_offset) - 1) << 14; +- *ebx = (L3_N_LINE_SIZE - 1) | \ +- ((L3_N_PARTITIONS - 1) << 12) | \ +- ((L3_N_ASSOCIATIVITY - 1) << 22); +- *ecx = L3_N_SETS - 1; +- *edx = CPUID_4_INCLUSIVE | CPUID_4_COMPLEX_IDX; +- break; ++ /* fall through */ + default: /* end of info */ +- *eax = 0; +- *ebx = 0; +- *ecx = 0; +- *edx = 0; ++ *eax = *ebx = *ecx = *edx = 0; + break; + } + } +- +- /* QEMU gives out its own APIC IDs, never pass down bits 31..26. */ +- if ((*eax & 31) && cs->nr_cores > 1) { +- *eax |= (cs->nr_cores - 1) << 26; +- } + break; + case 5: + /* mwait info: needed for Core compatibility */ +@@ -3609,10 +3867,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, + (L1_ITLB_2M_ASSOC << 8) | (L1_ITLB_2M_ENTRIES); + *ebx = (L1_DTLB_4K_ASSOC << 24) | (L1_DTLB_4K_ENTRIES << 16) | \ + (L1_ITLB_4K_ASSOC << 8) | (L1_ITLB_4K_ENTRIES); +- *ecx = (L1D_SIZE_KB_AMD << 24) | (L1D_ASSOCIATIVITY_AMD << 16) | \ +- (L1D_LINES_PER_TAG << 8) | (L1D_LINE_SIZE); +- *edx = (L1I_SIZE_KB_AMD << 24) | (L1I_ASSOCIATIVITY_AMD << 16) | \ +- (L1I_LINES_PER_TAG << 8) | (L1I_LINE_SIZE); ++ *ecx = encode_cache_cpuid80000005(&l1d_cache_amd); ++ *edx = encode_cache_cpuid80000005(&l1i_cache_amd); + break; + case 0x80000006: + /* cache info (L2 cache) */ +@@ -3628,18 +3884,9 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, + (L2_DTLB_4K_ENTRIES << 16) | \ + (AMD_ENC_ASSOC(L2_ITLB_4K_ASSOC) << 12) | \ + (L2_ITLB_4K_ENTRIES); +- *ecx = (L2_SIZE_KB_AMD << 16) | \ +- (AMD_ENC_ASSOC(L2_ASSOCIATIVITY) << 12) | \ +- (L2_LINES_PER_TAG << 8) | (L2_LINE_SIZE); +- if (!cpu->enable_l3_cache) { +- *edx = ((L3_SIZE_KB / 512) << 18) | \ +- (AMD_ENC_ASSOC(L3_ASSOCIATIVITY) << 12) | \ +- (L3_LINES_PER_TAG << 8) | (L3_LINE_SIZE); +- } else { +- *edx = ((L3_N_SIZE_KB_AMD / 512) << 18) | \ +- (AMD_ENC_ASSOC(L3_N_ASSOCIATIVITY) << 12) | \ +- (L3_N_LINES_PER_TAG << 8) | (L3_N_LINE_SIZE); +- } ++ encode_cache_cpuid80000006(&l2_cache_amd, ++ cpu->enable_l3_cache ? &l3_cache : NULL, ++ ecx, edx); + break; + case 0x80000007: + *eax = 0; +diff --git a/target/i386/cpu.h b/target/i386/cpu.h +index 9aebe64..a0e4eeb 100644 +--- a/target/i386/cpu.h ++++ b/target/i386/cpu.h +@@ -1046,6 +1046,59 @@ typedef enum TPRAccess { + TPR_ACCESS_WRITE, + } TPRAccess; + ++/* Cache information data structures: */ ++ ++enum CacheType { ++ DCACHE, ++ ICACHE, ++ UNIFIED_CACHE ++}; ++ ++typedef struct CPUCacheInfo { ++ enum CacheType type; ++ uint8_t level; ++ /* Size in bytes */ ++ uint32_t size; ++ /* Line size, in bytes */ ++ uint16_t line_size; ++ /* ++ * Associativity. ++ * Note: representation of fully-associative caches is not implemented ++ */ ++ uint8_t associativity; ++ /* Physical line partitions. CPUID[0x8000001D].EBX, CPUID[4].EBX */ ++ uint8_t partitions; ++ /* Number of sets. CPUID[0x8000001D].ECX, CPUID[4].ECX */ ++ uint32_t sets; ++ /* ++ * Lines per tag. ++ * AMD-specific: CPUID[0x80000005], CPUID[0x80000006]. ++ * (Is this synonym to @partitions?) ++ */ ++ uint8_t lines_per_tag; ++ ++ /* Self-initializing cache */ ++ bool self_init; ++ /* ++ * WBINVD/INVD is not guaranteed to act upon lower level caches of ++ * non-originating threads sharing this cache. ++ * CPUID[4].EDX[bit 0], CPUID[0x8000001D].EDX[bit 0] ++ */ ++ bool no_invd_sharing; ++ /* ++ * Cache is inclusive of lower cache levels. ++ * CPUID[4].EDX[bit 1], CPUID[0x8000001D].EDX[bit 1]. ++ */ ++ bool inclusive; ++ /* ++ * A complex function is used to index the cache, potentially using all ++ * address bits. CPUID[4].EDX[bit 2]. ++ */ ++ bool complex_indexing; ++} CPUCacheInfo; ++ ++ ++ + typedef struct CPUX86State { + /* standard registers */ + target_ulong regs[CPU_NB_REGS]; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-Initialize-cache-information-for-EPYC-family-pr.patch b/SOURCES/kvm-i386-Initialize-cache-information-for-EPYC-family-pr.patch new file mode 100644 index 0000000..0bbbe0b --- /dev/null +++ b/SOURCES/kvm-i386-Initialize-cache-information-for-EPYC-family-pr.patch @@ -0,0 +1,110 @@ +From 28b619943c16c8015899b0808d844fbcea586487 Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Tue, 3 Jul 2018 17:23:49 +0200 +Subject: [PATCH 04/89] i386: Initialize cache information for EPYC family + processors + +RH-Author: Eduardo Habkost +Message-id: <20180703172356.21038-4-ehabkost@redhat.com> +Patchwork-id: 81214 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v3 03/10] i386: Initialize cache information for EPYC family processors +Bugzilla: 1481253 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Igor Mammedov +RH-Acked-by: Miroslav Rezanina + +From: Babu Moger + +Initialize pre-determined cache information for EPYC processors. + +Signed-off-by: Babu Moger +Tested-by: Geoffrey McRae +Message-Id: <20180510204148.11687-5-babu.moger@amd.com> +Signed-off-by: Eduardo Habkost +(cherry picked from commit fe52acd2a054b97765963a42037f2f886545e30c) +Signed-off-by: Eduardo Habkost +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 52 insertions(+) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 7e81e08..23eb47d 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -1108,6 +1108,56 @@ struct X86CPUDefinition { + CPUCaches *cache_info; + }; + ++static CPUCaches epyc_cache_info = { ++ .l1d_cache = { ++ .type = DCACHE, ++ .level = 1, ++ .size = 32 * KiB, ++ .line_size = 64, ++ .associativity = 8, ++ .partitions = 1, ++ .sets = 64, ++ .lines_per_tag = 1, ++ .self_init = 1, ++ .no_invd_sharing = true, ++ }, ++ .l1i_cache = { ++ .type = ICACHE, ++ .level = 1, ++ .size = 64 * KiB, ++ .line_size = 64, ++ .associativity = 4, ++ .partitions = 1, ++ .sets = 256, ++ .lines_per_tag = 1, ++ .self_init = 1, ++ .no_invd_sharing = true, ++ }, ++ .l2_cache = { ++ .type = UNIFIED_CACHE, ++ .level = 2, ++ .size = 512 * KiB, ++ .line_size = 64, ++ .associativity = 8, ++ .partitions = 1, ++ .sets = 1024, ++ .lines_per_tag = 1, ++ }, ++ .l3_cache = { ++ .type = UNIFIED_CACHE, ++ .level = 3, ++ .size = 8 * MiB, ++ .line_size = 64, ++ .associativity = 16, ++ .partitions = 1, ++ .sets = 8192, ++ .lines_per_tag = 1, ++ .self_init = true, ++ .inclusive = true, ++ .complex_indexing = true, ++ }, ++}; ++ + static X86CPUDefinition builtin_x86_defs[] = { + { + /* qemu64 is the default CPU model for all *-rhel7.* machine-types. +@@ -2327,6 +2377,7 @@ static X86CPUDefinition builtin_x86_defs[] = { + CPUID_6_EAX_ARAT, + .xlevel = 0x8000000A, + .model_id = "AMD EPYC Processor", ++ .cache_info = &epyc_cache_info, + }, + { + .name = "EPYC-IBPB", +@@ -2373,6 +2424,7 @@ static X86CPUDefinition builtin_x86_defs[] = { + CPUID_6_EAX_ARAT, + .xlevel = 0x8000000A, + .model_id = "AMD EPYC Processor (with IBPB)", ++ .cache_info = &epyc_cache_info, + }, + }; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-Make-arch_capabilities-migratable.patch b/SOURCES/kvm-i386-Make-arch_capabilities-migratable.patch new file mode 100644 index 0000000..570d4b2 --- /dev/null +++ b/SOURCES/kvm-i386-Make-arch_capabilities-migratable.patch @@ -0,0 +1,45 @@ +From 94a85e1b51fb7e2f646938330c4a4ebf4ffeb281 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Tue, 4 Jun 2019 21:47:27 +0200 +Subject: [PATCH 08/23] i386: Make arch_capabilities migratable + +RH-Author: plai@redhat.com +Message-id: <1559684847-10889-9-git-send-email-plai@redhat.com> +Patchwork-id: 88530 +O-Subject: [RHEL7.7 qemu-kvm-rhev PATCH v4 8/8] i386: Make arch_capabilities migratable +Bugzilla: 1709972 +RH-Acked-by: Eduardo Habkost +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Miroslav Rezanina + +From: Eduardo Habkost + +Now that kvm_arch_get_supported_cpuid() will only return +arch_capabilities if QEMU is able to initialize the MSR properly, +we know that the feature is safely migratable. + +Signed-off-by: Eduardo Habkost +Message-Id: <20190125220606.4864-3-ehabkost@redhat.com> +Signed-off-by: Eduardo Habkost +(cherry picked from commit 014018e19b3c54dd1bf5072bc912ceffea40abe8) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 478c5a4..1f2f286 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -1053,7 +1053,6 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + .reg = R_EDX, + }, + .tcg_features = TCG_7_0_EDX_FEATURES, +- .unmigratable_flags = CPUID_7_0_EDX_ARCH_CAPABILITIES, + }, + [FEAT_8000_0007_EDX] = { + .type = CPUID_FEATURE_WORD, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-Populate-AMD-Processor-Cache-Information-for-cp.patch b/SOURCES/kvm-i386-Populate-AMD-Processor-Cache-Information-for-cp.patch new file mode 100644 index 0000000..0c3e3fb --- /dev/null +++ b/SOURCES/kvm-i386-Populate-AMD-Processor-Cache-Information-for-cp.patch @@ -0,0 +1,212 @@ +From a24dd4151d27389947c011293d6775327268b1c2 Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Tue, 3 Jul 2018 17:23:52 +0200 +Subject: [PATCH 07/89] i386: Populate AMD Processor Cache Information for + cpuid 0x8000001D + +RH-Author: Eduardo Habkost +Message-id: <20180703172356.21038-7-ehabkost@redhat.com> +Patchwork-id: 81216 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v3 06/10] i386: Populate AMD Processor Cache Information for cpuid 0x8000001D +Bugzilla: 1481253 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Igor Mammedov +RH-Acked-by: Miroslav Rezanina + +From: Babu Moger + +Add information for cpuid 0x8000001D leaf. Populate cache topology information +for different cache types (Data Cache, Instruction Cache, L2 and L3) supported +by 0x8000001D leaf. Please refer to the Processor Programming Reference (PPR) +for AMD Family 17h Model for more details. + +Signed-off-by: Babu Moger +Message-Id: <1527176614-26271-3-git-send-email-babu.moger@amd.com> +Reviewed-by: Eduardo Habkost +Signed-off-by: Eduardo Habkost +(cherry picked from commit 8f4202fb1080f86958782b1fca0bf0279f67d136) +Signed-off-by: Eduardo Habkost +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + target/i386/kvm.c | 29 ++++++++++++-- + 2 files changed, 143 insertions(+), 3 deletions(-) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 41d0b72..57f74c6 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -337,6 +337,99 @@ static void encode_cache_cpuid80000006(CPUCacheInfo *l2, + } + + /* ++ * Definitions used for building CPUID Leaf 0x8000001D and 0x8000001E ++ * Please refer to the AMD64 Architecture Programmer’s Manual Volume 3. ++ * Define the constants to build the cpu topology. Right now, TOPOEXT ++ * feature is enabled only on EPYC. So, these constants are based on ++ * EPYC supported configurations. We may need to handle the cases if ++ * these values change in future. ++ */ ++/* Maximum core complexes in a node */ ++#define MAX_CCX 2 ++/* Maximum cores in a core complex */ ++#define MAX_CORES_IN_CCX 4 ++/* Maximum cores in a node */ ++#define MAX_CORES_IN_NODE 8 ++/* Maximum nodes in a socket */ ++#define MAX_NODES_PER_SOCKET 4 ++ ++/* ++ * Figure out the number of nodes required to build this config. ++ * Max cores in a node is 8 ++ */ ++static int nodes_in_socket(int nr_cores) ++{ ++ int nodes; ++ ++ nodes = DIV_ROUND_UP(nr_cores, MAX_CORES_IN_NODE); ++ ++ /* Hardware does not support config with 3 nodes, return 4 in that case */ ++ return (nodes == 3) ? 4 : nodes; ++} ++ ++/* ++ * Decide the number of cores in a core complex with the given nr_cores using ++ * following set constants MAX_CCX, MAX_CORES_IN_CCX, MAX_CORES_IN_NODE and ++ * MAX_NODES_PER_SOCKET. Maintain symmetry as much as possible ++ * L3 cache is shared across all cores in a core complex. So, this will also ++ * tell us how many cores are sharing the L3 cache. ++ */ ++static int cores_in_core_complex(int nr_cores) ++{ ++ int nodes; ++ ++ /* Check if we can fit all the cores in one core complex */ ++ if (nr_cores <= MAX_CORES_IN_CCX) { ++ return nr_cores; ++ } ++ /* Get the number of nodes required to build this config */ ++ nodes = nodes_in_socket(nr_cores); ++ ++ /* ++ * Divide the cores accros all the core complexes ++ * Return rounded up value ++ */ ++ return DIV_ROUND_UP(nr_cores, nodes * MAX_CCX); ++} ++ ++/* Encode cache info for CPUID[8000001D] */ ++static void encode_cache_cpuid8000001d(CPUCacheInfo *cache, CPUState *cs, ++ uint32_t *eax, uint32_t *ebx, ++ uint32_t *ecx, uint32_t *edx) ++{ ++ uint32_t l3_cores; ++ assert(cache->size == cache->line_size * cache->associativity * ++ cache->partitions * cache->sets); ++ ++ *eax = CACHE_TYPE(cache->type) | CACHE_LEVEL(cache->level) | ++ (cache->self_init ? CACHE_SELF_INIT_LEVEL : 0); ++ ++ /* L3 is shared among multiple cores */ ++ if (cache->level == 3) { ++ l3_cores = cores_in_core_complex(cs->nr_cores); ++ *eax |= ((l3_cores * cs->nr_threads) - 1) << 14; ++ } else { ++ *eax |= ((cs->nr_threads - 1) << 14); ++ } ++ ++ assert(cache->line_size > 0); ++ assert(cache->partitions > 0); ++ assert(cache->associativity > 0); ++ /* We don't implement fully-associative caches */ ++ assert(cache->associativity < cache->sets); ++ *ebx = (cache->line_size - 1) | ++ ((cache->partitions - 1) << 12) | ++ ((cache->associativity - 1) << 22); ++ ++ assert(cache->sets > 0); ++ *ecx = cache->sets - 1; ++ ++ *edx = (cache->no_invd_sharing ? CACHE_NO_INVD_SHARING : 0) | ++ (cache->inclusive ? CACHE_INCLUSIVE : 0) | ++ (cache->complex_indexing ? CACHE_COMPLEX_IDX : 0); ++} ++ ++/* + * Definitions of the hardcoded cache entries we expose: + * These are legacy cache values. If there is a need to change any + * of these values please use builtin_x86_defs +@@ -3988,6 +4081,30 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, + *edx = 0; + } + break; ++ case 0x8000001D: ++ *eax = 0; ++ switch (count) { ++ case 0: /* L1 dcache info */ ++ encode_cache_cpuid8000001d(env->cache_info_amd.l1d_cache, cs, ++ eax, ebx, ecx, edx); ++ break; ++ case 1: /* L1 icache info */ ++ encode_cache_cpuid8000001d(env->cache_info_amd.l1i_cache, cs, ++ eax, ebx, ecx, edx); ++ break; ++ case 2: /* L2 cache info */ ++ encode_cache_cpuid8000001d(env->cache_info_amd.l2_cache, cs, ++ eax, ebx, ecx, edx); ++ break; ++ case 3: /* L3 cache info */ ++ encode_cache_cpuid8000001d(env->cache_info_amd.l3_cache, cs, ++ eax, ebx, ecx, edx); ++ break; ++ default: /* end of info */ ++ *eax = *ebx = *ecx = *edx = 0; ++ break; ++ } ++ break; + case 0xC0000000: + *eax = env->cpuid_xlevel2; + *ebx = 0; +diff --git a/target/i386/kvm.c b/target/i386/kvm.c +index 19e6aa3..447215b 100644 +--- a/target/i386/kvm.c ++++ b/target/i386/kvm.c +@@ -968,9 +968,32 @@ int kvm_arch_init_vcpu(CPUState *cs) + } + c = &cpuid_data.entries[cpuid_i++]; + +- c->function = i; +- c->flags = 0; +- cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); ++ switch (i) { ++ case 0x8000001d: ++ /* Query for all AMD cache information leaves */ ++ for (j = 0; ; j++) { ++ c->function = i; ++ c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; ++ c->index = j; ++ cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); ++ ++ if (c->eax == 0) { ++ break; ++ } ++ if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { ++ fprintf(stderr, "cpuid_data is full, no space for " ++ "cpuid(eax:0x%x,ecx:0x%x)\n", i, j); ++ abort(); ++ } ++ c = &cpuid_data.entries[cpuid_i++]; ++ } ++ break; ++ default: ++ c->function = i; ++ c->flags = 0; ++ cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); ++ break; ++ } + } + + /* Call Centaur's CPUID instructions they are supported. */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-Remove-generic-SMT-thread-check.patch b/SOURCES/kvm-i386-Remove-generic-SMT-thread-check.patch new file mode 100644 index 0000000..1c065c3 --- /dev/null +++ b/SOURCES/kvm-i386-Remove-generic-SMT-thread-check.patch @@ -0,0 +1,69 @@ +From 65165d3130b3f1d85cfb3078f96f9b0b29e8f42e Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Tue, 3 Jul 2018 17:23:56 +0200 +Subject: [PATCH 11/89] i386: Remove generic SMT thread check + +RH-Author: Eduardo Habkost +Message-id: <20180703172356.21038-11-ehabkost@redhat.com> +Patchwork-id: 81220 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v3 10/10] i386: Remove generic SMT thread check +Bugzilla: 1481253 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Igor Mammedov +RH-Acked-by: Miroslav Rezanina + +From: Babu Moger + +Remove generic non-intel check while validating hyperthreading support. +Certain AMD CPUs can support hyperthreading now. + +CPU family with TOPOEXT feature can support hyperthreading now. + +Signed-off-by: Babu Moger +Tested-by: Geoffrey McRae +Reviewed-by: Eduardo Habkost +Message-Id: <1529443919-67509-4-git-send-email-babu.moger@amd.com> +Signed-off-by: Eduardo Habkost +(cherry picked from commit 6b2942f966d5e54c37d305c80f5f98d504c2bc55) +Signed-off-by: Eduardo Habkost +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 17 +++++++++++------ + 1 file changed, 11 insertions(+), 6 deletions(-) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 5d5d7e6..eabe4ea 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -4952,17 +4952,22 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) + + qemu_init_vcpu(cs); + +- /* Only Intel CPUs support hyperthreading. Even though QEMU fixes this +- * issue by adjusting CPUID_0000_0001_EBX and CPUID_8000_0008_ECX +- * based on inputs (sockets,cores,threads), it is still better to gives ++ /* ++ * Most Intel and certain AMD CPUs support hyperthreading. Even though QEMU ++ * fixes this issue by adjusting CPUID_0000_0001_EBX and CPUID_8000_0008_ECX ++ * based on inputs (sockets,cores,threads), it is still better to give + * users a warning. + * + * NOTE: the following code has to follow qemu_init_vcpu(). Otherwise + * cs->nr_threads hasn't be populated yet and the checking is incorrect. + */ +- if (!IS_INTEL_CPU(env) && cs->nr_threads > 1 && !ht_warned) { +- error_report("AMD CPU doesn't support hyperthreading. Please configure" +- " -smp options properly."); ++ if (IS_AMD_CPU(env) && ++ !(env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_TOPOEXT) && ++ cs->nr_threads > 1 && !ht_warned) { ++ error_report("This family of AMD CPU doesn't support " ++ "hyperthreading(%d). Please configure -smp " ++ "options properly or try enabling topoext feature.", ++ cs->nr_threads); + ht_warned = true; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-define-the-AMD-virt-ssbd-CPUID-feature-bit-CVE-.patch b/SOURCES/kvm-i386-define-the-AMD-virt-ssbd-CPUID-feature-bit-CVE-.patch new file mode 100644 index 0000000..e9bbcf9 --- /dev/null +++ b/SOURCES/kvm-i386-define-the-AMD-virt-ssbd-CPUID-feature-bit-CVE-.patch @@ -0,0 +1,54 @@ +From e51d47fda8344f093a28c49de9daea38ba2527dd Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Wed, 13 Jun 2018 18:08:12 +0200 +Subject: [PATCH 05/57] i386: define the AMD 'virt-ssbd' CPUID feature bit + (CVE-2018-3639) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Eduardo Habkost +Message-id: <20180613180812.28169-3-ehabkost@redhat.com> +Patchwork-id: 80676 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 2/2] i386: define the AMD 'virt-ssbd' CPUID feature bit (CVE-2018-3639) +Bugzilla: 1574216 +RH-Acked-by: Igor Mammedov +RH-Acked-by: Miroslav Rezanina +RH-Acked-by: Laurent Vivier + +From: Konrad Rzeszutek Wilk + +AMD Zen expose the Intel equivalant to Speculative Store Bypass Disable +via the 0x80000008_EBX[25] CPUID feature bit. + +This needs to be exposed to guest OS to allow them to protect +against CVE-2018-3639. + +Signed-off-by: Konrad Rzeszutek Wilk +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Daniel P. Berrangé +Message-Id: <20180521215424.13520-3-berrange@redhat.com> +Signed-off-by: Eduardo Habkost +(cherry picked from commit 403503b162ffc33fb64cfefdf7b880acf41772cd) +Signed-off-by: Eduardo Habkost +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 0fc7fb0..5d60d76 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -541,7 +541,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + "ibpb", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, +- NULL, NULL, NULL, NULL, ++ NULL, "virt-ssbd", NULL, NULL, + NULL, NULL, NULL, NULL, + }, + .cpuid_eax = 0x80000008, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-define-the-ssbd-CPUID-feature-bit-CVE-2018-3639.patch b/SOURCES/kvm-i386-define-the-ssbd-CPUID-feature-bit-CVE-2018-3639.patch new file mode 100644 index 0000000..5e2de7d --- /dev/null +++ b/SOURCES/kvm-i386-define-the-ssbd-CPUID-feature-bit-CVE-2018-3639.patch @@ -0,0 +1,65 @@ +From a075f54393d3a00554855b938f3adfd7aaafec76 Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Thu, 30 Aug 2018 14:18:05 +0200 +Subject: [PATCH 06/29] i386: define the 'ssbd' CPUID feature bit + (CVE-2018-3639) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Eduardo Habkost +Message-id: <20180830141805.6667-2-ehabkost@redhat.com> +Patchwork-id: 81977 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/1] i386: define the 'ssbd' CPUID feature bit (CVE-2018-3639) +Bugzilla: 1574216 +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Igor Mammedov +RH-Acked-by: Daniel P. Berrange + +From: Daniel P. Berrangé + +New microcode introduces the "Speculative Store Bypass Disable" +CPUID feature bit. This needs to be exposed to guest OS to allow +them to protect against CVE-2018-3639. + +Signed-off-by: Daniel P. Berrangé +Reviewed-by: Konrad Rzeszutek Wilk +Signed-off-by: Konrad Rzeszutek Wilk +Message-Id: <20180521215424.13520-2-berrange@redhat.com> +Signed-off-by: Eduardo Habkost +(cherry picked from commit d19d1f965904a533998739698020ff4ee8a103da) +Signed-off-by: Eduardo Habkost +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 2 +- + target/i386/cpu.h | 1 + + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index e6ad66c..e317aaf 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -1008,7 +1008,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, "spec-ctrl", NULL, +- NULL, NULL, NULL, NULL, ++ NULL, NULL, NULL, "ssbd", + }, + .cpuid_eax = 7, + .cpuid_needs_ecx = true, .cpuid_ecx = 0, +diff --git a/target/i386/cpu.h b/target/i386/cpu.h +index c80c461..75cf5ed 100644 +--- a/target/i386/cpu.h ++++ b/target/i386/cpu.h +@@ -686,6 +686,7 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS]; + #define CPUID_7_0_EDX_AVX512_4VNNIW (1U << 2) /* AVX512 Neural Network Instructions */ + #define CPUID_7_0_EDX_AVX512_4FMAPS (1U << 3) /* AVX512 Multiply Accumulation Single Precision */ + #define CPUID_7_0_EDX_SPEC_CTRL (1U << 26) /* Speculation Control */ ++#define CPUID_7_0_EDX_SPEC_CTRL_SSBD (1U << 31) /* Speculative Store Bypass Disable */ + + #define KVM_HINTS_DEDICATED (1U << 0) + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-halt-poll-control-MSR-support.patch b/SOURCES/kvm-i386-halt-poll-control-MSR-support.patch new file mode 100644 index 0000000..34230d3 --- /dev/null +++ b/SOURCES/kvm-i386-halt-poll-control-MSR-support.patch @@ -0,0 +1,187 @@ +From ebfeb40743756dfa7087b43ce4e9afe3e85f9710 Mon Sep 17 00:00:00 2001 +From: Marcelo Tosatti +Date: Wed, 28 Aug 2019 01:00:55 +0200 +Subject: [PATCH 1/4] kvm: i386: halt poll control MSR support + +RH-Author: Marcelo Tosatti +Message-id: <20190828010051.GA30252@amt.cnet> +Patchwork-id: 90171 +O-Subject: [RHEL-7.8 qemu-kvm PATCH] kvm: i386: halt poll control MSR support +Bugzilla: 1734502 +RH-Acked-by: Eduardo Habkost +RH-Acked-by: Igor Mammedov +RH-Acked-by: Miroslav Rezanina + +This is the qemu part of the haltpoll driver: allows migrating +the MSR which the guest uses to disable host side polling. + +Add support for halt poll control MSR: save/restore, migration +and new feature name. + +The purpose of this MSR is to allow the guest to disable +host halt poll. + +(cherry picked from commit d645e1328726b38b3c79525eb57842ce29c1df7c) +Signed-off-by: Marcelo Tosatti +Message-Id: <20190603230408.GA7938@amt.cnet> +[Do not enable by default, as pointed out by Mark Kanda. - Paolo] +Signed-off-by: Paolo Bonzini + +Signed-off-by: Miroslav Rezanina +--- + linux-headers/asm-x86/kvm_para.h | 2 ++ + target/i386/cpu.c | 4 +++- + target/i386/cpu.h | 1 + + target/i386/kvm.c | 15 +++++++++++++++ + target/i386/machine.c | 20 ++++++++++++++++++++ + 5 files changed, 41 insertions(+), 1 deletion(-) + +diff --git a/linux-headers/asm-x86/kvm_para.h b/linux-headers/asm-x86/kvm_para.h +index 4c58184..38ab00d 100644 +--- a/linux-headers/asm-x86/kvm_para.h ++++ b/linux-headers/asm-x86/kvm_para.h +@@ -27,6 +27,7 @@ + #define KVM_FEATURE_PV_UNHALT 7 + #define KVM_FEATURE_PV_TLB_FLUSH 9 + #define KVM_FEATURE_ASYNC_PF_VMEXIT 10 ++#define KVM_FEATURE_POLL_CONTROL 12 + + /* The last 8 bits are used to indicate how to interpret the flags field + * in pvclock structure. If no bits are set, all flags are ignored. +@@ -43,6 +44,7 @@ + #define MSR_KVM_ASYNC_PF_EN 0x4b564d02 + #define MSR_KVM_STEAL_TIME 0x4b564d03 + #define MSR_KVM_PV_EOI_EN 0x4b564d04 ++#define MSR_KVM_POLL_CONTROL 0x4b564d05 + + struct kvm_steal_time { + __u64 steal; +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 1f2f286..928e53c 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -903,7 +903,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + "kvmclock", "kvm-nopiodelay", "kvm-mmu", "kvmclock", + "kvm-asyncpf", "kvm-steal-time", "kvm-pv-eoi", "kvm-pv-unhalt", + NULL, "kvm-pv-tlb-flush", NULL, NULL, +- NULL, NULL, NULL, NULL, ++ "kvm-poll-control", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + "kvmclock-stable-bit", NULL, NULL, NULL, +@@ -5342,6 +5342,8 @@ static void x86_cpu_initfn(Object *obj) + object_property_add_alias(obj, "kvm_steal_time", obj, "kvm-steal-time", &error_abort); + object_property_add_alias(obj, "kvm_pv_eoi", obj, "kvm-pv-eoi", &error_abort); + object_property_add_alias(obj, "kvm_pv_unhalt", obj, "kvm-pv-unhalt", &error_abort); ++ object_property_add_alias(obj, "kvm_poll_control", obj, "kvm-poll-control", ++ &error_abort); + object_property_add_alias(obj, "svm_lock", obj, "svm-lock", &error_abort); + object_property_add_alias(obj, "nrip_save", obj, "nrip-save", &error_abort); + object_property_add_alias(obj, "tsc_scale", obj, "tsc-scale", &error_abort); +diff --git a/target/i386/cpu.h b/target/i386/cpu.h +index 8ab313e..095e695 100644 +--- a/target/i386/cpu.h ++++ b/target/i386/cpu.h +@@ -1233,6 +1233,7 @@ typedef struct CPUX86State { + uint64_t steal_time_msr; + uint64_t async_pf_en_msr; + uint64_t pv_eoi_en_msr; ++ uint64_t poll_control_msr; + + /* Partition-wide HV MSRs, will be updated only on the first vcpu */ + uint64_t msr_hv_hypercall; +diff --git a/target/i386/kvm.c b/target/i386/kvm.c +index 7afad93..72901e1 100644 +--- a/target/i386/kvm.c ++++ b/target/i386/kvm.c +@@ -1199,6 +1199,9 @@ void kvm_arch_reset_vcpu(X86CPU *cpu) + env->msr_hv_synic_sint[i] = HV_SINT_MASKED; + } + } ++ ++ /* enabled by default */ ++ env->poll_control_msr = 1; + } + + void kvm_arch_do_init_vcpu(X86CPU *cpu) +@@ -1869,6 +1872,11 @@ static int kvm_put_msrs(X86CPU *cpu, int level) + if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_STEAL_TIME)) { + kvm_msr_entry_add(cpu, MSR_KVM_STEAL_TIME, env->steal_time_msr); + } ++ ++ if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_POLL_CONTROL)) { ++ kvm_msr_entry_add(cpu, MSR_KVM_POLL_CONTROL, env->poll_control_msr); ++ } ++ + if (has_architectural_pmu_version > 0) { + if (has_architectural_pmu_version > 1) { + /* Stop the counter. */ +@@ -2241,6 +2249,9 @@ static int kvm_get_msrs(X86CPU *cpu) + if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_STEAL_TIME)) { + kvm_msr_entry_add(cpu, MSR_KVM_STEAL_TIME, 0); + } ++ if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_POLL_CONTROL)) { ++ kvm_msr_entry_add(cpu, MSR_KVM_POLL_CONTROL, 1); ++ } + if (has_architectural_pmu_version > 0) { + if (has_architectural_pmu_version > 1) { + kvm_msr_entry_add(cpu, MSR_CORE_PERF_FIXED_CTR_CTRL, 0); +@@ -2470,6 +2481,10 @@ static int kvm_get_msrs(X86CPU *cpu) + case MSR_KVM_STEAL_TIME: + env->steal_time_msr = msrs[i].data; + break; ++ case MSR_KVM_POLL_CONTROL: { ++ env->poll_control_msr = msrs[i].data; ++ break; ++ } + case MSR_CORE_PERF_FIXED_CTR_CTRL: + env->msr_fixed_ctr_ctrl = msrs[i].data; + break; +diff --git a/target/i386/machine.c b/target/i386/machine.c +index 9e7256a..52b1eae 100644 +--- a/target/i386/machine.c ++++ b/target/i386/machine.c +@@ -325,6 +325,14 @@ static bool steal_time_msr_needed(void *opaque) + return cpu->env.steal_time_msr != 0; + } + ++/* Poll control MSR enabled by default */ ++static bool poll_control_msr_needed(void *opaque) ++{ ++ X86CPU *cpu = opaque; ++ ++ return cpu->env.poll_control_msr != 1; ++} ++ + static const VMStateDescription vmstate_steal_time_msr = { + .name = "cpu/steal_time_msr", + .version_id = 1, +@@ -358,6 +366,17 @@ static const VMStateDescription vmstate_pv_eoi_msr = { + } + }; + ++static const VMStateDescription vmstate_poll_control_msr = { ++ .name = "cpu/poll_control_msr", ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .needed = poll_control_msr_needed, ++ .fields = (VMStateField[]) { ++ VMSTATE_UINT64(env.poll_control_msr, X86CPU), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ + static bool fpop_ip_dp_needed(void *opaque) + { + X86CPU *cpu = opaque; +@@ -1033,6 +1052,7 @@ VMStateDescription vmstate_x86_cpu = { + &vmstate_async_pf_msr, + &vmstate_pv_eoi_msr, + &vmstate_steal_time_msr, ++ &vmstate_poll_control_msr, + &vmstate_fpop_ip_dp, + &vmstate_msr_tsc_adjust, + &vmstate_msr_tscdeadline, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-i386-kvm-Disable-arch_capabilities-if-MSR-can-t-be-s.patch b/SOURCES/kvm-i386-kvm-Disable-arch_capabilities-if-MSR-can-t-be-s.patch new file mode 100644 index 0000000..fb1256b --- /dev/null +++ b/SOURCES/kvm-i386-kvm-Disable-arch_capabilities-if-MSR-can-t-be-s.patch @@ -0,0 +1,71 @@ +From 38633bfc500ac284a13f7da53ea5aabacb8006e1 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Tue, 4 Jun 2019 21:47:26 +0200 +Subject: [PATCH 07/23] i386: kvm: Disable arch_capabilities if MSR can't be + set + +RH-Author: plai@redhat.com +Message-id: <1559684847-10889-8-git-send-email-plai@redhat.com> +Patchwork-id: 88536 +O-Subject: [RHEL7.7 qemu-kvm-rhev PATCH v4 7/8] i386: kvm: Disable arch_capabilities if MSR can't be set +Bugzilla: 1709972 +RH-Acked-by: Eduardo Habkost +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Miroslav Rezanina + +From: Eduardo Habkost + +KVM has two bugs in the handling of MSR_IA32_ARCH_CAPABILITIES: + +1) Linux commit commit 1eaafe91a0df ("kvm: x86: IA32_ARCH_CAPABILITIES + is always supported") makes GET_SUPPORTED_CPUID return + arch_capabilities even if running on SVM. This makes "-cpu + host,migratable=off" incorrectly expose arch_capabilities on CPUID on + AMD hosts (where the MSR is not emulated by KVM). + +2) KVM_GET_MSR_INDEX_LIST does not return MSR_IA32_ARCH_CAPABILITIES if + the MSR is not supported by the host CPU. This makes QEMU not + initialize the MSR properly at kvm_put_msrs() on those hosts. + +Work around both bugs on the QEMU side, by checking if the MSR +was returned by KVM_GET_MSR_INDEX_LIST before returning the +feature flag on kvm_arch_get_supported_cpuid(). + +This has the unfortunate side effect of making arch_capabilities +unavailable on hosts without hardware support for the MSR until bug #2 +is fixed on KVM, but I can't see another way to work around bug #1 +without that side effect. + +Signed-off-by: Eduardo Habkost +Message-Id: <20190125220606.4864-2-ehabkost@redhat.com> +Signed-off-by: Eduardo Habkost +(cherry picked from commit 485b1d256bcb0874bcde0223727c159b6837e6f8) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + target/i386/kvm.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/target/i386/kvm.c b/target/i386/kvm.c +index c99c0ef..7afad93 100644 +--- a/target/i386/kvm.c ++++ b/target/i386/kvm.c +@@ -374,6 +374,15 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, + if (host_tsx_blacklisted()) { + ret &= ~(CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_HLE); + } ++ } else if (function == 7 && index == 0 && reg == R_EDX) { ++ /* ++ * Linux v4.17-v4.20 incorrectly return ARCH_CAPABILITIES on SVM hosts. ++ * We can detect the bug by checking if MSR_IA32_ARCH_CAPABILITIES is ++ * returned by KVM_GET_MSR_INDEX_LIST. ++ */ ++ if (!has_msr_arch_capabs) { ++ ret &= ~CPUID_7_0_EDX_ARCH_CAPABILITIES; ++ } + } else if (function == 0x80000001 && reg == R_ECX) { + /* + * It's safe to enable TOPOEXT even if it's not returned by +-- +1.8.3.1 + diff --git a/SOURCES/kvm-include-Add-IEC-binary-prefixes-in-qemu-units.h.patch b/SOURCES/kvm-include-Add-IEC-binary-prefixes-in-qemu-units.h.patch new file mode 100644 index 0000000..64691bb --- /dev/null +++ b/SOURCES/kvm-include-Add-IEC-binary-prefixes-in-qemu-units.h.patch @@ -0,0 +1,61 @@ +From 887a7e12d07315fe03f0b8efe7054d7c4f946bc2 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Wed, 7 Nov 2018 17:59:59 +0100 +Subject: [PATCH 25/34] include: Add IEC binary prefixes in "qemu/units.h" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Paolo Bonzini +Message-id: <20181107180007.22954-2-pbonzini@redhat.com> +Patchwork-id: 82941 +O-Subject: [RHEL7.6.z qemu-kvm-rhev PATCH 1/9] include: Add IEC binary prefixes in "qemu/units.h" +Bugzilla: 1566195 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Philippe Mathieu-Daudé + +From: Philippe Mathieu-Daudé + +Loosely based on 076b35b5a56. + +Suggested-by: Stefan Weil +Signed-off-by: Philippe Mathieu-Daudé +Message-Id: <20180625124238.25339-2-f4bug@amsat.org> +Signed-off-by: Paolo Bonzini +(cherry picked from commit 7ecdc94c40f4958a66893c0eac423c6a80f376d4) +Signed-off-by: Miroslav Rezanina +--- + include/qemu/units.h | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + create mode 100644 include/qemu/units.h + +diff --git a/include/qemu/units.h b/include/qemu/units.h +new file mode 100644 +index 0000000..692db3f +--- /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 +-- +1.8.3.1 + diff --git a/SOURCES/kvm-include-Add-a-lookup-table-of-sizes.patch b/SOURCES/kvm-include-Add-a-lookup-table-of-sizes.patch new file mode 100644 index 0000000..7f6849c --- /dev/null +++ b/SOURCES/kvm-include-Add-a-lookup-table-of-sizes.patch @@ -0,0 +1,113 @@ +From c2b9a080a8c98bb536ce97c47efda84538458bd0 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 19 Feb 2019 17:00:15 +0100 +Subject: [PATCH 14/23] include: Add a lookup table of sizes + +RH-Author: Kevin Wolf +Message-id: <20190219170023.27826-6-kwolf@redhat.com> +Patchwork-id: 84545 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 05/13] include: Add a lookup table of sizes +Bugzilla: 1656913 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Leonid Bloch + +Adding a lookup table for the powers of two, with the appropriate size +prefixes. This is needed when a size has to be stringified, in which +case something like '(1 * KiB)' would become a literal '(1 * (1L << 10))' +string. Powers of two are used very often for sizes, so such a table +will also make it easier and more intuitive to write them. + +This table is generatred using the following AWK script: + +BEGIN { + suffix="KMGTPE"; + for(i=10; i<64; i++) { + val=2**i; + s=substr(suffix, int(i/10), 1); + n=2**(i%10); + pad=21-int(log(n)/log(10)); + printf("#define S_%d%siB %*d\n", n, s, pad, val); + } +} + +Signed-off-by: Leonid Bloch +Reviewed-by: Alberto Garcia +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit 540b8492618ebbe98e7462bd7d31361b8cb10a05) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + include/qemu/units.h | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 55 insertions(+) + +diff --git a/include/qemu/units.h b/include/qemu/units.h +index 692db3f..68a7758 100644 +--- a/include/qemu/units.h ++++ b/include/qemu/units.h +@@ -17,4 +17,59 @@ + #define PiB (INT64_C(1) << 50) + #define EiB (INT64_C(1) << 60) + ++#define S_1KiB 1024 ++#define S_2KiB 2048 ++#define S_4KiB 4096 ++#define S_8KiB 8192 ++#define S_16KiB 16384 ++#define S_32KiB 32768 ++#define S_64KiB 65536 ++#define S_128KiB 131072 ++#define S_256KiB 262144 ++#define S_512KiB 524288 ++#define S_1MiB 1048576 ++#define S_2MiB 2097152 ++#define S_4MiB 4194304 ++#define S_8MiB 8388608 ++#define S_16MiB 16777216 ++#define S_32MiB 33554432 ++#define S_64MiB 67108864 ++#define S_128MiB 134217728 ++#define S_256MiB 268435456 ++#define S_512MiB 536870912 ++#define S_1GiB 1073741824 ++#define S_2GiB 2147483648 ++#define S_4GiB 4294967296 ++#define S_8GiB 8589934592 ++#define S_16GiB 17179869184 ++#define S_32GiB 34359738368 ++#define S_64GiB 68719476736 ++#define S_128GiB 137438953472 ++#define S_256GiB 274877906944 ++#define S_512GiB 549755813888 ++#define S_1TiB 1099511627776 ++#define S_2TiB 2199023255552 ++#define S_4TiB 4398046511104 ++#define S_8TiB 8796093022208 ++#define S_16TiB 17592186044416 ++#define S_32TiB 35184372088832 ++#define S_64TiB 70368744177664 ++#define S_128TiB 140737488355328 ++#define S_256TiB 281474976710656 ++#define S_512TiB 562949953421312 ++#define S_1PiB 1125899906842624 ++#define S_2PiB 2251799813685248 ++#define S_4PiB 4503599627370496 ++#define S_8PiB 9007199254740992 ++#define S_16PiB 18014398509481984 ++#define S_32PiB 36028797018963968 ++#define S_64PiB 72057594037927936 ++#define S_128PiB 144115188075855872 ++#define S_256PiB 288230376151711744 ++#define S_512PiB 576460752303423488 ++#define S_1EiB 1152921504606846976 ++#define S_2EiB 2305843009213693952 ++#define S_4EiB 4611686018427387904 ++#define S_8EiB 9223372036854775808 ++ + #endif +-- +1.8.3.1 + diff --git a/SOURCES/kvm-intel-iommu-add-iommu-lock.patch b/SOURCES/kvm-intel-iommu-add-iommu-lock.patch new file mode 100644 index 0000000..e699f30 --- /dev/null +++ b/SOURCES/kvm-intel-iommu-add-iommu-lock.patch @@ -0,0 +1,252 @@ +From c865a8af8574b64c06b9cbdf080d93e75dd8019c Mon Sep 17 00:00:00 2001 +From: Peter Xu +Date: Mon, 3 Sep 2018 04:52:35 +0200 +Subject: [PATCH 20/29] intel-iommu: add iommu lock + +RH-Author: Peter Xu +Message-id: <20180903045241.6456-4-peterx@redhat.com> +Patchwork-id: 82022 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 3/9] intel-iommu: add iommu lock +Bugzilla: 1623859 +RH-Acked-by: Xiao Wang +RH-Acked-by: Auger Eric +RH-Acked-by: Michael S. Tsirkin + +SECURITY IMPLICATION: this patch fixes a potential race when multiple +threads access the IOMMU IOTLB cache. + +Add a per-iommu big lock to protect IOMMU status. Currently the only +thing to be protected is the IOTLB/context cache, since that can be +accessed even without BQL, e.g., in IO dataplane. + +Note that we don't need to protect device page tables since that's fully +controlled by the guest kernel. However there is still possibility that +malicious drivers will program the device to not obey the rule. In that +case QEMU can't really do anything useful, instead the guest itself will +be responsible for all uncertainties. + +CC: QEMU Stable +Reported-by: Fam Zheng +Signed-off-by: Peter Xu +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 1d9efa73e12ddf361ea997c2d532cc4afa6674d1) +Signed-off-by: Peter Xu +Signed-off-by: Miroslav Rezanina +--- + hw/i386/intel_iommu.c | 56 ++++++++++++++++++++++++++++++++++++------- + include/hw/i386/intel_iommu.h | 6 +++++ + 2 files changed, 53 insertions(+), 9 deletions(-) + +diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c +index 3df9045..8d4069d 100644 +--- a/hw/i386/intel_iommu.c ++++ b/hw/i386/intel_iommu.c +@@ -128,6 +128,16 @@ static uint64_t vtd_set_clear_mask_quad(IntelIOMMUState *s, hwaddr addr, + return new_val; + } + ++static inline void vtd_iommu_lock(IntelIOMMUState *s) ++{ ++ qemu_mutex_lock(&s->iommu_lock); ++} ++ ++static inline void vtd_iommu_unlock(IntelIOMMUState *s) ++{ ++ qemu_mutex_unlock(&s->iommu_lock); ++} ++ + /* GHashTable functions */ + static gboolean vtd_uint64_equal(gconstpointer v1, gconstpointer v2) + { +@@ -172,9 +182,9 @@ static gboolean vtd_hash_remove_by_page(gpointer key, gpointer value, + } + + /* Reset all the gen of VTDAddressSpace to zero and set the gen of +- * IntelIOMMUState to 1. ++ * IntelIOMMUState to 1. Must be called with IOMMU lock held. + */ +-static void vtd_reset_context_cache(IntelIOMMUState *s) ++static void vtd_reset_context_cache_locked(IntelIOMMUState *s) + { + VTDAddressSpace *vtd_as; + VTDBus *vtd_bus; +@@ -197,12 +207,20 @@ static void vtd_reset_context_cache(IntelIOMMUState *s) + s->context_cache_gen = 1; + } + +-static void vtd_reset_iotlb(IntelIOMMUState *s) ++/* Must be called with IOMMU lock held. */ ++static void vtd_reset_iotlb_locked(IntelIOMMUState *s) + { + assert(s->iotlb); + g_hash_table_remove_all(s->iotlb); + } + ++static void vtd_reset_iotlb(IntelIOMMUState *s) ++{ ++ vtd_iommu_lock(s); ++ vtd_reset_iotlb_locked(s); ++ vtd_iommu_unlock(s); ++} ++ + static uint64_t vtd_get_iotlb_key(uint64_t gfn, uint16_t source_id, + uint32_t level) + { +@@ -215,6 +233,7 @@ static uint64_t vtd_get_iotlb_gfn(hwaddr addr, uint32_t level) + return (addr & vtd_slpt_level_page_mask(level)) >> VTD_PAGE_SHIFT_4K; + } + ++/* Must be called with IOMMU lock held */ + static VTDIOTLBEntry *vtd_lookup_iotlb(IntelIOMMUState *s, uint16_t source_id, + hwaddr addr) + { +@@ -235,6 +254,7 @@ out: + return entry; + } + ++/* Must be with IOMMU lock held */ + static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, + uint16_t domain_id, hwaddr addr, uint64_t slpte, + uint8_t access_flags, uint32_t level) +@@ -246,7 +266,7 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, + trace_vtd_iotlb_page_update(source_id, addr, slpte, domain_id); + if (g_hash_table_size(s->iotlb) >= VTD_IOTLB_MAX_SIZE) { + trace_vtd_iotlb_reset("iotlb exceeds size limit"); +- vtd_reset_iotlb(s); ++ vtd_reset_iotlb_locked(s); + } + + entry->gfn = gfn; +@@ -1106,7 +1126,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, + IntelIOMMUState *s = vtd_as->iommu_state; + VTDContextEntry ce; + uint8_t bus_num = pci_bus_num(bus); +- VTDContextCacheEntry *cc_entry = &vtd_as->context_cache_entry; ++ VTDContextCacheEntry *cc_entry; + uint64_t slpte, page_mask; + uint32_t level; + uint16_t source_id = vtd_make_source_id(bus_num, devfn); +@@ -1123,6 +1143,10 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, + */ + assert(!vtd_is_interrupt_addr(addr)); + ++ vtd_iommu_lock(s); ++ ++ cc_entry = &vtd_as->context_cache_entry; ++ + /* Try to fetch slpte form IOTLB */ + iotlb_entry = vtd_lookup_iotlb(s, source_id, addr); + if (iotlb_entry) { +@@ -1182,7 +1206,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, + * IOMMU region can be swapped back. + */ + vtd_pt_enable_fast_path(s, source_id); +- ++ vtd_iommu_unlock(s); + return true; + } + +@@ -1203,6 +1227,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, + vtd_update_iotlb(s, source_id, VTD_CONTEXT_ENTRY_DID(ce.hi), addr, slpte, + access_flags, level); + out: ++ vtd_iommu_unlock(s); + entry->iova = addr & page_mask; + entry->translated_addr = vtd_get_slpte_addr(slpte, s->aw_bits) & page_mask; + entry->addr_mask = ~page_mask; +@@ -1210,6 +1235,7 @@ out: + return true; + + error: ++ vtd_iommu_unlock(s); + entry->iova = 0; + entry->translated_addr = 0; + entry->addr_mask = 0; +@@ -1258,10 +1284,13 @@ static void vtd_iommu_replay_all(IntelIOMMUState *s) + static void vtd_context_global_invalidate(IntelIOMMUState *s) + { + trace_vtd_inv_desc_cc_global(); ++ /* Protects context cache */ ++ vtd_iommu_lock(s); + s->context_cache_gen++; + if (s->context_cache_gen == VTD_CONTEXT_CACHE_GEN_MAX) { +- vtd_reset_context_cache(s); ++ vtd_reset_context_cache_locked(s); + } ++ vtd_iommu_unlock(s); + vtd_switch_address_space_all(s); + /* + * From VT-d spec 6.5.2.1, a global context entry invalidation +@@ -1313,7 +1342,9 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s, + if (vtd_as && ((devfn_it & mask) == (devfn & mask))) { + trace_vtd_inv_desc_cc_device(bus_n, VTD_PCI_SLOT(devfn_it), + VTD_PCI_FUNC(devfn_it)); ++ vtd_iommu_lock(s); + vtd_as->context_cache_entry.context_cache_gen = 0; ++ vtd_iommu_unlock(s); + /* + * Do switch address space when needed, in case if the + * device passthrough bit is switched. +@@ -1377,8 +1408,10 @@ static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) + + trace_vtd_inv_desc_iotlb_domain(domain_id); + ++ vtd_iommu_lock(s); + g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_domain, + &domain_id); ++ vtd_iommu_unlock(s); + + QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { + if (!vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), +@@ -1426,7 +1459,9 @@ static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id, + info.domain_id = domain_id; + info.addr = addr; + info.mask = ~((1 << am) - 1); ++ vtd_iommu_lock(s); + g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_page, &info); ++ vtd_iommu_unlock(s); + vtd_iotlb_page_invalidate_notify(s, domain_id, addr, am); + } + +@@ -2929,8 +2964,10 @@ static void vtd_init(IntelIOMMUState *s) + s->cap |= VTD_CAP_CM; + } + +- vtd_reset_context_cache(s); +- vtd_reset_iotlb(s); ++ vtd_iommu_lock(s); ++ vtd_reset_context_cache_locked(s); ++ vtd_reset_iotlb_locked(s); ++ vtd_iommu_unlock(s); + + /* Define registers with default values and bit semantics */ + vtd_define_long(s, DMAR_VER_REG, 0x10UL, 0, 0); +@@ -3070,6 +3107,7 @@ static void vtd_realize(DeviceState *dev, Error **errp) + } + + QLIST_INIT(&s->vtd_as_with_notifiers); ++ qemu_mutex_init(&s->iommu_lock); + memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num)); + memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s, + "intel_iommu", DMAR_REG_SIZE); +diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h +index 032e33b..016e74b 100644 +--- a/include/hw/i386/intel_iommu.h ++++ b/include/hw/i386/intel_iommu.h +@@ -300,6 +300,12 @@ struct IntelIOMMUState { + OnOffAuto intr_eim; /* Toggle for EIM cabability */ + bool buggy_eim; /* Force buggy EIM unless eim=off */ + uint8_t aw_bits; /* Host/IOVA address width (in bits) */ ++ ++ /* ++ * Protects IOMMU states in general. Currently it protects the ++ * per-IOMMU IOTLB cache, and context entry cache in VTDAddressSpace. ++ */ ++ QemuMutex iommu_lock; + }; + + /* Find the VTD Address space associated with the given bus pointer, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-intel-iommu-introduce-vtd_page_walk_info.patch b/SOURCES/kvm-intel-iommu-introduce-vtd_page_walk_info.patch new file mode 100644 index 0000000..f7a65a9 --- /dev/null +++ b/SOURCES/kvm-intel-iommu-introduce-vtd_page_walk_info.patch @@ -0,0 +1,206 @@ +From a0ee4c7297a134808024b069d8612146e32e2322 Mon Sep 17 00:00:00 2001 +From: Peter Xu +Date: Mon, 3 Sep 2018 04:52:37 +0200 +Subject: [PATCH 22/29] intel-iommu: introduce vtd_page_walk_info + +RH-Author: Peter Xu +Message-id: <20180903045241.6456-6-peterx@redhat.com> +Patchwork-id: 82025 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 5/9] intel-iommu: introduce vtd_page_walk_info +Bugzilla: 1623859 +RH-Acked-by: Xiao Wang +RH-Acked-by: Auger Eric +RH-Acked-by: Michael S. Tsirkin + +During the recursive page walking of IOVA page tables, some stack +variables are constant variables and never changed during the whole page +walking procedure. Isolate them into a struct so that we don't need to +pass those contants down the stack every time and multiple times. + +CC: QEMU Stable +Signed-off-by: Peter Xu +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit fe215b0cbb8c1f4b4af0a64aa5c02042080dd537) +Signed-off-by: Peter Xu +Signed-off-by: Miroslav Rezanina +--- + hw/i386/intel_iommu.c | 84 +++++++++++++++++++++++++++++++-------------------- + 1 file changed, 52 insertions(+), 32 deletions(-) + +diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c +index 38ccc74..e247269 100644 +--- a/hw/i386/intel_iommu.c ++++ b/hw/i386/intel_iommu.c +@@ -748,9 +748,27 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write, + + typedef int (*vtd_page_walk_hook)(IOMMUTLBEntry *entry, void *private); + ++/** ++ * Constant information used during page walking ++ * ++ * @hook_fn: hook func to be called when detected page ++ * @private: private data to be passed into hook func ++ * @notify_unmap: whether we should notify invalid entries ++ * @aw: maximum address width ++ */ ++typedef struct { ++ vtd_page_walk_hook hook_fn; ++ void *private; ++ bool notify_unmap; ++ uint8_t aw; ++} vtd_page_walk_info; ++ + static int vtd_page_walk_one(IOMMUTLBEntry *entry, int level, +- vtd_page_walk_hook hook_fn, void *private) ++ vtd_page_walk_info *info) + { ++ vtd_page_walk_hook hook_fn = info->hook_fn; ++ void *private = info->private; ++ + assert(hook_fn); + trace_vtd_page_walk_one(level, entry->iova, entry->translated_addr, + entry->addr_mask, entry->perm); +@@ -763,17 +781,13 @@ static int vtd_page_walk_one(IOMMUTLBEntry *entry, int level, + * @addr: base GPA addr to start the walk + * @start: IOVA range start address + * @end: IOVA range end address (start <= addr < end) +- * @hook_fn: hook func to be called when detected page +- * @private: private data to be passed into hook func + * @read: whether parent level has read permission + * @write: whether parent level has write permission +- * @notify_unmap: whether we should notify invalid entries +- * @aw: maximum address width ++ * @info: constant information for the page walk + */ + static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, +- uint64_t end, vtd_page_walk_hook hook_fn, +- void *private, uint32_t level, bool read, +- bool write, bool notify_unmap, uint8_t aw) ++ uint64_t end, uint32_t level, bool read, ++ bool write, vtd_page_walk_info *info) + { + bool read_cur, write_cur, entry_valid; + uint32_t offset; +@@ -823,24 +837,24 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, + + if (vtd_is_last_slpte(slpte, level)) { + /* NOTE: this is only meaningful if entry_valid == true */ +- entry.translated_addr = vtd_get_slpte_addr(slpte, aw); +- if (!entry_valid && !notify_unmap) { ++ entry.translated_addr = vtd_get_slpte_addr(slpte, info->aw); ++ if (!entry_valid && !info->notify_unmap) { + trace_vtd_page_walk_skip_perm(iova, iova_next); + goto next; + } +- ret = vtd_page_walk_one(&entry, level, hook_fn, private); ++ ret = vtd_page_walk_one(&entry, level, info); + if (ret < 0) { + return ret; + } + } else { + if (!entry_valid) { +- if (notify_unmap) { ++ if (info->notify_unmap) { + /* + * The whole entry is invalid; unmap it all. + * Translated address is meaningless, zero it. + */ + entry.translated_addr = 0x0; +- ret = vtd_page_walk_one(&entry, level, hook_fn, private); ++ ret = vtd_page_walk_one(&entry, level, info); + if (ret < 0) { + return ret; + } +@@ -849,10 +863,9 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, + } + goto next; + } +- ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte, aw), iova, +- MIN(iova_next, end), hook_fn, private, +- level - 1, read_cur, write_cur, +- notify_unmap, aw); ++ ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte, info->aw), ++ iova, MIN(iova_next, end), level - 1, ++ read_cur, write_cur, info); + if (ret < 0) { + return ret; + } +@@ -871,28 +884,24 @@ next: + * @ce: context entry to walk upon + * @start: IOVA address to start the walk + * @end: IOVA range end address (start <= addr < end) +- * @hook_fn: the hook that to be called for each detected area +- * @private: private data for the hook function +- * @aw: maximum address width ++ * @info: page walking information struct + */ + static int vtd_page_walk(VTDContextEntry *ce, uint64_t start, uint64_t end, +- vtd_page_walk_hook hook_fn, void *private, +- bool notify_unmap, uint8_t aw) ++ vtd_page_walk_info *info) + { + dma_addr_t addr = vtd_ce_get_slpt_base(ce); + uint32_t level = vtd_ce_get_level(ce); + +- if (!vtd_iova_range_check(start, ce, aw)) { ++ if (!vtd_iova_range_check(start, ce, info->aw)) { + return -VTD_FR_ADDR_BEYOND_MGAW; + } + +- if (!vtd_iova_range_check(end, ce, aw)) { ++ if (!vtd_iova_range_check(end, ce, info->aw)) { + /* Fix end so that it reaches the maximum */ +- end = vtd_iova_limit(ce, aw); ++ end = vtd_iova_limit(ce, info->aw); + } + +- return vtd_page_walk_level(addr, start, end, hook_fn, private, +- level, true, true, notify_unmap, aw); ++ return vtd_page_walk_level(addr, start, end, level, true, true, info); + } + + /* Map a device to its corresponding domain (context-entry) */ +@@ -1449,14 +1458,19 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, + vtd_as->devfn, &ce); + if (!ret && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { + if (vtd_as_has_map_notifier(vtd_as)) { ++ vtd_page_walk_info info = { ++ .hook_fn = vtd_page_invalidate_notify_hook, ++ .private = (void *)&vtd_as->iommu, ++ .notify_unmap = true, ++ .aw = s->aw_bits, ++ }; ++ + /* + * As long as we have MAP notifications registered in + * any of our IOMMU notifiers, we need to sync the + * shadow page table. + */ +- vtd_page_walk(&ce, addr, addr + size, +- vtd_page_invalidate_notify_hook, +- (void *)&vtd_as->iommu, true, s->aw_bits); ++ vtd_page_walk(&ce, addr, addr + size, &info); + } else { + /* + * For UNMAP-only notifiers, we don't need to walk the +@@ -2924,8 +2938,14 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) + ce.hi, ce.lo); + if (vtd_as_has_map_notifier(vtd_as)) { + /* This is required only for MAP typed notifiers */ +- vtd_page_walk(&ce, 0, ~0ULL, vtd_replay_hook, (void *)n, false, +- s->aw_bits); ++ vtd_page_walk_info info = { ++ .hook_fn = vtd_replay_hook, ++ .private = (void *)n, ++ .notify_unmap = false, ++ .aw = s->aw_bits, ++ }; ++ ++ vtd_page_walk(&ce, 0, ~0ULL, &info); + } + } else { + trace_vtd_replay_ce_invalid(bus_n, PCI_SLOT(vtd_as->devfn), +-- +1.8.3.1 + diff --git a/SOURCES/kvm-intel-iommu-only-do-page-walk-for-MAP-notifiers.patch b/SOURCES/kvm-intel-iommu-only-do-page-walk-for-MAP-notifiers.patch new file mode 100644 index 0000000..458e525 --- /dev/null +++ b/SOURCES/kvm-intel-iommu-only-do-page-walk-for-MAP-notifiers.patch @@ -0,0 +1,127 @@ +From b6bc2d692f4f3c41b61c0917d4709e91827c8432 Mon Sep 17 00:00:00 2001 +From: Peter Xu +Date: Mon, 3 Sep 2018 04:52:36 +0200 +Subject: [PATCH 21/29] intel-iommu: only do page walk for MAP notifiers + +RH-Author: Peter Xu +Message-id: <20180903045241.6456-5-peterx@redhat.com> +Patchwork-id: 82024 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 4/9] intel-iommu: only do page walk for MAP notifiers +Bugzilla: 1623859 +RH-Acked-by: Xiao Wang +RH-Acked-by: Auger Eric +RH-Acked-by: Michael S. Tsirkin + +For UNMAP-only IOMMU notifiers, we don't need to walk the page tables. +Fasten that procedure by skipping the page table walk. That should +boost performance for UNMAP-only notifiers like vhost. + +CC: QEMU Stable +Signed-off-by: Peter Xu +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 4f8a62a933a79094e44bc1b16b63bb23e62d67b4) +Signed-off-by: Peter Xu +Signed-off-by: Miroslav Rezanina +--- + hw/i386/intel_iommu.c | 44 ++++++++++++++++++++++++++++++++++++++----- + include/hw/i386/intel_iommu.h | 2 ++ + 2 files changed, 41 insertions(+), 5 deletions(-) + +diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c +index 8d4069d..38ccc74 100644 +--- a/hw/i386/intel_iommu.c ++++ b/hw/i386/intel_iommu.c +@@ -138,6 +138,12 @@ static inline void vtd_iommu_unlock(IntelIOMMUState *s) + qemu_mutex_unlock(&s->iommu_lock); + } + ++/* Whether the address space needs to notify new mappings */ ++static inline gboolean vtd_as_has_map_notifier(VTDAddressSpace *as) ++{ ++ return as->notifier_flags & IOMMU_NOTIFIER_MAP; ++} ++ + /* GHashTable functions */ + static gboolean vtd_uint64_equal(gconstpointer v1, gconstpointer v2) + { +@@ -1436,14 +1442,36 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, + VTDAddressSpace *vtd_as; + VTDContextEntry ce; + int ret; ++ hwaddr size = (1 << am) * VTD_PAGE_SIZE; + + QLIST_FOREACH(vtd_as, &(s->vtd_as_with_notifiers), next) { + ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), + vtd_as->devfn, &ce); + if (!ret && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { +- vtd_page_walk(&ce, addr, addr + (1 << am) * VTD_PAGE_SIZE, +- vtd_page_invalidate_notify_hook, +- (void *)&vtd_as->iommu, true, s->aw_bits); ++ if (vtd_as_has_map_notifier(vtd_as)) { ++ /* ++ * As long as we have MAP notifications registered in ++ * any of our IOMMU notifiers, we need to sync the ++ * shadow page table. ++ */ ++ vtd_page_walk(&ce, addr, addr + size, ++ vtd_page_invalidate_notify_hook, ++ (void *)&vtd_as->iommu, true, s->aw_bits); ++ } else { ++ /* ++ * For UNMAP-only notifiers, we don't need to walk the ++ * page tables. We just deliver the PSI down to ++ * invalidate caches. ++ */ ++ IOMMUTLBEntry entry = { ++ .target_as = &address_space_memory, ++ .iova = addr, ++ .translated_addr = 0, ++ .addr_mask = size - 1, ++ .perm = IOMMU_NONE, ++ }; ++ memory_region_notify_iommu(&vtd_as->iommu, entry); ++ } + } + } + } +@@ -2383,6 +2411,9 @@ static void vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, + exit(1); + } + ++ /* Update per-address-space notifier flags */ ++ vtd_as->notifier_flags = new; ++ + if (old == IOMMU_NOTIFIER_NONE) { + QLIST_INSERT_HEAD(&s->vtd_as_with_notifiers, vtd_as, next); + } else if (new == IOMMU_NOTIFIER_NONE) { +@@ -2891,8 +2922,11 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) + PCI_FUNC(vtd_as->devfn), + VTD_CONTEXT_ENTRY_DID(ce.hi), + ce.hi, ce.lo); +- vtd_page_walk(&ce, 0, ~0ULL, vtd_replay_hook, (void *)n, false, +- s->aw_bits); ++ if (vtd_as_has_map_notifier(vtd_as)) { ++ /* This is required only for MAP typed notifiers */ ++ vtd_page_walk(&ce, 0, ~0ULL, vtd_replay_hook, (void *)n, false, ++ s->aw_bits); ++ } + } else { + trace_vtd_replay_ce_invalid(bus_n, PCI_SLOT(vtd_as->devfn), + PCI_FUNC(vtd_as->devfn)); +diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h +index 016e74b..156f35e 100644 +--- a/include/hw/i386/intel_iommu.h ++++ b/include/hw/i386/intel_iommu.h +@@ -93,6 +93,8 @@ struct VTDAddressSpace { + IntelIOMMUState *iommu_state; + VTDContextCacheEntry context_cache_entry; + QLIST_ENTRY(VTDAddressSpace) next; ++ /* Superset of notifier flags that this address space has */ ++ IOMMUNotifierFlag notifier_flags; + }; + + struct VTDBus { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-intel-iommu-pass-in-address-space-when-page-walk.patch b/SOURCES/kvm-intel-iommu-pass-in-address-space-when-page-walk.patch new file mode 100644 index 0000000..1575638 --- /dev/null +++ b/SOURCES/kvm-intel-iommu-pass-in-address-space-when-page-walk.patch @@ -0,0 +1,63 @@ +From 6e4599e1863990d7f8aa409fb67ca115b99c2fdf Mon Sep 17 00:00:00 2001 +From: Peter Xu +Date: Mon, 3 Sep 2018 04:52:38 +0200 +Subject: [PATCH 23/29] intel-iommu: pass in address space when page walk + +RH-Author: Peter Xu +Message-id: <20180903045241.6456-7-peterx@redhat.com> +Patchwork-id: 82027 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 6/9] intel-iommu: pass in address space when page walk +Bugzilla: 1623859 +RH-Acked-by: Xiao Wang +RH-Acked-by: Auger Eric +RH-Acked-by: Michael S. Tsirkin + +We pass in the VTDAddressSpace too. It'll be used in the follow up +patches. + +CC: QEMU Stable +Signed-off-by: Peter Xu +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 2f764fa87d2a81812b313dd6d998e10126292653) +Signed-off-by: Peter Xu +Signed-off-by: Miroslav Rezanina +--- + hw/i386/intel_iommu.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c +index e247269..a882894 100644 +--- a/hw/i386/intel_iommu.c ++++ b/hw/i386/intel_iommu.c +@@ -754,9 +754,11 @@ typedef int (*vtd_page_walk_hook)(IOMMUTLBEntry *entry, void *private); + * @hook_fn: hook func to be called when detected page + * @private: private data to be passed into hook func + * @notify_unmap: whether we should notify invalid entries ++ * @as: VT-d address space of the device + * @aw: maximum address width + */ + typedef struct { ++ VTDAddressSpace *as; + vtd_page_walk_hook hook_fn; + void *private; + bool notify_unmap; +@@ -1463,6 +1465,7 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, + .private = (void *)&vtd_as->iommu, + .notify_unmap = true, + .aw = s->aw_bits, ++ .as = vtd_as, + }; + + /* +@@ -2943,6 +2946,7 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) + .private = (void *)n, + .notify_unmap = false, + .aw = s->aw_bits, ++ .as = vtd_as, + }; + + vtd_page_walk(&ce, 0, ~0ULL, &info); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-intel-iommu-remove-IntelIOMMUNotifierNode.patch b/SOURCES/kvm-intel-iommu-remove-IntelIOMMUNotifierNode.patch new file mode 100644 index 0000000..2bd0bd1 --- /dev/null +++ b/SOURCES/kvm-intel-iommu-remove-IntelIOMMUNotifierNode.patch @@ -0,0 +1,182 @@ +From fd9f21135b1317ddfdf70d5d749e9c2ef51e4c22 Mon Sep 17 00:00:00 2001 +From: Peter Xu +Date: Mon, 3 Sep 2018 04:52:34 +0200 +Subject: [PATCH 19/29] intel-iommu: remove IntelIOMMUNotifierNode + +RH-Author: Peter Xu +Message-id: <20180903045241.6456-3-peterx@redhat.com> +Patchwork-id: 82023 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 2/9] intel-iommu: remove IntelIOMMUNotifierNode +Bugzilla: 1623859 +RH-Acked-by: Xiao Wang +RH-Acked-by: Auger Eric +RH-Acked-by: Michael S. Tsirkin + +That is not really necessary. Removing that node struct and put the +list entry directly into VTDAddressSpace. It simplfies the code a lot. +Since at it, rename the old notifiers_list into vtd_as_with_notifiers. + +CC: QEMU Stable +Signed-off-by: Peter Xu +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit b4a4ba0d68f50f218ee3957b6638dbee32a5eeef) +Signed-off-by: Peter Xu +Signed-off-by: Miroslav Rezanina +--- + hw/i386/intel_iommu.c | 41 +++++++++++------------------------------ + include/hw/i386/intel_iommu.h | 9 ++------- + 2 files changed, 13 insertions(+), 37 deletions(-) + +diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c +index b359efd..3df9045 100644 +--- a/hw/i386/intel_iommu.c ++++ b/hw/i386/intel_iommu.c +@@ -1248,10 +1248,10 @@ static void vtd_interrupt_remap_table_setup(IntelIOMMUState *s) + + static void vtd_iommu_replay_all(IntelIOMMUState *s) + { +- IntelIOMMUNotifierNode *node; ++ VTDAddressSpace *vtd_as; + +- QLIST_FOREACH(node, &s->notifiers_list, next) { +- memory_region_iommu_replay_all(&node->vtd_as->iommu); ++ QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { ++ memory_region_iommu_replay_all(&vtd_as->iommu); + } + } + +@@ -1372,7 +1372,6 @@ static void vtd_iotlb_global_invalidate(IntelIOMMUState *s) + + static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) + { +- IntelIOMMUNotifierNode *node; + VTDContextEntry ce; + VTDAddressSpace *vtd_as; + +@@ -1381,8 +1380,7 @@ static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) + g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_domain, + &domain_id); + +- QLIST_FOREACH(node, &s->notifiers_list, next) { +- vtd_as = node->vtd_as; ++ QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { + if (!vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), + vtd_as->devfn, &ce) && + domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { +@@ -1402,12 +1400,11 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, + uint16_t domain_id, hwaddr addr, + uint8_t am) + { +- IntelIOMMUNotifierNode *node; ++ VTDAddressSpace *vtd_as; + VTDContextEntry ce; + int ret; + +- QLIST_FOREACH(node, &(s->notifiers_list), next) { +- VTDAddressSpace *vtd_as = node->vtd_as; ++ QLIST_FOREACH(vtd_as, &(s->vtd_as_with_notifiers), next) { + ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), + vtd_as->devfn, &ce); + if (!ret && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { +@@ -2344,8 +2341,6 @@ static void vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, + { + VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu); + IntelIOMMUState *s = vtd_as->iommu_state; +- IntelIOMMUNotifierNode *node = NULL; +- IntelIOMMUNotifierNode *next_node = NULL; + + if (!s->caching_mode && new & IOMMU_NOTIFIER_MAP) { + error_report("We need to set caching-mode=1 for intel-iommu to enable " +@@ -2354,21 +2349,9 @@ static void vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, + } + + if (old == IOMMU_NOTIFIER_NONE) { +- node = g_malloc0(sizeof(*node)); +- node->vtd_as = vtd_as; +- QLIST_INSERT_HEAD(&s->notifiers_list, node, next); +- return; +- } +- +- /* update notifier node with new flags */ +- QLIST_FOREACH_SAFE(node, &s->notifiers_list, next, next_node) { +- if (node->vtd_as == vtd_as) { +- if (new == IOMMU_NOTIFIER_NONE) { +- QLIST_REMOVE(node, next); +- g_free(node); +- } +- return; +- } ++ QLIST_INSERT_HEAD(&s->vtd_as_with_notifiers, vtd_as, next); ++ } else if (new == IOMMU_NOTIFIER_NONE) { ++ QLIST_REMOVE(vtd_as, next); + } + } + +@@ -2838,12 +2821,10 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) + + static void vtd_address_space_unmap_all(IntelIOMMUState *s) + { +- IntelIOMMUNotifierNode *node; + VTDAddressSpace *vtd_as; + IOMMUNotifier *n; + +- QLIST_FOREACH(node, &s->notifiers_list, next) { +- vtd_as = node->vtd_as; ++ QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { + IOMMU_NOTIFIER_FOREACH(n, &vtd_as->iommu) { + vtd_address_space_unmap(vtd_as, n); + } +@@ -3088,7 +3069,7 @@ static void vtd_realize(DeviceState *dev, Error **errp) + return; + } + +- QLIST_INIT(&s->notifiers_list); ++ QLIST_INIT(&s->vtd_as_with_notifiers); + memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num)); + memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s, + "intel_iommu", DMAR_REG_SIZE); +diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h +index 45ec891..032e33b 100644 +--- a/include/hw/i386/intel_iommu.h ++++ b/include/hw/i386/intel_iommu.h +@@ -67,7 +67,6 @@ typedef union VTD_IR_TableEntry VTD_IR_TableEntry; + typedef union VTD_IR_MSIAddress VTD_IR_MSIAddress; + typedef struct VTDIrq VTDIrq; + typedef struct VTD_MSIMessage VTD_MSIMessage; +-typedef struct IntelIOMMUNotifierNode IntelIOMMUNotifierNode; + + /* Context-Entry */ + struct VTDContextEntry { +@@ -93,6 +92,7 @@ struct VTDAddressSpace { + MemoryRegion iommu_ir; /* Interrupt region: 0xfeeXXXXX */ + IntelIOMMUState *iommu_state; + VTDContextCacheEntry context_cache_entry; ++ QLIST_ENTRY(VTDAddressSpace) next; + }; + + struct VTDBus { +@@ -253,11 +253,6 @@ struct VTD_MSIMessage { + /* When IR is enabled, all MSI/MSI-X data bits should be zero */ + #define VTD_IR_MSI_DATA (0) + +-struct IntelIOMMUNotifierNode { +- VTDAddressSpace *vtd_as; +- QLIST_ENTRY(IntelIOMMUNotifierNode) next; +-}; +- + /* The iommu (DMAR) device state struct */ + struct IntelIOMMUState { + X86IOMMUState x86_iommu; +@@ -295,7 +290,7 @@ struct IntelIOMMUState { + GHashTable *vtd_as_by_busptr; /* VTDBus objects indexed by PCIBus* reference */ + VTDBus *vtd_as_by_bus_num[VTD_PCI_BUS_MAX]; /* VTDBus objects indexed by bus number */ + /* list of registered notifiers */ +- QLIST_HEAD(, IntelIOMMUNotifierNode) notifiers_list; ++ QLIST_HEAD(, VTDAddressSpace) vtd_as_with_notifiers; + + /* interrupt remapping */ + bool intr_enabled; /* Whether guest enabled IR */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-intel-iommu-replace-more-vtd_err_-traces.patch b/SOURCES/kvm-intel-iommu-replace-more-vtd_err_-traces.patch new file mode 100644 index 0000000..876c913 --- /dev/null +++ b/SOURCES/kvm-intel-iommu-replace-more-vtd_err_-traces.patch @@ -0,0 +1,212 @@ +From 20c0317b7984c5264e80c909227e1a78e82cc45a Mon Sep 17 00:00:00 2001 +From: Peter Xu +Date: Thu, 8 Nov 2018 05:37:17 +0100 +Subject: [PATCH 10/22] intel-iommu: replace more vtd_err_* traces +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Peter Xu +Message-id: <20181108053721.13162-4-peterx@redhat.com> +Patchwork-id: 82953 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 3/7] intel-iommu: replace more vtd_err_* traces +Bugzilla: 1627272 +RH-Acked-by: Auger Eric +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Philippe Mathieu-Daudé + +Replace all the trace_vtd_err_*() hooks with the new error_report_once() +since they are similar to trace_vtd_err() - dumping the first error +would be mostly enough, then we have them on by default too. + +Signed-off-by: Peter Xu +Message-Id: <20180815095328.32414-4-peterx@redhat.com> +[Use "%x" instead of "%" PRIx16 to print uint16_t, whitespace tidied up] +Signed-off-by: Markus Armbruster +(cherry picked from commit 4e4abd111a2af0179a4467368d695958844bf113) +Signed-off-by: Peter Xu + +Signed-off-by: Miroslav Rezanina +--- + hw/i386/intel_iommu.c | 64 ++++++++++++++++++++++++++++++++++++--------------- + hw/i386/trace-events | 12 ---------- + 2 files changed, 46 insertions(+), 30 deletions(-) + +diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c +index ab11cc4..aab86e9 100644 +--- a/hw/i386/intel_iommu.c ++++ b/hw/i386/intel_iommu.c +@@ -705,7 +705,8 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write, + uint64_t access_right_check; + + if (!vtd_iova_range_check(iova, ce, aw_bits)) { +- trace_vtd_err_dmar_iova_overflow(iova); ++ error_report_once("%s: detected IOVA overflow (iova=0x%" PRIx64 ")", ++ __func__, iova); + return -VTD_FR_ADDR_BEYOND_MGAW; + } + +@@ -717,7 +718,8 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write, + slpte = vtd_get_slpte(addr, offset); + + if (slpte == (uint64_t)-1) { +- trace_vtd_err_dmar_slpte_read_error(iova, level); ++ error_report_once("%s: detected read error on DMAR slpte " ++ "(iova=0x%" PRIx64 ")", __func__, iova); + if (level == vtd_ce_get_level(ce)) { + /* Invalid programming of context-entry */ + return -VTD_FR_CONTEXT_ENTRY_INV; +@@ -728,11 +730,17 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write, + *reads = (*reads) && (slpte & VTD_SL_R); + *writes = (*writes) && (slpte & VTD_SL_W); + if (!(slpte & access_right_check)) { +- trace_vtd_err_dmar_slpte_perm_error(iova, level, slpte, is_write); ++ error_report_once("%s: detected slpte permission error " ++ "(iova=0x%" PRIx64 ", level=0x%" PRIx32 ", " ++ "slpte=0x%" PRIx64 ", write=%d)", __func__, ++ iova, level, slpte, is_write); + return is_write ? -VTD_FR_WRITE : -VTD_FR_READ; + } + if (vtd_slpte_nonzero_rsvd(slpte, level)) { +- trace_vtd_err_dmar_slpte_resv_error(iova, level, slpte); ++ error_report_once("%s: detected splte reserve non-zero " ++ "iova=0x%" PRIx64 ", level=0x%" PRIx32 ++ "slpte=0x%" PRIx64 ")", __func__, iova, ++ level, slpte); + return -VTD_FR_PAGING_ENTRY_RSVD; + } + +@@ -1697,7 +1705,10 @@ static void vtd_handle_gcmd_qie(IntelIOMMUState *s, bool en) + /* Ok - report back to driver */ + vtd_set_clear_mask_long(s, DMAR_GSTS_REG, VTD_GSTS_QIES, 0); + } else { +- trace_vtd_err_qi_disable(s->iq_head, s->iq_tail, s->iq_last_desc_type); ++ error_report_once("%s: detected improper state when disable QI " ++ "(head=0x%x, tail=0x%x, last_type=%d)", ++ __func__, ++ s->iq_head, s->iq_tail, s->iq_last_desc_type); + } + } + } +@@ -2094,7 +2105,9 @@ static void vtd_fetch_inv_desc(IntelIOMMUState *s) + + if (s->iq_tail >= s->iq_size) { + /* Detects an invalid Tail pointer */ +- trace_vtd_err_qi_tail(s->iq_tail, s->iq_size); ++ error_report_once("%s: detected invalid QI tail " ++ "(tail=0x%x, size=0x%x)", ++ __func__, s->iq_tail, s->iq_size); + vtd_handle_inv_queue_error(s); + return; + } +@@ -2507,10 +2520,12 @@ static IOMMUTLBEntry vtd_iommu_translate(IOMMUMemoryRegion *iommu, hwaddr addr, + iotlb.iova, iotlb.translated_addr, + iotlb.addr_mask); + } else { +- trace_vtd_err_dmar_translate(pci_bus_num(vtd_as->bus), +- VTD_PCI_SLOT(vtd_as->devfn), +- VTD_PCI_FUNC(vtd_as->devfn), +- iotlb.iova); ++ error_report_once("%s: detected translation failure " ++ "(dev=%02x:%02x:%02x, iova=0x%" PRIx64 ")", ++ __func__, pci_bus_num(vtd_as->bus), ++ VTD_PCI_SLOT(vtd_as->devfn), ++ VTD_PCI_FUNC(vtd_as->devfn), ++ iotlb.iova); + } + + return iotlb; +@@ -2626,15 +2641,19 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, + le64_to_cpu(entry->data[0])); + + if (!entry->irte.present) { +- trace_vtd_err_irte(index, le64_to_cpu(entry->data[1]), +- le64_to_cpu(entry->data[0])); ++ error_report_once("%s: detected non-present IRTE " ++ "(index=%u, high=0x%" PRIx64 ", low=0x%" PRIx64 ")", ++ __func__, index, le64_to_cpu(entry->data[1]), ++ le64_to_cpu(entry->data[0])); + return -VTD_FR_IR_ENTRY_P; + } + + if (entry->irte.__reserved_0 || entry->irte.__reserved_1 || + entry->irte.__reserved_2) { +- trace_vtd_err_irte(index, le64_to_cpu(entry->data[1]), +- le64_to_cpu(entry->data[0])); ++ error_report_once("%s: detected non-zero reserved IRTE " ++ "(index=%u, high=0x%" PRIx64 ", low=0x%" PRIx64 ")", ++ __func__, index, le64_to_cpu(entry->data[1]), ++ le64_to_cpu(entry->data[0])); + return -VTD_FR_IR_IRTE_RSVD; + } + +@@ -2648,7 +2667,9 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, + case VTD_SVT_ALL: + mask = vtd_svt_mask[entry->irte.sid_q]; + if ((source_id & mask) != (sid & mask)) { +- trace_vtd_err_irte_sid(index, sid, source_id); ++ error_report_once("%s: invalid IRTE SID " ++ "(index=%u, sid=%u, source_id=%u)", ++ __func__, index, sid, source_id); + return -VTD_FR_IR_SID_ERR; + } + break; +@@ -2658,13 +2679,17 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, + bus_min = source_id & 0xff; + bus = sid >> 8; + if (bus > bus_max || bus < bus_min) { +- trace_vtd_err_irte_sid_bus(index, bus, bus_min, bus_max); ++ error_report_once("%s: invalid SVT_BUS " ++ "(index=%u, bus=%u, min=%u, max=%u)", ++ __func__, index, bus, bus_min, bus_max); + return -VTD_FR_IR_SID_ERR; + } + break; + + default: +- trace_vtd_err_irte_svt(index, entry->irte.sid_vtype); ++ error_report_once("%s: detected invalid IRTE SVT " ++ "(index=%u, type=%d)", __func__, ++ index, entry->irte.sid_vtype); + /* Take this as verification failure. */ + return -VTD_FR_IR_SID_ERR; + break; +@@ -2786,7 +2811,10 @@ static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu, + if (addr.addr.sub_valid) { + trace_vtd_ir_remap_type("MSI"); + if (origin->data & VTD_IR_MSI_DATA_RESERVED) { +- trace_vtd_err_ir_msi_invalid(sid, origin->address, origin->data); ++ error_report_once("%s: invalid IR MSI " ++ "(sid=%u, address=0x%" PRIx64 ++ ", data=0x%" PRIx32 ")", ++ __func__, sid, origin->address, origin->data); + return -VTD_FR_IR_REQ_RSVD; + } + } else { +diff --git a/hw/i386/trace-events b/hw/i386/trace-events +index 922431b..9e6fc4d 100644 +--- a/hw/i386/trace-events ++++ b/hw/i386/trace-events +@@ -69,19 +69,7 @@ vtd_ir_remap_msi_req(uint64_t addr, uint64_t data) "addr 0x%"PRIx64" data 0x%"PR + vtd_fsts_ppf(bool set) "FSTS PPF bit set to %d" + vtd_fsts_clear_ip(void) "" + vtd_frr_new(int index, uint64_t hi, uint64_t lo) "index %d high 0x%"PRIx64" low 0x%"PRIx64 +-vtd_err_dmar_iova_overflow(uint64_t iova) "iova 0x%"PRIx64 +-vtd_err_dmar_slpte_read_error(uint64_t iova, int level) "iova 0x%"PRIx64" level %d" +-vtd_err_dmar_slpte_perm_error(uint64_t iova, int level, uint64_t slpte, bool is_write) "iova 0x%"PRIx64" level %d slpte 0x%"PRIx64" write %d" +-vtd_err_dmar_slpte_resv_error(uint64_t iova, int level, uint64_t slpte) "iova 0x%"PRIx64" level %d slpte 0x%"PRIx64 +-vtd_err_dmar_translate(uint8_t bus, uint8_t slot, uint8_t func, uint64_t iova) "dev %02x:%02x.%02x iova 0x%"PRIx64 + vtd_warn_invalid_qi_tail(uint16_t tail) "tail 0x%"PRIx16 +-vtd_err_qi_disable(uint16_t head, uint16_t tail, int type) "head 0x%"PRIx16" tail 0x%"PRIx16" last_desc_type %d" +-vtd_err_qi_tail(uint16_t tail, uint16_t size) "tail 0x%"PRIx16" size 0x%"PRIx16 +-vtd_err_irte(int index, uint64_t lo, uint64_t hi) "index %d low 0x%"PRIx64" high 0x%"PRIx64 +-vtd_err_irte_sid(int index, uint16_t req, uint16_t target) "index %d SVT_ALL sid 0x%"PRIx16" (should be: 0x%"PRIx16")" +-vtd_err_irte_sid_bus(int index, uint8_t bus, uint8_t min, uint8_t max) "index %d SVT_BUS bus 0x%"PRIx8" (should be: 0x%"PRIx8"-0x%"PRIx8")" +-vtd_err_irte_svt(int index, int type) "index %d SVT type %d" +-vtd_err_ir_msi_invalid(uint16_t sid, uint64_t addr, uint64_t data) "sid 0x%"PRIx16" addr 0x%"PRIx64" data 0x%"PRIx64 + vtd_warn_ir_vector(uint16_t sid, int index, int vec, int target) "sid 0x%"PRIx16" index %d vec %d (should be: %d)" + vtd_warn_ir_trigger(uint16_t sid, int index, int trig, int target) "sid 0x%"PRIx16" index %d trigger %d (should be: %d)" + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-intel-iommu-rework-the-page-walk-logic.patch b/SOURCES/kvm-intel-iommu-rework-the-page-walk-logic.patch new file mode 100644 index 0000000..3f8f80a --- /dev/null +++ b/SOURCES/kvm-intel-iommu-rework-the-page-walk-logic.patch @@ -0,0 +1,401 @@ +From 0b6a29f743e4dcbf340d5abe0e7e230591b7d5d3 Mon Sep 17 00:00:00 2001 +From: Peter Xu +Date: Mon, 3 Sep 2018 04:52:41 +0200 +Subject: [PATCH 26/29] intel-iommu: rework the page walk logic + +RH-Author: Peter Xu +Message-id: <20180903045241.6456-10-peterx@redhat.com> +Patchwork-id: 82029 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 9/9] intel-iommu: rework the page walk logic +Bugzilla: 1623859 +RH-Acked-by: Xiao Wang +RH-Acked-by: Auger Eric +RH-Acked-by: Michael S. Tsirkin + +This patch fixes a potential small window that the DMA page table might +be incomplete or invalid when the guest sends domain/context +invalidations to a device. This can cause random DMA errors for +assigned devices. + +This is a major change to the VT-d shadow page walking logic. It +includes but is not limited to: + +- For each VTDAddressSpace, now we maintain what IOVA ranges we have + mapped and what we have not. With that information, now we only send + MAP or UNMAP when necessary. Say, we don't send MAP notifies if we + know we have already mapped the range, meanwhile we don't send UNMAP + notifies if we know we never mapped the range at all. + +- Introduce vtd_sync_shadow_page_table[_range] APIs so that we can call + in any places to resync the shadow page table for a device. + +- When we receive domain/context invalidation, we should not really run + the replay logic, instead we use the new sync shadow page table API to + resync the whole shadow page table without unmapping the whole + region. After this change, we'll only do the page walk once for each + domain invalidations (before this, it can be multiple, depending on + number of notifiers per address space). + +While at it, the page walking logic is also refactored to be simpler. + +CC: QEMU Stable +Reported-by: Jintack Lim +Tested-by: Jintack Lim +Signed-off-by: Peter Xu +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 63b88968f139b6a77f2f81e6f1eedf70c0170a85) +Signed-off-by: Peter Xu +Signed-off-by: Miroslav Rezanina +--- + hw/i386/intel_iommu.c | 213 ++++++++++++++++++++++++++++++------------ + hw/i386/trace-events | 3 +- + include/hw/i386/intel_iommu.h | 2 + + 3 files changed, 159 insertions(+), 59 deletions(-) + +diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c +index 61bb3d3..b5a09b7 100644 +--- a/hw/i386/intel_iommu.c ++++ b/hw/i386/intel_iommu.c +@@ -769,10 +769,77 @@ typedef struct { + + static int vtd_page_walk_one(IOMMUTLBEntry *entry, vtd_page_walk_info *info) + { ++ VTDAddressSpace *as = info->as; + vtd_page_walk_hook hook_fn = info->hook_fn; + void *private = info->private; ++ DMAMap target = { ++ .iova = entry->iova, ++ .size = entry->addr_mask, ++ .translated_addr = entry->translated_addr, ++ .perm = entry->perm, ++ }; ++ DMAMap *mapped = iova_tree_find(as->iova_tree, &target); ++ ++ if (entry->perm == IOMMU_NONE && !info->notify_unmap) { ++ trace_vtd_page_walk_one_skip_unmap(entry->iova, entry->addr_mask); ++ return 0; ++ } + + assert(hook_fn); ++ ++ /* Update local IOVA mapped ranges */ ++ if (entry->perm) { ++ if (mapped) { ++ /* If it's exactly the same translation, skip */ ++ if (!memcmp(mapped, &target, sizeof(target))) { ++ trace_vtd_page_walk_one_skip_map(entry->iova, entry->addr_mask, ++ entry->translated_addr); ++ return 0; ++ } else { ++ /* ++ * Translation changed. Normally this should not ++ * happen, but it can happen when with buggy guest ++ * OSes. Note that there will be a small window that ++ * we don't have map at all. But that's the best ++ * effort we can do. The ideal way to emulate this is ++ * atomically modify the PTE to follow what has ++ * changed, but we can't. One example is that vfio ++ * driver only has VFIO_IOMMU_[UN]MAP_DMA but no ++ * interface to modify a mapping (meanwhile it seems ++ * meaningless to even provide one). Anyway, let's ++ * mark this as a TODO in case one day we'll have ++ * a better solution. ++ */ ++ IOMMUAccessFlags cache_perm = entry->perm; ++ int ret; ++ ++ /* Emulate an UNMAP */ ++ entry->perm = IOMMU_NONE; ++ trace_vtd_page_walk_one(info->domain_id, ++ entry->iova, ++ entry->translated_addr, ++ entry->addr_mask, ++ entry->perm); ++ ret = hook_fn(entry, private); ++ if (ret) { ++ return ret; ++ } ++ /* Drop any existing mapping */ ++ iova_tree_remove(as->iova_tree, &target); ++ /* Recover the correct permission */ ++ entry->perm = cache_perm; ++ } ++ } ++ iova_tree_insert(as->iova_tree, &target); ++ } else { ++ if (!mapped) { ++ /* Skip since we didn't map this range at all */ ++ trace_vtd_page_walk_one_skip_unmap(entry->iova, entry->addr_mask); ++ return 0; ++ } ++ iova_tree_remove(as->iova_tree, &target); ++ } ++ + trace_vtd_page_walk_one(info->domain_id, entry->iova, + entry->translated_addr, entry->addr_mask, + entry->perm); +@@ -834,45 +901,34 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, + */ + entry_valid = read_cur | write_cur; + +- entry.target_as = &address_space_memory; +- entry.iova = iova & subpage_mask; +- entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur); +- entry.addr_mask = ~subpage_mask; +- +- if (vtd_is_last_slpte(slpte, level)) { +- /* NOTE: this is only meaningful if entry_valid == true */ +- entry.translated_addr = vtd_get_slpte_addr(slpte, info->aw); +- if (!entry_valid && !info->notify_unmap) { +- trace_vtd_page_walk_skip_perm(iova, iova_next); +- goto next; +- } +- ret = vtd_page_walk_one(&entry, info); +- if (ret < 0) { +- return ret; +- } +- } else { +- if (!entry_valid) { +- if (info->notify_unmap) { +- /* +- * The whole entry is invalid; unmap it all. +- * Translated address is meaningless, zero it. +- */ +- entry.translated_addr = 0x0; +- ret = vtd_page_walk_one(&entry, info); +- if (ret < 0) { +- return ret; +- } +- } else { +- trace_vtd_page_walk_skip_perm(iova, iova_next); +- } +- goto next; +- } ++ if (!vtd_is_last_slpte(slpte, level) && entry_valid) { ++ /* ++ * This is a valid PDE (or even bigger than PDE). We need ++ * to walk one further level. ++ */ + ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte, info->aw), + iova, MIN(iova_next, end), level - 1, + read_cur, write_cur, info); +- if (ret < 0) { +- return ret; +- } ++ } else { ++ /* ++ * This means we are either: ++ * ++ * (1) the real page entry (either 4K page, or huge page) ++ * (2) the whole range is invalid ++ * ++ * In either case, we send an IOTLB notification down. ++ */ ++ entry.target_as = &address_space_memory; ++ entry.iova = iova & subpage_mask; ++ entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur); ++ entry.addr_mask = ~subpage_mask; ++ /* NOTE: this is only meaningful if entry_valid == true */ ++ entry.translated_addr = vtd_get_slpte_addr(slpte, info->aw); ++ ret = vtd_page_walk_one(&entry, info); ++ } ++ ++ if (ret < 0) { ++ return ret; + } + + next: +@@ -964,6 +1020,58 @@ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, + return 0; + } + ++static int vtd_sync_shadow_page_hook(IOMMUTLBEntry *entry, ++ void *private) ++{ ++ memory_region_notify_iommu((IOMMUMemoryRegion *)private, *entry); ++ return 0; ++} ++ ++/* If context entry is NULL, we'll try to fetch it on our own. */ ++static int vtd_sync_shadow_page_table_range(VTDAddressSpace *vtd_as, ++ VTDContextEntry *ce, ++ hwaddr addr, hwaddr size) ++{ ++ IntelIOMMUState *s = vtd_as->iommu_state; ++ vtd_page_walk_info info = { ++ .hook_fn = vtd_sync_shadow_page_hook, ++ .private = (void *)&vtd_as->iommu, ++ .notify_unmap = true, ++ .aw = s->aw_bits, ++ .as = vtd_as, ++ }; ++ VTDContextEntry ce_cache; ++ int ret; ++ ++ if (ce) { ++ /* If the caller provided context entry, use it */ ++ ce_cache = *ce; ++ } else { ++ /* If the caller didn't provide ce, try to fetch */ ++ ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), ++ vtd_as->devfn, &ce_cache); ++ if (ret) { ++ /* ++ * This should not really happen, but in case it happens, ++ * we just skip the sync for this time. After all we even ++ * don't have the root table pointer! ++ */ ++ trace_vtd_err("Detected invalid context entry when " ++ "trying to sync shadow page table"); ++ return 0; ++ } ++ } ++ ++ info.domain_id = VTD_CONTEXT_ENTRY_DID(ce_cache.hi); ++ ++ return vtd_page_walk(&ce_cache, addr, addr + size, &info); ++} ++ ++static int vtd_sync_shadow_page_table(VTDAddressSpace *vtd_as) ++{ ++ return vtd_sync_shadow_page_table_range(vtd_as, NULL, 0, UINT64_MAX); ++} ++ + /* + * Fetch translation type for specific device. Returns <0 if error + * happens, otherwise return the shifted type to check against +@@ -1296,7 +1404,7 @@ static void vtd_iommu_replay_all(IntelIOMMUState *s) + VTDAddressSpace *vtd_as; + + QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { +- memory_region_iommu_replay_all(&vtd_as->iommu); ++ vtd_sync_shadow_page_table(vtd_as); + } + } + +@@ -1371,14 +1479,13 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s, + vtd_switch_address_space(vtd_as); + /* + * So a device is moving out of (or moving into) a +- * domain, a replay() suites here to notify all the +- * IOMMU_NOTIFIER_MAP registers about this change. ++ * domain, resync the shadow page table. + * This won't bring bad even if we have no such + * notifier registered - the IOMMU notification + * framework will skip MAP notifications if that + * happened. + */ +- memory_region_iommu_replay_all(&vtd_as->iommu); ++ vtd_sync_shadow_page_table(vtd_as); + } + } + } +@@ -1436,18 +1543,11 @@ static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) + if (!vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), + vtd_as->devfn, &ce) && + domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { +- memory_region_iommu_replay_all(&vtd_as->iommu); ++ vtd_sync_shadow_page_table(vtd_as); + } + } + } + +-static int vtd_page_invalidate_notify_hook(IOMMUTLBEntry *entry, +- void *private) +-{ +- memory_region_notify_iommu((IOMMUMemoryRegion *)private, *entry); +- return 0; +-} +- + static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, + uint16_t domain_id, hwaddr addr, + uint8_t am) +@@ -1462,21 +1562,12 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, + vtd_as->devfn, &ce); + if (!ret && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { + if (vtd_as_has_map_notifier(vtd_as)) { +- vtd_page_walk_info info = { +- .hook_fn = vtd_page_invalidate_notify_hook, +- .private = (void *)&vtd_as->iommu, +- .notify_unmap = true, +- .aw = s->aw_bits, +- .as = vtd_as, +- .domain_id = domain_id, +- }; +- + /* + * As long as we have MAP notifications registered in + * any of our IOMMU notifiers, we need to sync the + * shadow page table. + */ +- vtd_page_walk(&ce, addr, addr + size, &info); ++ vtd_sync_shadow_page_table_range(vtd_as, &ce, addr, size); + } else { + /* + * For UNMAP-only notifiers, we don't need to walk the +@@ -2806,6 +2897,7 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) + vtd_dev_as->devfn = (uint8_t)devfn; + vtd_dev_as->iommu_state = s; + vtd_dev_as->context_cache_entry.context_cache_gen = 0; ++ vtd_dev_as->iova_tree = iova_tree_new(); + + /* + * Memory region relationships looks like (Address range shows +@@ -2858,6 +2950,7 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) + hwaddr start = n->start; + hwaddr end = n->end; + IntelIOMMUState *s = as->iommu_state; ++ DMAMap map; + + /* + * Note: all the codes in this function has a assumption that IOVA +@@ -2902,6 +2995,10 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) + VTD_PCI_FUNC(as->devfn), + entry.iova, size); + ++ map.iova = entry.iova; ++ map.size = entry.addr_mask; ++ iova_tree_remove(as->iova_tree, &map); ++ + memory_region_notify_one(n, &entry); + } + +diff --git a/hw/i386/trace-events b/hw/i386/trace-events +index ca23ba9..e14d06e 100644 +--- a/hw/i386/trace-events ++++ b/hw/i386/trace-events +@@ -40,8 +40,9 @@ vtd_replay_ce_valid(uint8_t bus, uint8_t dev, uint8_t fn, uint16_t domain, uint6 + vtd_replay_ce_invalid(uint8_t bus, uint8_t dev, uint8_t fn) "replay invalid context device %02"PRIx8":%02"PRIx8".%02"PRIx8 + vtd_page_walk_level(uint64_t addr, uint32_t level, uint64_t start, uint64_t end) "walk (base=0x%"PRIx64", level=%"PRIu32") iova range 0x%"PRIx64" - 0x%"PRIx64 + vtd_page_walk_one(uint16_t domain, uint64_t iova, uint64_t gpa, uint64_t mask, int perm) "domain 0x%"PRIu16" iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64" perm %d" ++vtd_page_walk_one_skip_map(uint64_t iova, uint64_t mask, uint64_t translated) "iova 0x%"PRIx64" mask 0x%"PRIx64" translated 0x%"PRIx64 ++vtd_page_walk_one_skip_unmap(uint64_t iova, uint64_t mask) "iova 0x%"PRIx64" mask 0x%"PRIx64 + vtd_page_walk_skip_read(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to unable to read" +-vtd_page_walk_skip_perm(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to perm empty" + vtd_page_walk_skip_reserve(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to rsrv set" + vtd_switch_address_space(uint8_t bus, uint8_t slot, uint8_t fn, bool on) "Device %02x:%02x.%x switching address space (iommu enabled=%d)" + vtd_as_unmap_whole(uint8_t bus, uint8_t slot, uint8_t fn, uint64_t iova, uint64_t size) "Device %02x:%02x.%x start 0x%"PRIx64" size 0x%"PRIx64 +diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h +index 156f35e..fbfedcb 100644 +--- a/include/hw/i386/intel_iommu.h ++++ b/include/hw/i386/intel_iommu.h +@@ -27,6 +27,7 @@ + #include "hw/i386/ioapic.h" + #include "hw/pci/msi.h" + #include "hw/sysbus.h" ++#include "qemu/iova-tree.h" + + #define TYPE_INTEL_IOMMU_DEVICE "intel-iommu" + #define INTEL_IOMMU_DEVICE(obj) \ +@@ -95,6 +96,7 @@ struct VTDAddressSpace { + QLIST_ENTRY(VTDAddressSpace) next; + /* Superset of notifier flags that this address space has */ + IOMMUNotifierFlag notifier_flags; ++ IOVATree *iova_tree; /* Traces mapped IOVA ranges */ + }; + + struct VTDBus { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-intel-iommu-send-PSI-always-even-if-across-PDEs.patch b/SOURCES/kvm-intel-iommu-send-PSI-always-even-if-across-PDEs.patch new file mode 100644 index 0000000..f668ad1 --- /dev/null +++ b/SOURCES/kvm-intel-iommu-send-PSI-always-even-if-across-PDEs.patch @@ -0,0 +1,131 @@ +From db590f2a02907a6762edd0877b32e79405ed4932 Mon Sep 17 00:00:00 2001 +From: Peter Xu +Date: Mon, 3 Sep 2018 04:52:33 +0200 +Subject: [PATCH 18/29] intel-iommu: send PSI always even if across PDEs + +RH-Author: Peter Xu +Message-id: <20180903045241.6456-2-peterx@redhat.com> +Patchwork-id: 82021 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/9] intel-iommu: send PSI always even if across PDEs +Bugzilla: 1623859 +RH-Acked-by: Xiao Wang +RH-Acked-by: Auger Eric +RH-Acked-by: Michael S. Tsirkin + +SECURITY IMPLICATION: without this patch, any guest with both assigned +device and a vIOMMU might encounter stale IO page mappings even if guest +has already unmapped the page, which may lead to guest memory +corruption. The stale mappings will only be limited to the guest's own +memory range, so it should not affect the host memory or other guests on +the host. + +During IOVA page table walking, there is a special case when the PSI +covers one whole PDE (Page Directory Entry, which contains 512 Page +Table Entries) or more. In the past, we skip that entry and we don't +notify the IOMMU notifiers. This is not correct. We should send UNMAP +notification to registered UNMAP notifiers in this case. + +For UNMAP only notifiers, this might cause IOTLBs cached in the devices +even if they were already invalid. For MAP/UNMAP notifiers like +vfio-pci, this will cause stale page mappings. + +This special case doesn't trigger often, but it is very easy to be +triggered by nested device assignments, since in that case we'll +possibly map the whole L2 guest RAM region into the device's IOVA +address space (several GBs at least), which is far bigger than normal +kernel driver usages of the device (tens of MBs normally). + +Without this patch applied to L1 QEMU, nested device assignment to L2 +guests will dump some errors like: + +qemu-system-x86_64: VFIO_MAP_DMA: -17 +qemu-system-x86_64: vfio_dma_map(0x557305420c30, 0xad000, 0x1000, + 0x7f89a920d000) = -17 (File exists) + +CC: QEMU Stable +Acked-by: Jason Wang +[peterx: rewrite the commit message] +Signed-off-by: Peter Xu +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 36d2d52bdb45f5b753a61fdaf0fe7891f1f5b61d) +Signed-off-by: Peter Xu + +Signed-off-by: Miroslav Rezanina +--- + hw/i386/intel_iommu.c | 42 ++++++++++++++++++++++++++++++------------ + 1 file changed, 30 insertions(+), 12 deletions(-) + +diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c +index fb31de9..b359efd 100644 +--- a/hw/i386/intel_iommu.c ++++ b/hw/i386/intel_iommu.c +@@ -722,6 +722,15 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write, + + typedef int (*vtd_page_walk_hook)(IOMMUTLBEntry *entry, void *private); + ++static int vtd_page_walk_one(IOMMUTLBEntry *entry, int level, ++ vtd_page_walk_hook hook_fn, void *private) ++{ ++ assert(hook_fn); ++ trace_vtd_page_walk_one(level, entry->iova, entry->translated_addr, ++ entry->addr_mask, entry->perm); ++ return hook_fn(entry, private); ++} ++ + /** + * vtd_page_walk_level - walk over specific level for IOVA range + * +@@ -781,28 +790,37 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, + */ + entry_valid = read_cur | write_cur; + ++ entry.target_as = &address_space_memory; ++ entry.iova = iova & subpage_mask; ++ entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur); ++ entry.addr_mask = ~subpage_mask; ++ + if (vtd_is_last_slpte(slpte, level)) { +- entry.target_as = &address_space_memory; +- entry.iova = iova & subpage_mask; + /* NOTE: this is only meaningful if entry_valid == true */ + entry.translated_addr = vtd_get_slpte_addr(slpte, aw); +- entry.addr_mask = ~subpage_mask; +- entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur); + if (!entry_valid && !notify_unmap) { + trace_vtd_page_walk_skip_perm(iova, iova_next); + goto next; + } +- trace_vtd_page_walk_one(level, entry.iova, entry.translated_addr, +- entry.addr_mask, entry.perm); +- if (hook_fn) { +- ret = hook_fn(&entry, private); +- if (ret < 0) { +- return ret; +- } ++ ret = vtd_page_walk_one(&entry, level, hook_fn, private); ++ if (ret < 0) { ++ return ret; + } + } else { + if (!entry_valid) { +- trace_vtd_page_walk_skip_perm(iova, iova_next); ++ if (notify_unmap) { ++ /* ++ * The whole entry is invalid; unmap it all. ++ * Translated address is meaningless, zero it. ++ */ ++ entry.translated_addr = 0x0; ++ ret = vtd_page_walk_one(&entry, level, hook_fn, private); ++ if (ret < 0) { ++ return ret; ++ } ++ } else { ++ trace_vtd_page_walk_skip_perm(iova, iova_next); ++ } + goto next; + } + ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte, aw), iova, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-intel-iommu-start-to-use-error_report_once.patch b/SOURCES/kvm-intel-iommu-start-to-use-error_report_once.patch new file mode 100644 index 0000000..9f44d7a --- /dev/null +++ b/SOURCES/kvm-intel-iommu-start-to-use-error_report_once.patch @@ -0,0 +1,236 @@ +From 6c5d8bb3a12e24bdd909beb2ed2c9d6adffdef36 Mon Sep 17 00:00:00 2001 +From: Peter Xu +Date: Thu, 8 Nov 2018 05:37:16 +0100 +Subject: [PATCH 09/22] intel-iommu: start to use error_report_once +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Peter Xu +Message-id: <20181108053721.13162-3-peterx@redhat.com> +Patchwork-id: 82954 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 2/7] intel-iommu: start to use error_report_once +Bugzilla: 1627272 +RH-Acked-by: Auger Eric +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Philippe Mathieu-Daudé + +Replace existing trace_vtd_err() with error_report_once() then stderr +will capture something if any of the error happens, meanwhile we don't +suffer from any DDOS. Then remove the trace point. Since at it, +provide more information where proper (now we can pass parameters into +the report function). + +Signed-off-by: Peter Xu +Message-Id: <20180815095328.32414-3-peterx@redhat.com> +Reviewed-by: Philippe Mathieu-Daudé +[Two format strings fixed, whitespace tidied up] +Signed-off-by: Markus Armbruster +(cherry picked from commit 1376211f77bdcd84dc4acb877690f7399d8cf58a) +Signed-off-by: Peter Xu + +Signed-off-by: Miroslav Rezanina +--- + hw/i386/intel_iommu.c | 65 +++++++++++++++++++++++++++++---------------------- + hw/i386/trace-events | 1 - + 2 files changed, 37 insertions(+), 29 deletions(-) + +diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c +index b5a09b7..ab11cc4 100644 +--- a/hw/i386/intel_iommu.c ++++ b/hw/i386/intel_iommu.c +@@ -311,14 +311,14 @@ static void vtd_generate_fault_event(IntelIOMMUState *s, uint32_t pre_fsts) + { + if (pre_fsts & VTD_FSTS_PPF || pre_fsts & VTD_FSTS_PFO || + pre_fsts & VTD_FSTS_IQE) { +- trace_vtd_err("There are previous interrupt conditions " +- "to be serviced by software, fault event " +- "is not generated."); ++ error_report_once("There are previous interrupt conditions " ++ "to be serviced by software, fault event " ++ "is not generated"); + return; + } + vtd_set_clear_mask_long(s, DMAR_FECTL_REG, 0, VTD_FECTL_IP); + if (vtd_get_long_raw(s, DMAR_FECTL_REG) & VTD_FECTL_IM) { +- trace_vtd_err("Interrupt Mask set, irq is not generated."); ++ error_report_once("Interrupt Mask set, irq is not generated"); + } else { + vtd_generate_interrupt(s, DMAR_FEADDR_REG, DMAR_FEDATA_REG); + vtd_set_clear_mask_long(s, DMAR_FECTL_REG, VTD_FECTL_IP, 0); +@@ -426,20 +426,20 @@ static void vtd_report_dmar_fault(IntelIOMMUState *s, uint16_t source_id, + trace_vtd_dmar_fault(source_id, fault, addr, is_write); + + if (fsts_reg & VTD_FSTS_PFO) { +- trace_vtd_err("New fault is not recorded due to " +- "Primary Fault Overflow."); ++ error_report_once("New fault is not recorded due to " ++ "Primary Fault Overflow"); + return; + } + + if (vtd_try_collapse_fault(s, source_id)) { +- trace_vtd_err("New fault is not recorded due to " +- "compression of faults."); ++ error_report_once("New fault is not recorded due to " ++ "compression of faults"); + return; + } + + if (vtd_is_frcd_set(s, s->next_frcd_reg)) { +- trace_vtd_err("Next Fault Recording Reg is used, " +- "new fault is not recorded, set PFO field."); ++ error_report_once("Next Fault Recording Reg is used, " ++ "new fault is not recorded, set PFO field"); + vtd_set_clear_mask_long(s, DMAR_FSTS_REG, 0, VTD_FSTS_PFO); + return; + } +@@ -447,8 +447,8 @@ static void vtd_report_dmar_fault(IntelIOMMUState *s, uint16_t source_id, + vtd_record_frcd(s, s->next_frcd_reg, source_id, addr, fault, is_write); + + if (fsts_reg & VTD_FSTS_PPF) { +- trace_vtd_err("There are pending faults already, " +- "fault event is not generated."); ++ error_report_once("There are pending faults already, " ++ "fault event is not generated"); + vtd_set_frcd_and_update_ppf(s, s->next_frcd_reg); + s->next_frcd_reg++; + if (s->next_frcd_reg == DMAR_FRCD_REG_NR) { +@@ -1056,8 +1056,10 @@ static int vtd_sync_shadow_page_table_range(VTDAddressSpace *vtd_as, + * we just skip the sync for this time. After all we even + * don't have the root table pointer! + */ +- trace_vtd_err("Detected invalid context entry when " +- "trying to sync shadow page table"); ++ error_report_once("%s: invalid context entry for bus 0x%x" ++ " devfn 0x%x", ++ __func__, pci_bus_num(vtd_as->bus), ++ vtd_as->devfn); + return 0; + } + } +@@ -1514,7 +1516,8 @@ static uint64_t vtd_context_cache_invalidate(IntelIOMMUState *s, uint64_t val) + break; + + default: +- trace_vtd_err("Context cache invalidate type error."); ++ error_report_once("%s: invalid context: 0x%" PRIx64, ++ __func__, val); + caig = 0; + } + return caig; +@@ -1634,7 +1637,8 @@ static uint64_t vtd_iotlb_flush(IntelIOMMUState *s, uint64_t val) + am = VTD_IVA_AM(addr); + addr = VTD_IVA_ADDR(addr); + if (am > VTD_MAMV) { +- trace_vtd_err("IOTLB PSI flush: address mask overflow."); ++ error_report_once("%s: address mask overflow: 0x%" PRIx64, ++ __func__, vtd_get_quad_raw(s, DMAR_IVA_REG)); + iaig = 0; + break; + } +@@ -1643,7 +1647,8 @@ static uint64_t vtd_iotlb_flush(IntelIOMMUState *s, uint64_t val) + break; + + default: +- trace_vtd_err("IOTLB flush: invalid granularity."); ++ error_report_once("%s: invalid granularity: 0x%" PRIx64, ++ __func__, val); + iaig = 0; + } + return iaig; +@@ -1793,8 +1798,8 @@ static void vtd_handle_ccmd_write(IntelIOMMUState *s) + /* Context-cache invalidation request */ + if (val & VTD_CCMD_ICC) { + if (s->qi_enabled) { +- trace_vtd_err("Queued Invalidation enabled, " +- "should not use register-based invalidation"); ++ error_report_once("Queued Invalidation enabled, " ++ "should not use register-based invalidation"); + return; + } + ret = vtd_context_cache_invalidate(s, val); +@@ -1814,8 +1819,8 @@ static void vtd_handle_iotlb_write(IntelIOMMUState *s) + /* IOTLB invalidation request */ + if (val & VTD_TLB_IVT) { + if (s->qi_enabled) { +- trace_vtd_err("Queued Invalidation enabled, " +- "should not use register-based invalidation."); ++ error_report_once("Queued Invalidation enabled, " ++ "should not use register-based invalidation"); + return; + } + ret = vtd_iotlb_flush(s, val); +@@ -1833,7 +1838,7 @@ static bool vtd_get_inv_desc(dma_addr_t base_addr, uint32_t offset, + dma_addr_t addr = base_addr + offset * sizeof(*inv_desc); + if (dma_memory_read(&address_space_memory, addr, inv_desc, + sizeof(*inv_desc))) { +- trace_vtd_err("Read INV DESC failed."); ++ error_report_once("Read INV DESC failed"); + inv_desc->lo = 0; + inv_desc->hi = 0; + return false; +@@ -2188,7 +2193,8 @@ static uint64_t vtd_mem_read(void *opaque, hwaddr addr, unsigned size) + trace_vtd_reg_read(addr, size); + + if (addr + size > DMAR_REG_SIZE) { +- trace_vtd_err("Read MMIO over range."); ++ error_report_once("%s: MMIO over range: addr=0x%" PRIx64 ++ " size=0x%u", __func__, addr, size); + return (uint64_t)-1; + } + +@@ -2239,7 +2245,8 @@ static void vtd_mem_write(void *opaque, hwaddr addr, + trace_vtd_reg_write(addr, size, val); + + if (addr + size > DMAR_REG_SIZE) { +- trace_vtd_err("Write MMIO over range."); ++ error_report_once("%s: MMIO over range: addr=0x%" PRIx64 ++ " size=0x%u", __func__, addr, size); + return; + } + +@@ -2610,7 +2617,8 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, + addr = iommu->intr_root + index * sizeof(*entry); + if (dma_memory_read(&address_space_memory, addr, entry, + sizeof(*entry))) { +- trace_vtd_err("Memory read failed for IRTE."); ++ error_report_once("%s: read failed: ind=0x%x addr=0x%" PRIx64, ++ __func__, index, addr); + return -VTD_FR_IR_ROOT_INVAL; + } + +@@ -2742,14 +2750,15 @@ static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu, + } + + if (origin->address & VTD_MSI_ADDR_HI_MASK) { +- trace_vtd_err("MSI address high 32 bits non-zero when " +- "Interrupt Remapping enabled."); ++ error_report_once("%s: MSI address high 32 bits non-zero detected: " ++ "address=0x%" PRIx64, __func__, origin->address); + return -VTD_FR_IR_REQ_RSVD; + } + + addr.data = origin->address & VTD_MSI_ADDR_LO_MASK; + if (addr.addr.__head != 0xfee) { +- trace_vtd_err("MSI addr low 32 bit invalid."); ++ error_report_once("%s: MSI address low 32 bit invalid: 0x%" PRIx32, ++ __func__, addr.data); + return -VTD_FR_IR_REQ_RSVD; + } + +diff --git a/hw/i386/trace-events b/hw/i386/trace-events +index e14d06e..922431b 100644 +--- a/hw/i386/trace-events ++++ b/hw/i386/trace-events +@@ -69,7 +69,6 @@ vtd_ir_remap_msi_req(uint64_t addr, uint64_t data) "addr 0x%"PRIx64" data 0x%"PR + vtd_fsts_ppf(bool set) "FSTS PPF bit set to %d" + vtd_fsts_clear_ip(void) "" + vtd_frr_new(int index, uint64_t hi, uint64_t lo) "index %d high 0x%"PRIx64" low 0x%"PRIx64 +-vtd_err(const char *str) "%s" + vtd_err_dmar_iova_overflow(uint64_t iova) "iova 0x%"PRIx64 + vtd_err_dmar_slpte_read_error(uint64_t iova, int level) "iova 0x%"PRIx64" level %d" + vtd_err_dmar_slpte_perm_error(uint64_t iova, int level, uint64_t slpte, bool is_write) "iova 0x%"PRIx64" level %d slpte 0x%"PRIx64" write %d" +-- +1.8.3.1 + diff --git a/SOURCES/kvm-intel-iommu-trace-domain-id-during-page-walk.patch b/SOURCES/kvm-intel-iommu-trace-domain-id-during-page-walk.patch new file mode 100644 index 0000000..141ecec --- /dev/null +++ b/SOURCES/kvm-intel-iommu-trace-domain-id-during-page-walk.patch @@ -0,0 +1,119 @@ +From 2511801028d31c9a6cee77d8a978d2642b82e0df Mon Sep 17 00:00:00 2001 +From: Peter Xu +Date: Mon, 3 Sep 2018 04:52:39 +0200 +Subject: [PATCH 24/29] intel-iommu: trace domain id during page walk + +RH-Author: Peter Xu +Message-id: <20180903045241.6456-8-peterx@redhat.com> +Patchwork-id: 82028 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 7/9] intel-iommu: trace domain id during page walk +Bugzilla: 1623859 +RH-Acked-by: Xiao Wang +RH-Acked-by: Auger Eric +RH-Acked-by: Michael S. Tsirkin + +This patch only modifies the trace points. + +Previously we were tracing page walk levels. They are redundant since +we have page mask (size) already. Now we trace something much more +useful which is the domain ID of the page walking. That can be very +useful when we trace more than one devices on the same system, so that +we can know which map is for which domain. + +CC: QEMU Stable +Signed-off-by: Peter Xu +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit d118c06ebbee2d23ddf873cae4a809311aa61310) +Signed-off-by: Peter Xu +Signed-off-by: Miroslav Rezanina +--- + hw/i386/intel_iommu.c | 16 ++++++++++------ + hw/i386/trace-events | 2 +- + 2 files changed, 11 insertions(+), 7 deletions(-) + +diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c +index a882894..61bb3d3 100644 +--- a/hw/i386/intel_iommu.c ++++ b/hw/i386/intel_iommu.c +@@ -756,6 +756,7 @@ typedef int (*vtd_page_walk_hook)(IOMMUTLBEntry *entry, void *private); + * @notify_unmap: whether we should notify invalid entries + * @as: VT-d address space of the device + * @aw: maximum address width ++ * @domain: domain ID of the page walk + */ + typedef struct { + VTDAddressSpace *as; +@@ -763,17 +764,18 @@ typedef struct { + void *private; + bool notify_unmap; + uint8_t aw; ++ uint16_t domain_id; + } vtd_page_walk_info; + +-static int vtd_page_walk_one(IOMMUTLBEntry *entry, int level, +- vtd_page_walk_info *info) ++static int vtd_page_walk_one(IOMMUTLBEntry *entry, vtd_page_walk_info *info) + { + vtd_page_walk_hook hook_fn = info->hook_fn; + void *private = info->private; + + assert(hook_fn); +- trace_vtd_page_walk_one(level, entry->iova, entry->translated_addr, +- entry->addr_mask, entry->perm); ++ trace_vtd_page_walk_one(info->domain_id, entry->iova, ++ entry->translated_addr, entry->addr_mask, ++ entry->perm); + return hook_fn(entry, private); + } + +@@ -844,7 +846,7 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, + trace_vtd_page_walk_skip_perm(iova, iova_next); + goto next; + } +- ret = vtd_page_walk_one(&entry, level, info); ++ ret = vtd_page_walk_one(&entry, info); + if (ret < 0) { + return ret; + } +@@ -856,7 +858,7 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, + * Translated address is meaningless, zero it. + */ + entry.translated_addr = 0x0; +- ret = vtd_page_walk_one(&entry, level, info); ++ ret = vtd_page_walk_one(&entry, info); + if (ret < 0) { + return ret; + } +@@ -1466,6 +1468,7 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, + .notify_unmap = true, + .aw = s->aw_bits, + .as = vtd_as, ++ .domain_id = domain_id, + }; + + /* +@@ -2947,6 +2950,7 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) + .notify_unmap = false, + .aw = s->aw_bits, + .as = vtd_as, ++ .domain_id = VTD_CONTEXT_ENTRY_DID(ce.hi), + }; + + vtd_page_walk(&ce, 0, ~0ULL, &info); +diff --git a/hw/i386/trace-events b/hw/i386/trace-events +index 22d4464..ca23ba9 100644 +--- a/hw/i386/trace-events ++++ b/hw/i386/trace-events +@@ -39,7 +39,7 @@ vtd_fault_disabled(void) "Fault processing disabled for context entry" + vtd_replay_ce_valid(uint8_t bus, uint8_t dev, uint8_t fn, uint16_t domain, uint64_t hi, uint64_t lo) "replay valid context device %02"PRIx8":%02"PRIx8".%02"PRIx8" domain 0x%"PRIx16" hi 0x%"PRIx64" lo 0x%"PRIx64 + vtd_replay_ce_invalid(uint8_t bus, uint8_t dev, uint8_t fn) "replay invalid context device %02"PRIx8":%02"PRIx8".%02"PRIx8 + vtd_page_walk_level(uint64_t addr, uint32_t level, uint64_t start, uint64_t end) "walk (base=0x%"PRIx64", level=%"PRIu32") iova range 0x%"PRIx64" - 0x%"PRIx64 +-vtd_page_walk_one(uint32_t level, uint64_t iova, uint64_t gpa, uint64_t mask, int perm) "detected page level 0x%"PRIx32" iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64" perm %d" ++vtd_page_walk_one(uint16_t domain, uint64_t iova, uint64_t gpa, uint64_t mask, int perm) "domain 0x%"PRIu16" iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64" perm %d" + vtd_page_walk_skip_read(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to unable to read" + vtd_page_walk_skip_perm(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to perm empty" + vtd_page_walk_skip_reserve(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to rsrv set" +-- +1.8.3.1 + diff --git a/SOURCES/kvm-intel_iommu-better-handling-of-dmar-state-switch.patch b/SOURCES/kvm-intel_iommu-better-handling-of-dmar-state-switch.patch new file mode 100644 index 0000000..734b44d --- /dev/null +++ b/SOURCES/kvm-intel_iommu-better-handling-of-dmar-state-switch.patch @@ -0,0 +1,150 @@ +From ad285b807f37f16571d03e7667ea7200fb9b25a7 Mon Sep 17 00:00:00 2001 +From: Peter Xu +Date: Thu, 8 Nov 2018 05:37:19 +0100 +Subject: [PATCH 12/22] intel_iommu: better handling of dmar state switch +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Peter Xu +Message-id: <20181108053721.13162-6-peterx@redhat.com> +Patchwork-id: 82956 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 5/7] intel_iommu: better handling of dmar state switch +Bugzilla: 1627272 +RH-Acked-by: Auger Eric +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Philippe Mathieu-Daudé + +QEMU is not handling the global DMAR switch well, especially when from +"on" to "off". + +Let's first take the example of system reset. + +Assuming that a guest has IOMMU enabled. When it reboots, we will drop +all the existing DMAR mappings to handle the system reset, however we'll +still keep the existing memory layouts which has the IOMMU memory region +enabled. So after the reboot and before the kernel reloads again, there +will be no mapping at all for the host device. That's problematic since +any software (for example, SeaBIOS) that runs earlier than the kernel +after the reboot will assume the IOMMU is disabled, so any DMA from the +software will fail. + +For example, a guest that boots on an assigned NVMe device might fail to +find the boot device after a system reboot/reset and we'll be able to +observe SeaBIOS errors if we capture the debugging log: + + WARNING - Timeout at nvme_wait:144! + +Meanwhile, we should see DMAR errors on the host of that NVMe device. +It's the DMA fault that caused a NVMe driver timeout. + +The correct fix should be that we do proper switching of device DMA +address spaces when system resets, which will setup correct memory +regions and notify the backend of the devices. This might not affect +much on non-assigned devices since QEMU VT-d emulation will assume a +default passthrough mapping if DMAR is not enabled in the GCMD +register (please refer to vtd_iommu_translate). However that's required +for an assigned devices, since that'll rebuild the correct GPA to HPA +mapping that is needed for any DMA operation during guest bootstrap. + +Besides the system reset, we have some other places that might change +the global DMAR status and we'd better do the same thing there. For +example, when we change the state of GCMD register, or the DMAR root +pointer. Do the same refresh for all these places. For these two +places we'll also need to explicitly invalidate the context entry cache +and iotlb cache. + +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1625173 +CC: QEMU Stable +Reported-by: Cong Li +Signed-off-by: Peter Xu +-- +v2: +- do the same for GCMD write, or root pointer update [Alex] +- test is carried out by me this time, by observing the + vtd_switch_address_space tracepoint after system reboot +v3: +- rewrite commit message as suggested by Alex +Signed-off-by: Peter Xu +Reviewed-by: Eric Auger +Reviewed-by: Jason Wang +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 2cc9ddccebcaa48b3debfc279a83761fcbb7616c) +Signed-off-by: Peter Xu + +Signed-off-by: Miroslav Rezanina +--- + hw/i386/intel_iommu.c | 21 ++++++++++++++------- + 1 file changed, 14 insertions(+), 7 deletions(-) + +diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c +index 48d0ba3..a6e87a9 100644 +--- a/hw/i386/intel_iommu.c ++++ b/hw/i386/intel_iommu.c +@@ -37,6 +37,8 @@ + #include "kvm_i386.h" + #include "trace.h" + ++static void vtd_address_space_refresh_all(IntelIOMMUState *s); ++ + static void vtd_define_quad(IntelIOMMUState *s, hwaddr addr, uint64_t val, + uint64_t wmask, uint64_t w1cmask) + { +@@ -1436,7 +1438,7 @@ static void vtd_context_global_invalidate(IntelIOMMUState *s) + vtd_reset_context_cache_locked(s); + } + vtd_iommu_unlock(s); +- vtd_switch_address_space_all(s); ++ vtd_address_space_refresh_all(s); + /* + * From VT-d spec 6.5.2.1, a global context entry invalidation + * should be followed by a IOTLB global invalidation, so we should +@@ -1727,6 +1729,8 @@ static void vtd_handle_gcmd_srtp(IntelIOMMUState *s) + vtd_root_table_setup(s); + /* Ok - report back to driver */ + vtd_set_clear_mask_long(s, DMAR_GSTS_REG, 0, VTD_GSTS_RTPS); ++ vtd_reset_caches(s); ++ vtd_address_space_refresh_all(s); + } + + /* Set Interrupt Remap Table Pointer */ +@@ -1759,7 +1763,8 @@ static void vtd_handle_gcmd_te(IntelIOMMUState *s, bool en) + vtd_set_clear_mask_long(s, DMAR_GSTS_REG, VTD_GSTS_TES, 0); + } + +- vtd_switch_address_space_all(s); ++ vtd_reset_caches(s); ++ vtd_address_space_refresh_all(s); + } + + /* Handle Interrupt Remap Enable/Disable */ +@@ -3059,6 +3064,12 @@ static void vtd_address_space_unmap_all(IntelIOMMUState *s) + } + } + ++static void vtd_address_space_refresh_all(IntelIOMMUState *s) ++{ ++ vtd_address_space_unmap_all(s); ++ vtd_switch_address_space_all(s); ++} ++ + static int vtd_replay_hook(IOMMUTLBEntry *entry, void *private) + { + memory_region_notify_one((IOMMUNotifier *)private, entry); +@@ -3231,11 +3242,7 @@ static void vtd_reset(DeviceState *dev) + IntelIOMMUState *s = INTEL_IOMMU_DEVICE(dev); + + vtd_init(s); +- +- /* +- * When device reset, throw away all mappings and external caches +- */ +- vtd_address_space_unmap_all(s); ++ vtd_address_space_refresh_all(s); + } + + static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-intel_iommu-handle-invalid-ce-for-shadow-sync.patch b/SOURCES/kvm-intel_iommu-handle-invalid-ce-for-shadow-sync.patch new file mode 100644 index 0000000..64f8ee0 --- /dev/null +++ b/SOURCES/kvm-intel_iommu-handle-invalid-ce-for-shadow-sync.patch @@ -0,0 +1,89 @@ +From 80fe503d7c126fa2be6cc424593cae6c522f65e0 Mon Sep 17 00:00:00 2001 +From: Peter Xu +Date: Thu, 8 Nov 2018 05:37:21 +0100 +Subject: [PATCH 14/22] intel_iommu: handle invalid ce for shadow sync +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Peter Xu +Message-id: <20181108053721.13162-8-peterx@redhat.com> +Patchwork-id: 82958 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 7/7] intel_iommu: handle invalid ce for shadow sync +Bugzilla: 1627272 +RH-Acked-by: Auger Eric +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Philippe Mathieu-Daudé + +We should handle VTD_FR_CONTEXT_ENTRY_P properly when synchronizing +shadow page tables. Having invalid context entry there is perfectly +valid when we move a device out of an existing domain. When that +happens, instead of posting an error we invalidate the whole region. + +Without this patch, QEMU will crash if we do these steps: + +(1) start QEMU with VT-d IOMMU and two 10G NICs (ixgbe) +(2) bind the NICs with vfio-pci in the guest +(3) start testpmd with the NICs applied +(4) stop testpmd +(5) rebind the NIC back to ixgbe kernel driver + +The patch should fix it. + +Reported-by: Pei Zhang +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1627272 +Signed-off-by: Peter Xu +Reviewed-by: Eric Auger +Reviewed-by: Maxime Coquelin +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit c28b535d083d0a263d38d9ceeada83cdae8c64f0) +Signed-off-by: Peter Xu +Signed-off-by: Miroslav Rezanina +--- + hw/i386/intel_iommu.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c +index c95128d..12af410 100644 +--- a/hw/i386/intel_iommu.c ++++ b/hw/i386/intel_iommu.c +@@ -38,6 +38,7 @@ + #include "trace.h" + + static void vtd_address_space_refresh_all(IntelIOMMUState *s); ++static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n); + + static void vtd_define_quad(IntelIOMMUState *s, hwaddr addr, uint64_t val, + uint64_t wmask, uint64_t w1cmask) +@@ -1066,11 +1067,27 @@ static int vtd_sync_shadow_page_table(VTDAddressSpace *vtd_as) + { + int ret; + VTDContextEntry ce; ++ IOMMUNotifier *n; + + ret = vtd_dev_to_context_entry(vtd_as->iommu_state, + pci_bus_num(vtd_as->bus), + vtd_as->devfn, &ce); + if (ret) { ++ if (ret == -VTD_FR_CONTEXT_ENTRY_P) { ++ /* ++ * It's a valid scenario to have a context entry that is ++ * not present. For example, when a device is removed ++ * from an existing domain then the context entry will be ++ * zeroed by the guest before it was put into another ++ * domain. When this happens, instead of synchronizing ++ * the shadow pages we should invalidate all existing ++ * mappings and notify the backends. ++ */ ++ IOMMU_NOTIFIER_FOREACH(n, &vtd_as->iommu) { ++ vtd_address_space_unmap(vtd_as, n); ++ } ++ ret = 0; ++ } + return ret; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-intel_iommu-introduce-vtd_reset_caches.patch b/SOURCES/kvm-intel_iommu-introduce-vtd_reset_caches.patch new file mode 100644 index 0000000..4e6c1b2 --- /dev/null +++ b/SOURCES/kvm-intel_iommu-introduce-vtd_reset_caches.patch @@ -0,0 +1,66 @@ +From 7fc26da32e430d69df4ca9f74047310583c97e79 Mon Sep 17 00:00:00 2001 +From: Peter Xu +Date: Thu, 8 Nov 2018 05:37:18 +0100 +Subject: [PATCH 11/22] intel_iommu: introduce vtd_reset_caches() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Peter Xu +Message-id: <20181108053721.13162-5-peterx@redhat.com> +Patchwork-id: 82955 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 4/7] intel_iommu: introduce vtd_reset_caches() +Bugzilla: 1627272 +RH-Acked-by: Auger Eric +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Philippe Mathieu-Daudé + +Provide the function and use it in vtd_init(). Used to reset both +context entry cache and iotlb cache for the whole IOMMU unit. + +Signed-off-by: Peter Xu +Reviewed-by: Eric Auger +Reviewed-by: Jason Wang +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 06aba4ca52fd2c8718b8ba486f22f0aa7c99ed55) +Signed-off-by: Peter Xu +Signed-off-by: Miroslav Rezanina +--- + hw/i386/intel_iommu.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c +index aab86e9..48d0ba3 100644 +--- a/hw/i386/intel_iommu.c ++++ b/hw/i386/intel_iommu.c +@@ -227,6 +227,14 @@ static void vtd_reset_iotlb(IntelIOMMUState *s) + vtd_iommu_unlock(s); + } + ++static void vtd_reset_caches(IntelIOMMUState *s) ++{ ++ vtd_iommu_lock(s); ++ vtd_reset_iotlb_locked(s); ++ vtd_reset_context_cache_locked(s); ++ vtd_iommu_unlock(s); ++} ++ + static uint64_t vtd_get_iotlb_key(uint64_t gfn, uint16_t source_id, + uint32_t level) + { +@@ -3160,10 +3168,7 @@ static void vtd_init(IntelIOMMUState *s) + s->cap |= VTD_CAP_CM; + } + +- vtd_iommu_lock(s); +- vtd_reset_context_cache_locked(s); +- vtd_reset_iotlb_locked(s); +- vtd_iommu_unlock(s); ++ vtd_reset_caches(s); + + /* Define registers with default values and bit semantics */ + vtd_define_long(s, DMAR_VER_REG, 0x10UL, 0, 0); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-intel_iommu-move-ce-fetching-out-when-sync-shadow.patch b/SOURCES/kvm-intel_iommu-move-ce-fetching-out-when-sync-shadow.patch new file mode 100644 index 0000000..d19c107 --- /dev/null +++ b/SOURCES/kvm-intel_iommu-move-ce-fetching-out-when-sync-shadow.patch @@ -0,0 +1,110 @@ +From 5caab2b4c1876c564f8f9afee0a80a00e1a8880f Mon Sep 17 00:00:00 2001 +From: Peter Xu +Date: Thu, 8 Nov 2018 05:37:20 +0100 +Subject: [PATCH 13/22] intel_iommu: move ce fetching out when sync shadow +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Peter Xu +Message-id: <20181108053721.13162-7-peterx@redhat.com> +Patchwork-id: 82957 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 6/7] intel_iommu: move ce fetching out when sync shadow +Bugzilla: 1627272 +RH-Acked-by: Auger Eric +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Philippe Mathieu-Daudé + +There are two callers for vtd_sync_shadow_page_table_range(): one +provided a valid context entry and one not. Move that fetching +operation into the caller vtd_sync_shadow_page_table() where we need to +fetch the context entry. + +Meanwhile, remove the error_report_once() directly since we're already +tracing all the error cases in the previous call. Instead, return error +number back to caller. This will not change anything functional since +callers are dropping it after all. + +We do this move majorly because we want to do something more later in +vtd_sync_shadow_page_table(). + +Signed-off-by: Peter Xu +Reviewed-by: Eric Auger +Reviewed-by: Maxime Coquelin +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 95ecd3df7815b4bc4f9a0f47e1c64d81434715aa) +Signed-off-by: Peter Xu +Signed-off-by: Miroslav Rezanina +--- + hw/i386/intel_iommu.c | 41 +++++++++++++---------------------------- + 1 file changed, 13 insertions(+), 28 deletions(-) + +diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c +index a6e87a9..c95128d 100644 +--- a/hw/i386/intel_iommu.c ++++ b/hw/i386/intel_iommu.c +@@ -1045,7 +1045,6 @@ static int vtd_sync_shadow_page_hook(IOMMUTLBEntry *entry, + return 0; + } + +-/* If context entry is NULL, we'll try to fetch it on our own. */ + static int vtd_sync_shadow_page_table_range(VTDAddressSpace *vtd_as, + VTDContextEntry *ce, + hwaddr addr, hwaddr size) +@@ -1057,39 +1056,25 @@ static int vtd_sync_shadow_page_table_range(VTDAddressSpace *vtd_as, + .notify_unmap = true, + .aw = s->aw_bits, + .as = vtd_as, ++ .domain_id = VTD_CONTEXT_ENTRY_DID(ce->hi), + }; +- VTDContextEntry ce_cache; +- int ret; +- +- if (ce) { +- /* If the caller provided context entry, use it */ +- ce_cache = *ce; +- } else { +- /* If the caller didn't provide ce, try to fetch */ +- ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), +- vtd_as->devfn, &ce_cache); +- if (ret) { +- /* +- * This should not really happen, but in case it happens, +- * we just skip the sync for this time. After all we even +- * don't have the root table pointer! +- */ +- error_report_once("%s: invalid context entry for bus 0x%x" +- " devfn 0x%x", +- __func__, pci_bus_num(vtd_as->bus), +- vtd_as->devfn); +- return 0; +- } +- } + +- info.domain_id = VTD_CONTEXT_ENTRY_DID(ce_cache.hi); +- +- return vtd_page_walk(&ce_cache, addr, addr + size, &info); ++ return vtd_page_walk(ce, addr, addr + size, &info); + } + + static int vtd_sync_shadow_page_table(VTDAddressSpace *vtd_as) + { +- return vtd_sync_shadow_page_table_range(vtd_as, NULL, 0, UINT64_MAX); ++ int ret; ++ VTDContextEntry ce; ++ ++ ret = vtd_dev_to_context_entry(vtd_as->iommu_state, ++ pci_bus_num(vtd_as->bus), ++ vtd_as->devfn, &ce); ++ if (ret) { ++ return ret; ++ } ++ ++ return vtd_sync_shadow_page_table_range(vtd_as, &ce, 0, UINT64_MAX); + } + + /* +-- +1.8.3.1 + diff --git a/SOURCES/kvm-io-return-0-for-EOF-in-TLS-session-read-after-shutdo.patch b/SOURCES/kvm-io-return-0-for-EOF-in-TLS-session-read-after-shutdo.patch new file mode 100644 index 0000000..53781f6 --- /dev/null +++ b/SOURCES/kvm-io-return-0-for-EOF-in-TLS-session-read-after-shutdo.patch @@ -0,0 +1,127 @@ +From d3244c405b0968db4629855567c3a2c7729bea36 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:31 +0100 +Subject: [PATCH 064/163] io: return 0 for EOF in TLS session read after + shutdown +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190322032241.8111-19-jsnow@redhat.com> +Patchwork-id: 85099 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 18/28] io: return 0 for EOF in TLS session read after shutdown +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Daniel P. Berrangé + +GNUTLS takes a paranoid approach when seeing 0 bytes returned by the +underlying OS read() function. It will consider this an error and +return GNUTLS_E_PREMATURE_TERMINATION instead of propagating the 0 +return value. It expects apps to arrange for clean termination at +the protocol level and not rely on seeing EOF from a read call to +detect shutdown. This is to harden apps against a malicious 3rd party +causing termination of the sockets layer. + +This is unhelpful for the QEMU NBD code which does have a clean +protocol level shutdown, but still relies on seeing 0 from the I/O +channel read in the coroutine handling incoming replies. + +The upshot is that when using a plain NBD connection shutdown is +silent, but when using TLS, the client spams the console with + + Cannot read from TLS channel: Broken pipe + +The NBD connection has, however, called qio_channel_shutdown() +at this point to indicate that it is done with I/O. This gives +the opportunity to optimize the code such that when the channel +has been shutdown in the read direction, the error code +GNUTLS_E_PREMATURE_TERMINATION gets turned into a '0' return +instead of an error. + +Signed-off-by: Daniel P. Berrangé +Message-Id: <20181119134228.11031-1-berrange@redhat.com> +Reviewed-by: Eric Blake +Signed-off-by: Eric Blake +(cherry picked from commit a2458b6f6998c9a079f710ed7495d5c6f037e942) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + crypto/tlssession.c | 3 +++ + include/io/channel-tls.h | 1 + + include/io/channel.h | 6 +++--- + io/channel-tls.c | 5 +++++ + 4 files changed, 12 insertions(+), 3 deletions(-) + +diff --git a/crypto/tlssession.c b/crypto/tlssession.c +index 96a02de..68e0114 100644 +--- a/crypto/tlssession.c ++++ b/crypto/tlssession.c +@@ -427,6 +427,9 @@ qcrypto_tls_session_read(QCryptoTLSSession *session, + case GNUTLS_E_INTERRUPTED: + errno = EINTR; + break; ++ case GNUTLS_E_PREMATURE_TERMINATION: ++ errno = ECONNABORTED; ++ break; + default: + errno = EIO; + break; +diff --git a/include/io/channel-tls.h b/include/io/channel-tls.h +index 87fcaf9..fdbdf12 100644 +--- a/include/io/channel-tls.h ++++ b/include/io/channel-tls.h +@@ -48,6 +48,7 @@ struct QIOChannelTLS { + QIOChannel parent; + QIOChannel *master; + QCryptoTLSSession *session; ++ QIOChannelShutdown shutdown; + }; + + /** +diff --git a/include/io/channel.h b/include/io/channel.h +index e8cdadb..da2f138 100644 +--- a/include/io/channel.h ++++ b/include/io/channel.h +@@ -51,9 +51,9 @@ enum QIOChannelFeature { + typedef enum QIOChannelShutdown QIOChannelShutdown; + + enum QIOChannelShutdown { +- QIO_CHANNEL_SHUTDOWN_BOTH, +- QIO_CHANNEL_SHUTDOWN_READ, +- QIO_CHANNEL_SHUTDOWN_WRITE, ++ QIO_CHANNEL_SHUTDOWN_READ = 1, ++ QIO_CHANNEL_SHUTDOWN_WRITE = 2, ++ QIO_CHANNEL_SHUTDOWN_BOTH = 3, + }; + + typedef gboolean (*QIOChannelFunc)(QIOChannel *ioc, +diff --git a/io/channel-tls.c b/io/channel-tls.c +index 9628e6f..c98ead2 100644 +--- a/io/channel-tls.c ++++ b/io/channel-tls.c +@@ -275,6 +275,9 @@ static ssize_t qio_channel_tls_readv(QIOChannel *ioc, + } else { + return QIO_CHANNEL_ERR_BLOCK; + } ++ } else if (errno == ECONNABORTED && ++ (tioc->shutdown & QIO_CHANNEL_SHUTDOWN_READ)) { ++ return 0; + } + + error_setg_errno(errp, errno, +@@ -357,6 +360,8 @@ static int qio_channel_tls_shutdown(QIOChannel *ioc, + { + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); + ++ tioc->shutdown |= how; ++ + return qio_channel_shutdown(tioc->master, how, errp); + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotest-Fix-241-to-run-in-generic-directory.patch b/SOURCES/kvm-iotest-Fix-241-to-run-in-generic-directory.patch new file mode 100644 index 0000000..85739ed --- /dev/null +++ b/SOURCES/kvm-iotest-Fix-241-to-run-in-generic-directory.patch @@ -0,0 +1,82 @@ +From 295b60369a025fd61f3abe3e6edbd2b298ba8314 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 6 May 2019 17:56:29 +0200 +Subject: [PATCH 19/53] iotest: Fix 241 to run in generic directory + +RH-Author: John Snow +Message-id: <20190506175629.11079-20-jsnow@redhat.com> +Patchwork-id: 87200 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 19/19] iotest: Fix 241 to run in generic directory +Bugzilla: 1692018 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Thomas Huth + +From: Eric Blake + +Filter the qemu-nbd server output to get rid of a direct reference +to my build directory. + +Fixes: e9dce9cb +Reported-by: Max Reitz +Signed-off-by: Eric Blake +Signed-off-by: Kevin Wolf +(cherry picked from commit 9749636b005d118259810afb92482df2fe0ae2ad) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/241 | 4 +++- + tests/qemu-iotests/241.out | 6 +++--- + 2 files changed, 6 insertions(+), 4 deletions(-) + +diff --git a/tests/qemu-iotests/241 b/tests/qemu-iotests/241 +index 4b19685..017a736 100755 +--- a/tests/qemu-iotests/241 ++++ b/tests/qemu-iotests/241 +@@ -28,6 +28,7 @@ nbd_unix_socket=$TEST_DIR/test_qemu_nbd_socket + _cleanup() + { + _cleanup_test_img ++ rm -f "$TEST_DIR/server.log" + nbd_server_stop + } + trap "_cleanup; exit \$status" 0 1 2 3 15 +@@ -69,12 +70,13 @@ echo + + # Intentionally omit '-f' to force image probing, which in turn forces + # sector alignment, here at the server. +-nbd_server_start_unix_socket "$TEST_IMG_FILE" ++nbd_server_start_unix_socket "$TEST_IMG_FILE" 2> "$TEST_DIR/server.log" + + $QEMU_NBD_PROG --list -k $nbd_unix_socket | grep '\(size\|min\)' + $QEMU_IMG map -f raw --output=json "$TEST_IMG" | _filter_qemu_img_map + $QEMU_IO -f raw -c map "$TEST_IMG" + nbd_server_stop ++cat "$TEST_DIR/server.log" | _filter_testdir + + echo + echo "=== Exporting unaligned raw image, forced client sector alignment ===" +diff --git a/tests/qemu-iotests/241.out b/tests/qemu-iotests/241.out +index f481074..75f9f46 100644 +--- a/tests/qemu-iotests/241.out ++++ b/tests/qemu-iotests/241.out +@@ -10,13 +10,13 @@ QA output created by 241 + + === Exporting unaligned raw image, forced server sector alignment === + +-WARNING: Image format was not specified for '/home/eblake/qemu/tests/qemu-iotests/scratch/t.raw' and probing guessed raw. +- Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +- Specify the 'raw' format explicitly to remove the restrictions. + size: 1024 + min block: 512 + [{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET}] + 1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) ++WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. ++ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. ++ Specify the 'raw' format explicitly to remove the restrictions. + + === Exporting unaligned raw image, forced client sector alignment === + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotest-Fix-filtering-order-in-226.patch b/SOURCES/kvm-iotest-Fix-filtering-order-in-226.patch new file mode 100644 index 0000000..9574eac --- /dev/null +++ b/SOURCES/kvm-iotest-Fix-filtering-order-in-226.patch @@ -0,0 +1,55 @@ +From 97f1ac94b611b8e6c319696ac8a92aded5395ef5 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 17:12:05 +0100 +Subject: [PATCH 034/163] iotest: Fix filtering order in 226 + +RH-Author: John Snow +Message-id: <20190320171206.19236-2-jsnow@redhat.com> +Patchwork-id: 84960 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/2] iotest: Fix filtering order in 226 +Bugzilla: 1691018 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Sergio Lopez Pascual + +From: Max Reitz + +The test directory should be filtered before the image format, otherwise +the test will fail if the image format is part of the test directory, +like so: + +[...] +-can't open: Could not open 'TEST_DIR/t.IMGFMT': Is a directory ++can't open: Could not open '/tmp/test-IMGFMT/t.IMGFMT': Is a directory +[...] + +Signed-off-by: Max Reitz +Reviewed-by: John Snow +Signed-off-by: Kevin Wolf +(cherry picked from commit d6e4ca902148f33cfaf117396f57c7fff7c635f0) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/226 | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/tests/qemu-iotests/226 b/tests/qemu-iotests/226 +index 460aea2..a5a1f67 100755 +--- a/tests/qemu-iotests/226 ++++ b/tests/qemu-iotests/226 +@@ -52,10 +52,10 @@ for PROTO in "file" "host_device" "host_cdrom"; do + echo "=== Testing with driver:$PROTO ===" + echo + echo "== Testing RO ==" +- $QEMU_IO -c "open -r -o driver=$PROTO,filename=$TEST_IMG" 2>&1 | _filter_imgfmt | _filter_testdir ++ $QEMU_IO -c "open -r -o driver=$PROTO,filename=$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt + $QEMU_IO -c "open -r -o driver=$PROTO,filename=/dev/null" 2>&1 | _filter_imgfmt + echo "== Testing RW ==" +- $QEMU_IO -c "open -o driver=$PROTO,filename=$TEST_IMG" 2>&1 | _filter_imgfmt | _filter_testdir ++ $QEMU_IO -c "open -o driver=$PROTO,filename=$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt + $QEMU_IO -c "open -o driver=$PROTO,filename=/dev/null" 2>&1 | _filter_imgfmt + done + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-153-Fix-dead-code.patch b/SOURCES/kvm-iotests-153-Fix-dead-code.patch new file mode 100644 index 0000000..87c7c34 --- /dev/null +++ b/SOURCES/kvm-iotests-153-Fix-dead-code.patch @@ -0,0 +1,90 @@ +From ddc6da501740a60c1e7356e60928845be6ca2cda Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 4 Feb 2019 20:42:01 +0100 +Subject: [PATCH 01/33] iotests: 153: Fix dead code + +RH-Author: Max Reitz +Message-id: <20190204204207.18079-2-mreitz@redhat.com> +Patchwork-id: 84219 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/7] iotests: 153: Fix dead code +Bugzilla: 1551486 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Kevin Wolf + +From: Fam Zheng + +This step was left behind my mistake. As suggested by the echoed text, +the intention was to test two devices with the same image, with +different options. The behavior should be the same as two QEMU +processes. Complete it. + +Signed-off-by: Fam Zheng +Signed-off-by: Kevin Wolf +(cherry picked from commit 0e1a582750269d3dde0481ca034b08a5784e430c) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/153 | 2 ++ + tests/qemu-iotests/153.out | 25 +++++++++++++++++++++++++ + 2 files changed, 27 insertions(+) + +diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153 +index 673813c..0daeb1b 100755 +--- a/tests/qemu-iotests/153 ++++ b/tests/qemu-iotests/153 +@@ -162,6 +162,7 @@ for opts1 in "" "read-only=on" "read-only=on,force-share=on"; do + _cleanup_qemu + done + ++test_opts="read-only=off read-only=on read-only=on,force-share=on" + for opt1 in $test_opts; do + for opt2 in $test_opts; do + echo +@@ -170,6 +171,7 @@ for opt1 in $test_opts; do + done + done + ++echo + echo "== Creating ${TEST_IMG}.[abc] ==" | _filter_testdir + ( + $QEMU_IMG create -f qcow2 "${TEST_IMG}.a" -b "${TEST_IMG}" +diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out +index 3492ba7..93eaf10 100644 +--- a/tests/qemu-iotests/153.out ++++ b/tests/qemu-iotests/153.out +@@ -369,6 +369,31 @@ _qemu_img_wrapper bench -U -w -c 1 TEST_DIR/t.qcow2 + qemu-img: Could not open 'TEST_DIR/t.qcow2': force-share=on can only be used with read-only images + + Round done ++ ++== Two devices with the same image (read-only=off - read-only=off) == ++QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,read-only=off: Failed to get "write" lock ++Is another process using the image? ++ ++== Two devices with the same image (read-only=off - read-only=on) == ++QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,read-only=on: Failed to get shared "write" lock ++Is another process using the image? ++ ++== Two devices with the same image (read-only=off - read-only=on,force-share=on) == ++ ++== Two devices with the same image (read-only=on - read-only=off) == ++QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,read-only=off: Failed to get "write" lock ++Is another process using the image? ++ ++== Two devices with the same image (read-only=on - read-only=on) == ++ ++== Two devices with the same image (read-only=on - read-only=on,force-share=on) == ++ ++== Two devices with the same image (read-only=on,force-share=on - read-only=off) == ++ ++== Two devices with the same image (read-only=on,force-share=on - read-only=on) == ++ ++== Two devices with the same image (read-only=on,force-share=on - read-only=on,force-share=on) == ++ + == Creating TEST_DIR/t.qcow2.[abc] == + Formatting 'TEST_DIR/t.IMGFMT.a', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT + Formatting 'TEST_DIR/t.IMGFMT.b', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-169-add-cases-for-source-vm-resuming.patch b/SOURCES/kvm-iotests-169-add-cases-for-source-vm-resuming.patch new file mode 100644 index 0000000..ff5bc13 --- /dev/null +++ b/SOURCES/kvm-iotests-169-add-cases-for-source-vm-resuming.patch @@ -0,0 +1,129 @@ +From b12adbb5e07d5c146f500e920a3dc1278dfebe53 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:43 +0100 +Subject: [PATCH 33/33] iotests: 169: add cases for source vm resuming + +RH-Author: John Snow +Message-id: <20190206221243.7407-24-jsnow@redhat.com> +Patchwork-id: 84277 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 23/23] iotests: 169: add cases for source vm resuming +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +From: Vladimir Sementsov-Ogievskiy + +Test that we can resume source vm after [failed] migration, and bitmaps +are ok. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Signed-off-by: John Snow +(cherry picked from commit 3e6d88f280a53b5b399e73b1f80efe4c3db306f1) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/169 | 60 +++++++++++++++++++++++++++++++++++++++++++++- + tests/qemu-iotests/169.out | 4 ++-- + 2 files changed, 61 insertions(+), 3 deletions(-) + +diff --git a/tests/qemu-iotests/169 b/tests/qemu-iotests/169 +index 8b7947d..69850c4 100755 +--- a/tests/qemu-iotests/169 ++++ b/tests/qemu-iotests/169 +@@ -77,6 +77,58 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): + self.assert_qmp(result, 'error/desc', + "Dirty bitmap 'bitmap0' not found"); + ++ def do_test_migration_resume_source(self, persistent, migrate_bitmaps): ++ granularity = 512 ++ ++ # regions = ((start, count), ...) ++ regions = ((0, 0x10000), ++ (0xf0000, 0x10000), ++ (0xa0201, 0x1000)) ++ ++ mig_caps = [{'capability': 'events', 'state': True}] ++ if migrate_bitmaps: ++ mig_caps.append({'capability': 'dirty-bitmaps', 'state': True}) ++ ++ result = self.vm_a.qmp('migrate-set-capabilities', ++ capabilities=mig_caps) ++ self.assert_qmp(result, 'return', {}) ++ ++ self.add_bitmap(self.vm_a, granularity, persistent) ++ for r in regions: ++ self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % r) ++ sha256 = self.get_bitmap_hash(self.vm_a) ++ ++ result = self.vm_a.qmp('migrate', uri=mig_cmd) ++ while True: ++ event = self.vm_a.event_wait('MIGRATION') ++ if event['data']['status'] == 'completed': ++ break ++ ++ # test that bitmap is still here ++ removed = (not migrate_bitmaps) and persistent ++ self.check_bitmap(self.vm_a, False if removed else sha256) ++ ++ self.vm_a.qmp('cont') ++ ++ # test that bitmap is still here after invalidation ++ self.check_bitmap(self.vm_a, sha256) ++ ++ # shutdown and check that invalidation didn't fail ++ self.vm_a.shutdown() ++ ++ # catch 'Could not reopen qcow2 layer: Bitmap already exists' ++ # possible error ++ log = self.vm_a.get_log() ++ log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) ++ log = re.sub(r'^(wrote .* bytes at offset .*\n.*KiB.*ops.*sec.*\n){3}', ++ '', log) ++ log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) ++ self.assertEqual(log, '') ++ ++ # test that bitmap is still persistent ++ self.vm_a.launch() ++ self.check_bitmap(self.vm_a, sha256 if persistent else False) ++ + def do_test_migration(self, persistent, migrate_bitmaps, online, + shared_storage): + granularity = 512 +@@ -152,7 +204,7 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): + + def inject_test_case(klass, name, method, *args, **kwargs): + mc = operator.methodcaller(method, *args, **kwargs) +- setattr(klass, 'test_' + name, new.instancemethod(mc, None, klass)) ++ setattr(klass, 'test_' + method + name, new.instancemethod(mc, None, klass)) + + for cmb in list(itertools.product((True, False), repeat=4)): + name = ('_' if cmb[0] else '_not_') + 'persistent_' +@@ -163,6 +215,12 @@ for cmb in list(itertools.product((True, False), repeat=4)): + inject_test_case(TestDirtyBitmapMigration, name, 'do_test_migration', + *list(cmb)) + ++for cmb in list(itertools.product((True, False), repeat=2)): ++ name = ('_' if cmb[0] else '_not_') + 'persistent_' ++ name += ('_' if cmb[1] else '_not_') + 'migbitmap' ++ ++ inject_test_case(TestDirtyBitmapMigration, name, ++ 'do_test_migration_resume_source', *list(cmb)) + + if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2']) +diff --git a/tests/qemu-iotests/169.out b/tests/qemu-iotests/169.out +index b6f2576..3a89159 100644 +--- a/tests/qemu-iotests/169.out ++++ b/tests/qemu-iotests/169.out +@@ -1,5 +1,5 @@ +-................ ++.................... + ---------------------------------------------------------------------- +-Ran 16 tests ++Ran 20 tests + + OK +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-169-drop-deprecated-autoload-parameter.patch b/SOURCES/kvm-iotests-169-drop-deprecated-autoload-parameter.patch new file mode 100644 index 0000000..713a91c --- /dev/null +++ b/SOURCES/kvm-iotests-169-drop-deprecated-autoload-parameter.patch @@ -0,0 +1,41 @@ +From 4b6af7c190fb1366d5c7f43fdfe39fddc2465e89 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:38 +0100 +Subject: [PATCH 28/33] iotests: 169: drop deprecated 'autoload' parameter + +RH-Author: John Snow +Message-id: <20190206221243.7407-19-jsnow@redhat.com> +Patchwork-id: 84276 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 18/23] iotests: 169: drop deprecated 'autoload' parameter +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +From: Vladimir Sementsov-Ogievskiy + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: John Snow +Signed-off-by: John Snow +(cherry picked from commit 304cc429a07eb6601020212a478050ebbe87df88) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/169 | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/tests/qemu-iotests/169 b/tests/qemu-iotests/169 +index f243db9..df408f8 100755 +--- a/tests/qemu-iotests/169 ++++ b/tests/qemu-iotests/169 +@@ -58,7 +58,6 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): + 'granularity': granularity} + if persistent: + params['persistent'] = True +- params['autoload'] = True + + result = vm.qmp('block-dirty-bitmap-add', **params) + self.assert_qmp(result, 'return', {}); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-222-Don-t-run-with-luks.patch b/SOURCES/kvm-iotests-222-Don-t-run-with-luks.patch new file mode 100644 index 0000000..f327c98 --- /dev/null +++ b/SOURCES/kvm-iotests-222-Don-t-run-with-luks.patch @@ -0,0 +1,44 @@ +From cd3796e20a98a0a6274514005bec886e291b1984 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:54:54 +0200 +Subject: [PATCH 69/89] iotests: 222: Don't run with luks + +RH-Author: John Snow +Message-id: <20180718225511.14878-19-jsnow@redhat.com> +Patchwork-id: 81422 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 18/35] iotests: 222: Don't run with luks +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Fam Zheng + +Luks needs special parameters to operate the image. Since this test is +focusing on image fleecing, skip skip that format. + +Signed-off-by: Fam Zheng +Signed-off-by: Kevin Wolf +(cherry picked from commit e79c4cd1909c05a2cab6517a9c00445bd2d880a6) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/222 | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/tests/qemu-iotests/222 b/tests/qemu-iotests/222 +index ff3bfc1..0ead56d 100644 +--- a/tests/qemu-iotests/222 ++++ b/tests/qemu-iotests/222 +@@ -25,6 +25,8 @@ import iotests + from iotests import log, qemu_img, qemu_io, qemu_io_silent + + iotests.verify_platform(['linux']) ++iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk', ++ 'vhdx', 'raw']) + + patterns = [("0x5d", "0", "64k"), + ("0xd5", "1M", "64k"), +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-236-fix-transaction-kwarg-order.patch b/SOURCES/kvm-iotests-236-fix-transaction-kwarg-order.patch new file mode 100644 index 0000000..0dd3cf6 --- /dev/null +++ b/SOURCES/kvm-iotests-236-fix-transaction-kwarg-order.patch @@ -0,0 +1,219 @@ +From 206d8696a89a1b4076f7afd46b6ae0dcc0f377f5 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 16:16:25 +0100 +Subject: [PATCH 027/163] iotests/236: fix transaction kwarg order + +RH-Author: John Snow +Message-id: <20190320161631.14841-14-jsnow@redhat.com> +Patchwork-id: 84945 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 13/19] iotests/236: fix transaction kwarg order +Bugzilla: 1668956 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +It's not enough to order the kwargs for consistent QMP log output, +we must also sort any sub-dictionaries in lists that appear as values. + +Reported-by: Kevin Wolf +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Signed-off-by: Kevin Wolf +(cherry picked from commit 039be85c410bfb4b53cdee2083b4245e0d4e4181) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/236.out | 56 +++++++++++++++++++++---------------------- + tests/qemu-iotests/iotests.py | 21 ++++++++-------- + 2 files changed, 39 insertions(+), 38 deletions(-) + +diff --git a/tests/qemu-iotests/236.out b/tests/qemu-iotests/236.out +index 1dad24d..bb2d71e 100644 +--- a/tests/qemu-iotests/236.out ++++ b/tests/qemu-iotests/236.out +@@ -45,23 +45,23 @@ write -P0xcd 0x3ff0000 64k + "actions": [ + { + "data": { +- "node": "drive0", +- "name": "bitmapB" ++ "name": "bitmapB", ++ "node": "drive0" + }, + "type": "block-dirty-bitmap-disable" + }, + { + "data": { +- "node": "drive0", ++ "granularity": 65536, + "name": "bitmapC", +- "granularity": 65536 ++ "node": "drive0" + }, + "type": "block-dirty-bitmap-add" + }, + { + "data": { +- "node": "drive0", +- "name": "bitmapA" ++ "name": "bitmapA", ++ "node": "drive0" + }, + "type": "block-dirty-bitmap-clear" + }, +@@ -105,30 +105,30 @@ write -P0xcd 0x3ff0000 64k + "actions": [ + { + "data": { +- "node": "drive0", +- "name": "bitmapB" ++ "name": "bitmapB", ++ "node": "drive0" + }, + "type": "block-dirty-bitmap-disable" + }, + { + "data": { +- "node": "drive0", ++ "granularity": 65536, + "name": "bitmapC", +- "granularity": 65536 ++ "node": "drive0" + }, + "type": "block-dirty-bitmap-add" + }, + { + "data": { +- "node": "drive0", +- "name": "bitmapC" ++ "name": "bitmapC", ++ "node": "drive0" + }, + "type": "block-dirty-bitmap-disable" + }, + { + "data": { +- "node": "drive0", +- "name": "bitmapC" ++ "name": "bitmapC", ++ "node": "drive0" + }, + "type": "block-dirty-bitmap-enable" + } +@@ -158,15 +158,15 @@ write -P0xea 0x3fe0000 64k + "actions": [ + { + "data": { +- "node": "drive0", +- "name": "bitmapA" ++ "name": "bitmapA", ++ "node": "drive0" + }, + "type": "block-dirty-bitmap-disable" + }, + { + "data": { +- "node": "drive0", +- "name": "bitmapC" ++ "name": "bitmapC", ++ "node": "drive0" + }, + "type": "block-dirty-bitmap-disable" + } +@@ -209,21 +209,21 @@ write -P0xea 0x3fe0000 64k + "actions": [ + { + "data": { +- "node": "drive0", + "disabled": true, ++ "granularity": 65536, + "name": "bitmapD", +- "granularity": 65536 ++ "node": "drive0" + }, + "type": "block-dirty-bitmap-add" + }, + { + "data": { +- "node": "drive0", +- "target": "bitmapD", + "bitmaps": [ + "bitmapB", + "bitmapC" +- ] ++ ], ++ "node": "drive0", ++ "target": "bitmapD" + }, + "type": "block-dirty-bitmap-merge" + }, +@@ -273,21 +273,21 @@ write -P0xea 0x3fe0000 64k + "actions": [ + { + "data": { +- "node": "drive0", + "disabled": true, ++ "granularity": 65536, + "name": "bitmapD", +- "granularity": 65536 ++ "node": "drive0" + }, + "type": "block-dirty-bitmap-add" + }, + { + "data": { +- "node": "drive0", +- "target": "bitmapD", + "bitmaps": [ + "bitmapB", + "bitmapC" +- ] ++ ], ++ "node": "drive0", ++ "target": "bitmapD" + }, + "type": "block-dirty-bitmap-merge" + } +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index d178469..4e9b2c4 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -74,15 +74,16 @@ def qemu_img(*args): + sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args)))) + return exitcode + +-def ordered_kwargs(kwargs): +- # kwargs prior to 3.6 are not ordered, so: +- od = OrderedDict() +- for k, v in sorted(kwargs.items()): +- if isinstance(v, dict): +- od[k] = ordered_kwargs(v) +- else: +- od[k] = v +- return od ++def ordered_qmp(qmsg): ++ # Dictionaries are not ordered prior to 3.6, therefore: ++ if isinstance(qmsg, list): ++ return [ordered_qmp(atom) for atom in qmsg] ++ if isinstance(qmsg, dict): ++ od = OrderedDict() ++ for k, v in sorted(qmsg.items()): ++ od[k] = ordered_qmp(v) ++ return od ++ return qmsg + + def qemu_img_create(*args): + args = list(args) +@@ -485,7 +486,7 @@ class VM(qtest.QEMUQtestMachine): + def qmp_log(self, cmd, filters=[], indent=None, **kwargs): + full_cmd = OrderedDict(( + ("execute", cmd), +- ("arguments", ordered_kwargs(kwargs)) ++ ("arguments", ordered_qmp(kwargs)) + )) + log(full_cmd, filters, indent=indent) + result = self.qmp(cmd, **kwargs) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Add-241-to-test-NBD-on-unaligned-images.patch b/SOURCES/kvm-iotests-Add-241-to-test-NBD-on-unaligned-images.patch new file mode 100644 index 0000000..6d6b353 --- /dev/null +++ b/SOURCES/kvm-iotests-Add-241-to-test-NBD-on-unaligned-images.patch @@ -0,0 +1,202 @@ +From 8b23ebef39aa3e2817db7d42c8143139697e0adc Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 6 May 2019 17:56:17 +0200 +Subject: [PATCH 07/53] iotests: Add 241 to test NBD on unaligned images + +RH-Author: John Snow +Message-id: <20190506175629.11079-8-jsnow@redhat.com> +Patchwork-id: 87182 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 07/19] iotests: Add 241 to test NBD on unaligned images +Bugzilla: 1692018 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Thomas Huth + +From: Eric Blake + +Add a test for the NBD client workaround in the previous patch. It's +not really feasible for an iotest to assume a specific tracing engine, +so we can't really probe trace_nbd_parse_blockstatus_compliance to see +if the server was fixed vs. whether the client just worked around the +server (other than by rearranging order between code patches and this +test). But having a successful exchange sure beats the previous state +of an error message. Since format probing can change alignment, we can +use that as an easy way to test several configurations. + +Not tested yet, but worth adding to this test in future patches: an +NBD server that can advertise a non-sector-aligned size (such as +nbdkit) causes qemu as the NBD client to misbehave when it rounds the +size up and accesses beyond the advertised size. Qemu as NBD server +never advertises a non-sector-aligned size (since bdrv_getlength() +currently rounds up to sector boundaries); until qemu can act as such +a server, testing that flaw will have to rely on external binaries. + +Signed-off-by: Eric Blake +Message-Id: <20190329042750.14704-2-eblake@redhat.com> +Tested-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: Vladimir Sementsov-Ogievskiy +[eblake: add forced-512 alignment, and nbdkit reproducer comment] +(cherry picked from commit e9dce9cb6eae57834cd80324ff43069299198bab) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/241 | 100 +++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/241.out | 26 ++++++++++++ + tests/qemu-iotests/group | 1 + + 3 files changed, 127 insertions(+) + create mode 100755 tests/qemu-iotests/241 + create mode 100644 tests/qemu-iotests/241.out + +diff --git a/tests/qemu-iotests/241 b/tests/qemu-iotests/241 +new file mode 100755 +index 0000000..4b19685 +--- /dev/null ++++ b/tests/qemu-iotests/241 +@@ -0,0 +1,100 @@ ++#!/bin/bash ++# ++# Test qemu-nbd vs. unaligned images ++# ++# Copyright (C) 2018-2019 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 . ++# ++ ++seq="$(basename $0)" ++echo "QA output created by $seq" ++ ++status=1 # failure is the default! ++ ++nbd_unix_socket=$TEST_DIR/test_qemu_nbd_socket ++ ++_cleanup() ++{ ++ _cleanup_test_img ++ nbd_server_stop ++} ++trap "_cleanup; exit \$status" 0 1 2 3 15 ++ ++# get standard environment, filters and checks ++. ./common.rc ++. ./common.filter ++. ./common.nbd ++ ++_supported_fmt raw ++_supported_proto nbd ++_supported_os Linux ++_require_command QEMU_NBD ++ ++# can't use _make_test_img, because qemu-img rounds image size up, ++# and because we want to use Unix socket rather than TCP port. Likewise, ++# we have to redirect TEST_IMG to our server. ++# This tests that we can deal with the hole at the end of an unaligned ++# raw file (either because the server doesn't advertise alignment too ++# large, or because the client ignores the server's noncompliance - even ++# though we can't actually wire iotests into checking trace messages). ++printf %01000d 0 > "$TEST_IMG_FILE" ++TEST_IMG="nbd:unix:$nbd_unix_socket" ++ ++echo ++echo "=== Exporting unaligned raw image, natural alignment ===" ++echo ++ ++nbd_server_start_unix_socket -f $IMGFMT "$TEST_IMG_FILE" ++ ++$QEMU_NBD_PROG --list -k $nbd_unix_socket | grep '\(size\|min\)' ++$QEMU_IMG map -f raw --output=json "$TEST_IMG" | _filter_qemu_img_map ++$QEMU_IO -f raw -c map "$TEST_IMG" ++nbd_server_stop ++ ++echo ++echo "=== Exporting unaligned raw image, forced server sector alignment ===" ++echo ++ ++# Intentionally omit '-f' to force image probing, which in turn forces ++# sector alignment, here at the server. ++nbd_server_start_unix_socket "$TEST_IMG_FILE" ++ ++$QEMU_NBD_PROG --list -k $nbd_unix_socket | grep '\(size\|min\)' ++$QEMU_IMG map -f raw --output=json "$TEST_IMG" | _filter_qemu_img_map ++$QEMU_IO -f raw -c map "$TEST_IMG" ++nbd_server_stop ++ ++echo ++echo "=== Exporting unaligned raw image, forced client sector alignment ===" ++echo ++ ++# Now force sector alignment at the client. ++nbd_server_start_unix_socket -f $IMGFMT "$TEST_IMG_FILE" ++ ++$QEMU_NBD_PROG --list -k $nbd_unix_socket | grep '\(size\|min\)' ++$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map ++$QEMU_IO -c map "$TEST_IMG" ++nbd_server_stop ++ ++# Not tested yet: we also want to ensure that qemu as NBD client does ++# not access beyond the end of a server's advertised unaligned size: ++# nbdkit -U - memory size=513 --run 'qemu-io -f raw -c "r 512 512" $nbd' ++# However, since qemu as server always rounds up to a sector alignment, ++# we would have to use nbdkit to provoke the current client failures. ++ ++# success, all done ++echo '*** done' ++rm -f $seq.full ++status=0 +diff --git a/tests/qemu-iotests/241.out b/tests/qemu-iotests/241.out +new file mode 100644 +index 0000000..b76a623 +--- /dev/null ++++ b/tests/qemu-iotests/241.out +@@ -0,0 +1,26 @@ ++QA output created by 241 ++ ++=== Exporting unaligned raw image, natural alignment === ++ ++ size: 1024 ++ min block: 512 ++[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true}] ++1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) ++ ++=== Exporting unaligned raw image, forced server sector alignment === ++ ++WARNING: Image format was not specified for '/home/eblake/qemu/tests/qemu-iotests/scratch/t.raw' and probing guessed raw. ++ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. ++ Specify the 'raw' format explicitly to remove the restrictions. ++ size: 1024 ++ min block: 512 ++[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true}] ++1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) ++ ++=== Exporting unaligned raw image, forced client sector alignment === ++ ++ size: 1024 ++ min block: 512 ++[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true}] ++1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) ++*** done +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index 7da1334..882ff66 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -229,5 +229,6 @@ + 233 auto quick + 234 auto quick migration + 236 auto quick ++241 rw auto quick + 242 rw auto quick + 246 rw auto quick +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Add-case-for-a-corrupted-inactive-image.patch b/SOURCES/kvm-iotests-Add-case-for-a-corrupted-inactive-image.patch new file mode 100644 index 0000000..5a3446f --- /dev/null +++ b/SOURCES/kvm-iotests-Add-case-for-a-corrupted-inactive-image.patch @@ -0,0 +1,95 @@ +From e9dd4092392c53954bbd394e4286f3ac431f47aa Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 18:00:55 +0200 +Subject: [PATCH 49/54] iotests: Add case for a corrupted inactive image + +RH-Author: Max Reitz +Message-id: <20180618180055.22739-4-mreitz@redhat.com> +Patchwork-id: 80794 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 3/3] iotests: Add case for a corrupted inactive image +Bugzilla: 1588039 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf +RH-Acked-by: John Snow + +Reviewed-by: John Snow +Tested-by: Jeff Cody +Reviewed-by: Jeff Cody +Signed-off-by: Max Reitz +Message-id: 20180606193702.7113-4-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit c50abd175a88cd41c2c08339de91f6f6e4a7b162) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/060 | 30 ++++++++++++++++++++++++++++++ + tests/qemu-iotests/060.out | 14 ++++++++++++++ + 2 files changed, 44 insertions(+) + +diff --git a/tests/qemu-iotests/060 b/tests/qemu-iotests/060 +index 6c7407f..7bdf609 100755 +--- a/tests/qemu-iotests/060 ++++ b/tests/qemu-iotests/060 +@@ -440,6 +440,36 @@ echo "{'execute': 'qmp_capabilities'} + -drive if=none,node-name=drive,file="$TEST_IMG",driver=qcow2 \ + | _filter_qmp | _filter_qemu_io + ++echo ++echo "=== Testing incoming inactive corrupted image ===" ++echo ++ ++_make_test_img 64M ++# Create an unaligned L1 entry, so qemu will signal a corruption when ++# reading from the covered area ++poke_file "$TEST_IMG" "$l1_offset" "\x00\x00\x00\x00\x2a\x2a\x2a\x2a" ++ ++# Inactive images are effectively read-only images, so this should be a ++# non-fatal corruption (which does not modify the image) ++echo "{'execute': 'qmp_capabilities'} ++ {'execute': 'human-monitor-command', ++ 'arguments': {'command-line': 'qemu-io drive \"read 0 512\"'}} ++ {'execute': 'quit'}" \ ++ | $QEMU -qmp stdio -nographic -nodefaults \ ++ -blockdev "{'node-name': 'drive', ++ 'driver': 'qcow2', ++ 'file': { ++ 'driver': 'file', ++ 'filename': '$TEST_IMG' ++ }}" \ ++ -incoming exec:'cat /dev/null' \ ++ 2>&1 \ ++ | _filter_qmp | _filter_qemu_io ++ ++echo ++# Image should not have been marked corrupt ++_img_info --format-specific | grep 'corrupt:' ++ + # success, all done + echo "*** done" + rm -f $seq.full +diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out +index 25d5c39..99234fb 100644 +--- a/tests/qemu-iotests/060.out ++++ b/tests/qemu-iotests/060.out +@@ -420,4 +420,18 @@ write failed: Input/output error + {"return": ""} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} ++ ++=== Testing incoming inactive corrupted image === ++ ++Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 ++QMP_VERSION ++{"return": {}} ++qcow2: Image is corrupt: L2 table offset 0x2a2a2a00 unaligned (L1 index: 0); further non-fatal corruption events will be suppressed ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_IMAGE_CORRUPTED", "data": {"device": "", "msg": "L2 table offset 0x2a2a2a00 unaligned (L1 index: 0)", "node-name": "drive", "fatal": false}} ++read failed: Input/output error ++{"return": ""} ++{"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} ++ ++ corrupt: false + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Add-creation-test-to-153.patch b/SOURCES/kvm-iotests-Add-creation-test-to-153.patch new file mode 100644 index 0000000..af4bb84 --- /dev/null +++ b/SOURCES/kvm-iotests-Add-creation-test-to-153.patch @@ -0,0 +1,101 @@ +From e37408d4eee39e39d653ac2ce35155bed6db9a9e Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 14:53:37 +0200 +Subject: [PATCH 08/57] iotests: Add creation test to 153 + +RH-Author: Max Reitz +Message-id: <20180618145337.633-4-mreitz@redhat.com> +Patchwork-id: 80752 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 3/3] iotests: Add creation test to 153 +Bugzilla: 1519144 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng +RH-Acked-by: Miroslav Rezanina + +This patch adds a test case to 153 which tries to overwrite an image +(using qemu-img create) while it is in use. Without the original user +explicitly sharing the necessary permissions (writing and truncation), +this should not be allowed. + +Signed-off-by: Max Reitz +Reviewed-by: Fam Zheng +Message-id: 20180509215336.31304-4-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit f45b638f9f967cdbea4e24704bd16a858ddcde03) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/153 | 18 ++++++++++++++++++ + tests/qemu-iotests/153.out | 13 +++++++++++++ + 2 files changed, 31 insertions(+) + +diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153 +index ec508c7..673813c 100755 +--- a/tests/qemu-iotests/153 ++++ b/tests/qemu-iotests/153 +@@ -137,6 +137,24 @@ for opts1 in "" "read-only=on" "read-only=on,force-share=on"; do + _run_cmd $QEMU_IMG dd $L if="${TEST_IMG}" of="${TEST_IMG}.convert" bs=512 count=1 + _run_cmd $QEMU_IMG bench $L -c 1 "${TEST_IMG}" + _run_cmd $QEMU_IMG bench $L -w -c 1 "${TEST_IMG}" ++ ++ # qemu-img create does not support -U ++ if [ -z "$L" ]; then ++ _run_cmd $QEMU_IMG create -f $IMGFMT "${TEST_IMG}" \ ++ -b ${TEST_IMG}.base ++ # Read the file format. It used to be the case that ++ # file-posix simply truncated the file, but the qcow2 ++ # driver then failed to format it because it was unable ++ # to acquire the necessary WRITE permission. However, the ++ # truncation was already wrong, and the whole process ++ # resulted in the file being completely empty and thus its ++ # format would be detected to be raw. ++ # So we read it here to see that creation either completed ++ # successfully (thus the format is qcow2) or it aborted ++ # before the file was changed at all (thus the format stays ++ # qcow2). ++ _img_info -U | grep 'file format' ++ fi + done + _send_qemu_cmd $h "{ 'execute': 'quit', }" "" + echo +diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out +index 2510762..3492ba7 100644 +--- a/tests/qemu-iotests/153.out ++++ b/tests/qemu-iotests/153.out +@@ -92,6 +92,11 @@ _qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2 + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock + Is another process using the image? + ++_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base ++qemu-img: TEST_DIR/t.qcow2: Failed to get "write" lock ++Is another process using the image? ++file format: IMGFMT ++ + == Running utility commands -U == + + _qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2 +@@ -209,6 +214,11 @@ _qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2 + qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock + Is another process using the image? + ++_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base ++qemu-img: TEST_DIR/t.qcow2: Failed to get "write" lock ++Is another process using the image? ++file format: IMGFMT ++ + == Running utility commands -U == + + _qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2 +@@ -309,6 +319,9 @@ _qemu_img_wrapper bench -c 1 TEST_DIR/t.qcow2 + + _qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2 + ++_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base ++file format: IMGFMT ++ + == Running utility commands -U == + + _qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2 +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Add-failure-matching-to-common.qemu.patch b/SOURCES/kvm-iotests-Add-failure-matching-to-common.qemu.patch new file mode 100644 index 0000000..72d80a5 --- /dev/null +++ b/SOURCES/kvm-iotests-Add-failure-matching-to-common.qemu.patch @@ -0,0 +1,156 @@ +From 2ffef795b5aed20b6fc2fba3951ff8f5c97cef5a Mon Sep 17 00:00:00 2001 +From: Jeffrey Cody +Date: Tue, 28 Aug 2018 21:08:17 +0200 +Subject: [PATCH 04/29] iotests: Add failure matching to common.qemu + +RH-Author: Jeffrey Cody +Message-id: <7850ea2daac60087a2cd5e300f8b7042b167ef24.1535490170.git.jcody@redhat.com> +Patchwork-id: 81955 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 2/3] iotests: Add failure matching to common.qemu +Bugzilla: 1605026 +RH-Acked-by: Max Reitz +RH-Acked-by: John Snow +RH-Acked-by: Miroslav Rezanina + +From: Max Reitz + +Currently, common.qemu only allows to match for results indicating +success. The only way to fail is by provoking a timeout. However, +sometimes we do have a defined failure output and can match for that, +which saves us from having to wait for the timeout in case of failure. +Because failure can sometimes just result in a _notrun in the test, it +is actually important to care about being able to fail quickly. + +Also, sometimes we simply do not get any specific output in case of +success. The only way to handle this currently would be to define an +error message as the string to look for, which means that actual success +results in a timeout. This is really bad because it unnecessarily slows +down a succeeding test. + +Therefore, this patch adds a new parameter $success_or_failure to +_timed_wait_for and _send_qemu_cmd. Setting this to a non-empty string +makes both commands expect two match parameters: If the first matches, +the function succeeds. If the second matches, the function fails. + +Signed-off-by: Max Reitz +Message-id: 20180406151731.4285-2-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit 81c6ddf49a76a663cea16c07a07d51b67c853209) +Signed-off-by: Jeff Cody +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/common.qemu | 58 +++++++++++++++++++++++++++++++++++++----- + 1 file changed, 51 insertions(+), 7 deletions(-) + +diff --git a/tests/qemu-iotests/common.qemu b/tests/qemu-iotests/common.qemu +index 85f66b8..f285484 100644 +--- a/tests/qemu-iotests/common.qemu ++++ b/tests/qemu-iotests/common.qemu +@@ -52,11 +52,29 @@ _in_fd=4 + # response is not echoed out. + # If $mismatch_only is set, only non-matching responses will + # be echoed. ++# ++# If $success_or_failure is set, the meaning of the arguments is ++# changed as follows: ++# $2: A string to search for in the response; if found, this indicates ++# success and ${QEMU_STATUS[$1]} is set to 0. ++# $3: A string to search for in the response; if found, this indicates ++# failure and the test is either aborted (if $qemu_error_no_exit ++# is not set) or ${QEMU_STATUS[$1]} is set to -1 (otherwise). + function _timed_wait_for() + { + local h=${1} + shift + ++ if [ -z "${success_or_failure}" ]; then ++ success_match=${*} ++ failure_match= ++ else ++ success_match=${1} ++ failure_match=${2} ++ fi ++ ++ timeout=yes ++ + QEMU_STATUS[$h]=0 + while IFS= read -t ${QEMU_COMM_TIMEOUT} resp <&${QEMU_OUT[$h]} + do +@@ -64,10 +82,18 @@ function _timed_wait_for() + echo "${resp}" | _filter_testdir | _filter_qemu \ + | _filter_qemu_io | _filter_qmp | _filter_hmp + fi +- grep -q "${*}" < <(echo "${resp}") ++ if [ -n "${failure_match}" ]; then ++ grep -q "${failure_match}" < <(echo "${resp}") ++ if [ $? -eq 0 ]; then ++ timeout= ++ break ++ fi ++ fi ++ grep -q "${success_match}" < <(echo "${resp}") + if [ $? -eq 0 ]; then + return +- elif [ -z "${silent}" ] && [ -n "${mismatch_only}" ]; then ++ fi ++ if [ -z "${silent}" ] && [ -n "${mismatch_only}" ]; then + echo "${resp}" | _filter_testdir | _filter_qemu \ + | _filter_qemu_io | _filter_qmp | _filter_hmp + fi +@@ -75,8 +101,12 @@ function _timed_wait_for() + done + QEMU_STATUS[$h]=-1 + if [ -z "${qemu_error_no_exit}" ]; then +- echo "Timeout waiting for ${*} on handle ${h}" +- exit 1 # Timeout means the test failed ++ if [ -n "${timeout}" ]; then ++ echo "Timeout waiting for ${success_match} on handle ${h}" ++ else ++ echo "Wrong response matching ${failure_match} on handle ${h}" ++ fi ++ exit 1 # Timeout or wrong match mean the test failed + fi + } + +@@ -96,6 +126,11 @@ function _timed_wait_for() + # If $qemu_error_no_exit is set, then even if the expected response + # is not seen, we will not exit. $QEMU_STATUS[$1] will be set it -1 in + # that case. ++# ++# If $success_or_failure is set, then the last two strings are the ++# strings the response will be scanned for. The first of the two ++# indicates success, the latter indicates failure. Failure is handled ++# like a timeout. + function _send_qemu_cmd() + { + local h=${1} +@@ -109,14 +144,23 @@ function _send_qemu_cmd() + use_error="no" + fi + # This array element extraction is done to accommodate pathnames with spaces +- cmd=${@: 1:${#@}-1} +- shift $(($# - 1)) ++ if [ -z "${success_or_failure}" ]; then ++ cmd=${@: 1:${#@}-1} ++ shift $(($# - 1)) ++ else ++ cmd=${@: 1:${#@}-2} ++ shift $(($# - 2)) ++ fi + + while [ ${count} -gt 0 ] + do + echo "${cmd}" >&${QEMU_IN[${h}]} + if [ -n "${1}" ]; then +- qemu_error_no_exit=${use_error} _timed_wait_for ${h} "${1}" ++ if [ -z "${success_or_failure}" ]; then ++ qemu_error_no_exit=${use_error} _timed_wait_for ${h} "${1}" ++ else ++ qemu_error_no_exit=${use_error} _timed_wait_for ${h} "${1}" "${2}" ++ fi + if [ ${QEMU_STATUS[$h]} -eq 0 ]; then + return + fi +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Add-test-221-to-catch-qemu-img-map-regressio.patch b/SOURCES/kvm-iotests-Add-test-221-to-catch-qemu-img-map-regressio.patch new file mode 100644 index 0000000..074fcc9 --- /dev/null +++ b/SOURCES/kvm-iotests-Add-test-221-to-catch-qemu-img-map-regressio.patch @@ -0,0 +1,142 @@ +From 267cb4b010be63dd4d8af995f496ae03cffe5f8f Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 6 Aug 2018 16:35:53 +0200 +Subject: [PATCH 04/13] iotests: Add test 221 to catch qemu-img map regression + +RH-Author: Max Reitz +Message-id: <20180806163553.13344-3-mreitz@redhat.com> +Patchwork-id: 81649 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 2/2] iotests: Add test 221 to catch qemu-img map regression +Bugzilla: 1601310 +RH-Acked-by: Eric Blake +RH-Acked-by: Kevin Wolf +RH-Acked-by: John Snow + +From: Eric Blake + +Although qemu-img creates aligned files (by rounding up), it +must also gracefully handle files that are not sector-aligned. +Test that the bug fixed in the previous patch does not recur. + +It's a bit annoying that we can see the (implicit) hole past +the end of the file on to the next sector boundary, so if we +ever reach the point where we report a byte-accurate size rather +than our current behavior of always rounding up, this test will +probably need a slight modification. + +Signed-off-by: Eric Blake +Signed-off-by: Kevin Wolf +(cherry picked from commit c6a9d2f6f9bc0c163b3a3073126464a2446bad5f) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/221 | 60 ++++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/221.out | 16 +++++++++++++ + tests/qemu-iotests/group | 1 + + 3 files changed, 77 insertions(+) + create mode 100755 tests/qemu-iotests/221 + create mode 100644 tests/qemu-iotests/221.out + +diff --git a/tests/qemu-iotests/221 b/tests/qemu-iotests/221 +new file mode 100755 +index 0000000..41c4e4b +--- /dev/null ++++ b/tests/qemu-iotests/221 +@@ -0,0 +1,60 @@ ++#!/bin/bash ++# ++# Test qemu-img vs. unaligned images ++# ++# Copyright (C) 2018 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 . ++# ++ ++seq="$(basename $0)" ++echo "QA output created by $seq" ++ ++here="$PWD" ++status=1 # failure is the default! ++ ++_cleanup() ++{ ++ _cleanup_test_img ++} ++trap "_cleanup; exit \$status" 0 1 2 3 15 ++ ++# get standard environment, filters and checks ++. ./common.rc ++. ./common.filter ++ ++_supported_fmt raw ++_supported_proto file ++_supported_os Linux ++ ++echo ++echo "=== Check mapping of unaligned raw image ===" ++echo ++ ++_make_test_img 43009 # qemu-img create rounds size up ++$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map ++ ++truncate --size=43009 "$TEST_IMG" # so we resize it and check again ++$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map ++ ++$QEMU_IO -c 'w 43008 1' "$TEST_IMG" | _filter_qemu_io # writing also rounds up ++$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map ++ ++truncate --size=43009 "$TEST_IMG" # so we resize it and check again ++$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map ++ ++# success, all done ++echo '*** done' ++rm -f $seq.full ++status=0 +diff --git a/tests/qemu-iotests/221.out b/tests/qemu-iotests/221.out +new file mode 100644 +index 0000000..a9c0190 +--- /dev/null ++++ b/tests/qemu-iotests/221.out +@@ -0,0 +1,16 @@ ++QA output created by 221 ++ ++=== Check mapping of unaligned raw image === ++ ++Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=43009 ++[{ "start": 0, "length": 43520, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] ++[{ "start": 0, "length": 43520, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] ++wrote 1/1 bytes at offset 43008 ++1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++[{ "start": 0, "length": 40960, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, ++{ "start": 40960, "length": 2049, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, ++{ "start": 43009, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] ++[{ "start": 0, "length": 40960, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, ++{ "start": 40960, "length": 2049, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, ++{ "start": 43009, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] ++*** done +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index 401258f..e479fe1 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -218,6 +218,7 @@ + 217 rw auto quick + 218 rw auto quick + 219 rw auto ++221 rw auto quick + 222 rw auto quick + 223 rw auto quick + 226 auto quick +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Add-test-for-COR-across-nodes.patch b/SOURCES/kvm-iotests-Add-test-for-COR-across-nodes.patch new file mode 100644 index 0000000..0afcad3 --- /dev/null +++ b/SOURCES/kvm-iotests-Add-test-for-COR-across-nodes.patch @@ -0,0 +1,214 @@ +From d1e2ed07917410e71f10b8dda80ef94a3d333d3c Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 16:12:12 +0200 +Subject: [PATCH 36/54] iotests: Add test for COR across nodes + +RH-Author: Max Reitz +Message-id: <20180618161212.14444-11-mreitz@redhat.com> +Patchwork-id: 80771 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 10/10] iotests: Add test for COR across nodes +Bugzilla: 1518738 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Miroslav Rezanina + +COR across nodes (that is, you have some filter node between the +actually COR target and the node that performs the COR) cannot reliably +work together with the permission system when there is no explicit COR +node that can request the WRITE_UNCHANGED permission for its child. +This is because COR (currently) sneaks its requests by the usual +permission checks, so it can work without a WRITE* permission; but if +there is a filter node in between, that will re-issue the request, which +then passes through the usual check -- and if nobody has requested a +WRITE_UNCHANGED permission, that check will fail. + +There is no real direct fix apart from hoping that there is someone who +has requested that permission; in case of just the qemu-io HMP command +(and no guest device), however, that is not the case. The real real fix +is to implement the copy-on-read flag through an implicitly added COR +node. Such a node can request the necessary permissions as shown in +this test. + +Signed-off-by: Max Reitz +Message-id: 20180421132929.21610-10-mreitz@redhat.com +Reviewed-by: Kevin Wolf +Signed-off-by: Max Reitz +(cherry picked from commit 3e7a95feb9b5d66cff7fee38b3c423135ed245f6) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/216 | 115 +++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/216.out | 28 +++++++++++ + tests/qemu-iotests/group | 1 + + 3 files changed, 144 insertions(+) + create mode 100755 tests/qemu-iotests/216 + create mode 100644 tests/qemu-iotests/216.out + +diff --git a/tests/qemu-iotests/216 b/tests/qemu-iotests/216 +new file mode 100755 +index 0000000..ca9b47a +--- /dev/null ++++ b/tests/qemu-iotests/216 +@@ -0,0 +1,115 @@ ++#!/usr/bin/env python ++# ++# Copy-on-read tests using a COR filter node ++# ++# Copyright (C) 2018 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 . ++# ++# Creator/Owner: Max Reitz ++ ++import iotests ++from iotests import log, qemu_img_pipe, qemu_io, filter_qemu_io ++ ++# Need backing file support ++iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk']) ++iotests.verify_platform(['linux']) ++ ++log('') ++log('=== Copy-on-read across nodes ===') ++log('') ++ ++# The old copy-on-read mechanism without a filter node cannot request ++# WRITE_UNCHANGED permissions for its child. Therefore it just tries ++# to sneak its write by the usual permission system and holds its ++# fingers crossed. However, that sneaking does not work so well when ++# there is a filter node in the way: That will receive the write ++# request and re-issue a new one to its child, which this time is a ++# proper write request that will make the permission system cough -- ++# unless there is someone at the top (like a guest device) that has ++# requested write permissions. ++# ++# A COR filter node, however, can request the proper permissions for ++# its child and therefore is not hit by this issue. ++ ++with iotests.FilePath('base.img') as base_img_path, \ ++ iotests.FilePath('top.img') as top_img_path, \ ++ iotests.VM() as vm: ++ ++ log('--- Setting up images ---') ++ log('') ++ ++ qemu_img_pipe('create', '-f', iotests.imgfmt, base_img_path, '64M') ++ ++ log(filter_qemu_io(qemu_io(base_img_path, '-c', 'write -P 1 0M 1M'))) ++ ++ qemu_img_pipe('create', '-f', iotests.imgfmt, '-b', base_img_path, ++ top_img_path) ++ ++ log(filter_qemu_io(qemu_io(top_img_path, '-c', 'write -P 2 1M 1M'))) ++ ++ log('') ++ log('--- Doing COR ---') ++ log('') ++ ++ # Compare with e.g. the following: ++ # vm.add_drive_raw('if=none,node-name=node0,copy-on-read=on,driver=raw,' \ ++ # 'file.driver=%s,file.file.filename=%s' % ++ # (iotests.imgfmt, top_img_path)) ++ # (Remove the blockdev-add instead.) ++ # ((Not tested here because it hits an assertion in the permission ++ # system.)) ++ ++ vm.launch() ++ ++ log(vm.qmp('blockdev-add', ++ node_name='node0', ++ driver='copy-on-read', ++ file={ ++ 'driver': 'raw', ++ 'file': { ++ 'driver': 'copy-on-read', ++ 'file': { ++ 'driver': 'raw', ++ 'file': { ++ 'driver': iotests.imgfmt, ++ 'file': { ++ 'driver': 'file', ++ 'filename': top_img_path ++ }, ++ 'backing': { ++ 'driver': iotests.imgfmt, ++ 'file': { ++ 'driver': 'file', ++ 'filename': base_img_path ++ } ++ } ++ } ++ } ++ } ++ })) ++ ++ # Trigger COR ++ log(vm.qmp('human-monitor-command', ++ command_line='qemu-io node0 "read 0 64M"')) ++ ++ vm.shutdown() ++ ++ log('') ++ log('--- Checking COR result ---') ++ log('') ++ ++ log(filter_qemu_io(qemu_io(base_img_path, '-c', 'discard 0 64M'))) ++ log(filter_qemu_io(qemu_io(top_img_path, '-c', 'read -P 1 0M 1M'))) ++ log(filter_qemu_io(qemu_io(top_img_path, '-c', 'read -P 2 1M 1M'))) +diff --git a/tests/qemu-iotests/216.out b/tests/qemu-iotests/216.out +new file mode 100644 +index 0000000..d3fc590 +--- /dev/null ++++ b/tests/qemu-iotests/216.out +@@ -0,0 +1,28 @@ ++ ++=== Copy-on-read across nodes === ++ ++--- Setting up images --- ++ ++wrote 1048576/1048576 bytes at offset 0 ++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++ ++wrote 1048576/1048576 bytes at offset 1048576 ++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++ ++ ++--- Doing COR --- ++ ++{u'return': {}} ++{u'return': u''} ++ ++--- Checking COR result --- ++ ++discard 67108864/67108864 bytes at offset 0 ++64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++ ++read 1048576/1048576 bytes at offset 0 ++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++ ++read 1048576/1048576 bytes at offset 1048576 ++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++ +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index cd5d26c..d228008 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -214,4 +214,5 @@ + 213 rw auto quick + 214 rw auto + 215 rw auto quick ++216 rw auto quick + 218 rw auto quick +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Add-test-for-U-force-share-conflicts.patch b/SOURCES/kvm-iotests-Add-test-for-U-force-share-conflicts.patch new file mode 100644 index 0000000..5916b31 --- /dev/null +++ b/SOURCES/kvm-iotests-Add-test-for-U-force-share-conflicts.patch @@ -0,0 +1,82 @@ +From 071df911bc33bc213570bf40135272c8cd3bcf77 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 16:31:06 +0200 +Subject: [PATCH 39/54] iotests: Add test for -U/force-share conflicts + +RH-Author: Max Reitz +Message-id: <20180618163106.23010-4-mreitz@redhat.com> +Patchwork-id: 80773 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 3/3] iotests: Add test for -U/force-share conflicts +Bugzilla: 1576598 +RH-Acked-by: Kevin Wolf +RH-Acked-by: Fam Zheng +RH-Acked-by: Miroslav Rezanina + +Signed-off-by: Max Reitz +Message-id: 20180502202051.15493-4-mreitz@redhat.com +Reviewed-by: Eric Blake +Signed-off-by: Max Reitz +(cherry picked from commit 4e7d73c5fbd97e55ffe5af02f24d1f7dbe3bbf20) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/153 | 17 +++++++++++++++++ + tests/qemu-iotests/153.out | 16 ++++++++++++++++ + 2 files changed, 33 insertions(+) + +diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153 +index a0fd815..ec508c7 100755 +--- a/tests/qemu-iotests/153 ++++ b/tests/qemu-iotests/153 +@@ -242,6 +242,23 @@ _run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512' + + _cleanup_qemu + ++echo ++echo "== Detecting -U and force-share conflicts ==" ++ ++echo ++echo 'No conflict:' ++$QEMU_IMG info -U --image-opts driver=null-co,force-share=on ++echo ++echo 'Conflict:' ++$QEMU_IMG info -U --image-opts driver=null-co,force-share=off ++ ++echo ++echo 'No conflict:' ++$QEMU_IO -c 'open -r -U -o driver=null-co,force-share=on' ++echo ++echo 'Conflict:' ++$QEMU_IO -c 'open -r -U -o driver=null-co,force-share=off' ++ + # success, all done + echo "*** done" + rm -f $seq.full +diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out +index bb721cb..2510762 100644 +--- a/tests/qemu-iotests/153.out ++++ b/tests/qemu-iotests/153.out +@@ -399,4 +399,20 @@ Is another process using the image? + Closing the other + + _qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512 ++ ++== Detecting -U and force-share conflicts == ++ ++No conflict: ++image: null-co:// ++file format: null-co ++virtual size: 1.0G (1073741824 bytes) ++disk size: unavailable ++ ++Conflict: ++qemu-img: --force-share/-U conflicts with image options ++ ++No conflict: ++ ++Conflict: ++-U conflicts with image options + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Add-test-for-cancelling-a-mirror-job.patch b/SOURCES/kvm-iotests-Add-test-for-cancelling-a-mirror-job.patch new file mode 100644 index 0000000..e9b8aa2 --- /dev/null +++ b/SOURCES/kvm-iotests-Add-test-for-cancelling-a-mirror-job.patch @@ -0,0 +1,231 @@ +From 2476593dd4bc78bbf9183e873f2ca641ce15fddf Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 14:47:36 +0200 +Subject: [PATCH 26/54] iotests: Add test for cancelling a mirror job + +RH-Author: Max Reitz +Message-id: <20180618144736.29873-4-mreitz@redhat.com> +Patchwork-id: 80745 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 3/3] iotests: Add test for cancelling a mirror job +Bugzilla: 1572856 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf +RH-Acked-by: John Snow + +We already have an extensive mirror test (041) which does cover +cancelling a mirror job, especially after it has emitted the READY +event. However, it does not check what exact events are emitted after +block-job-cancel is executed. More importantly, it does not use +throttling to ensure that it covers the case of block-job-cancel before +READY. + +It would be possible to add this case to 041, but considering it is +already our largest test file, it makes sense to create a new file for +these cases. + +Signed-off-by: Max Reitz +Message-id: 20180501220509.14152-3-mreitz@redhat.com +Signed-off-by: Jeff Cody +(cherry picked from commit dc885fff972c447f51572afc4c921a26b880731b) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/218 | 138 +++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/218.out | 30 ++++++++++ + tests/qemu-iotests/group | 1 + + 3 files changed, 169 insertions(+) + create mode 100644 tests/qemu-iotests/218 + create mode 100644 tests/qemu-iotests/218.out + +diff --git a/tests/qemu-iotests/218 b/tests/qemu-iotests/218 +new file mode 100644 +index 0000000..92c331b +--- /dev/null ++++ b/tests/qemu-iotests/218 +@@ -0,0 +1,138 @@ ++#!/usr/bin/env python ++# ++# This test covers what happens when a mirror block job is cancelled ++# in various phases of its existence. ++# ++# Note that this test only checks the emitted events (i.e. ++# BLOCK_JOB_COMPLETED vs. BLOCK_JOB_CANCELLED), it does not compare ++# whether the target is in sync with the source when the ++# BLOCK_JOB_COMPLETED event occurs. This is covered by other tests ++# (such as 041). ++# ++# Copyright (C) 2018 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 . ++# ++# Creator/Owner: Max Reitz ++ ++import iotests ++from iotests import log ++ ++iotests.verify_platform(['linux']) ++ ++ ++# Launches the VM, adds two null-co nodes (source and target), and ++# starts a blockdev-mirror job on them. ++# ++# Either both or none of speed and buf_size must be given. ++ ++def start_mirror(vm, speed=None, buf_size=None): ++ vm.launch() ++ ++ ret = vm.qmp('blockdev-add', ++ node_name='source', ++ driver='null-co', ++ size=1048576) ++ assert ret['return'] == {} ++ ++ ret = vm.qmp('blockdev-add', ++ node_name='target', ++ driver='null-co', ++ size=1048576) ++ assert ret['return'] == {} ++ ++ if speed is not None: ++ ret = vm.qmp('blockdev-mirror', ++ job_id='mirror', ++ device='source', ++ target='target', ++ sync='full', ++ speed=speed, ++ buf_size=buf_size) ++ else: ++ ret = vm.qmp('blockdev-mirror', ++ job_id='mirror', ++ device='source', ++ target='target', ++ sync='full') ++ ++ assert ret['return'] == {} ++ ++ ++log('') ++log('=== Cancel mirror job before convergence ===') ++log('') ++ ++log('--- force=false ---') ++log('') ++ ++with iotests.VM() as vm: ++ # Low speed so it does not converge ++ start_mirror(vm, 65536, 65536) ++ ++ log('Cancelling job') ++ log(vm.qmp('block-job-cancel', device='mirror', force=False)) ++ ++ log(vm.event_wait('BLOCK_JOB_CANCELLED'), ++ filters=[iotests.filter_qmp_event]) ++ ++log('') ++log('--- force=true ---') ++log('') ++ ++with iotests.VM() as vm: ++ # Low speed so it does not converge ++ start_mirror(vm, 65536, 65536) ++ ++ log('Cancelling job') ++ log(vm.qmp('block-job-cancel', device='mirror', force=True)) ++ ++ log(vm.event_wait('BLOCK_JOB_CANCELLED'), ++ filters=[iotests.filter_qmp_event]) ++ ++ ++log('') ++log('=== Cancel mirror job after convergence ===') ++log('') ++ ++log('--- force=false ---') ++log('') ++ ++with iotests.VM() as vm: ++ start_mirror(vm) ++ ++ log(vm.event_wait('BLOCK_JOB_READY'), ++ filters=[iotests.filter_qmp_event]) ++ ++ log('Cancelling job') ++ log(vm.qmp('block-job-cancel', device='mirror', force=False)) ++ ++ log(vm.event_wait('BLOCK_JOB_COMPLETED'), ++ filters=[iotests.filter_qmp_event]) ++ ++log('') ++log('--- force=true ---') ++log('') ++ ++with iotests.VM() as vm: ++ start_mirror(vm) ++ ++ log(vm.event_wait('BLOCK_JOB_READY'), ++ filters=[iotests.filter_qmp_event]) ++ ++ log('Cancelling job') ++ log(vm.qmp('block-job-cancel', device='mirror', force=True)) ++ ++ log(vm.event_wait('BLOCK_JOB_CANCELLED'), ++ filters=[iotests.filter_qmp_event]) +diff --git a/tests/qemu-iotests/218.out b/tests/qemu-iotests/218.out +new file mode 100644 +index 0000000..7dbf78e +--- /dev/null ++++ b/tests/qemu-iotests/218.out +@@ -0,0 +1,30 @@ ++ ++=== Cancel mirror job before convergence === ++ ++--- force=false --- ++ ++Cancelling job ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 65536, u'len': 1048576, u'offset': 65536}, u'event': u'BLOCK_JOB_CANCELLED'} ++ ++--- force=true --- ++ ++Cancelling job ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 65536, u'len': 1048576, u'offset': 65536}, u'event': u'BLOCK_JOB_CANCELLED'} ++ ++=== Cancel mirror job after convergence === ++ ++--- force=false --- ++ ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_READY'} ++Cancelling job ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_COMPLETED'} ++ ++--- force=true --- ++ ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_READY'} ++Cancelling job ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_CANCELLED'} +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index 99777ec..3a89aed 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -212,3 +212,4 @@ + 211 rw auto quick + 212 rw auto quick + 213 rw auto quick ++218 rw auto quick +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Add-test-for-qemu-img-convert-C-compatibilit.patch b/SOURCES/kvm-iotests-Add-test-for-qemu-img-convert-C-compatibilit.patch new file mode 100644 index 0000000..149e0d2 --- /dev/null +++ b/SOURCES/kvm-iotests-Add-test-for-qemu-img-convert-C-compatibilit.patch @@ -0,0 +1,69 @@ +From 1ed06a7c22bbae0f9e17147c86ad2ecc69efb9bf Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Wed, 1 Aug 2018 06:35:38 +0200 +Subject: [PATCH 02/13] iotests: Add test for 'qemu-img convert -C' + compatibility + +RH-Author: Fam Zheng +Message-id: <20180801063538.32582-3-famz@redhat.com> +Patchwork-id: 81562 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 2/2] iotests: Add test for 'qemu-img convert -C' compatibility +Bugzilla: 1607774 +RH-Acked-by: Kevin Wolf +RH-Acked-by: John Snow +RH-Acked-by: Miroslav Rezanina + +Signed-off-by: Fam Zheng +Signed-off-by: Kevin Wolf +(cherry picked from commit 8ba4f10fa689251facd483c3ee0ef4dd4e9bec53) +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/082 | 8 ++++++++ + tests/qemu-iotests/082.out | 11 +++++++++++ + 2 files changed, 19 insertions(+) + +diff --git a/tests/qemu-iotests/082 b/tests/qemu-iotests/082 +index a872f77..3e605d5 100755 +--- a/tests/qemu-iotests/082 ++++ b/tests/qemu-iotests/082 +@@ -158,6 +158,14 @@ run_qemu_img convert -o help + run_qemu_img convert -O bochs -o help + + echo ++echo === convert: -C and other options === ++ ++# Adding the help option to a command without other -o options ++run_qemu_img convert -C -S 4k -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target ++run_qemu_img convert -C -S 8k -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target ++run_qemu_img convert -C -c -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target ++ ++echo + echo === amend: Options specified more than once === + + # Last -f should win +diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out +index 60ef87c..19e9fb1 100644 +--- a/tests/qemu-iotests/082.out ++++ b/tests/qemu-iotests/082.out +@@ -508,6 +508,17 @@ size Virtual disk size + Testing: convert -O bochs -o help + qemu-img: Format driver 'bochs' does not support image creation + ++=== convert: -C and other options === ++ ++Testing: convert -C -S 4k -O qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.target ++qemu-img: Cannot enable copy offloading when -S is used ++ ++Testing: convert -C -S 8k -O qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.target ++qemu-img: Cannot enable copy offloading when -S is used ++ ++Testing: convert -C -c -O qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.target ++qemu-img: Cannot enable copy offloading when -c is used ++ + === amend: Options specified more than once === + + Testing: amend -f foo -f qcow2 -o lazy_refcounts=on TEST_DIR/t.qcow2 +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Add-test-for-rebasing-with-relative-paths.patch b/SOURCES/kvm-iotests-Add-test-for-rebasing-with-relative-paths.patch new file mode 100644 index 0000000..19d4eff --- /dev/null +++ b/SOURCES/kvm-iotests-Add-test-for-rebasing-with-relative-paths.patch @@ -0,0 +1,168 @@ +From f5f8208d1e759b789da9f2ae03b2320c355dbf89 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 17:48:33 +0200 +Subject: [PATCH 11/89] iotests: Add test for rebasing with relative paths + +RH-Author: Max Reitz +Message-id: <20180618174833.19439-3-mreitz@redhat.com> +Patchwork-id: 80789 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 2/2] iotests: Add test for rebasing with relative paths +Bugzilla: 1569835 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Stefan Hajnoczi + +Signed-off-by: Max Reitz +Reviewed-by: Eric Blake +Message-id: 20180509182002.8044-3-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit 28036a7f7044fddb79819e3c8fcb4ae5605c60e0) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/024 | 82 ++++++++++++++++++++++++++++++++++++++++++++-- + tests/qemu-iotests/024.out | 30 +++++++++++++++++ + 2 files changed, 109 insertions(+), 3 deletions(-) + +diff --git a/tests/qemu-iotests/024 b/tests/qemu-iotests/024 +index e0d77ce..4071ed6 100755 +--- a/tests/qemu-iotests/024 ++++ b/tests/qemu-iotests/024 +@@ -29,9 +29,14 @@ status=1 # failure is the default! + + _cleanup() + { +- _cleanup_test_img +- rm -f "$TEST_DIR/t.$IMGFMT.base_old" +- rm -f "$TEST_DIR/t.$IMGFMT.base_new" ++ _cleanup_test_img ++ rm -f "$TEST_DIR/t.$IMGFMT.base_old" ++ rm -f "$TEST_DIR/t.$IMGFMT.base_new" ++ ++ rm -f "$TEST_DIR/subdir/t.$IMGFMT" ++ rm -f "$TEST_DIR/subdir/t.$IMGFMT.base_old" ++ rm -f "$TEST_DIR/subdir/t.$IMGFMT.base_new" ++ rmdir "$TEST_DIR/subdir" 2> /dev/null + } + trap "_cleanup; exit \$status" 0 1 2 3 15 + +@@ -123,6 +128,77 @@ io_pattern readv $((13 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x00 + io_pattern readv $((14 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x11 + io_pattern readv $((15 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x00 + ++echo ++echo "=== Test rebase in a subdirectory of the working directory ===" ++echo ++ ++# Clean up the old images beforehand so they do not interfere with ++# this test ++_cleanup ++ ++mkdir "$TEST_DIR/subdir" ++ ++# Relative to the overlay ++BASE_OLD_OREL="t.$IMGFMT.base_old" ++BASE_NEW_OREL="t.$IMGFMT.base_new" ++ ++# Relative to $TEST_DIR (which is going to be our working directory) ++OVERLAY_WREL="subdir/t.$IMGFMT" ++ ++BASE_OLD="$TEST_DIR/subdir/$BASE_OLD_OREL" ++BASE_NEW="$TEST_DIR/subdir/$BASE_NEW_OREL" ++OVERLAY="$TEST_DIR/$OVERLAY_WREL" ++ ++# Test done here: ++# ++# Backing (old): 11 11 -- 11 ++# Backing (new): -- 22 22 11 ++# Overlay: -- -- -- -- ++# ++# Rebasing works, we have verified that above. Here, we just want to ++# see that rebasing is done for the correct target backing file. ++ ++TEST_IMG=$BASE_OLD _make_test_img 1M ++TEST_IMG=$BASE_NEW _make_test_img 1M ++TEST_IMG=$OVERLAY _make_test_img -b "$BASE_OLD_OREL" 1M ++ ++echo ++ ++$QEMU_IO "$BASE_OLD" \ ++ -c "write -P 0x11 $((0 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \ ++ -c "write -P 0x11 $((3 * CLUSTER_SIZE)) $((1 * CLUSTER_SIZE))" \ ++ | _filter_qemu_io ++ ++$QEMU_IO "$BASE_NEW" \ ++ -c "write -P 0x22 $((1 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \ ++ -c "write -P 0x11 $((3 * CLUSTER_SIZE)) $((1 * CLUSTER_SIZE))" \ ++ | _filter_qemu_io ++ ++echo ++ ++pushd "$TEST_DIR" >/dev/null ++$QEMU_IMG rebase -f "$IMGFMT" -b "$BASE_NEW_OREL" "$OVERLAY_WREL" ++popd >/dev/null ++ ++# Verify the backing path is correct ++TEST_IMG=$OVERLAY _img_info | grep '^backing file' ++ ++echo ++ ++# Verify the data is correct ++$QEMU_IO "$OVERLAY" \ ++ -c "read -P 0x11 $((0 * CLUSTER_SIZE)) $CLUSTER_SIZE" \ ++ -c "read -P 0x11 $((1 * CLUSTER_SIZE)) $CLUSTER_SIZE" \ ++ -c "read -P 0x00 $((2 * CLUSTER_SIZE)) $CLUSTER_SIZE" \ ++ -c "read -P 0x11 $((3 * CLUSTER_SIZE)) $CLUSTER_SIZE" \ ++ | _filter_qemu_io ++ ++echo ++ ++# Verify that cluster #3 is not allocated (because it is the same in ++# $BASE_OLD and $BASE_NEW) ++$QEMU_IMG map "$OVERLAY" | _filter_qemu_img_map ++ + + # success, all done + echo "*** done" +diff --git a/tests/qemu-iotests/024.out b/tests/qemu-iotests/024.out +index 33cfaf5..024dc78 100644 +--- a/tests/qemu-iotests/024.out ++++ b/tests/qemu-iotests/024.out +@@ -141,4 +141,34 @@ read 65536/65536 bytes at offset 917504 + === IO: pattern 0x00 + read 65536/65536 bytes at offset 983040 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++ ++=== Test rebase in a subdirectory of the working directory === ++ ++Formatting 'TEST_DIR/subdir/t.IMGFMT.base_old', fmt=IMGFMT size=1048576 ++Formatting 'TEST_DIR/subdir/t.IMGFMT.base_new', fmt=IMGFMT size=1048576 ++Formatting 'TEST_DIR/subdir/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=t.IMGFMT.base_old ++ ++wrote 131072/131072 bytes at offset 0 ++128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++wrote 65536/65536 bytes at offset 196608 ++64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++wrote 131072/131072 bytes at offset 65536 ++128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++wrote 65536/65536 bytes at offset 196608 ++64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++ ++backing file: t.IMGFMT.base_new (actual path: TEST_DIR/subdir/t.IMGFMT.base_new) ++ ++read 65536/65536 bytes at offset 0 ++64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++read 65536/65536 bytes at offset 65536 ++64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++read 65536/65536 bytes at offset 131072 ++64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++read 65536/65536 bytes at offset 196608 ++64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++ ++Offset Length File ++0 0x30000 TEST_DIR/subdir/t.IMGFMT ++0x30000 0x10000 TEST_DIR/subdir/t.IMGFMT.base_new + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Also-test-I-O-over-NBD-TLS.patch b/SOURCES/kvm-iotests-Also-test-I-O-over-NBD-TLS.patch new file mode 100644 index 0000000..4780d21 --- /dev/null +++ b/SOURCES/kvm-iotests-Also-test-I-O-over-NBD-TLS.patch @@ -0,0 +1,91 @@ +From 5074b9b995f708fe57995267b40d3fedc4368c3b Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:36 +0100 +Subject: [PATCH 069/163] iotests: Also test I/O over NBD TLS +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190322032241.8111-24-jsnow@redhat.com> +Patchwork-id: 85113 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 23/28] iotests: Also test I/O over NBD TLS +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Enhance test 233 to also perform I/O beyond the initial handshake. + +Signed-off-by: Eric Blake +Message-Id: <20181118022403.2211483-1-eblake@redhat.com> +Reviewed-by: Daniel P. Berrangé +(cherry picked from commit bb39c47d70e84acf5066f79eba27ae5945b837be) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/233 | 12 +++++++++++- + tests/qemu-iotests/233.out | 10 ++++++++++ + 2 files changed, 21 insertions(+), 1 deletion(-) + +diff --git a/tests/qemu-iotests/233 b/tests/qemu-iotests/233 +index 46013ce..a4da60d 100755 +--- a/tests/qemu-iotests/233 ++++ b/tests/qemu-iotests/233 +@@ -62,7 +62,7 @@ tls_x509_create_client "ca2" "client2" + echo + echo "== preparing image ==" + _make_test_img 64M +- ++$QEMU_IO -c 'w -P 0x11 1m 1m' "$TEST_IMG" | _filter_qemu_io + + echo + echo "== check TLS client to plain server fails ==" +@@ -96,6 +96,16 @@ $QEMU_IMG info --image-opts \ + driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ + 2>&1 | sed "s/$nbd_tcp_port/PORT/g" + ++echo ++echo "== perform I/O over TLS ==" ++QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT ++$QEMU_IO -c 'r -P 0x11 1m 1m' -c 'w -P 0x22 1m 1m' --image-opts \ ++ --object tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 \ ++ driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ ++ 2>&1 | _filter_qemu_io ++ ++$QEMU_IO -f qcow2 -r -U -c 'r -P 0x22 1m 1m' "$TEST_IMG" | _filter_qemu_io ++ + # success, all done + echo "*** done" + rm -f $seq.full +diff --git a/tests/qemu-iotests/233.out b/tests/qemu-iotests/233.out +index 616e923..94acd9b 100644 +--- a/tests/qemu-iotests/233.out ++++ b/tests/qemu-iotests/233.out +@@ -9,6 +9,8 @@ Generating a signed certificate... + + == preparing image == + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 ++wrote 1048576/1048576 bytes at offset 1048576 ++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + + == check TLS client to plain server fails == + qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Denied by server for option 5 (starttls) +@@ -27,4 +29,12 @@ disk size: unavailable + == check TLS with different CA fails == + option negotiation failed: Verify failed: No certificate was found. + qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': The certificate hasn't got a known issuer ++ ++== perform I/O over TLS == ++read 1048576/1048576 bytes at offset 1048576 ++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++wrote 1048576/1048576 bytes at offset 1048576 ++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++read 1048576/1048576 bytes at offset 1048576 ++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Clean-up-wrap-image-in-197.patch b/SOURCES/kvm-iotests-Clean-up-wrap-image-in-197.patch new file mode 100644 index 0000000..8ffc184 --- /dev/null +++ b/SOURCES/kvm-iotests-Clean-up-wrap-image-in-197.patch @@ -0,0 +1,42 @@ +From 4a2de12de1790d63467be43c1af8da4800b15c2c Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 16:12:10 +0200 +Subject: [PATCH 34/54] iotests: Clean up wrap image in 197 + +RH-Author: Max Reitz +Message-id: <20180618161212.14444-9-mreitz@redhat.com> +Patchwork-id: 80768 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 08/10] iotests: Clean up wrap image in 197 +Bugzilla: 1518738 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Miroslav Rezanina + +Signed-off-by: Max Reitz +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Alberto Garcia +Message-id: 20180421132929.21610-8-mreitz@redhat.com +Reviewed-by: Kevin Wolf +Signed-off-by: Max Reitz +(cherry picked from commit 5fdc0b73eb68d107944cfa65185fb155b511e496) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/197 | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197 +index 5e869fe..3ae4975 100755 +--- a/tests/qemu-iotests/197 ++++ b/tests/qemu-iotests/197 +@@ -44,6 +44,7 @@ esac + _cleanup() + { + _cleanup_test_img ++ rm -f "$TEST_WRAP" + rm -f "$BLKDBG_CONF" + } + trap "_cleanup; exit \$status" 0 1 2 3 15 +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Copy-197-for-COR-filter-driver.patch b/SOURCES/kvm-iotests-Copy-197-for-COR-filter-driver.patch new file mode 100644 index 0000000..eafabba --- /dev/null +++ b/SOURCES/kvm-iotests-Copy-197-for-COR-filter-driver.patch @@ -0,0 +1,203 @@ +From 6acc674a6d77b4d1b36545c7f00251b264f99d78 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 16:12:11 +0200 +Subject: [PATCH 35/54] iotests: Copy 197 for COR filter driver + +RH-Author: Max Reitz +Message-id: <20180618161212.14444-10-mreitz@redhat.com> +Patchwork-id: 80769 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 09/10] iotests: Copy 197 for COR filter driver +Bugzilla: 1518738 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Miroslav Rezanina + +iotest 197 tests copy-on-read using the (now old) copy-on-read flag. +Copy it to 215 and modify it to use the COR filter driver instead. + +Signed-off-by: Max Reitz +Message-id: 20180421132929.21610-9-mreitz@redhat.com +Reviewed-by: Kevin Wolf +Signed-off-by: Max Reitz +(cherry picked from commit a62cbac4ce2db79c14ff299e98ee556b57467c19) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/215 | 120 +++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/215.out | 26 ++++++++++ + tests/qemu-iotests/group | 1 + + 3 files changed, 147 insertions(+) + create mode 100755 tests/qemu-iotests/215 + create mode 100644 tests/qemu-iotests/215.out + +diff --git a/tests/qemu-iotests/215 b/tests/qemu-iotests/215 +new file mode 100755 +index 0000000..2e616ed +--- /dev/null ++++ b/tests/qemu-iotests/215 +@@ -0,0 +1,120 @@ ++#!/bin/bash ++# ++# Test case for copy-on-read into qcow2, using the COR filter driver ++# ++# Copyright (C) 2018 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 . ++# ++ ++seq="$(basename $0)" ++echo "QA output created by $seq" ++ ++here="$PWD" ++status=1 # failure is the default! ++ ++# get standard environment, filters and checks ++. ./common.rc ++. ./common.filter ++ ++TEST_WRAP="$TEST_DIR/t.wrap.qcow2" ++BLKDBG_CONF="$TEST_DIR/blkdebug.conf" ++ ++# Sanity check: our use of blkdebug fails if $TEST_DIR contains spaces ++# or other problems ++case "$TEST_DIR" in ++ *[^-_a-zA-Z0-9/]*) ++ _notrun "Suspicious TEST_DIR='$TEST_DIR', cowardly refusing to run" ;; ++esac ++ ++_cleanup() ++{ ++ _cleanup_test_img ++ rm -f "$TEST_WRAP" ++ rm -f "$BLKDBG_CONF" ++} ++trap "_cleanup; exit \$status" 0 1 2 3 15 ++ ++# Test is supported for any backing file; but we force qcow2 for our wrapper. ++_supported_fmt generic ++_supported_proto generic ++_supported_os Linux ++# LUKS support may be possible, but it complicates things. ++_unsupported_fmt luks ++ ++echo ++echo '=== Copy-on-read ===' ++echo ++ ++# Prep the images ++# VPC rounds image sizes to a specific geometry, force a specific size. ++if [ "$IMGFMT" = "vpc" ]; then ++ IMGOPTS=$(_optstr_add "$IMGOPTS" "force_size") ++fi ++_make_test_img 4G ++$QEMU_IO -c "write -P 55 3G 1k" "$TEST_IMG" | _filter_qemu_io ++IMGPROTO=file IMGFMT=qcow2 IMGOPTS= TEST_IMG_FILE="$TEST_WRAP" \ ++ _make_test_img -F "$IMGFMT" -b "$TEST_IMG" | _filter_img_create ++$QEMU_IO -f qcow2 -c "write -z -u 1M 64k" "$TEST_WRAP" | _filter_qemu_io ++ ++# Ensure that a read of two clusters, but where one is already allocated, ++# does not re-write the allocated cluster ++cat > "$BLKDBG_CONF" <&1 | _filter_qemu_io) ++case $output in ++ *allocate*) ++ _notrun "Insufficent memory to run test" ;; ++ *) printf '%s\n' "$output" ;; ++esac ++$QEMU_IO \ ++ -c "open -o driver=copy-on-read,file.driver=qcow2 $TEST_WRAP" \ ++ -c "read -P 0 $((3*1024*1024*1024 + 1024)) 1k" \ ++ | _filter_qemu_io ++ ++# Copy-on-read is incompatible with read-only ++$QEMU_IO \ ++ -c "open -r -o driver=copy-on-read,file.driver=qcow2 $TEST_WRAP" \ ++ 2>&1 | _filter_testdir ++ ++# Break the backing chain, and show that images are identical, and that ++# we properly copied over explicit zeros. ++$QEMU_IMG rebase -u -b "" -f qcow2 "$TEST_WRAP" ++$QEMU_IO -f qcow2 -c map "$TEST_WRAP" ++_check_test_img ++$QEMU_IMG compare -f $IMGFMT -F qcow2 "$TEST_IMG" "$TEST_WRAP" ++ ++# success, all done ++echo '*** done' ++status=0 +diff --git a/tests/qemu-iotests/215.out b/tests/qemu-iotests/215.out +new file mode 100644 +index 0000000..70b0f5f +--- /dev/null ++++ b/tests/qemu-iotests/215.out +@@ -0,0 +1,26 @@ ++QA output created by 215 ++ ++=== Copy-on-read === ++ ++Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 ++wrote 1024/1024 bytes at offset 3221225472 ++1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++Formatting 'TEST_DIR/t.wrap.IMGFMT', fmt=IMGFMT size=4294967296 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT ++wrote 65536/65536 bytes at offset 1048576 ++64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++read 131072/131072 bytes at offset 1048576 ++128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++read 0/0 bytes at offset 0 ++0 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++read 2147483136/2147483136 bytes at offset 1024 ++2 GiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++read 1024/1024 bytes at offset 3221226496 ++1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++can't open device TEST_DIR/t.wrap.qcow2: Block node is read-only ++2 GiB (0x80010000) bytes allocated at offset 0 bytes (0x0) ++1023.938 MiB (0x3fff0000) bytes not allocated at offset 2 GiB (0x80010000) ++64 KiB (0x10000) bytes allocated at offset 3 GiB (0xc0000000) ++1023.938 MiB (0x3fff0000) bytes not allocated at offset 3 GiB (0xc0010000) ++No errors were found on the image. ++Images are identical. ++*** done +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index ba7a2d1..cd5d26c 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -213,4 +213,5 @@ + 212 rw auto quick + 213 rw auto quick + 214 rw auto ++215 rw auto quick + 218 rw auto quick +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Disallow-compat-0.10-in-223.patch b/SOURCES/kvm-iotests-Disallow-compat-0.10-in-223.patch new file mode 100644 index 0000000..029419b --- /dev/null +++ b/SOURCES/kvm-iotests-Disallow-compat-0.10-in-223.patch @@ -0,0 +1,46 @@ +From 97ef4db37dea9ef4fd5e082cc45be1ee7915be1f Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:19 +0100 +Subject: [PATCH 052/163] iotests: Disallow compat=0.10 in 223 + +RH-Author: John Snow +Message-id: <20190322032241.8111-7-jsnow@redhat.com> +Patchwork-id: 85089 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 06/28] iotests: Disallow compat=0.10 in 223 +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Max Reitz + +223 tests persistent dirty bitmaps which are not supported in +compat=0.10, so that option is unsupported for this test. + +Signed-off-by: Max Reitz +Tested-by: John Snow +Reviewed-by: John Snow +Signed-off-by: Kevin Wolf +(cherry picked from commit 092b9c408fb22010747f17e2fb19521cfafd45d6) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/223 | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 +index e59411e..d011b3e 100755 +--- a/tests/qemu-iotests/223 ++++ b/tests/qemu-iotests/223 +@@ -40,6 +40,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 + _supported_fmt qcow2 + _supported_proto file # uses NBD as well + _supported_os Linux ++# Persistent dirty bitmaps require compat=1.1 ++_unsupported_imgopts 'compat=0.10' + + function do_run_qemu() + { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Don-t-lock-dev-null-in-226.patch b/SOURCES/kvm-iotests-Don-t-lock-dev-null-in-226.patch new file mode 100644 index 0000000..656bd56 --- /dev/null +++ b/SOURCES/kvm-iotests-Don-t-lock-dev-null-in-226.patch @@ -0,0 +1,51 @@ +From 0290b82d90f8010677d46d3625fe5db50f25f763 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 17:12:06 +0100 +Subject: [PATCH 035/163] iotests: Don't lock /dev/null in 226 + +RH-Author: John Snow +Message-id: <20190320171206.19236-3-jsnow@redhat.com> +Patchwork-id: 84962 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 2/2] iotests: Don't lock /dev/null in 226 +Bugzilla: 1691018 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Sergio Lopez Pascual + +From: Fam Zheng + +On my system (Fedora 28), this script reports a 'failed to get +"consistent read" lock' error. Following docs/devel/testing.rst, it's +better to add locking=off here. + +Signed-off-by: Fam Zheng +Reviewed-by: Eric Blake +Reviewed-by: John Snow +Signed-off-by: Kevin Wolf +(cherry picked from commit ac49c189b4fd48251314a2b5d5a251bcc1687d66) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/226 | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/tests/qemu-iotests/226 b/tests/qemu-iotests/226 +index a5a1f67..0bc227f 100755 +--- a/tests/qemu-iotests/226 ++++ b/tests/qemu-iotests/226 +@@ -53,10 +53,10 @@ for PROTO in "file" "host_device" "host_cdrom"; do + echo + echo "== Testing RO ==" + $QEMU_IO -c "open -r -o driver=$PROTO,filename=$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt +- $QEMU_IO -c "open -r -o driver=$PROTO,filename=/dev/null" 2>&1 | _filter_imgfmt ++ $QEMU_IO -c "open -r -o driver=$PROTO,filename=/dev/null,locking=off" 2>&1 | _filter_imgfmt + echo "== Testing RW ==" + $QEMU_IO -c "open -o driver=$PROTO,filename=$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt +- $QEMU_IO -c "open -o driver=$PROTO,filename=/dev/null" 2>&1 | _filter_imgfmt ++ $QEMU_IO -c "open -o driver=$PROTO,filename=/dev/null,locking=off" 2>&1 | _filter_imgfmt + done + + # success, all done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Drop-use-of-bash-keyword-function.patch b/SOURCES/kvm-iotests-Drop-use-of-bash-keyword-function.patch new file mode 100644 index 0000000..e68fe53 --- /dev/null +++ b/SOURCES/kvm-iotests-Drop-use-of-bash-keyword-function.patch @@ -0,0 +1,962 @@ +From 65003ace540a37ec135d30020e2c0f829a0979ae Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:37 +0100 +Subject: [PATCH 070/163] iotests: Drop use of bash keyword 'function' +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190322032241.8111-25-jsnow@redhat.com> +Patchwork-id: 85112 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 24/28] iotests: Drop use of bash keyword 'function' +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Bash allows functions to be declared with or without the leading +keyword 'function'; but including the keyword does not comply with +POSIX syntax, and is confusing to ksh users where the use of the +keyword changes the scoping rules for functions. Stick to the +POSIX form through iotests. + +Done mechanically with: + sed -i 's/^function //' $(git ls-files tests/qemu-iotests) + +Signed-off-by: Eric Blake +Message-Id: <20181116215002.2124581-1-eblake@redhat.com> +Reviewed-by: Daniel P. Berrangé +Reviewed-by: Philippe Mathieu-Daudé +(cherry picked from commit 8cedcffdc195bc39aeb1373826ba0a45629741e0) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/035 | 2 +- + tests/qemu-iotests/037 | 2 +- + tests/qemu-iotests/038 | 6 +++--- + tests/qemu-iotests/046 | 6 +++--- + tests/qemu-iotests/047 | 2 +- + tests/qemu-iotests/049 | 4 ++-- + tests/qemu-iotests/051 | 4 ++-- + tests/qemu-iotests/067 | 4 ++-- + tests/qemu-iotests/071 | 4 ++-- + tests/qemu-iotests/077 | 4 ++-- + tests/qemu-iotests/081 | 4 ++-- + tests/qemu-iotests/082 | 2 +- + tests/qemu-iotests/085 | 10 +++++----- + tests/qemu-iotests/086 | 2 +- + tests/qemu-iotests/087 | 6 +++--- + tests/qemu-iotests/099 | 6 +++--- + tests/qemu-iotests/109 | 2 +- + tests/qemu-iotests/112 | 2 +- + tests/qemu-iotests/142 | 8 ++++---- + tests/qemu-iotests/153 | 4 ++-- + tests/qemu-iotests/157 | 4 ++-- + tests/qemu-iotests/172 | 6 +++--- + tests/qemu-iotests/176 | 2 +- + tests/qemu-iotests/177 | 2 +- + tests/qemu-iotests/184 | 4 ++-- + tests/qemu-iotests/186 | 4 ++-- + tests/qemu-iotests/195 | 4 ++-- + tests/qemu-iotests/204 | 2 +- + tests/qemu-iotests/223 | 4 ++-- + tests/qemu-iotests/227 | 4 ++-- + tests/qemu-iotests/232 | 6 +++--- + tests/qemu-iotests/common.nbd | 12 ++++++------ + tests/qemu-iotests/common.pattern | 16 ++++++++-------- + tests/qemu-iotests/common.qemu | 8 ++++---- + tests/qemu-iotests/common.tls | 10 +++++----- + 35 files changed, 86 insertions(+), 86 deletions(-) + +diff --git a/tests/qemu-iotests/035 b/tests/qemu-iotests/035 +index a5716ca..85d9ef7 100755 +--- a/tests/qemu-iotests/035 ++++ b/tests/qemu-iotests/035 +@@ -49,7 +49,7 @@ echo + echo "creating image" + _make_test_img $size + +-function generate_requests() { ++generate_requests() { + for i in $(seq 0 63); do + echo "aio_write ${i}M 512" + echo "aio_write ${i}M 512" +diff --git a/tests/qemu-iotests/037 b/tests/qemu-iotests/037 +index 2e43b19..a11992d 100755 +--- a/tests/qemu-iotests/037 ++++ b/tests/qemu-iotests/037 +@@ -54,7 +54,7 @@ TEST_IMG="$TEST_IMG.base" + + _make_test_img $size + +-function backing_io() ++backing_io() + { + local offset=$1 + local sectors=$2 +diff --git a/tests/qemu-iotests/038 b/tests/qemu-iotests/038 +index 4e03976..575093e 100755 +--- a/tests/qemu-iotests/038 ++++ b/tests/qemu-iotests/038 +@@ -51,7 +51,7 @@ TEST_IMG="$TEST_IMG.base" + + _make_test_img $size + +-function backing_io() ++backing_io() + { + local offset=$1 + local sectors=$2 +@@ -76,7 +76,7 @@ _make_test_img -b "$TEST_IMG.base" 6G + echo + echo "== Some concurrent requests touching the same cluster ==" + +-function overlay_io() ++overlay_io() + { + # Start with a request touching two clusters + echo aio_write -P 0x80 2020k 80k +@@ -102,7 +102,7 @@ overlay_io | $QEMU_IO "$TEST_IMG" | _filter_qemu_io |\ + echo + echo "== Verify image content ==" + +-function verify_io() ++verify_io() + { + echo read -P 31 2016k 4k + echo read -P 0x80 2020k 80k +diff --git a/tests/qemu-iotests/046 b/tests/qemu-iotests/046 +index 01c0de6..5e41d96 100755 +--- a/tests/qemu-iotests/046 ++++ b/tests/qemu-iotests/046 +@@ -48,7 +48,7 @@ echo "== creating backing file for COW tests ==" + + _make_test_img $size + +-function backing_io() ++backing_io() + { + local offset=$1 + local sectors=$2 +@@ -73,7 +73,7 @@ _make_test_img -b "$TEST_IMG.base" 6G + echo + echo "== Some concurrent requests touching the same cluster ==" + +-function overlay_io() ++overlay_io() + { + # Allocate middle of cluster 1, then write to somewhere before and after it + cat < /dev/null); then + # For v2 images, discarded clusters are read from the backing file +diff --git a/tests/qemu-iotests/047 b/tests/qemu-iotests/047 +index c168373..6e776d2 100755 +--- a/tests/qemu-iotests/047 ++++ b/tests/qemu-iotests/047 +@@ -45,7 +45,7 @@ size=128M + + _make_test_img $size + +-function qemu_io_cmds() ++qemu_io_cmds() + { + cat <&1 | filter_test_dir +diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 +index 4f20265..a73c73f 100755 +--- a/tests/qemu-iotests/051 ++++ b/tests/qemu-iotests/051 +@@ -43,7 +43,7 @@ _supported_os Linux + # other than refcount_bits=16 + _unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' + +-function do_run_qemu() ++do_run_qemu() + { + echo Testing: "$@" + ( +@@ -57,7 +57,7 @@ function do_run_qemu() + echo + } + +-function run_qemu() ++run_qemu() + { + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | + _filter_generated_node_ids | _filter_hmp +diff --git a/tests/qemu-iotests/067 b/tests/qemu-iotests/067 +index f8d584f..342b2b0 100755 +--- a/tests/qemu-iotests/067 ++++ b/tests/qemu-iotests/067 +@@ -36,7 +36,7 @@ _supported_os Linux + # Because anything other than 16 would change the output of query-block + _unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' + +-function do_run_qemu() ++do_run_qemu() + { + echo Testing: "$@" + $QEMU -nographic -qmp-pretty stdio -serial none "$@" +@@ -52,7 +52,7 @@ _filter_qmp_events() + | tr '\t' '\n' + } + +-function run_qemu() ++run_qemu() + { + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp | _filter_qemu \ + | _filter_actual_image_size \ +diff --git a/tests/qemu-iotests/071 b/tests/qemu-iotests/071 +index 6448e9e..6e467dc 100755 +--- a/tests/qemu-iotests/071 ++++ b/tests/qemu-iotests/071 +@@ -40,14 +40,14 @@ _supported_fmt qcow2 + _supported_proto file + _supported_os Linux + +-function do_run_qemu() ++do_run_qemu() + { + echo Testing: "$@" | _filter_imgfmt + $QEMU -nographic -qmp stdio -serial none "$@" + echo + } + +-function run_qemu() ++run_qemu() + { + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_qmp | _filter_qemu_io + } +diff --git a/tests/qemu-iotests/077 b/tests/qemu-iotests/077 +index a40f319..58fe893 100755 +--- a/tests/qemu-iotests/077 ++++ b/tests/qemu-iotests/077 +@@ -48,7 +48,7 @@ _make_test_img $size + echo + echo "== Some concurrent requests involving RMW ==" + +-function test_io() ++test_io() + { + echo "open -o driver=$IMGFMT,file.align=4k blkdebug::$TEST_IMG" + # A simple RMW request +@@ -193,7 +193,7 @@ test_io | $QEMU_IO | _filter_qemu_io | \ + echo + echo "== Verify image content ==" + +-function verify_io() ++verify_io() + { + # A simple RMW request + echo read -P 0 0 0x200 +diff --git a/tests/qemu-iotests/081 b/tests/qemu-iotests/081 +index c5d4616..4dc6b04 100755 +--- a/tests/qemu-iotests/081 ++++ b/tests/qemu-iotests/081 +@@ -42,14 +42,14 @@ _supported_fmt raw + _supported_proto file + _supported_os Linux + +-function do_run_qemu() ++do_run_qemu() + { + echo Testing: "$@" | _filter_imgfmt + $QEMU -nographic -qmp stdio -serial none "$@" + echo + } + +-function run_qemu() ++run_qemu() + { + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_qmp\ + | _filter_qemu_io | _filter_generated_node_ids +diff --git a/tests/qemu-iotests/082 b/tests/qemu-iotests/082 +index 14f6631..61eec63 100755 +--- a/tests/qemu-iotests/082 ++++ b/tests/qemu-iotests/082 +@@ -40,7 +40,7 @@ _supported_fmt qcow2 + _supported_proto file nfs + _supported_os Linux + +-function run_qemu_img() ++run_qemu_img() + { + echo + echo Testing: "$@" | _filter_testdir +diff --git a/tests/qemu-iotests/085 b/tests/qemu-iotests/085 +index 2ef8407..ade68ef 100755 +--- a/tests/qemu-iotests/085 ++++ b/tests/qemu-iotests/085 +@@ -60,7 +60,7 @@ _supported_os Linux + + + # ${1}: unique identifier for the snapshot filename +-function create_single_snapshot() ++create_single_snapshot() + { + cmd="{ 'execute': 'blockdev-snapshot-sync', + 'arguments': { 'device': 'virtio0', +@@ -70,7 +70,7 @@ function create_single_snapshot() + } + + # ${1}: unique identifier for the snapshot filename +-function create_group_snapshot() ++create_group_snapshot() + { + cmd="{ 'execute': 'transaction', 'arguments': + {'actions': [ +@@ -88,7 +88,7 @@ function create_group_snapshot() + # ${1}: unique identifier for the snapshot filename + # ${2}: extra_params to the blockdev-add command + # ${3}: filename +-function do_blockdev_add() ++do_blockdev_add() + { + cmd="{ 'execute': 'blockdev-add', 'arguments': + { 'driver': 'qcow2', 'node-name': 'snap_${1}', ${2} +@@ -99,7 +99,7 @@ function do_blockdev_add() + } + + # ${1}: unique identifier for the snapshot filename +-function add_snapshot_image() ++add_snapshot_image() + { + base_image="${TEST_DIR}/$((${1}-1))-${snapshot_virt0}" + snapshot_file="${TEST_DIR}/${1}-${snapshot_virt0}" +@@ -110,7 +110,7 @@ function add_snapshot_image() + + # ${1}: unique identifier for the snapshot filename + # ${2}: expected response, defaults to 'return' +-function blockdev_snapshot() ++blockdev_snapshot() + { + cmd="{ 'execute': 'blockdev-snapshot', + 'arguments': { 'node': 'virtio0', +diff --git a/tests/qemu-iotests/086 b/tests/qemu-iotests/086 +index f9e4722..06613fd 100755 +--- a/tests/qemu-iotests/086 ++++ b/tests/qemu-iotests/086 +@@ -40,7 +40,7 @@ _supported_fmt qcow2 raw + _supported_proto file nfs + _supported_os Linux + +-function run_qemu_img() ++run_qemu_img() + { + echo + echo Testing: "$@" | _filter_testdir +diff --git a/tests/qemu-iotests/087 b/tests/qemu-iotests/087 +index 109cdf5..f625887 100755 +--- a/tests/qemu-iotests/087 ++++ b/tests/qemu-iotests/087 +@@ -34,14 +34,14 @@ _supported_fmt qcow2 + _supported_proto file + _supported_os Linux + +-function do_run_qemu() ++do_run_qemu() + { + echo Testing: "$@" + $QEMU -nographic -qmp stdio -serial none "$@" + echo + } + +-function run_qemu() ++run_qemu() + { + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ + | _filter_qemu | _filter_imgfmt \ +@@ -102,7 +102,7 @@ echo === aio=native without O_DIRECT === + echo + + # Skip this test if AIO is not enabled in this build +-function run_qemu_filter_aio() ++run_qemu_filter_aio() + { + run_qemu "$@" | \ + sed -e 's/is not supported in this build/it requires cache.direct=on, which was not specified/' +diff --git a/tests/qemu-iotests/099 b/tests/qemu-iotests/099 +index 4a6275d..578808b 100755 +--- a/tests/qemu-iotests/099 ++++ b/tests/qemu-iotests/099 +@@ -45,12 +45,12 @@ _supported_os Linux + _unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat" \ + "subformat=twoGbMaxExtentSparse" + +-function do_run_qemu() ++do_run_qemu() + { + $QEMU -nographic -qmp stdio -serial none "$@" + } + +-function run_qemu() ++run_qemu() + { + # Get the "file": "foo" entry ($foo may only contain escaped double quotes, + # which is how we can extract it) +@@ -59,7 +59,7 @@ function run_qemu() + | sed -e 's/^.*"file": "\(\(\\"\|[^"]\)*\)".*$/\1/' -e 's/\\"/"/g' + } + +-function test_qemu() ++test_qemu() + { + run_qemu -drive "if=none,id=drv0,$1" <&1 | _filter_testdir | _filter_qemu | _filter_hmp + } +@@ -88,7 +88,7 @@ echo + files="if=none,file=$TEST_IMG,backing.file.filename=$TEST_IMG.base" + ids="node-name=image,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file" + +-function check_cache_all() ++check_cache_all() + { + # cache.direct is supposed to be inherited by both bs->file and + # bs->backing +@@ -231,7 +231,7 @@ drv_bk="if=none,file=json:{'driver':'$IMGFMT',,'file':'backing-file',,'node-name + drv_file="if=none,driver=file,filename=$TEST_IMG,node-name=file" + drv_img="if=none,id=blk,file=json:{'driver':'$IMGFMT',,'file':'file',,'backing':'backing',,'node-name':'image'}" + +-function check_cache_all_separate() ++check_cache_all_separate() + { + # Check cache.direct + +diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153 +index 00092b8..3120a61 100755 +--- a/tests/qemu-iotests/153 ++++ b/tests/qemu-iotests/153 +@@ -70,7 +70,7 @@ _run_cmd() + (echo "$@"; "$@" 2>&1 1>/dev/null) | _filter_testdir + } + +-function _do_run_qemu() ++_do_run_qemu() + { + ( + if ! test -t 0; then +@@ -82,7 +82,7 @@ function _do_run_qemu() + ) | $QEMU -nographic -monitor stdio -serial none "$@" 1>/dev/null + } + +-function _run_qemu_with_images() ++_run_qemu_with_images() + { + _do_run_qemu \ + $(for i in $@; do echo "-drive if=none,file=$i"; done) 2>&1 \ +diff --git a/tests/qemu-iotests/157 b/tests/qemu-iotests/157 +index c3231b7..6fb2659 100755 +--- a/tests/qemu-iotests/157 ++++ b/tests/qemu-iotests/157 +@@ -40,7 +40,7 @@ _supported_fmt generic + _supported_proto file + _supported_os Linux + +-function do_run_qemu() ++do_run_qemu() + { + ( + if ! test -t 0; then +@@ -53,7 +53,7 @@ function do_run_qemu() + echo + } + +-function run_qemu() ++run_qemu() + { + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_imgfmt \ + | _filter_qemu | _filter_generated_node_ids +diff --git a/tests/qemu-iotests/172 b/tests/qemu-iotests/172 +index c5ee33e..1e60a7e 100755 +--- a/tests/qemu-iotests/172 ++++ b/tests/qemu-iotests/172 +@@ -46,7 +46,7 @@ if [ "$QEMU_DEFAULT_MACHINE" != "pc" ]; then + _notrun "Requires a PC machine" + fi + +-function do_run_qemu() ++do_run_qemu() + { + ( + if ! test -t 0; then +@@ -59,7 +59,7 @@ function do_run_qemu() + echo + } + +-function check_floppy_qtree() ++check_floppy_qtree() + { + echo + echo Testing: "$@" | _filter_testdir +@@ -75,7 +75,7 @@ function check_floppy_qtree() + _filter_win32 | _filter_qemu + } + +-function check_cache_mode() ++check_cache_mode() + { + echo "info block none0" | + QEMU_OPTIONS="" do_run_qemu -drive if=none,file="$TEST_IMG" "$@" | +diff --git a/tests/qemu-iotests/176 b/tests/qemu-iotests/176 +index c091d0b..4ecd589 100755 +--- a/tests/qemu-iotests/176 ++++ b/tests/qemu-iotests/176 +@@ -50,7 +50,7 @@ _supported_os Linux + # Persistent dirty bitmaps require compat=1.1 + _unsupported_imgopts 'compat=0.10' + +-function run_qemu() ++run_qemu() + { + $QEMU -nographic -qmp stdio -serial none "$@" 2>&1 \ + | _filter_testdir | _filter_qmp | _filter_qemu \ +diff --git a/tests/qemu-iotests/177 b/tests/qemu-iotests/177 +index 7bf8e1d..f0c1155 100755 +--- a/tests/qemu-iotests/177 ++++ b/tests/qemu-iotests/177 +@@ -85,7 +85,7 @@ $QEMU_IO -c "open -o $options,$limits blkdebug::$TEST_IMG" \ + echo + echo "== verify image content ==" + +-function verify_io() ++verify_io() + { + if ($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | + grep "compat: 0.10" > /dev/null); then +diff --git a/tests/qemu-iotests/184 b/tests/qemu-iotests/184 +index 2f3259d..0af7a73 100755 +--- a/tests/qemu-iotests/184 ++++ b/tests/qemu-iotests/184 +@@ -34,14 +34,14 @@ trap "exit \$status" 0 1 2 3 15 + + _supported_os Linux + +-function do_run_qemu() ++do_run_qemu() + { + echo Testing: "$@" | _filter_imgfmt + $QEMU -nographic -qmp-pretty stdio -serial none "$@" + echo + } + +-function run_qemu() ++run_qemu() + { + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_qmp\ + | _filter_qemu_io | _filter_generated_node_ids \ +diff --git a/tests/qemu-iotests/186 b/tests/qemu-iotests/186 +index 29681bf..c27dc95 100755 +--- a/tests/qemu-iotests/186 ++++ b/tests/qemu-iotests/186 +@@ -44,7 +44,7 @@ if [ "$QEMU_DEFAULT_MACHINE" != "pc" ]; then + _notrun "Requires a PC machine" + fi + +-function do_run_qemu() ++do_run_qemu() + { + echo Testing: "$@" + +@@ -59,7 +59,7 @@ function do_run_qemu() + echo + } + +-function check_info_block() ++check_info_block() + { + echo "info block" | + do_run_qemu "$@" | _filter_win32 | _filter_hmp | _filter_qemu | +diff --git a/tests/qemu-iotests/195 b/tests/qemu-iotests/195 +index f56f255..a977c97 100755 +--- a/tests/qemu-iotests/195 ++++ b/tests/qemu-iotests/195 +@@ -41,14 +41,14 @@ _supported_fmt qcow2 + _supported_proto file + _supported_os Linux + +-function do_run_qemu() ++do_run_qemu() + { + echo Testing: "$@" + $QEMU -nographic -qmp-pretty stdio -serial none "$@" + echo + } + +-function run_qemu() ++run_qemu() + { + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_imgfmt | _filter_qemu \ + | _filter_qmp | _filter_qemu_io \ +diff --git a/tests/qemu-iotests/204 b/tests/qemu-iotests/204 +index 57f3afe..30f0653 100755 +--- a/tests/qemu-iotests/204 ++++ b/tests/qemu-iotests/204 +@@ -93,7 +93,7 @@ $QEMU_IO -c "open -o $options,$limits blkdebug::$TEST_IMG" \ + echo + echo "== verify image content ==" + +-function verify_io() ++verify_io() + { + echo read -P 22 0 1000 + echo read -P 33 1000 128k +diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 +index 29e1951..5513dc6 100755 +--- a/tests/qemu-iotests/223 ++++ b/tests/qemu-iotests/223 +@@ -42,14 +42,14 @@ _supported_os Linux + # Persistent dirty bitmaps require compat=1.1 + _unsupported_imgopts 'compat=0.10' + +-function do_run_qemu() ++do_run_qemu() + { + echo Testing: "$@" + $QEMU -nographic -qmp stdio -serial none "$@" + echo + } + +-function run_qemu() ++run_qemu() + { + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ + | _filter_qemu | _filter_imgfmt \ +diff --git a/tests/qemu-iotests/227 b/tests/qemu-iotests/227 +index 43f2323..be1b636 100755 +--- a/tests/qemu-iotests/227 ++++ b/tests/qemu-iotests/227 +@@ -40,14 +40,14 @@ _supported_fmt generic + _supported_proto file + _supported_os Linux + +-function do_run_qemu() ++do_run_qemu() + { + echo Testing: "$@" + $QEMU -nographic -qmp-pretty stdio -serial none "$@" + echo + } + +-function run_qemu() ++run_qemu() + { + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ + | _filter_qemu | _filter_imgfmt \ +diff --git a/tests/qemu-iotests/232 b/tests/qemu-iotests/232 +index ae63f13..e48bc8f 100755 +--- a/tests/qemu-iotests/232 ++++ b/tests/qemu-iotests/232 +@@ -40,7 +40,7 @@ _supported_fmt generic + _supported_proto file + _supported_os Linux + +-function do_run_qemu() ++do_run_qemu() + { + echo Testing: "$@" + ( +@@ -54,13 +54,13 @@ function do_run_qemu() + echo + } + +-function run_qemu() ++run_qemu() + { + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_hmp | + _filter_generated_node_ids | _filter_imgfmt + } + +-function run_qemu_info_block() ++run_qemu_info_block() + { + echo "info block -n" | run_qemu "$@" | grep -e "(file" -e "QEMU_PROG" + } +diff --git a/tests/qemu-iotests/common.nbd b/tests/qemu-iotests/common.nbd +index 0f4497a..233187a 100644 +--- a/tests/qemu-iotests/common.nbd ++++ b/tests/qemu-iotests/common.nbd +@@ -23,7 +23,7 @@ nbd_unix_socket="${TEST_DIR}/qemu-nbd.sock" + nbd_tcp_addr="127.0.0.1" + nbd_pid_file="${TEST_DIR}/qemu-nbd.pid" + +-function nbd_server_stop() ++nbd_server_stop() + { + local NBD_PID + if [ -f "$nbd_pid_file" ]; then +@@ -36,7 +36,7 @@ function nbd_server_stop() + rm -f "$nbd_unix_socket" + } + +-function nbd_server_wait_for_unix_socket() ++nbd_server_wait_for_unix_socket() + { + pid=$1 + +@@ -57,14 +57,14 @@ function nbd_server_wait_for_unix_socket() + exit 1 + } + +-function nbd_server_start_unix_socket() ++nbd_server_start_unix_socket() + { + nbd_server_stop + $QEMU_NBD -v -t -k "$nbd_unix_socket" "$@" & + nbd_server_wait_for_unix_socket $! + } + +-function nbd_server_set_tcp_port() ++nbd_server_set_tcp_port() + { + (ss --help) >/dev/null 2>&1 || _notrun "ss utility not found, skipping test" + +@@ -80,7 +80,7 @@ function nbd_server_set_tcp_port() + exit 1 + } + +-function nbd_server_wait_for_tcp_socket() ++nbd_server_wait_for_tcp_socket() + { + pid=$1 + +@@ -101,7 +101,7 @@ function nbd_server_wait_for_tcp_socket() + exit 1 + } + +-function nbd_server_start_tcp_socket() ++nbd_server_start_tcp_socket() + { + nbd_server_stop + $QEMU_NBD -v -t -b $nbd_tcp_addr -p $nbd_tcp_port "$@" & +diff --git a/tests/qemu-iotests/common.pattern b/tests/qemu-iotests/common.pattern +index 34f4a8d..b67bb34 100644 +--- a/tests/qemu-iotests/common.pattern ++++ b/tests/qemu-iotests/common.pattern +@@ -16,7 +16,7 @@ + # along with this program. If not, see . + # + +-function do_is_allocated() { ++do_is_allocated() { + local start=$1 + local size=$2 + local step=$3 +@@ -27,11 +27,11 @@ function do_is_allocated() { + done + } + +-function is_allocated() { ++is_allocated() { + do_is_allocated "$@" | $QEMU_IO "$TEST_IMG" | _filter_qemu_io + } + +-function do_io() { ++do_io() { + local op=$1 + local start=$2 + local size=$3 +@@ -45,22 +45,22 @@ function do_io() { + done + } + +-function io_pattern() { ++io_pattern() { + do_io "$@" | $QEMU_IO "$TEST_IMG" | _filter_qemu_io + } + +-function io() { ++io() { + local start=$2 + local pattern=$(( (start >> 9) % 256 )) + + do_io "$@" $pattern | $QEMU_IO "$TEST_IMG" | _filter_qemu_io + } + +-function io_zero() { ++io_zero() { + do_io "$@" 0 | $QEMU_IO "$TEST_IMG" | _filter_qemu_io + } + +-function io_test() { ++io_test() { + local op=$1 + local offset=$2 + local cluster_size=$3 +@@ -100,7 +100,7 @@ function io_test() { + offset=$((offset + num_large * ( l2_size + half_cluster ))) + } + +-function io_test2() { ++io_test2() { + local orig_offset=$1 + local cluster_size=$2 + local num=$3 +diff --git a/tests/qemu-iotests/common.qemu b/tests/qemu-iotests/common.qemu +index f285484..6b8eb9b 100644 +--- a/tests/qemu-iotests/common.qemu ++++ b/tests/qemu-iotests/common.qemu +@@ -60,7 +60,7 @@ _in_fd=4 + # $3: A string to search for in the response; if found, this indicates + # failure and the test is either aborted (if $qemu_error_no_exit + # is not set) or ${QEMU_STATUS[$1]} is set to -1 (otherwise). +-function _timed_wait_for() ++_timed_wait_for() + { + local h=${1} + shift +@@ -131,7 +131,7 @@ function _timed_wait_for() + # strings the response will be scanned for. The first of the two + # indicates success, the latter indicates failure. Failure is handled + # like a timeout. +-function _send_qemu_cmd() ++_send_qemu_cmd() + { + local h=${1} + local count=1 +@@ -186,7 +186,7 @@ function _send_qemu_cmd() + # Returns: + # $QEMU_HANDLE: set to a handle value to communicate with this QEMU instance. + # +-function _launch_qemu() ++_launch_qemu() + { + local comm= + local fifo_out= +@@ -262,7 +262,7 @@ function _launch_qemu() + # If $wait is set to anything other than the empty string, the process will not + # be killed but only waited for, and any output will be forwarded to stdout. If + # $wait is empty, the process will be killed and all output will be suppressed. +-function _cleanup_qemu() ++_cleanup_qemu() + { + # QEMU_PID[], QEMU_IN[], QEMU_OUT[] all use same indices + for i in "${!QEMU_OUT[@]}" +diff --git a/tests/qemu-iotests/common.tls b/tests/qemu-iotests/common.tls +index cecab26..39f17c1 100644 +--- a/tests/qemu-iotests/common.tls ++++ b/tests/qemu-iotests/common.tls +@@ -20,7 +20,7 @@ + + tls_dir="${TEST_DIR}/tls" + +-function tls_x509_cleanup() ++tls_x509_cleanup() + { + rm -f "${tls_dir}"/*.pem + rm -f "${tls_dir}"/*/*.pem +@@ -29,7 +29,7 @@ function tls_x509_cleanup() + } + + +-function tls_x509_init() ++tls_x509_init() + { + mkdir -p "${tls_dir}" + +@@ -58,7 +58,7 @@ EOF + } + + +-function tls_x509_create_root_ca() ++tls_x509_create_root_ca() + { + name=${1:-ca-cert} + +@@ -77,7 +77,7 @@ EOF + } + + +-function tls_x509_create_server() ++tls_x509_create_server() + { + caname=$1 + name=$2 +@@ -108,7 +108,7 @@ EOF + } + + +-function tls_x509_create_client() ++tls_x509_create_client() + { + caname=$1 + name=$2 +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Enhance-223-233-to-cover-qemu-nbd-list.patch b/SOURCES/kvm-iotests-Enhance-223-233-to-cover-qemu-nbd-list.patch new file mode 100644 index 0000000..41ab501 --- /dev/null +++ b/SOURCES/kvm-iotests-Enhance-223-233-to-cover-qemu-nbd-list.patch @@ -0,0 +1,191 @@ +From a4e0439bc039710f1c7f3133ea1cfd88ae38fda5 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:55 +0100 +Subject: [PATCH 117/163] iotests: Enhance 223, 233 to cover 'qemu-nbd --list' + +RH-Author: John Snow +Message-id: <20190327172308.31077-43-jsnow@redhat.com> +Patchwork-id: 85215 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 42/55] iotests: Enhance 223, 233 to cover 'qemu-nbd --list' +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Any good new feature deserves some regression testing :) +Coverage includes: +- 223: what happens when there are 0 or more than 1 export, +proof that we can see multiple contexts including qemu:dirty-bitmap +- 233: proof that we can list over TLS, and that mix-and-match of +plain/TLS listings will behave sanely + +Signed-off-by: Eric Blake +Reviewed-by: Richard W.M. Jones +Tested-by: Richard W.M. Jones +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20190117193658.16413-22-eblake@redhat.com> +(cherry picked from commit ddd09448fd833d646952c769ae9ce3d39bee989f) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/223 | 2 ++ + tests/qemu-iotests/223.out | 20 ++++++++++++++++++++ + tests/qemu-iotests/233 | 19 +++++++++++++------ + tests/qemu-iotests/233.out | 15 +++++++++++++++ + 4 files changed, 50 insertions(+), 6 deletions(-) + +diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 +index 773892d..f120a01 100755 +--- a/tests/qemu-iotests/223 ++++ b/tests/qemu-iotests/223 +@@ -127,6 +127,7 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start", + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start", + "arguments":{"addr":{"type":"unix", + "data":{"path":"'"$TEST_DIR/nbd"1'"}}}}' "error" # Attempt second server ++$QEMU_NBD_PROG -L -k "$TEST_DIR/nbd" + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments":{"device":"n", "bitmap":"b"}}' "return" + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", +@@ -142,6 +143,7 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments":{"device":"n", "name":"n2", "writable":true, + "bitmap":"b2"}}' "return" ++$QEMU_NBD_PROG -L -k "$TEST_DIR/nbd" + + echo + echo "=== Contrast normal status to large granularity dirty-bitmap ===" +diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out +index 4012bb0..963ae28 100644 +--- a/tests/qemu-iotests/223.out ++++ b/tests/qemu-iotests/223.out +@@ -30,12 +30,32 @@ wrote 2097152/2097152 bytes at offset 2097152 + {"error": {"class": "GenericError", "desc": "NBD server not running"}} + {"return": {}} + {"error": {"class": "GenericError", "desc": "NBD server already running"}} ++exports available: 0 + {"return": {}} + {"error": {"class": "GenericError", "desc": "Cannot find device=nosuch nor node_name=nosuch"}} + {"error": {"class": "GenericError", "desc": "NBD server already has export named 'n'"}} + {"error": {"class": "GenericError", "desc": "Enabled bitmap 'b2' incompatible with readonly export"}} + {"error": {"class": "GenericError", "desc": "Bitmap 'b3' is not found"}} + {"return": {}} ++exports available: 2 ++ export: 'n' ++ size: 4194304 ++ flags: 0x4ef ( readonly flush fua trim zeroes df cache ) ++ min block: 512 ++ opt block: 4096 ++ max block: 33554432 ++ available meta contexts: 2 ++ base:allocation ++ qemu:dirty-bitmap:b ++ export: 'n2' ++ size: 4194304 ++ flags: 0x4ed ( flush fua trim zeroes df cache ) ++ min block: 512 ++ opt block: 4096 ++ max block: 33554432 ++ available meta contexts: 2 ++ base:allocation ++ qemu:dirty-bitmap:b2 + + === Contrast normal status to large granularity dirty-bitmap === + +diff --git a/tests/qemu-iotests/233 b/tests/qemu-iotests/233 +index ab15c77..fc345a1 100755 +--- a/tests/qemu-iotests/233 ++++ b/tests/qemu-iotests/233 +@@ -69,10 +69,12 @@ echo + echo "== check TLS client to plain server fails ==" + nbd_server_start_tcp_socket -f $IMGFMT "$TEST_IMG" 2> "$TEST_DIR/server.log" + +-$QEMU_IMG info --image-opts \ +- --object tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 \ ++obj=tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 ++$QEMU_IMG info --image-opts --object $obj \ + driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ + 2>&1 | sed "s/$nbd_tcp_port/PORT/g" ++$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port --object $obj \ ++ --tls-creds=tls0 + + nbd_server_stop + +@@ -85,20 +87,25 @@ nbd_server_start_tcp_socket \ + -f $IMGFMT "$TEST_IMG" 2>> "$TEST_DIR/server.log" + + $QEMU_IMG info nbd://localhost:$nbd_tcp_port 2>&1 | sed "s/$nbd_tcp_port/PORT/g" ++$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port + + echo + echo "== check TLS works ==" +-$QEMU_IMG info --image-opts \ +- --object tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 \ ++obj=tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 ++$QEMU_IMG info --image-opts --object $obj \ + driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ + 2>&1 | sed "s/$nbd_tcp_port/PORT/g" ++$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port --object $obj \ ++ --tls-creds=tls0 + + echo + echo "== check TLS with different CA fails ==" +-$QEMU_IMG info --image-opts \ +- --object tls-creds-x509,dir=${tls_dir}/client2,endpoint=client,id=tls0 \ ++obj=tls-creds-x509,dir=${tls_dir}/client2,endpoint=client,id=tls0 ++$QEMU_IMG info --image-opts --object $obj \ + driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ + 2>&1 | sed "s/$nbd_tcp_port/PORT/g" ++$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port --object $obj \ ++ --tls-creds=tls0 + + echo + echo "== perform I/O over TLS ==" +diff --git a/tests/qemu-iotests/233.out b/tests/qemu-iotests/233.out +index 2199d8a..6d45f3b 100644 +--- a/tests/qemu-iotests/233.out ++++ b/tests/qemu-iotests/233.out +@@ -15,19 +15,33 @@ wrote 1048576/1048576 bytes at offset 1048576 + == check TLS client to plain server fails == + qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Denied by server for option 5 (starttls) + server reported: TLS not configured ++qemu-nbd: Denied by server for option 5 (starttls) ++server reported: TLS not configured + + == check plain client to TLS server fails == + qemu-img: Could not open 'nbd://localhost:PORT': TLS negotiation required before option 8 (structured reply) + server reported: Option 0x8 not permitted before TLS ++qemu-nbd: TLS negotiation required before option 8 (structured reply) ++server reported: Option 0x8 not permitted before TLS + + == check TLS works == + image: nbd://127.0.0.1:PORT + file format: nbd + virtual size: 64M (67108864 bytes) + disk size: unavailable ++exports available: 1 ++ export: '' ++ size: 67108864 ++ flags: 0x4ed ( flush fua trim zeroes df cache ) ++ min block: 512 ++ opt block: 4096 ++ max block: 33554432 ++ available meta contexts: 1 ++ base:allocation + + == check TLS with different CA fails == + qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': The certificate hasn't got a known issuer ++qemu-nbd: The certificate hasn't got a known issuer + + == perform I/O over TLS == + read 1048576/1048576 bytes at offset 1048576 +@@ -39,4 +53,5 @@ read 1048576/1048576 bytes at offset 1048576 + + == final server log == + qemu-nbd: option negotiation failed: Verify failed: No certificate was found. ++qemu-nbd: option negotiation failed: Verify failed: No certificate was found. + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Enhance-223-to-cover-multiple-bitmap-granula.patch b/SOURCES/kvm-iotests-Enhance-223-to-cover-multiple-bitmap-granula.patch new file mode 100644 index 0000000..76d0354 --- /dev/null +++ b/SOURCES/kvm-iotests-Enhance-223-to-cover-multiple-bitmap-granula.patch @@ -0,0 +1,216 @@ +From 4398dec3833e9a4c27c0394aaeee4329008d489a Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 16:16:14 +0100 +Subject: [PATCH 016/163] iotests: Enhance 223 to cover multiple bitmap + granularities + +RH-Author: John Snow +Message-id: <20190320161631.14841-3-jsnow@redhat.com> +Patchwork-id: 84939 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 02/19] iotests: Enhance 223 to cover multiple bitmap granularities +Bugzilla: 1668956 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Testing granularity at the same size as the cluster isn't quite +as fun as what happens when it is larger or smaller. This +enhancement also shows that qemu's nbd server can serve the +same disk over multiple exports simultaneously. + +Signed-off-by: Eric Blake +Tested-by: John Snow +Reviewed-by: John Snow +Signed-off-by: Kevin Wolf +(cherry picked from commit a237dea330a2be9a2cbe95056b9a8d67627d95c6) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/223 | 43 +++++++++++++++++++++++++++++++++++-------- + tests/qemu-iotests/223.out | 32 +++++++++++++++++++++++++------- + 2 files changed, 60 insertions(+), 15 deletions(-) + +diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 +index b63b7a4..a462f41 100755 +--- a/tests/qemu-iotests/223 ++++ b/tests/qemu-iotests/223 +@@ -56,10 +56,11 @@ function run_qemu() + } + + echo +-echo "=== Create partially sparse image, then add dirty bitmap ===" ++echo "=== Create partially sparse image, then add dirty bitmaps ===" + echo + +-_make_test_img 4M ++# Two bitmaps, to contrast granularity issues ++_make_test_img -o cluster_size=4k 4M + $QEMU_IO -c 'w -P 0x11 1M 2M' "$TEST_IMG" | _filter_qemu_io + run_qemu < >(_filter_nbd) +@@ -102,6 +113,8 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"blockdev-add", + "file":{"driver":"file", "filename":"'"$TEST_IMG"'"}}}' "return" + _send_qemu_cmd $QEMU_HANDLE '{"execute":"x-block-dirty-bitmap-disable", + "arguments":{"node":"n", "name":"b"}}' "return" ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-block-dirty-bitmap-disable", ++ "arguments":{"node":"n", "name":"b2"}}' "return" + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start", + "arguments":{"addr":{"type":"unix", + "data":{"path":"'"$TEST_DIR/nbd"'"}}}}' "return" +@@ -109,26 +122,40 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments":{"device":"n"}}' "return" + _send_qemu_cmd $QEMU_HANDLE '{"execute":"x-nbd-server-add-bitmap", + "arguments":{"name":"n", "bitmap":"b"}}' "return" ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", ++ "arguments":{"device":"n", "name":"n2"}}' "return" ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-nbd-server-add-bitmap", ++ "arguments":{"name":"n2", "bitmap":"b2"}}' "return" + + echo +-echo "=== Contrast normal status with dirty-bitmap status ===" ++echo "=== Contrast normal status to large granularity dirty-bitmap ===" + echo + + QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT + IMG="driver=nbd,export=n,server.type=unix,server.path=$TEST_DIR/nbd" +-$QEMU_IO -r -c 'r -P 0 0 1m' -c 'r -P 0x11 1m 1m' \ +- -c 'r -P 0x22 2m 2m' --image-opts "$IMG" | _filter_qemu_io ++$QEMU_IO -r -c 'r -P 0x22 512 512' -c 'r -P 0 512k 512k' -c 'r -P 0x11 1m 1m' \ ++ -c 'r -P 0x33 2m 2m' --image-opts "$IMG" | _filter_qemu_io + $QEMU_IMG map --output=json --image-opts \ + "$IMG" | _filter_qemu_img_map + $QEMU_IMG map --output=json --image-opts \ + "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b" | _filter_qemu_img_map + + echo ++echo "=== Contrast to small granularity dirty-bitmap ===" ++echo ++ ++IMG="driver=nbd,export=n2,server.type=unix,server.path=$TEST_DIR/nbd" ++$QEMU_IMG map --output=json --image-opts \ ++ "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b2" | _filter_qemu_img_map ++ ++echo + echo "=== End NBD server ===" + echo + + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove", + "arguments":{"name":"n"}}' "return" ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove", ++ "arguments":{"name":"n2"}}' "return" + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-stop"}' "return" + _send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' "return" + +diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out +index 33021c8..de41747 100644 +--- a/tests/qemu-iotests/223.out ++++ b/tests/qemu-iotests/223.out +@@ -1,6 +1,6 @@ + QA output created by 223 + +-=== Create partially sparse image, then add dirty bitmap === ++=== Create partially sparse image, then add dirty bitmaps === + + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 + wrote 2097152/2097152 bytes at offset 1048576 +@@ -11,15 +11,18 @@ QMP_VERSION + {"return": {}} + {"return": {}} + {"return": {}} ++{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + + + === Write part of the file under active bitmap === + ++wrote 512/512 bytes at offset 512 ++512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + wrote 2097152/2097152 bytes at offset 2097152 + 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +-=== End dirty bitmap, and start serving image over NBD === ++=== End dirty bitmaps, and start serving image over NBD === + + {"return": {}} + {"return": {}} +@@ -27,18 +30,32 @@ wrote 2097152/2097152 bytes at offset 2097152 + {"return": {}} + {"return": {}} + {"return": {}} ++{"return": {}} ++{"return": {}} ++{"return": {}} + +-=== Contrast normal status with dirty-bitmap status === ++=== Contrast normal status to large granularity dirty-bitmap === + +-read 1048576/1048576 bytes at offset 0 +-1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++read 512/512 bytes at offset 512 ++512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++read 524288/524288 bytes at offset 524288 ++512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + read 1048576/1048576 bytes at offset 1048576 + 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + read 2097152/2097152 bytes at offset 2097152 + 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +-[{ "start": 0, "length": 1048576, "depth": 0, "zero": true, "data": false}, ++[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true}, ++{ "start": 4096, "length": 1044480, "depth": 0, "zero": true, "data": false}, + { "start": 1048576, "length": 3145728, "depth": 0, "zero": false, "data": true}] +-[{ "start": 0, "length": 2097152, "depth": 0, "zero": false, "data": true}, ++[{ "start": 0, "length": 65536, "depth": 0, "zero": false, "data": false}, ++{ "start": 65536, "length": 2031616, "depth": 0, "zero": false, "data": true}, ++{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}] ++ ++=== Contrast to small granularity dirty-bitmap === ++ ++[{ "start": 0, "length": 512, "depth": 0, "zero": false, "data": true}, ++{ "start": 512, "length": 512, "depth": 0, "zero": false, "data": false}, ++{ "start": 1024, "length": 2096128, "depth": 0, "zero": false, "data": true}, + { "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}] + + === End NBD server === +@@ -46,4 +63,5 @@ read 2097152/2097152 bytes at offset 2097152 + {"return": {}} + {"return": {}} + {"return": {}} ++{"return": {}} + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Fix-207-to-use-QMP-filters-for-qmp_log.patch b/SOURCES/kvm-iotests-Fix-207-to-use-QMP-filters-for-qmp_log.patch new file mode 100644 index 0000000..ad5b60f --- /dev/null +++ b/SOURCES/kvm-iotests-Fix-207-to-use-QMP-filters-for-qmp_log.patch @@ -0,0 +1,78 @@ +From c0db4cef886d62fa701b3c8000e1e6d5f4ac9ec5 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 16:16:29 +0100 +Subject: [PATCH 031/163] iotests: Fix 207 to use QMP filters for qmp_log + +RH-Author: John Snow +Message-id: <20190320161631.14841-18-jsnow@redhat.com> +Patchwork-id: 84958 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 17/19] iotests: Fix 207 to use QMP filters for qmp_log +Bugzilla: 1668956 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Max Reitz + +Fixes: 08fcd6111e1949f456e1b232ebeeb0cc17019a92 +Signed-off-by: Max Reitz +Reviewed-by: John Snow +Message-id: 20190210145736.1486-6-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit 9ac10f2e2c93e647ba6830c7083310aa04f65021) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/207 | 10 +++++++--- + tests/qemu-iotests/207.out | 4 ++-- + 2 files changed, 9 insertions(+), 5 deletions(-) + +diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207 +index 687b2ca..e0d2a2c 100755 +--- a/tests/qemu-iotests/207 ++++ b/tests/qemu-iotests/207 +@@ -27,12 +27,16 @@ import re + iotests.verify_image_format(supported_fmts=['raw']) + iotests.verify_protocol(supported=['ssh']) + +-def filter_hash(msg): +- return re.sub('"hash": "[0-9a-f]+"', '"hash": HASH', msg) ++def filter_hash(qmsg): ++ def _filter(key, value): ++ if key == 'hash' and re.match('[0-9a-f]+', value): ++ return 'HASH' ++ return value ++ return iotests.filter_qmp(qmsg, _filter) + + def blockdev_create(vm, options): + result = vm.qmp_log('blockdev-create', job_id='job0', options=options, +- filters=[iotests.filter_testfiles, filter_hash]) ++ filters=[iotests.filter_qmp_testfiles, filter_hash]) + + if 'return' in result: + assert result['return'] == {} +diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out +index 45ac7c2..88d2240 100644 +--- a/tests/qemu-iotests/207.out ++++ b/tests/qemu-iotests/207.out +@@ -40,7 +40,7 @@ Job failed: remote host key does not match host_key_check 'wrong' + {"execute": "job-dismiss", "arguments": {"id": "job0"}} + {"return": {}} + +-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": HASH, "mode": "hash", "type": "md5"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 8388608}}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "HASH", "mode": "hash", "type": "md5"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 8388608}}} + {"return": {}} + {"execute": "job-dismiss", "arguments": {"id": "job0"}} + {"return": {}} +@@ -55,7 +55,7 @@ Job failed: remote host key does not match host_key_check 'wrong' + {"execute": "job-dismiss", "arguments": {"id": "job0"}} + {"return": {}} + +-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": HASH, "mode": "hash", "type": "sha1"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "HASH", "mode": "hash", "type": "sha1"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}} + {"return": {}} + {"execute": "job-dismiss", "arguments": {"id": "job0"}} + {"return": {}} +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Fix-219-s-timing.patch b/SOURCES/kvm-iotests-Fix-219-s-timing.patch new file mode 100644 index 0000000..f07a3e4 --- /dev/null +++ b/SOURCES/kvm-iotests-Fix-219-s-timing.patch @@ -0,0 +1,149 @@ +From 36d1b90499cb0b8ab992d249d742de66e6fc24b7 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:55 +0200 +Subject: [PATCH 86/89] iotests: Fix 219's timing + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-73-kwolf@redhat.com> +Patchwork-id: 81111 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 72/73] iotests: Fix 219's timing +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +From: Max Reitz + +219 has two issues that may lead to sporadic failure, both of which are +the result of issuing query-jobs too early after a job has been +modified. This can then lead to different results based on whether the +modification has taken effect already or not. + +First, query-jobs is issued right after the job has been created. +Besides its current progress possibly being in any random state (which +has already been taken care of), its total progress too is basically +arbitrary, because the job may not yet have been able to determine it. +This patch addresses this by just filtering the total progress, like +what has been done for the current progress already. However, for more +clarity, the filtering is changed to replace the values by a string +'FILTERED' instead of deleting them. + +Secondly, query-jobs is issued right after a job has been resumed. The +job may or may not yet have had the time to actually perform any I/O, +and thus its current progress may or may not have advanced. To make +sure it has indeed advanced (which is what the reference output already +assumes), keep querying it until it has. + +Signed-off-by: Max Reitz +Message-id: 20180606190628.8170-1-mreitz@redhat.com +Reviewed-by: Eric Blake +Signed-off-by: Max Reitz +(cherry picked from commit 83f90b535a0d5e64056c087aa4022ea35c59bcd0) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/219 | 26 ++++++++++++++++++++------ + tests/qemu-iotests/219.out | 10 +++++----- + 2 files changed, 25 insertions(+), 11 deletions(-) + +diff --git a/tests/qemu-iotests/219 b/tests/qemu-iotests/219 +index 898a26e..c03bbdb 100755 +--- a/tests/qemu-iotests/219 ++++ b/tests/qemu-iotests/219 +@@ -42,11 +42,24 @@ def test_pause_resume(vm): + iotests.log(vm.qmp(pause_cmd, **{pause_arg: 'job0'})) + pause_wait(vm, 'job0') + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) +- iotests.log(vm.qmp('query-jobs')) ++ result = vm.qmp('query-jobs') ++ iotests.log(result) ++ ++ old_progress = result['return'][0]['current-progress'] ++ total_progress = result['return'][0]['total-progress'] + + iotests.log(vm.qmp(resume_cmd, **{resume_arg: 'job0'})) + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) +- iotests.log(vm.qmp('query-jobs')) ++ if old_progress < total_progress: ++ # Wait for the job to advance ++ while result['return'][0]['current-progress'] == old_progress: ++ result = vm.qmp('query-jobs') ++ iotests.log(result) ++ else: ++ # Already reached the end, so the job cannot advance ++ # any further; therefore, the query-jobs result can be ++ # logged immediately ++ iotests.log(vm.qmp('query-jobs')) + + def test_job_lifecycle(vm, job, job_args, has_ready=False): + iotests.log('') +@@ -58,12 +71,13 @@ def test_job_lifecycle(vm, job, job_args, has_ready=False): + iotests.log(vm.qmp(job, job_id='job0', **job_args)) + + # Depending on the storage, the first request may or may not have completed +- # yet, so filter out the progress. Later query-job calls don't need the +- # filtering because the progress is made deterministic by the block job +- # speed ++ # yet (and the total progress may not have been fully determined yet), so ++ # filter out the progress. Later query-job calls don't need the filtering ++ # because the progress is made deterministic by the block job speed + result = vm.qmp('query-jobs') + for j in result['return']: +- del j['current-progress'] ++ j['current-progress'] = 'FILTERED' ++ j['total-progress'] = 'FILTERED' + iotests.log(result) + + # undefined -> created -> running +diff --git a/tests/qemu-iotests/219.out b/tests/qemu-iotests/219.out +index 346801b..6dc07bc 100644 +--- a/tests/qemu-iotests/219.out ++++ b/tests/qemu-iotests/219.out +@@ -3,7 +3,7 @@ Launching VM... + + Starting block job: drive-mirror (auto-finalize: True; auto-dismiss: True) + {u'return': {}} +-{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'mirror'}]} + {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + +@@ -93,7 +93,7 @@ Waiting for PENDING state... + + Starting block job: drive-backup (auto-finalize: True; auto-dismiss: True) + {u'return': {}} +-{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]} + {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + +@@ -144,7 +144,7 @@ Waiting for PENDING state... + + Starting block job: drive-backup (auto-finalize: True; auto-dismiss: False) + {u'return': {}} +-{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]} + {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + +@@ -203,7 +203,7 @@ Waiting for PENDING state... + + Starting block job: drive-backup (auto-finalize: False; auto-dismiss: True) + {u'return': {}} +-{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]} + {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + +@@ -262,7 +262,7 @@ Waiting for PENDING state... + + Starting block job: drive-backup (auto-finalize: False; auto-dismiss: False) + {u'return': {}} +-{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]} + {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Fix-232-for-LUKS.patch b/SOURCES/kvm-iotests-Fix-232-for-LUKS.patch new file mode 100644 index 0000000..ba812ca --- /dev/null +++ b/SOURCES/kvm-iotests-Fix-232-for-LUKS.patch @@ -0,0 +1,52 @@ +From 7ccfbb9865064e0b41b6acf9e451c6c1471fe86e Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 16:16:28 +0100 +Subject: [PATCH 030/163] iotests: Fix 232 for LUKS + +RH-Author: John Snow +Message-id: <20190320161631.14841-17-jsnow@redhat.com> +Patchwork-id: 84955 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 16/19] iotests: Fix 232 for LUKS +Bugzilla: 1668956 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Max Reitz + +With IMGOPTSSYNTAX, $TEST_IMG is useless for this test (it only tests +the file-posix protocol driver). Therefore, if $TEST_IMG_FILE is set, +use that instead. + +Because this test requires the file protocol, $TEST_IMG_FILE will always +be set if $IMGOPTSSYNTAX is true. + +Signed-off-by: Max Reitz +Reviewed-by: John Snow +Message-id: 20190210145736.1486-5-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit c48221aa91d9078b86e23b19484227dc35d71840) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/232 | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/tests/qemu-iotests/232 b/tests/qemu-iotests/232 +index de884cd..2ed39d2 100755 +--- a/tests/qemu-iotests/232 ++++ b/tests/qemu-iotests/232 +@@ -70,6 +70,10 @@ size=128M + + _make_test_img $size + ++if [ -n "$TEST_IMG_FILE" ]; then ++ TEST_IMG=$TEST_IMG_FILE ++fi ++ + echo + echo "=== -drive with read-write image: read-only/auto-read-only combinations ===" + echo +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Let-216-make-use-of-qemu-io-s-exit-code.patch b/SOURCES/kvm-iotests-Let-216-make-use-of-qemu-io-s-exit-code.patch new file mode 100644 index 0000000..cc4ae18 --- /dev/null +++ b/SOURCES/kvm-iotests-Let-216-make-use-of-qemu-io-s-exit-code.patch @@ -0,0 +1,115 @@ +From 5914d4c0869766e245b5ed5ea857af72c3b0049e Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 16:43:12 +0200 +Subject: [PATCH 44/54] iotests: Let 216 make use of qemu-io's exit code + +RH-Author: Max Reitz +Message-id: <20180618164312.24423-6-mreitz@redhat.com> +Patchwork-id: 80776 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 5/5] iotests: Let 216 make use of qemu-io's exit code +Bugzilla: 1519617 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Miroslav Rezanina + +As a showcase of how you can use qemu-io's exit code to determine +success or failure (same for qemu-img), this test is changed to use +qemu_io_silent() instead of qemu_io(), and to assert the exit code +instead of logging the filtered result. + +One real advantage of this is that in case of an error, you get a +backtrace that helps you locate the issue in the test file quickly. + +Signed-off-by: Max Reitz +Reviewed-by: Eric Blake +Message-id: 20180509194302.21585-6-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit e4ca4e981a0a389d6af5dc5d8b5fbdd1a05276a0) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/216 | 23 ++++++++++++----------- + tests/qemu-iotests/216.out | 17 ++--------------- + 2 files changed, 14 insertions(+), 26 deletions(-) + +diff --git a/tests/qemu-iotests/216 b/tests/qemu-iotests/216 +index ca9b47a..3c0ae54 100755 +--- a/tests/qemu-iotests/216 ++++ b/tests/qemu-iotests/216 +@@ -20,7 +20,7 @@ + # Creator/Owner: Max Reitz + + import iotests +-from iotests import log, qemu_img_pipe, qemu_io, filter_qemu_io ++from iotests import log, qemu_img, qemu_io_silent + + # Need backing file support + iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk']) +@@ -50,14 +50,13 @@ with iotests.FilePath('base.img') as base_img_path, \ + log('--- Setting up images ---') + log('') + +- qemu_img_pipe('create', '-f', iotests.imgfmt, base_img_path, '64M') ++ assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0 ++ assert qemu_io_silent(base_img_path, '-c', 'write -P 1 0M 1M') == 0 ++ assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path, ++ top_img_path) == 0 ++ assert qemu_io_silent(top_img_path, '-c', 'write -P 2 1M 1M') == 0 + +- log(filter_qemu_io(qemu_io(base_img_path, '-c', 'write -P 1 0M 1M'))) +- +- qemu_img_pipe('create', '-f', iotests.imgfmt, '-b', base_img_path, +- top_img_path) +- +- log(filter_qemu_io(qemu_io(top_img_path, '-c', 'write -P 2 1M 1M'))) ++ log('Done') + + log('') + log('--- Doing COR ---') +@@ -110,6 +109,8 @@ with iotests.FilePath('base.img') as base_img_path, \ + log('--- Checking COR result ---') + log('') + +- log(filter_qemu_io(qemu_io(base_img_path, '-c', 'discard 0 64M'))) +- log(filter_qemu_io(qemu_io(top_img_path, '-c', 'read -P 1 0M 1M'))) +- log(filter_qemu_io(qemu_io(top_img_path, '-c', 'read -P 2 1M 1M'))) ++ assert qemu_io_silent(base_img_path, '-c', 'discard 0 64M') == 0 ++ assert qemu_io_silent(top_img_path, '-c', 'read -P 1 0M 1M') == 0 ++ assert qemu_io_silent(top_img_path, '-c', 'read -P 2 1M 1M') == 0 ++ ++ log('Done') +diff --git a/tests/qemu-iotests/216.out b/tests/qemu-iotests/216.out +index d3fc590..45ea857 100644 +--- a/tests/qemu-iotests/216.out ++++ b/tests/qemu-iotests/216.out +@@ -3,12 +3,7 @@ + + --- Setting up images --- + +-wrote 1048576/1048576 bytes at offset 0 +-1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +- +-wrote 1048576/1048576 bytes at offset 1048576 +-1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +- ++Done + + --- Doing COR --- + +@@ -17,12 +12,4 @@ wrote 1048576/1048576 bytes at offset 1048576 + + --- Checking COR result --- + +-discard 67108864/67108864 bytes at offset 0 +-64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +- +-read 1048576/1048576 bytes at offset 0 +-1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +- +-read 1048576/1048576 bytes at offset 1048576 +-1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +- ++Done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Make-233-output-more-reliable.patch b/SOURCES/kvm-iotests-Make-233-output-more-reliable.patch new file mode 100644 index 0000000..356cee8 --- /dev/null +++ b/SOURCES/kvm-iotests-Make-233-output-more-reliable.patch @@ -0,0 +1,115 @@ +From 0269fa2be355b2885e90bd676f63ae5fe4e8de08 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:35 +0100 +Subject: [PATCH 097/163] iotests: Make 233 output more reliable +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190327172308.31077-23-jsnow@redhat.com> +Patchwork-id: 85189 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 22/55] iotests: Make 233 output more reliable +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +We have a race between the nbd server and the client both trying +to report errors at once which can make the test sometimes fail +if the output lines swap order under load. Break the race by +collecting server messages into a file and then replaying that +at the end of the test. + +We may yet want to fix the server to not output ANYTHING for a +client action except when -v was used (to avoid malicious clients +from being able to DoS a server by filling up its logs), but that +is saved for a future patch. + +Signed-off-by: Eric Blake +CC: Daniel P. Berrangé +Message-Id: <20190117193658.16413-2-eblake@redhat.com> +Reviewed-by: Daniel P. Berrangé +(cherry picked from commit d08980511d78480ffbbd9897b7fd050bd03d518d) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/233 | 11 ++++++++--- + tests/qemu-iotests/233.out | 4 +++- + 2 files changed, 11 insertions(+), 4 deletions(-) + +diff --git a/tests/qemu-iotests/233 b/tests/qemu-iotests/233 +index 1814efe..ab15c77 100755 +--- a/tests/qemu-iotests/233 ++++ b/tests/qemu-iotests/233 +@@ -2,7 +2,7 @@ + # + # Test NBD TLS certificate / authorization integration + # +-# Copyright (C) 2018 Red Hat, Inc. ++# Copyright (C) 2018-2019 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 +@@ -30,6 +30,7 @@ _cleanup() + { + nbd_server_stop + _cleanup_test_img ++ rm -f "$TEST_DIR/server.log" + tls_x509_cleanup + } + trap "_cleanup; exit \$status" 0 1 2 3 15 +@@ -66,7 +67,7 @@ $QEMU_IO -c 'w -P 0x11 1m 1m' "$TEST_IMG" | _filter_qemu_io + + echo + echo "== check TLS client to plain server fails ==" +-nbd_server_start_tcp_socket -f $IMGFMT "$TEST_IMG" ++nbd_server_start_tcp_socket -f $IMGFMT "$TEST_IMG" 2> "$TEST_DIR/server.log" + + $QEMU_IMG info --image-opts \ + --object tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 \ +@@ -81,7 +82,7 @@ echo "== check plain client to TLS server fails ==" + nbd_server_start_tcp_socket \ + --object tls-creds-x509,dir=${tls_dir}/server1,endpoint=server,id=tls0,verify-peer=yes \ + --tls-creds tls0 \ +- -f $IMGFMT "$TEST_IMG" ++ -f $IMGFMT "$TEST_IMG" 2>> "$TEST_DIR/server.log" + + $QEMU_IMG info nbd://localhost:$nbd_tcp_port 2>&1 | sed "s/$nbd_tcp_port/PORT/g" + +@@ -109,6 +110,10 @@ $QEMU_IO -c 'r -P 0x11 1m 1m' -c 'w -P 0x22 1m 1m' --image-opts \ + + $QEMU_IO -f $IMGFMT -r -U -c 'r -P 0x22 1m 1m' "$TEST_IMG" | _filter_qemu_io + ++echo ++echo "== final server log ==" ++cat "$TEST_DIR/server.log" ++ + # success, all done + echo "*** done" + rm -f $seq.full +diff --git a/tests/qemu-iotests/233.out b/tests/qemu-iotests/233.out +index 5f41672..2199d8a 100644 +--- a/tests/qemu-iotests/233.out ++++ b/tests/qemu-iotests/233.out +@@ -27,7 +27,6 @@ virtual size: 64M (67108864 bytes) + disk size: unavailable + + == check TLS with different CA fails == +-qemu-nbd: option negotiation failed: Verify failed: No certificate was found. + qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': The certificate hasn't got a known issuer + + == perform I/O over TLS == +@@ -37,4 +36,7 @@ wrote 1048576/1048576 bytes at offset 1048576 + 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + read 1048576/1048576 bytes at offset 1048576 + 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++ ++== final server log == ++qemu-nbd: option negotiation failed: Verify failed: No certificate was found. + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Make-nbd-fault-injector-flush.patch b/SOURCES/kvm-iotests-Make-nbd-fault-injector-flush.patch new file mode 100644 index 0000000..6c4285c --- /dev/null +++ b/SOURCES/kvm-iotests-Make-nbd-fault-injector-flush.patch @@ -0,0 +1,103 @@ +From 1006959f0d9606cc07fd6c7944228b8e1a8ffe98 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:15 +0100 +Subject: [PATCH 076/163] iotests: Make nbd-fault-injector flush + +RH-Author: John Snow +Message-id: <20190327172308.31077-3-jsnow@redhat.com> +Patchwork-id: 85177 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 02/55] iotests: Make nbd-fault-injector flush +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Max Reitz + +When closing a connection, make the nbd-fault-injector flush the socket. +Without this, the output is a bit unreliable with Python 3. + +Signed-off-by: Max Reitz +Reviewed-by: Eduardo Habkost +Reviewed-by: Cleber Rosa +Reviewed-by: Eric Blake +Message-Id: <20181022135307.14398-2-mreitz@redhat.com> +Signed-off-by: Eduardo Habkost +(cherry picked from commit 6d39db96d23ebe0d4361d108fa202091aa9cbfc1) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/083.out | 9 +++++++++ + tests/qemu-iotests/nbd-fault-injector.py | 1 + + 2 files changed, 10 insertions(+) + +diff --git a/tests/qemu-iotests/083.out b/tests/qemu-iotests/083.out +index be6079d..f9af8bb 100644 +--- a/tests/qemu-iotests/083.out ++++ b/tests/qemu-iotests/083.out +@@ -41,6 +41,7 @@ can't open device nbd+tcp://127.0.0.1:PORT/foo + + === Check disconnect after neg2 === + ++Unable to read from socket: Connection reset by peer + Connection closed + read failed: Input/output error + +@@ -54,6 +55,7 @@ can't open device nbd+tcp://127.0.0.1:PORT/foo + + === Check disconnect before request === + ++Unable to read from socket: Connection reset by peer + Connection closed + read failed: Input/output error + +@@ -116,6 +118,7 @@ can't open device nbd+tcp://127.0.0.1:PORT/ + + === Check disconnect after neg-classic === + ++Unable to read from socket: Connection reset by peer + Connection closed + read failed: Input/output error + +@@ -161,6 +164,8 @@ can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock + + === Check disconnect after neg2 === + ++Unable to read from socket: Connection reset by peer ++Connection closed + read failed: Input/output error + + === Check disconnect 8 neg2 === +@@ -173,6 +178,8 @@ can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock + + === Check disconnect before request === + ++Unable to read from socket: Connection reset by peer ++Connection closed + read failed: Input/output error + + === Check disconnect after request === +@@ -234,6 +241,8 @@ can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock + + === Check disconnect after neg-classic === + ++Unable to read from socket: Connection reset by peer ++Connection closed + read failed: Input/output error + + *** done +diff --git a/tests/qemu-iotests/nbd-fault-injector.py b/tests/qemu-iotests/nbd-fault-injector.py +index 8a04d97..44823f4 100755 +--- a/tests/qemu-iotests/nbd-fault-injector.py ++++ b/tests/qemu-iotests/nbd-fault-injector.py +@@ -111,6 +111,7 @@ class FaultInjectionSocket(object): + if rule.match(event, io): + if rule.when == 0 or bufsize is None: + print 'Closing connection on rule match %s' % rule.name ++ self.sock.flush() + sys.exit(0) + if rule.when != -1: + return rule.when +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Move-qmp_to_opts-to-VM.patch b/SOURCES/kvm-iotests-Move-qmp_to_opts-to-VM.patch new file mode 100644 index 0000000..b3b6664 --- /dev/null +++ b/SOURCES/kvm-iotests-Move-qmp_to_opts-to-VM.patch @@ -0,0 +1,132 @@ +From 3ebb5a4471553aa32662d74676bcc5f8d01cacf1 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:35 +0200 +Subject: [PATCH 66/89] iotests: Move qmp_to_opts() to VM + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-53-kwolf@redhat.com> +Patchwork-id: 81085 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 52/73] iotests: Move qmp_to_opts() to VM +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +qmp_to_opts() used to be a method of QMPTestCase, but recently we +started to add more Python test cases that don't make use of +QMPTestCase. In order to make the method usable there, move it to VM. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit 62a9428812c0f4aacbf2f7fdf449fa4f4ab3775c) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/041 | 6 +++--- + tests/qemu-iotests/155 | 2 +- + tests/qemu-iotests/iotests.py | 45 ++++++++++++++++++++++--------------------- + 3 files changed, 27 insertions(+), 26 deletions(-) + +diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 +index e945879..c20ac7d 100755 +--- a/tests/qemu-iotests/041 ++++ b/tests/qemu-iotests/041 +@@ -1030,9 +1030,9 @@ class TestOrphanedSource(iotests.QMPTestCase): + 'read-only': 'on' } + + self.vm = iotests.VM() +- self.vm.add_blockdev(self.qmp_to_opts(blk0)) +- self.vm.add_blockdev(self.qmp_to_opts(blk1)) +- self.vm.add_blockdev(self.qmp_to_opts(blk2)) ++ self.vm.add_blockdev(self.vm.qmp_to_opts(blk0)) ++ self.vm.add_blockdev(self.vm.qmp_to_opts(blk1)) ++ self.vm.add_blockdev(self.vm.qmp_to_opts(blk2)) + self.vm.launch() + + def tearDown(self): +diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155 +index 42dae04..63a5b5e 100755 +--- a/tests/qemu-iotests/155 ++++ b/tests/qemu-iotests/155 +@@ -63,7 +63,7 @@ class BaseClass(iotests.QMPTestCase): + 'driver': iotests.imgfmt, + 'file': {'driver': 'file', + 'filename': source_img}} +- self.vm.add_blockdev(self.qmp_to_opts(blockdev)) ++ self.vm.add_blockdev(self.vm.qmp_to_opts(blockdev)) + self.vm.add_device('virtio-blk,id=qdev0,drive=source') + self.vm.launch() + +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index 3dab1bf..daf008a 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -372,6 +372,27 @@ class VM(qtest.QEMUQtestMachine): + return self.qmp('human-monitor-command', + command_line='qemu-io %s "%s"' % (drive, cmd)) + ++ def flatten_qmp_object(self, obj, output=None, basestr=''): ++ if output is None: ++ output = dict() ++ if isinstance(obj, list): ++ for i in range(len(obj)): ++ self.flatten_qmp_object(obj[i], output, basestr + str(i) + '.') ++ elif isinstance(obj, dict): ++ for key in obj: ++ self.flatten_qmp_object(obj[key], output, basestr + key + '.') ++ else: ++ output[basestr[:-1]] = obj # Strip trailing '.' ++ return output ++ ++ def qmp_to_opts(self, obj): ++ obj = self.flatten_qmp_object(obj) ++ output_list = list() ++ for key in obj: ++ output_list += [key + '=' + obj[key]] ++ return ','.join(output_list) ++ ++ + + index_re = re.compile(r'([^\[]+)\[([^\]]+)\]') + +@@ -399,26 +420,6 @@ class QMPTestCase(unittest.TestCase): + self.fail('invalid index "%s" in path "%s" in "%s"' % (idx, path, str(d))) + return d + +- def flatten_qmp_object(self, obj, output=None, basestr=''): +- if output is None: +- output = dict() +- if isinstance(obj, list): +- for i in range(len(obj)): +- self.flatten_qmp_object(obj[i], output, basestr + str(i) + '.') +- elif isinstance(obj, dict): +- for key in obj: +- self.flatten_qmp_object(obj[key], output, basestr + key + '.') +- else: +- output[basestr[:-1]] = obj # Strip trailing '.' +- return output +- +- def qmp_to_opts(self, obj): +- obj = self.flatten_qmp_object(obj) +- output_list = list() +- for key in obj: +- output_list += [key + '=' + obj[key]] +- return ','.join(output_list) +- + def assert_qmp_absent(self, d, path): + try: + result = self.dictpath(d, path) +@@ -453,8 +454,8 @@ class QMPTestCase(unittest.TestCase): + '''Asserts that the given filename is a json: filename and that its + content is equal to the given reference object''' + self.assertEqual(json_filename[:5], 'json:') +- self.assertEqual(self.flatten_qmp_object(json.loads(json_filename[5:])), +- self.flatten_qmp_object(reference)) ++ self.assertEqual(self.vm.flatten_qmp_object(json.loads(json_filename[5:])), ++ self.vm.flatten_qmp_object(reference)) + + def cancel_and_wait(self, drive='drive0', force=False, resume=False): + '''Cancel a block job and wait for it to finish, returning the event''' +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-New-test-223-for-exporting-dirty-bitmap-over.patch b/SOURCES/kvm-iotests-New-test-223-for-exporting-dirty-bitmap-over.patch new file mode 100644 index 0000000..661356f --- /dev/null +++ b/SOURCES/kvm-iotests-New-test-223-for-exporting-dirty-bitmap-over.patch @@ -0,0 +1,259 @@ +From a213535d2d03296c7b2f31845160757dbb959ca6 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Tue, 24 Jul 2018 13:06:13 +0200 +Subject: [PATCH 86/89] iotests: New test 223 for exporting dirty bitmap over + NBD + +RH-Author: John Snow +Message-id: <20180718225511.14878-36-jsnow@redhat.com> +Patchwork-id: 81424 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 35/35] iotests: New test 223 for exporting dirty bitmap over NBD +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Eric Blake + +Although this test is NOT a full test of image fleecing (as it +intentionally uses just a single block device directly exported +over NBD, rather than trying to set up a blockdev-backup job with +multiple BDS involved), it DOES prove that qemu as a server is +able to properly expose a dirty bitmap over NBD. + +When coupled with image fleecing, it is then possible for a +third-party client to do an incremental backup by using +qemu-img map with the x-dirty-bitmap option to learn which parts +of the file are dirty (perhaps confusingly, they are the portions +mapped as "data":false - which is part of the reason this is +still in the x- experimental namespace), along with another +normal client (perhaps 'qemu-nbd -c' to expose the server over +/dev/nbd0 and then just use normal I/O on that block device) to +read the dirty sections. + +Signed-off-by: Eric Blake +Message-Id: <20180702191458.28741-3-eblake@redhat.com> +Tested-by: John Snow +Reviewed-by: John Snow +(cherry picked from commit a1532a225a183c9fa60b9c1e8ac8a00c7771f64d) +Signed-off-by: John Snow +--- + tests/qemu-iotests/223 | 138 +++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/223.out | 49 ++++++++++++++++ + tests/qemu-iotests/group | 1 + + 3 files changed, 188 insertions(+) + create mode 100644 tests/qemu-iotests/223 + create mode 100644 tests/qemu-iotests/223.out + +diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 +new file mode 100644 +index 0000000..b63b7a4 +--- /dev/null ++++ b/tests/qemu-iotests/223 +@@ -0,0 +1,138 @@ ++#!/bin/bash ++# ++# Test reading dirty bitmap over NBD ++# ++# Copyright (C) 2018 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 . ++# ++ ++seq="$(basename $0)" ++echo "QA output created by $seq" ++ ++here="$PWD" ++status=1 # failure is the default! ++ ++_cleanup() ++{ ++ _cleanup_test_img ++ _cleanup_qemu ++ rm -f "$TEST_DIR/nbd" ++} ++trap "_cleanup; exit \$status" 0 1 2 3 15 ++ ++# get standard environment, filters and checks ++. ./common.rc ++. ./common.filter ++. ./common.qemu ++ ++_supported_fmt qcow2 ++_supported_proto file # uses NBD as well ++_supported_os Linux ++ ++function do_run_qemu() ++{ ++ echo Testing: "$@" ++ $QEMU -nographic -qmp stdio -serial none "$@" ++ echo ++} ++ ++function run_qemu() ++{ ++ do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ ++ | _filter_qemu | _filter_imgfmt \ ++ | _filter_actual_image_size ++} ++ ++echo ++echo "=== Create partially sparse image, then add dirty bitmap ===" ++echo ++ ++_make_test_img 4M ++$QEMU_IO -c 'w -P 0x11 1M 2M' "$TEST_IMG" | _filter_qemu_io ++run_qemu < >(_filter_nbd) ++ ++silent= ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"qmp_capabilities"}' "return" ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"blockdev-add", ++ "arguments":{"driver":"qcow2", "node-name":"n", ++ "file":{"driver":"file", "filename":"'"$TEST_IMG"'"}}}' "return" ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-block-dirty-bitmap-disable", ++ "arguments":{"node":"n", "name":"b"}}' "return" ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start", ++ "arguments":{"addr":{"type":"unix", ++ "data":{"path":"'"$TEST_DIR/nbd"'"}}}}' "return" ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", ++ "arguments":{"device":"n"}}' "return" ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-nbd-server-add-bitmap", ++ "arguments":{"name":"n", "bitmap":"b"}}' "return" ++ ++echo ++echo "=== Contrast normal status with dirty-bitmap status ===" ++echo ++ ++QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT ++IMG="driver=nbd,export=n,server.type=unix,server.path=$TEST_DIR/nbd" ++$QEMU_IO -r -c 'r -P 0 0 1m' -c 'r -P 0x11 1m 1m' \ ++ -c 'r -P 0x22 2m 2m' --image-opts "$IMG" | _filter_qemu_io ++$QEMU_IMG map --output=json --image-opts \ ++ "$IMG" | _filter_qemu_img_map ++$QEMU_IMG map --output=json --image-opts \ ++ "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b" | _filter_qemu_img_map ++ ++echo ++echo "=== End NBD server ===" ++echo ++ ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove", ++ "arguments":{"name":"n"}}' "return" ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-stop"}' "return" ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' "return" ++ ++# success, all done ++echo '*** done' ++rm -f $seq.full ++status=0 +diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out +new file mode 100644 +index 0000000..33021c8 +--- /dev/null ++++ b/tests/qemu-iotests/223.out +@@ -0,0 +1,49 @@ ++QA output created by 223 ++ ++=== Create partially sparse image, then add dirty bitmap === ++ ++Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 ++wrote 2097152/2097152 bytes at offset 1048576 ++2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++Testing: ++QMP_VERSION ++{"return": {}} ++{"return": {}} ++{"return": {}} ++{"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} ++ ++ ++=== Write part of the file under active bitmap === ++ ++wrote 2097152/2097152 bytes at offset 2097152 ++2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++ ++=== End dirty bitmap, and start serving image over NBD === ++ ++{"return": {}} ++{"return": {}} ++{"return": {}} ++{"return": {}} ++{"return": {}} ++{"return": {}} ++ ++=== Contrast normal status with dirty-bitmap status === ++ ++read 1048576/1048576 bytes at offset 0 ++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++read 1048576/1048576 bytes at offset 1048576 ++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++read 2097152/2097152 bytes at offset 2097152 ++2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++[{ "start": 0, "length": 1048576, "depth": 0, "zero": true, "data": false}, ++{ "start": 1048576, "length": 3145728, "depth": 0, "zero": false, "data": true}] ++[{ "start": 0, "length": 2097152, "depth": 0, "zero": false, "data": true}, ++{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}] ++ ++=== End NBD server === ++ ++{"return": {}} ++{"return": {}} ++{"return": {}} ++*** done +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index be55905..401258f 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -219,4 +219,5 @@ + 218 rw auto quick + 219 rw auto + 222 rw auto quick ++223 rw auto quick + 226 auto quick +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Re-add-filename-filters.patch b/SOURCES/kvm-iotests-Re-add-filename-filters.patch new file mode 100644 index 0000000..67ac98e --- /dev/null +++ b/SOURCES/kvm-iotests-Re-add-filename-filters.patch @@ -0,0 +1,134 @@ +From 04ee985760e2f82bf1a74ea0772116a2e2603a1e Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 16:16:26 +0100 +Subject: [PATCH 028/163] iotests: Re-add filename filters + +RH-Author: John Snow +Message-id: <20190320161631.14841-15-jsnow@redhat.com> +Patchwork-id: 84951 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 14/19] iotests: Re-add filename filters +Bugzilla: 1668956 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Max Reitz + +A previous commit removed the default filters for qmp_log with the +intention to make them explicit; but this happened only for test 206. +There are more tests (for more exotic image formats than qcow2) which +require the filename filter, though. + +Note that 237 is still broken for Python 2.x, which is fixed in the next +commit. + +Fixes: f8ca8609d8549def45b28e82ecac64adaeee9f12 +Signed-off-by: Max Reitz +Reviewed-by: John Snow +Message-id: 20190210145736.1486-2-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit 250c04f554a4a148eb3772af255e6a60450293b0) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/210 | 5 +++-- + tests/qemu-iotests/211 | 5 +++-- + tests/qemu-iotests/212 | 5 +++-- + tests/qemu-iotests/213 | 5 +++-- + 4 files changed, 12 insertions(+), 8 deletions(-) + +diff --git a/tests/qemu-iotests/210 b/tests/qemu-iotests/210 +index d142841..565e3b7 100755 +--- a/tests/qemu-iotests/210 ++++ b/tests/qemu-iotests/210 +@@ -27,7 +27,8 @@ iotests.verify_image_format(supported_fmts=['luks']) + iotests.verify_protocol(supported=['file']) + + def blockdev_create(vm, options): +- result = vm.qmp_log('blockdev-create', job_id='job0', options=options) ++ result = vm.qmp_log('blockdev-create', job_id='job0', options=options, ++ filters=[iotests.filter_qmp_testfiles]) + + if 'return' in result: + assert result['return'] == {} +@@ -53,7 +54,7 @@ with iotests.FilePath('t.luks') as disk_path, \ + 'size': 0 }) + + vm.qmp_log('blockdev-add', driver='file', filename=disk_path, +- node_name='imgfile') ++ node_name='imgfile', filters=[iotests.filter_qmp_testfiles]) + + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'imgfile', +diff --git a/tests/qemu-iotests/211 b/tests/qemu-iotests/211 +index 7b7985d..5d28545 100755 +--- a/tests/qemu-iotests/211 ++++ b/tests/qemu-iotests/211 +@@ -27,7 +27,8 @@ iotests.verify_image_format(supported_fmts=['vdi']) + iotests.verify_protocol(supported=['file']) + + def blockdev_create(vm, options): +- result = vm.qmp_log('blockdev-create', job_id='job0', options=options) ++ result = vm.qmp_log('blockdev-create', job_id='job0', options=options, ++ filters=[iotests.filter_qmp_testfiles]) + + if 'return' in result: + assert result['return'] == {} +@@ -51,7 +52,7 @@ with iotests.FilePath('t.vdi') as disk_path, \ + 'size': 0 }) + + vm.qmp_log('blockdev-add', driver='file', filename=disk_path, +- node_name='imgfile') ++ node_name='imgfile', filters=[iotests.filter_qmp_testfiles]) + + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'imgfile', +diff --git a/tests/qemu-iotests/212 b/tests/qemu-iotests/212 +index 95c8810..42b74f2 100755 +--- a/tests/qemu-iotests/212 ++++ b/tests/qemu-iotests/212 +@@ -27,7 +27,8 @@ iotests.verify_image_format(supported_fmts=['parallels']) + iotests.verify_protocol(supported=['file']) + + def blockdev_create(vm, options): +- result = vm.qmp_log('blockdev-create', job_id='job0', options=options) ++ result = vm.qmp_log('blockdev-create', job_id='job0', options=options, ++ filters=[iotests.filter_qmp_testfiles]) + + if 'return' in result: + assert result['return'] == {} +@@ -51,7 +52,7 @@ with iotests.FilePath('t.parallels') as disk_path, \ + 'size': 0 }) + + vm.qmp_log('blockdev-add', driver='file', filename=disk_path, +- node_name='imgfile') ++ node_name='imgfile', filters=[iotests.filter_qmp_testfiles]) + + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'imgfile', +diff --git a/tests/qemu-iotests/213 b/tests/qemu-iotests/213 +index 4054439..5604f3c 100755 +--- a/tests/qemu-iotests/213 ++++ b/tests/qemu-iotests/213 +@@ -27,7 +27,8 @@ iotests.verify_image_format(supported_fmts=['vhdx']) + iotests.verify_protocol(supported=['file']) + + def blockdev_create(vm, options): +- result = vm.qmp_log('blockdev-create', job_id='job0', options=options) ++ result = vm.qmp_log('blockdev-create', job_id='job0', options=options, ++ filters=[iotests.filter_qmp_testfiles]) + + if 'return' in result: + assert result['return'] == {} +@@ -51,7 +52,7 @@ with iotests.FilePath('t.vhdx') as disk_path, \ + 'size': 0 }) + + vm.qmp_log('blockdev-add', driver='file', filename=disk_path, +- node_name='imgfile') ++ node_name='imgfile', filters=[iotests.filter_qmp_testfiles]) + + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'imgfile', +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Remove-superfluous-rm-from-232.patch b/SOURCES/kvm-iotests-Remove-superfluous-rm-from-232.patch new file mode 100644 index 0000000..8d25ea0 --- /dev/null +++ b/SOURCES/kvm-iotests-Remove-superfluous-rm-from-232.patch @@ -0,0 +1,45 @@ +From f84dbeffdf969f2b06d4bf6d4cbd6b49c77bb58d Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 16:16:27 +0100 +Subject: [PATCH 029/163] iotests: Remove superfluous rm from 232 + +RH-Author: John Snow +Message-id: <20190320161631.14841-16-jsnow@redhat.com> +Patchwork-id: 84957 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 15/19] iotests: Remove superfluous rm from 232 +Bugzilla: 1668956 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Max Reitz + +This test creates no such file. + +Signed-off-by: Max Reitz +Reviewed-by: Eric Blake +Reviewed-by: John Snow +Message-id: 20190210145736.1486-4-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit 8f4ed6983ab1bda264074ac98d32657b411223bc) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/232 | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/tests/qemu-iotests/232 b/tests/qemu-iotests/232 +index bc2972d..de884cd 100755 +--- a/tests/qemu-iotests/232 ++++ b/tests/qemu-iotests/232 +@@ -30,7 +30,6 @@ status=1 # failure is the default! + _cleanup() + { + _cleanup_test_img +- rm -f $TEST_IMG.snap + } + trap "_cleanup; exit \$status" 0 1 2 3 15 + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Repairing-error-during-snapshot-deletion.patch b/SOURCES/kvm-iotests-Repairing-error-during-snapshot-deletion.patch new file mode 100644 index 0000000..e4a2a59 --- /dev/null +++ b/SOURCES/kvm-iotests-Repairing-error-during-snapshot-deletion.patch @@ -0,0 +1,192 @@ +From 4ffeaaff9109fbdf63bca8c0f944d6ebc46c9ffd Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 17:16:55 +0200 +Subject: [PATCH 46/54] iotests: Repairing error during snapshot deletion + +RH-Author: Max Reitz +Message-id: <20180618171655.25987-3-mreitz@redhat.com> +Patchwork-id: 80784 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 2/2] iotests: Repairing error during snapshot deletion +Bugzilla: 1527085 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Miroslav Rezanina + +This adds a test for an I/O error during snapshot deletion, and maybe +more importantly, for how to repair the resulting image. If the +snapshot has been deleted before the error occurs, the only negative +result will be leaked clusters -- and those should be repairable with +qemu-img check -r leaks. + +Signed-off-by: Max Reitz +Reviewed-by: Eric Blake +Message-id: 20180509200059.31125-3-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit b41ad73a3bb972eb43cf52d28669f67ea3fe1762) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/217 | 90 ++++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/217.out | 42 ++++++++++++++++++++++ + tests/qemu-iotests/group | 1 + + 3 files changed, 133 insertions(+) + create mode 100755 tests/qemu-iotests/217 + create mode 100644 tests/qemu-iotests/217.out + +diff --git a/tests/qemu-iotests/217 b/tests/qemu-iotests/217 +new file mode 100755 +index 0000000..d3ab5d7 +--- /dev/null ++++ b/tests/qemu-iotests/217 +@@ -0,0 +1,90 @@ ++#!/bin/bash ++# ++# I/O errors when working with internal qcow2 snapshots, and repairing ++# the result ++# ++# Copyright (C) 2018 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 . ++ ++seq=$(basename $0) ++echo "QA output created by $seq" ++ ++status=1 # failure is the default! ++ ++_cleanup() ++{ ++ _cleanup_test_img ++ rm -f "$TEST_DIR/blkdebug.conf" ++} ++trap "_cleanup; exit \$status" 0 1 2 3 15 ++ ++# get standard environment, filters and checks ++. ./common.rc ++. ./common.filter ++ ++# This test is specific to qcow2 ++_supported_fmt qcow2 ++_supported_proto file ++_supported_os Linux ++ ++# This test needs clusters with at least a refcount of 2 so that ++# OFLAG_COPIED is not set. refcount_bits=1 is therefore unsupported. ++_unsupported_imgopts 'refcount_bits=1[^0-9]' ++ ++echo ++echo '=== Simulating an I/O error during snapshot deletion ===' ++echo ++ ++_make_test_img 64M ++$QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io ++ ++# Create the snapshot ++$QEMU_IMG snapshot -c foo "$TEST_IMG" ++ ++# Verify the snapshot is there ++echo ++_img_info | grep 'Snapshot list' ++echo '(Snapshot filtered)' ++echo ++ ++# Try to delete the snapshot (with an error happening when freeing the ++# then leaked clusters) ++cat > "$TEST_DIR/blkdebug.conf" < +Date: Mon, 18 Jun 2018 14:59:43 +0200 +Subject: [PATCH 09/89] iotests: Rework 113 + +RH-Author: Max Reitz +Message-id: <20180618145943.4489-8-mreitz@redhat.com> +Patchwork-id: 80757 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 7/7] iotests: Rework 113 +Bugzilla: 1537956 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Stefan Hajnoczi + +This test case has been broken since 398e6ad014df261d (roughly half a +year). qemu-img amend requires its output image to be R/W, so it opens +it as such; the node is then turned into an read-only node automatically +which is now accompanied by a warning, however. This warning has not +been part of the reference output. + +For one thing, this warning shows that we cannot keep the test case as +it is. We would need a format that has no create_opts but that does +have write support -- we do not have such a format, though. + +Another thing is that qemu now actually checks whether an image format +supports amendment instead of whether it has create_opts (since the +former always implies the latter). So we can now use any format that +does not support amendment (even if it supports creation) and thus test +the same code path. + +The reason nobody has noticed the breakage until now of course is the +fact that nobody runs the iotests for nbd+bochs. There actually was +never any reason to set the protocol to "nbd" but because that was +technically correct; functionally it made no difference. So that is the +first thing we are going to change: Make the protocol "file" instead so +that people might actually notice breakage here. + +Secondly, now that bochs no longer works for the amend test case, we +have to change the format there anyway. Set let us just bend the truth +a bit, declare this test a raw test. In fact, that does not even +concern the bochs test cases, other than the output now reading 'bochs' +instead of 'IMGFMT'. + +So with this test now being a raw test, we can rework the amend test +case to use raw instead. + +Signed-off-by: Max Reitz +Reviewed-by: John Snow +Message-id: 20180509210023.20283-8-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit dee6ddd8a6b7978d0bc8ef8e1f006282ce30e4fa) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/113 | 19 +++++++++---------- + tests/qemu-iotests/113.out | 7 ++++--- + 2 files changed, 13 insertions(+), 13 deletions(-) + +diff --git a/tests/qemu-iotests/113 b/tests/qemu-iotests/113 +index 19b68b2..4e09810 100755 +--- a/tests/qemu-iotests/113 ++++ b/tests/qemu-iotests/113 +@@ -38,16 +38,17 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 + . ./common.rc + . ./common.filter + +-# We can only test one format here because we need its sample file +-_supported_fmt bochs +-_supported_proto nbd ++# Some of these test cases use bochs, but others do use raw, so this ++# is only half a lie. ++_supported_fmt raw ++_supported_proto file + _supported_os Linux + + echo + echo '=== Unsupported image creation in qemu-img create ===' + echo + +-$QEMU_IMG create -f $IMGFMT nbd://example.com 2>&1 64M | _filter_imgfmt ++$QEMU_IMG create -f bochs nbd://example.com 2>&1 64M + + echo + echo '=== Unsupported image creation in qemu-img convert ===' +@@ -56,17 +57,15 @@ echo + # We could use any input image format here, but this is a bochs test, so just + # use the bochs image + _use_sample_img empty.bochs.bz2 +-$QEMU_IMG convert -f $IMGFMT -O $IMGFMT "$TEST_IMG" nbd://example.com 2>&1 \ +- | _filter_imgfmt ++$QEMU_IMG convert -f bochs -O bochs "$TEST_IMG" nbd://example.com + + echo + echo '=== Unsupported format in qemu-img amend ===' + echo + +-# The protocol does not matter here +-_use_sample_img empty.bochs.bz2 +-$QEMU_IMG amend -f $IMGFMT -o foo=bar "$TEST_IMG" 2>&1 | _filter_imgfmt +- ++TEST_IMG="$TEST_DIR/t.$IMGFMT" ++_make_test_img 1M ++$QEMU_IMG amend -f $IMGFMT -o size=2M "$TEST_IMG" 2>&1 | _filter_imgfmt + + # success, all done + echo +diff --git a/tests/qemu-iotests/113.out b/tests/qemu-iotests/113.out +index 00bdfd6..3557e2b 100644 +--- a/tests/qemu-iotests/113.out ++++ b/tests/qemu-iotests/113.out +@@ -2,14 +2,15 @@ QA output created by 113 + + === Unsupported image creation in qemu-img create === + +-qemu-img: nbd://example.com: Format driver 'IMGFMT' does not support image creation ++qemu-img: nbd://example.com: Format driver 'bochs' does not support image creation + + === Unsupported image creation in qemu-img convert === + +-qemu-img: Format driver 'IMGFMT' does not support image creation ++qemu-img: Format driver 'bochs' does not support image creation + + === Unsupported format in qemu-img amend === + +-qemu-img: Format driver 'IMGFMT' does not support any options to amend ++Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 ++qemu-img: Format driver 'IMGFMT' does not support option amendment + + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Skip-233-if-certtool-not-installed.patch b/SOURCES/kvm-iotests-Skip-233-if-certtool-not-installed.patch new file mode 100644 index 0000000..170815a --- /dev/null +++ b/SOURCES/kvm-iotests-Skip-233-if-certtool-not-installed.patch @@ -0,0 +1,55 @@ +From 1764efd987a39de271eac2e5bc9900f29819f6f0 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:14 +0100 +Subject: [PATCH 075/163] iotests: Skip 233 if certtool not installed +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190327172308.31077-2-jsnow@redhat.com> +Patchwork-id: 85176 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 01/55] iotests: Skip 233 if certtool not installed +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +The use of TLS while building qemu is optional. While the +'certtool' binary should be available on every platform that +supports building against TLS, that does not imply that the +developer has installed it. Make the test gracefully skip +in that case. + +Reported-by: Kevin Wolf +Signed-off-by: Eric Blake +Reviewed-by: John Snow +Reviewed-by: Daniel P. Berrangé +Reviewed-by: Wainer dos Santos Moschetta +Signed-off-by: Kevin Wolf +(cherry picked from commit 155af09d44f584a790118f78448f50f140d0f788) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/common.tls | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/tests/qemu-iotests/common.tls b/tests/qemu-iotests/common.tls +index 39f17c1..eae8178 100644 +--- a/tests/qemu-iotests/common.tls ++++ b/tests/qemu-iotests/common.tls +@@ -31,6 +31,9 @@ tls_x509_cleanup() + + tls_x509_init() + { ++ (certtool --help) >/dev/null 2>&1 || \ ++ _notrun "certtool utility not found, skipping test" ++ + mkdir -p "${tls_dir}" + + # use a fixed key so we don't waste system entropy on +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Split-214-off-of-122.patch b/SOURCES/kvm-iotests-Split-214-off-of-122.patch new file mode 100644 index 0000000..d7220b1 --- /dev/null +++ b/SOURCES/kvm-iotests-Split-214-off-of-122.patch @@ -0,0 +1,301 @@ +From 6de5873f52d3fac1aef3091c1e283530e37c4b02 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 16:12:03 +0200 +Subject: [PATCH 27/54] iotests: Split 214 off of 122 + +RH-Author: Max Reitz +Message-id: <20180618161212.14444-2-mreitz@redhat.com> +Patchwork-id: 80761 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 01/10] iotests: Split 214 off of 122 +Bugzilla: 1518738 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Miroslav Rezanina + +Commit abd3622cc03cf41ed542126a540385f30a4c0175 added a case to 122 +regarding how the qcow2 driver handles an incorrect compressed data +length value. This does not really fit into 122, as that file is +supposed to contain qemu-img convert test cases, which this case is not. +So this patch splits it off into its own file; maybe we will even get +more qcow2-only compression tests in the future. + +Also, that test case does not work with refcount_bits=1, so mark that +option as unsupported. + +Signed-off-by: Max Reitz +Message-id: 20180406164108.26118-1-mreitz@redhat.com +Reviewed-by: Eric Blake +Signed-off-by: Alberto Garcia +Signed-off-by: Max Reitz +(cherry picked from commit 6cba5377f54d7ea859a29c1877785e7101794683) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/122 | 47 ---------------------- + tests/qemu-iotests/122.out | 33 ---------------- + tests/qemu-iotests/214 | 97 ++++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/214.out | 35 +++++++++++++++++ + tests/qemu-iotests/group | 1 + + 5 files changed, 133 insertions(+), 80 deletions(-) + create mode 100755 tests/qemu-iotests/214 + create mode 100644 tests/qemu-iotests/214.out + +diff --git a/tests/qemu-iotests/122 b/tests/qemu-iotests/122 +index 6cf4fcb..45b359c 100755 +--- a/tests/qemu-iotests/122 ++++ b/tests/qemu-iotests/122 +@@ -130,53 +130,6 @@ $QEMU_IO -c "read -P 0 1024k 1022k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _fil + + + echo +-echo "=== Corrupted size field in compressed cluster descriptor ===" +-echo +-# Create an empty image and fill half of it with compressed data. +-# The L2 entries of the two compressed clusters are located at +-# 0x800000 and 0x800008, their original values are 0x4008000000a00000 +-# and 0x4008000000a00802 (5 sectors for compressed data each). +-_make_test_img 8M -o cluster_size=2M +-$QEMU_IO -c "write -c -P 0x11 0 2M" -c "write -c -P 0x11 2M 2M" "$TEST_IMG" \ +- 2>&1 | _filter_qemu_io | _filter_testdir +- +-# Reduce size of compressed data to 4 sectors: this corrupts the image. +-poke_file "$TEST_IMG" $((0x800000)) "\x40\x06" +-$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir +- +-# 'qemu-img check' however doesn't see anything wrong because it +-# doesn't try to decompress the data and the refcounts are consistent. +-# TODO: update qemu-img so this can be detected. +-_check_test_img +- +-# Increase size of compressed data to the maximum (8192 sectors). +-# This makes QEMU read more data (8192 sectors instead of 5, host +-# addresses [0xa00000, 0xdfffff]), but the decompression algorithm +-# stops once we have enough to restore the uncompressed cluster, so +-# the rest of the data is ignored. +-poke_file "$TEST_IMG" $((0x800000)) "\x7f\xfe" +-# Do it also for the second compressed cluster (L2 entry at 0x800008). +-# In this case the compressed data would span 3 host clusters +-# (host addresses: [0xa00802, 0xe00801]) +-poke_file "$TEST_IMG" $((0x800008)) "\x7f\xfe" +- +-# Here the image is too small so we're asking QEMU to read beyond the +-# end of the image. +-$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir +-# But if we grow the image we won't be reading beyond its end anymore. +-$QEMU_IO -c "write -P 0x22 4M 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir +-$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir +- +-# The refcount data is however wrong because due to the increased size +-# of the compressed data it now reaches the following host clusters. +-# This can be repaired by qemu-img check by increasing the refcount of +-# those clusters. +-# TODO: update qemu-img to correct the compressed cluster size instead. +-_check_test_img -r all +-$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir +-$QEMU_IO -c "read -P 0x22 4M 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir +- +-echo + echo "=== Full allocation with -S 0 ===" + echo + +diff --git a/tests/qemu-iotests/122.out b/tests/qemu-iotests/122.out +index a6b7fe0..47d8656 100644 +--- a/tests/qemu-iotests/122.out ++++ b/tests/qemu-iotests/122.out +@@ -99,39 +99,6 @@ read 1024/1024 bytes at offset 1047552 + read 1046528/1046528 bytes at offset 1048576 + 1022 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +-=== Corrupted size field in compressed cluster descriptor === +- +-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8388608 +-wrote 2097152/2097152 bytes at offset 0 +-2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +-wrote 2097152/2097152 bytes at offset 2097152 +-2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +-read failed: Input/output error +-No errors were found on the image. +-read 4194304/4194304 bytes at offset 0 +-4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +-wrote 4194304/4194304 bytes at offset 4194304 +-4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +-read 4194304/4194304 bytes at offset 0 +-4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +-ERROR cluster 6 refcount=1 reference=3 +-ERROR cluster 7 refcount=1 reference=2 +-Repairing cluster 6 refcount=1 reference=3 +-Repairing cluster 7 refcount=1 reference=2 +-Repairing OFLAG_COPIED data cluster: l2_entry=8000000000c00000 refcount=3 +-Repairing OFLAG_COPIED data cluster: l2_entry=8000000000e00000 refcount=2 +-The following inconsistencies were found and repaired: +- +- 0 leaked clusters +- 4 corruptions +- +-Double checking the fixed image now... +-No errors were found on the image. +-read 4194304/4194304 bytes at offset 0 +-4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +-read 4194304/4194304 bytes at offset 4194304 +-4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +- + === Full allocation with -S 0 === + + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +diff --git a/tests/qemu-iotests/214 b/tests/qemu-iotests/214 +new file mode 100755 +index 0000000..c46ca2a +--- /dev/null ++++ b/tests/qemu-iotests/214 +@@ -0,0 +1,97 @@ ++#!/bin/bash ++# ++# Test qcow2 image compression ++# ++# Copyright (C) 2018 Igalia, S.L. ++# Author: 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) 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 . ++# ++ ++seq=$(basename "$0") ++echo "QA output created by $seq" ++ ++here=$PWD ++status=1 # failure is the default! ++ ++_cleanup() ++{ ++ _cleanup_test_img ++} ++trap "_cleanup; exit \$status" 0 1 2 3 15 ++ ++# get standard environment, filters and checks ++. ./common.rc ++. ./common.filter ++ ++_supported_fmt qcow2 ++_supported_proto file ++_supported_os Linux ++ ++# Repairing the corrupted image requires qemu-img check to store a ++# refcount up to 3, which requires at least two refcount bits. ++_unsupported_imgopts 'refcount_bits=1[^0-9]' ++ ++ ++echo ++echo "=== Corrupted size field in compressed cluster descriptor ===" ++echo ++# Create an empty image and fill half of it with compressed data. ++# The L2 entries of the two compressed clusters are located at ++# 0x800000 and 0x800008, their original values are 0x4008000000a00000 ++# and 0x4008000000a00802 (5 sectors for compressed data each). ++_make_test_img 8M -o cluster_size=2M ++$QEMU_IO -c "write -c -P 0x11 0 2M" -c "write -c -P 0x11 2M 2M" "$TEST_IMG" \ ++ 2>&1 | _filter_qemu_io | _filter_testdir ++ ++# Reduce size of compressed data to 4 sectors: this corrupts the image. ++poke_file "$TEST_IMG" $((0x800000)) "\x40\x06" ++$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir ++ ++# 'qemu-img check' however doesn't see anything wrong because it ++# doesn't try to decompress the data and the refcounts are consistent. ++# TODO: update qemu-img so this can be detected. ++_check_test_img ++ ++# Increase size of compressed data to the maximum (8192 sectors). ++# This makes QEMU read more data (8192 sectors instead of 5, host ++# addresses [0xa00000, 0xdfffff]), but the decompression algorithm ++# stops once we have enough to restore the uncompressed cluster, so ++# the rest of the data is ignored. ++poke_file "$TEST_IMG" $((0x800000)) "\x7f\xfe" ++# Do it also for the second compressed cluster (L2 entry at 0x800008). ++# In this case the compressed data would span 3 host clusters ++# (host addresses: [0xa00802, 0xe00801]) ++poke_file "$TEST_IMG" $((0x800008)) "\x7f\xfe" ++ ++# Here the image is too small so we're asking QEMU to read beyond the ++# end of the image. ++$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir ++# But if we grow the image we won't be reading beyond its end anymore. ++$QEMU_IO -c "write -P 0x22 4M 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir ++$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir ++ ++# The refcount data is however wrong because due to the increased size ++# of the compressed data it now reaches the following host clusters. ++# This can be repaired by qemu-img check by increasing the refcount of ++# those clusters. ++# TODO: update qemu-img to correct the compressed cluster size instead. ++_check_test_img -r all ++$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir ++$QEMU_IO -c "read -P 0x22 4M 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir ++ ++# success, all done ++echo '*** done' ++rm -f $seq.full ++status=0 +diff --git a/tests/qemu-iotests/214.out b/tests/qemu-iotests/214.out +new file mode 100644 +index 0000000..0fcd8dc +--- /dev/null ++++ b/tests/qemu-iotests/214.out +@@ -0,0 +1,35 @@ ++QA output created by 214 ++ ++=== Corrupted size field in compressed cluster descriptor === ++ ++Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8388608 ++wrote 2097152/2097152 bytes at offset 0 ++2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++wrote 2097152/2097152 bytes at offset 2097152 ++2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++read failed: Input/output error ++No errors were found on the image. ++read 4194304/4194304 bytes at offset 0 ++4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++wrote 4194304/4194304 bytes at offset 4194304 ++4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++read 4194304/4194304 bytes at offset 0 ++4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++ERROR cluster 6 refcount=1 reference=3 ++ERROR cluster 7 refcount=1 reference=2 ++Repairing cluster 6 refcount=1 reference=3 ++Repairing cluster 7 refcount=1 reference=2 ++Repairing OFLAG_COPIED data cluster: l2_entry=8000000000c00000 refcount=3 ++Repairing OFLAG_COPIED data cluster: l2_entry=8000000000e00000 refcount=2 ++The following inconsistencies were found and repaired: ++ ++ 0 leaked clusters ++ 4 corruptions ++ ++Double checking the fixed image now... ++No errors were found on the image. ++read 4194304/4194304 bytes at offset 0 ++4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++read 4194304/4194304 bytes at offset 4194304 ++4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++*** done +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index 3a89aed..ba7a2d1 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -212,4 +212,5 @@ + 211 rw auto quick + 212 rw auto quick + 213 rw auto quick ++214 rw auto + 218 rw auto quick +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Test-file-posix-locking-and-reopen.patch b/SOURCES/kvm-iotests-Test-file-posix-locking-and-reopen.patch new file mode 100644 index 0000000..0a3cc0b --- /dev/null +++ b/SOURCES/kvm-iotests-Test-file-posix-locking-and-reopen.patch @@ -0,0 +1,135 @@ +From bc3e5687b0f29cae1b5770aee8f4625b348deea1 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 4 Feb 2019 20:42:07 +0100 +Subject: [PATCH 07/33] iotests: Test file-posix locking and reopen + +RH-Author: Max Reitz +Message-id: <20190204204207.18079-8-mreitz@redhat.com> +Patchwork-id: 84226 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 7/7] iotests: Test file-posix locking and reopen +Bugzilla: 1551486 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Kevin Wolf + +Signed-off-by: Max Reitz +Reviewed-by: Alberto Garcia +Signed-off-by: Kevin Wolf +(cherry picked from commit 6d0a4a0fb5c8f10c8eb68b52cfda0082b00ae963) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/182 | 71 ++++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/182.out | 9 ++++++ + 2 files changed, 80 insertions(+) + +diff --git a/tests/qemu-iotests/182 b/tests/qemu-iotests/182 +index 4b31592..3b7689c 100755 +--- a/tests/qemu-iotests/182 ++++ b/tests/qemu-iotests/182 +@@ -31,6 +31,7 @@ status=1 # failure is the default! + _cleanup() + { + _cleanup_test_img ++ rm -f "$TEST_IMG.overlay" + } + trap "_cleanup; exit \$status" 0 1 2 3 15 + +@@ -71,6 +72,76 @@ echo 'quit' | $QEMU -nographic -monitor stdio \ + + _cleanup_qemu + ++echo ++echo '=== Testing reopen ===' ++echo ++ ++# This tests that reopening does not unshare any permissions it should ++# not unshare ++# (There was a bug where reopening shared exactly the opposite of the ++# permissions it was supposed to share) ++ ++_launch_qemu ++ ++_send_qemu_cmd $QEMU_HANDLE \ ++ "{'execute': 'qmp_capabilities'}" \ ++ 'return' ++ ++# Open the image without any format layer (we are not going to access ++# it, so that is fine) ++# This should keep all permissions shared. ++success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \ ++ "{'execute': 'blockdev-add', ++ 'arguments': { ++ 'node-name': 'node0', ++ 'driver': 'file', ++ 'filename': '$TEST_IMG', ++ 'locking': 'on' ++ } }" \ ++ 'return' \ ++ 'error' ++ ++# This snapshot will perform a reopen to drop R/W to RO. ++# It should still keep all permissions shared. ++success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \ ++ "{'execute': 'blockdev-snapshot-sync', ++ 'arguments': { ++ 'node-name': 'node0', ++ 'snapshot-file': '$TEST_IMG.overlay', ++ 'snapshot-node-name': 'node1' ++ } }" \ ++ 'return' \ ++ 'error' ++ ++# Now open the same file again ++# This does not require any permissions (and does not unshare any), so ++# this will not conflict with node0. ++success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \ ++ "{'execute': 'blockdev-add', ++ 'arguments': { ++ 'node-name': 'node1', ++ 'driver': 'file', ++ 'filename': '$TEST_IMG', ++ 'locking': 'on' ++ } }" \ ++ 'return' \ ++ 'error' ++ ++# Now we attach the image to a virtio-blk device. This device does ++# require some permissions (at least WRITE and READ_CONSISTENT), so if ++# reopening node0 unshared any (which it should not have), this will ++# fail (but it should not). ++success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \ ++ "{'execute': 'device_add', ++ 'arguments': { ++ 'driver': 'virtio-blk', ++ 'drive': 'node1' ++ } }" \ ++ 'return' \ ++ 'error' ++ ++_cleanup_qemu ++ + # success, all done + echo "*** done" + rm -f $seq.full +diff --git a/tests/qemu-iotests/182.out b/tests/qemu-iotests/182.out +index f1463c8..af501ca 100644 +--- a/tests/qemu-iotests/182.out ++++ b/tests/qemu-iotests/182.out +@@ -5,4 +5,13 @@ Starting QEMU + Starting a second QEMU using the same image should fail + QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=none,id=drive0,file.locking=on: Failed to get "write" lock + Is another process using the image [TEST_DIR/t.qcow2]? ++ ++=== Testing reopen === ++ ++{"return": {}} ++{"return": {}} ++Formatting 'TEST_DIR/t.qcow2.overlay', fmt=qcow2 size=197120 backing_file=TEST_DIR/t.qcow2 backing_fmt=file cluster_size=65536 lazy_refcounts=off refcount_bits=16 ++{"return": {}} ++{"return": {}} ++{"return": {}} + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Test-help-option-for-unsupporting-formats.patch b/SOURCES/kvm-iotests-Test-help-option-for-unsupporting-formats.patch new file mode 100644 index 0000000..7b77b0a --- /dev/null +++ b/SOURCES/kvm-iotests-Test-help-option-for-unsupporting-formats.patch @@ -0,0 +1,99 @@ +From 71c378083914a8ee1c4169bc47d1a21df38a16f7 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 14:59:42 +0200 +Subject: [PATCH 08/89] iotests: Test help option for unsupporting formats + +RH-Author: Max Reitz +Message-id: <20180618145943.4489-7-mreitz@redhat.com> +Patchwork-id: 80760 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 6/7] iotests: Test help option for unsupporting formats +Bugzilla: 1537956 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Stefan Hajnoczi + +This adds test cases to 082 for qemu-img create/convert/amend "-o help" +on formats that do not support creation or amendment, respectively. + +Signed-off-by: Max Reitz +Reviewed-by: John Snow +Reviewed-by: Eric Blake +Message-id: 20180509210023.20283-7-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit e53995eb19b546b18d1a34cd6eaa07faedbade79) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/082 | 9 +++++++++ + tests/qemu-iotests/082.out | 9 +++++++++ + 2 files changed, 18 insertions(+) + +diff --git a/tests/qemu-iotests/082 b/tests/qemu-iotests/082 +index d5c83d4..a872f77 100755 +--- a/tests/qemu-iotests/082 ++++ b/tests/qemu-iotests/082 +@@ -97,6 +97,9 @@ run_qemu_img create -f $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST_ + run_qemu_img create -f $IMGFMT -o help + run_qemu_img create -o help + ++# Try help option for a format that does not support creation ++run_qemu_img create -f bochs -o help ++ + echo + echo === convert: Options specified more than once === + +@@ -151,6 +154,9 @@ run_qemu_img convert -O $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST + run_qemu_img convert -O $IMGFMT -o help + run_qemu_img convert -o help + ++# Try help option for a format that does not support creation ++run_qemu_img convert -O bochs -o help ++ + echo + echo === amend: Options specified more than once === + +@@ -201,6 +207,9 @@ run_qemu_img amend -f $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST_I + run_qemu_img amend -f $IMGFMT -o help + run_qemu_img convert -o help + ++# Try help option for a format that does not support amendment ++run_qemu_img amend -f bochs -o help ++ + # success, all done + echo "*** done" + rm -f $seq.full +diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out +index 4e52dce..60ef87c 100644 +--- a/tests/qemu-iotests/082.out ++++ b/tests/qemu-iotests/082.out +@@ -249,6 +249,9 @@ Testing: create -o help + Supported options: + size Virtual disk size + ++Testing: create -f bochs -o help ++qemu-img: Format driver 'bochs' does not support image creation ++ + === convert: Options specified more than once === + + Testing: create -f qcow2 TEST_DIR/t.qcow2 128M +@@ -502,6 +505,9 @@ Testing: convert -o help + Supported options: + size Virtual disk size + ++Testing: convert -O bochs -o help ++qemu-img: Format driver 'bochs' does not support image creation ++ + === amend: Options specified more than once === + + Testing: amend -f foo -f qcow2 -o lazy_refcounts=on TEST_DIR/t.qcow2 +@@ -763,4 +769,7 @@ Note that not all of these options may be amendable. + Testing: convert -o help + Supported options: + size Virtual disk size ++ ++Testing: amend -f bochs -o help ++qemu-img: Format driver 'bochs' does not support option amendment + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Test-migration-with-blockdev.patch b/SOURCES/kvm-iotests-Test-migration-with-blockdev.patch new file mode 100644 index 0000000..fe2b5b1 --- /dev/null +++ b/SOURCES/kvm-iotests-Test-migration-with-blockdev.patch @@ -0,0 +1,209 @@ +From 15ebf0b3bcbca92d0d63a67be3deee35572f1f00 Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Thu, 6 Dec 2018 10:03:56 +0100 +Subject: [PATCH 16/34] iotests: Test migration with -blockdev + +RH-Author: Kevin Wolf +Message-id: <20181128092947.24543-3-kwolf@redhat.com> +Patchwork-id: 83181 +O-Subject: [RHEL-7.7/7.6.z qemu-kvm-rhev PATCH 2/2] iotests: Test migration with -blockdev +Bugzilla: 1633536 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Laurent Vivier + +Check that block node activation and inactivation works with a block +graph that is built with individually created nodes. + +RHEL: Disabled query-migrate call on the destination; this returns only +an empty object in 2.12. Changed reference output for Python 2 (we lack +the compatibility patches from upstream that make the output independent +from the Python version). + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit 330ca111ea0979d8c6fc9b3958f72d6dce164d5a) +Signed-off-by: Kevin Wolf +--- + tests/qemu-iotests/234 | 123 +++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/234.out | 28 +++++++++++ + tests/qemu-iotests/group | 1 + + 3 files changed, 152 insertions(+) + create mode 100755 tests/qemu-iotests/234 + create mode 100644 tests/qemu-iotests/234.out + +diff --git a/tests/qemu-iotests/234 b/tests/qemu-iotests/234 +new file mode 100755 +index 0000000..2b0f869 +--- /dev/null ++++ b/tests/qemu-iotests/234 +@@ -0,0 +1,123 @@ ++#!/usr/bin/env python ++# ++# Copyright (C) 2018 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 . ++# ++# Creator/Owner: Kevin Wolf ++# ++# Check that block node activation and inactivation works with a block graph ++# that is built with individually created nodes ++ ++import iotests ++import os ++ ++iotests.verify_image_format(supported_fmts=['qcow2']) ++iotests.verify_platform(['linux']) ++ ++with iotests.FilePath('img') as img_path, \ ++ iotests.FilePath('backing') as backing_path, \ ++ iotests.FilePath('mig_fifo_a') as fifo_a, \ ++ iotests.FilePath('mig_fifo_b') as fifo_b, \ ++ iotests.VM(path_suffix='a') as vm_a, \ ++ iotests.VM(path_suffix='b') as vm_b: ++ ++ iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, backing_path, '64M') ++ iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, img_path, '64M') ++ ++ os.mkfifo(fifo_a) ++ os.mkfifo(fifo_b) ++ ++ iotests.log('Launching source VM...') ++ (vm_a.add_blockdev('file,filename=%s,node-name=drive0-file' % (img_path)) ++ .add_blockdev('%s,file=drive0-file,node-name=drive0' % (iotests.imgfmt)) ++ .add_blockdev('file,filename=%s,node-name=drive0-backing-file' % (backing_path)) ++ .add_blockdev('%s,file=drive0-backing-file,node-name=drive0-backing' % (iotests.imgfmt)) ++ .launch()) ++ ++ iotests.log('Launching destination VM...') ++ (vm_b.add_blockdev('file,filename=%s,node-name=drive0-file' % (img_path)) ++ .add_blockdev('%s,file=drive0-file,node-name=drive0' % (iotests.imgfmt)) ++ .add_blockdev('file,filename=%s,node-name=drive0-backing-file' % (backing_path)) ++ .add_blockdev('%s,file=drive0-backing-file,node-name=drive0-backing' % (iotests.imgfmt)) ++ .add_incoming("exec: cat '%s'" % (fifo_a)) ++ .launch()) ++ ++ # Add a child node that was created after the parent node. The reverse case ++ # is covered by the -blockdev options above. ++ iotests.log(vm_a.qmp('blockdev-snapshot', node='drive0-backing', ++ overlay='drive0')) ++ iotests.log(vm_b.qmp('blockdev-snapshot', node='drive0-backing', ++ overlay='drive0')) ++ ++ iotests.log('Enabling migration QMP events on A...') ++ iotests.log(vm_a.qmp('migrate-set-capabilities', capabilities=[ ++ { ++ 'capability': 'events', ++ 'state': True ++ } ++ ])) ++ ++ iotests.log('Starting migration to B...') ++ iotests.log(vm_a.qmp('migrate', uri='exec:cat >%s' % (fifo_a))) ++ with iotests.Timeout(3, 'Migration does not complete'): ++ while True: ++ event = vm_a.event_wait('MIGRATION') ++ iotests.log(event, filters=[iotests.filter_qmp_event]) ++ if event['data']['status'] == 'completed': ++ break ++ ++ iotests.log(vm_a.qmp('query-migrate')['return']['status']) ++ # Returns only {} on this QEMU version (no status) ++ # iotests.log(vm_b.qmp('query-migrate')['return']['status']) ++ ++ iotests.log(vm_a.qmp('query-status')) ++ iotests.log(vm_b.qmp('query-status')) ++ ++ iotests.log('Add a second parent to drive0-file...') ++ iotests.log(vm_b.qmp('blockdev-add', driver='raw', file='drive0-file', ++ node_name='drive0-raw')) ++ ++ iotests.log('Restart A with -incoming and second parent...') ++ vm_a.shutdown() ++ (vm_a.add_blockdev('raw,file=drive0-file,node-name=drive0-raw') ++ .add_incoming("exec: cat '%s'" % (fifo_b)) ++ .launch()) ++ ++ iotests.log(vm_a.qmp('blockdev-snapshot', node='drive0-backing', ++ overlay='drive0')) ++ ++ iotests.log('Enabling migration QMP events on B...') ++ iotests.log(vm_b.qmp('migrate-set-capabilities', capabilities=[ ++ { ++ 'capability': 'events', ++ 'state': True ++ } ++ ])) ++ ++ iotests.log('Starting migration back to A...') ++ iotests.log(vm_b.qmp('migrate', uri='exec:cat >%s' % (fifo_b))) ++ with iotests.Timeout(3, 'Migration does not complete'): ++ while True: ++ event = vm_b.event_wait('MIGRATION') ++ iotests.log(event, filters=[iotests.filter_qmp_event]) ++ if event['data']['status'] == 'completed': ++ break ++ ++ # Returns only {} on this QEMU version (no status) ++ # iotests.log(vm_a.qmp('query-migrate')['return']['status']) ++ iotests.log(vm_b.qmp('query-migrate')['return']['status']) ++ ++ iotests.log(vm_a.qmp('query-status')) ++ iotests.log(vm_b.qmp('query-status')) +diff --git a/tests/qemu-iotests/234.out b/tests/qemu-iotests/234.out +new file mode 100644 +index 0000000..c7cd95f +--- /dev/null ++++ b/tests/qemu-iotests/234.out +@@ -0,0 +1,28 @@ ++Launching source VM... ++Launching destination VM... ++{u'return': {}} ++{u'return': {}} ++Enabling migration QMP events on A... ++{u'return': {}} ++Starting migration to B... ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'setup'}, u'event': u'MIGRATION'} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'active'}, u'event': u'MIGRATION'} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'completed'}, u'event': u'MIGRATION'} ++completed ++{u'return': {u'status': u'postmigrate', u'singlestep': False, u'running': False}} ++{u'return': {u'status': u'running', u'singlestep': False, u'running': True}} ++Add a second parent to drive0-file... ++{u'return': {}} ++Restart A with -incoming and second parent... ++{u'return': {}} ++Enabling migration QMP events on B... ++{u'return': {}} ++Starting migration back to A... ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'setup'}, u'event': u'MIGRATION'} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'active'}, u'event': u'MIGRATION'} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'completed'}, u'event': u'MIGRATION'} ++completed ++{u'return': {u'status': u'running', u'singlestep': False, u'running': True}} ++{u'return': {u'status': u'postmigrate', u'singlestep': False, u'running': False}} +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index fcbf3f3..0998dcd 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -226,3 +226,4 @@ + 229 auto quick + 231 auto quick + 232 auto quick ++234 auto quick migration +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Test-post-backing-convert-target-behavior.patch b/SOURCES/kvm-iotests-Test-post-backing-convert-target-behavior.patch new file mode 100644 index 0000000..cd8a4b6 --- /dev/null +++ b/SOURCES/kvm-iotests-Test-post-backing-convert-target-behavior.patch @@ -0,0 +1,116 @@ +From 4368733f4cfd8aa424c6f14f950b8e33db6829f2 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 18:04:51 +0200 +Subject: [PATCH 13/89] iotests: Test post-backing convert target behavior + +RH-Author: Max Reitz +Message-id: <20180618180451.23808-3-mreitz@redhat.com> +Patchwork-id: 80797 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 2/2] iotests: Test post-backing convert target behavior +Bugzilla: 1527898 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Kevin Wolf +RH-Acked-by: Jeffrey Cody + +This adds a test case to 122 for what happens when you convert to a +target with a backing file that is shorter than the target, and the +image format does not support efficient zero writes (as is the case with +qcow2 v2). + +Signed-off-by: Max Reitz +Message-id: 20180501165750.19242-3-mreitz@redhat.com +Reviewed-by: Eric Blake +Signed-off-by: Max Reitz +(cherry picked from commit 0682854f899d03c78befce9f5aae4a7e37655d25) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/122 | 42 ++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/122.out | 18 ++++++++++++++++++ + 2 files changed, 60 insertions(+) + +diff --git a/tests/qemu-iotests/122 b/tests/qemu-iotests/122 +index 45b359c..d8c8ad7 100755 +--- a/tests/qemu-iotests/122 ++++ b/tests/qemu-iotests/122 +@@ -77,6 +77,48 @@ $QEMU_IO -c "read -P 0 0 3M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_t + + + echo ++echo "=== Converting to an overlay larger than its backing file ===" ++echo ++ ++TEST_IMG="$TEST_IMG".base _make_test_img 256M ++# Needs to be at least how much an L2 table covers ++# (64 kB/entry * 64 kB / 8 B/entry = 512 MB) ++# That way, qcow2 will yield at least two status request responses. ++# With just a single response, it would always say "Allocated in the ++# backing file", so the optimization qemu-img convert tries to do is ++# done automatically. Once it has to be queried twice, however (and ++# one of the queries is completely after the end of the backing file), ++# the block layer will automatically add a ZERO flag that qemu-img ++# convert used to follow up with a zero write to the target. ++# We do not want such a zero write, however, because we are past the ++# end of the backing file on the target as well, so we do not need to ++# write anything there. ++_make_test_img -b "$TEST_IMG".base 768M ++ ++# Use compat=0.10 as the output so there is no zero cluster support ++$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base -o compat=0.10 \ ++ "$TEST_IMG" "$TEST_IMG".orig ++# See that nothing has been allocated past 64M ++$QEMU_IMG map "$TEST_IMG".orig | _filter_qemu_img_map ++ ++echo ++ ++# Just before the end of the backing file ++$QEMU_IO -c 'write -P 0x11 255M 1M' "$TEST_IMG".base 2>&1 | _filter_qemu_io ++# Somewhere in the second L2 table ++$QEMU_IO -c 'write -P 0x22 600M 1M' "$TEST_IMG" 2>&1 | _filter_qemu_io ++ ++$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base -o compat=0.10 \ ++ "$TEST_IMG" "$TEST_IMG".orig ++ ++$QEMU_IMG map "$TEST_IMG".orig | _filter_qemu_img_map ++$QEMU_IO -c 'read -P 0x11 255M 1M' \ ++ -c 'read -P 0x22 600M 1M' \ ++ "$TEST_IMG".orig \ ++ | _filter_qemu_io ++ ++ ++echo + echo "=== Concatenate multiple source images ===" + echo + +diff --git a/tests/qemu-iotests/122.out b/tests/qemu-iotests/122.out +index 47d8656..6c7ee1d 100644 +--- a/tests/qemu-iotests/122.out ++++ b/tests/qemu-iotests/122.out +@@ -28,6 +28,24 @@ read 3145728/3145728 bytes at offset 0 + read 3145728/3145728 bytes at offset 0 + 3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + ++=== Converting to an overlay larger than its backing file === ++ ++Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=268435456 ++Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=805306368 backing_file=TEST_DIR/t.IMGFMT.base ++Offset Length File ++ ++wrote 1048576/1048576 bytes at offset 267386880 ++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++wrote 1048576/1048576 bytes at offset 629145600 ++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++Offset Length File ++0xff00000 0x100000 TEST_DIR/t.IMGFMT.base ++0x25800000 0x100000 TEST_DIR/t.IMGFMT.orig ++read 1048576/1048576 bytes at offset 267386880 ++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++read 1048576/1048576 bytes at offset 629145600 ++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++ + === Concatenate multiple source images === + + Formatting 'TEST_DIR/t.IMGFMT.1', fmt=IMGFMT size=4194304 +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Unify-log-outputs-between-Python-2-and-3.patch b/SOURCES/kvm-iotests-Unify-log-outputs-between-Python-2-and-3.patch new file mode 100644 index 0000000..3a04046 --- /dev/null +++ b/SOURCES/kvm-iotests-Unify-log-outputs-between-Python-2-and-3.patch @@ -0,0 +1,2185 @@ +From 8d1e45a4b4c46bae4c2b453ef575407bfd8b3539 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 16:16:15 +0100 +Subject: [PATCH 017/163] iotests: Unify log outputs between Python 2 and 3 + +RH-Author: John Snow +Message-id: <20190320161631.14841-4-jsnow@redhat.com> +Patchwork-id: 84940 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 03/19] iotests: Unify log outputs between Python 2 and 3 +Bugzilla: 1668956 +RH-Acked-by: Eduardo Habkost +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Max Reitz + +When dumping an object into the log, there are differences between +Python 2 and 3. First, unicode strings are prefixed by 'u' in Python 2 +(they are no longer in 3, because unicode strings are the default +there). Second, the order of keys in dicts may differ. Third, +especially long numbers are longs in Python 2 and thus get an 'L' +suffix, which does not happen in Python 3. + +We can get around all of these differences by dumping objects (lists and +dicts) in a language-independent format, namely JSON. The JSON +generator even allows emitting dicts with their keys sorted +alphabetically. + +This changes the output of all tests that use these logging functions +(dict keys are ordered now, strings in dicts are now enclosed in double +quotes instead of single quotes, the 'L' suffix of large integers is +dropped, and "true" and "false" are now in lower case). +The quote change necessitates a small change to a filter used in test +207. + +Suggested-by: Eduardo Habkost +Signed-off-by: Max Reitz +Reviewed-by: Cleber Rosa +Message-Id: <20181022135307.14398-10-mreitz@redhat.com> +Signed-off-by: Eduardo Habkost +(cherry picked from commit e21b5f34d669b82087597273f3783626947291a0) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/194.out | 22 +- + tests/qemu-iotests/202.out | 12 +- + tests/qemu-iotests/203.out | 14 +- + tests/qemu-iotests/206.out | 218 ++++++++--------- + tests/qemu-iotests/207 | 2 +- + tests/qemu-iotests/207.out | 72 +++--- + tests/qemu-iotests/208.out | 8 +- + tests/qemu-iotests/210.out | 94 ++++---- + tests/qemu-iotests/211.out | 102 ++++---- + tests/qemu-iotests/212.out | 174 +++++++------- + tests/qemu-iotests/213.out | 182 +++++++-------- + tests/qemu-iotests/216.out | 4 +- + tests/qemu-iotests/218.out | 20 +- + tests/qemu-iotests/219.out | 526 +++++++++++++++++++++--------------------- + tests/qemu-iotests/222.out | 24 +- + tests/qemu-iotests/234.out | 36 +-- + tests/qemu-iotests/iotests.py | 10 +- + 17 files changed, 762 insertions(+), 758 deletions(-) + +diff --git a/tests/qemu-iotests/194.out b/tests/qemu-iotests/194.out +index 50ac50d..7185785 100644 +--- a/tests/qemu-iotests/194.out ++++ b/tests/qemu-iotests/194.out +@@ -1,18 +1,18 @@ + Launching VMs... + Launching NBD server on destination... +-{u'return': {}} +-{u'return': {}} ++{"return": {}} ++{"return": {}} + Starting `drive-mirror` on source... +-{u'return': {}} ++{"return": {}} + Waiting for `drive-mirror` to complete... +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror-job0', u'type': u'mirror', u'speed': 0, u'len': 1073741824, u'offset': 1073741824}, u'event': u'BLOCK_JOB_READY'} ++{"data": {"device": "mirror-job0", "len": 1073741824, "offset": 1073741824, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} + Starting migration... +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'setup'}, u'event': u'MIGRATION'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'active'}, u'event': u'MIGRATION'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'completed'}, u'event': u'MIGRATION'} ++{"return": {}} ++{"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} + Gracefully ending the `drive-mirror` job on source... +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror-job0', u'type': u'mirror', u'speed': 0, u'len': 1073741824, u'offset': 1073741824}, u'event': u'BLOCK_JOB_COMPLETED'} ++{"return": {}} ++{"data": {"device": "mirror-job0", "len": 1073741824, "offset": 1073741824, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} + Stopping the NBD server on destination... +-{u'return': {}} ++{"return": {}} +diff --git a/tests/qemu-iotests/202.out b/tests/qemu-iotests/202.out +index d5ea374..9a8619e 100644 +--- a/tests/qemu-iotests/202.out ++++ b/tests/qemu-iotests/202.out +@@ -1,11 +1,11 @@ + Launching VM... + Adding IOThread... +-{u'return': {}} ++{"return": {}} + Adding blockdevs... +-{u'return': {}} +-{u'return': {}} ++{"return": {}} ++{"return": {}} + Setting iothread... +-{u'return': {}} +-{u'return': {}} ++{"return": {}} ++{"return": {}} + Creating external snapshots... +-{u'return': {}} ++{"return": {}} +diff --git a/tests/qemu-iotests/203.out b/tests/qemu-iotests/203.out +index 1a11f09..9d4abba 100644 +--- a/tests/qemu-iotests/203.out ++++ b/tests/qemu-iotests/203.out +@@ -1,11 +1,11 @@ + Launching VM... + Setting IOThreads... +-{u'return': {}} +-{u'return': {}} ++{"return": {}} ++{"return": {}} + Enabling migration QMP events... +-{u'return': {}} ++{"return": {}} + Starting migration... +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'setup'}, u'event': u'MIGRATION'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'active'}, u'event': u'MIGRATION'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'completed'}, u'event': u'MIGRATION'} ++{"return": {}} ++{"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +diff --git a/tests/qemu-iotests/206.out b/tests/qemu-iotests/206.out +index 789eebe..91f4db5 100644 +--- a/tests/qemu-iotests/206.out ++++ b/tests/qemu-iotests/206.out +@@ -1,16 +1,16 @@ + === Successful image creation (defaults) === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} +- +-{'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}} +-{u'return': {}} +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'imgfile', 'size': 134217728}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "size": 0}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} ++ ++{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "node_name": "imgfile"}} ++{"return": {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "imgfile", "size": 134217728}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: TEST_IMG + file format: IMGFMT +@@ -24,15 +24,15 @@ Format specific information: + + === Successful image creation (inline blockdev-add, explicit defaults) === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'nocow': False, 'preallocation': 'off', 'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "nocow": false, "preallocation": "off", "size": 0}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 65536, 'refcount-bits': 16, 'version': 'v3', 'preallocation': 'off', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'lazy-refcounts': False, 'driver': 'qcow2', 'size': 67108864}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 65536, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "lazy-refcounts": false, "preallocation": "off", "refcount-bits": 16, "size": 67108864, "version": "v3"}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: TEST_IMG + file format: IMGFMT +@@ -46,15 +46,15 @@ Format specific information: + + === Successful image creation (v3 non-default options) === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'nocow': True, 'preallocation': 'falloc', 'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "nocow": true, "preallocation": "falloc", "size": 0}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 2097152, 'refcount-bits': 1, 'version': 'v3', 'preallocation': 'metadata', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'lazy-refcounts': True, 'driver': 'qcow2', 'size': 33554432}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 2097152, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "lazy-refcounts": true, "preallocation": "metadata", "refcount-bits": 1, "size": 33554432, "version": "v3"}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: TEST_IMG + file format: IMGFMT +@@ -68,15 +68,15 @@ Format specific information: + + === Successful image creation (v2 non-default options) === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "size": 0}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'backing-fmt': 'qcow2', 'driver': 'qcow2', 'version': 'v2', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'backing-file': 'TEST_DIR/PID-t.qcow2.base', 'size': 33554432}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"backing-file": "TEST_DIR/PID-t.qcow2.base", "backing-fmt": "qcow2", "cluster-size": 512, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "size": 33554432, "version": "v2"}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: TEST_IMG + file format: IMGFMT +@@ -90,10 +90,10 @@ Format specific information: + + === Successful image creation (encrypted) === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'encrypt': {'key-secret': 'keysec0', 'iter-time': 10, 'cipher-mode': 'ctr', 'ivgen-hash-alg': 'md5', 'cipher-alg': 'twofish-128', 'format': 'luks', 'ivgen-alg': 'plain64', 'hash-alg': 'sha1'}, 'driver': 'qcow2', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'size': 33554432}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "encrypt": {"cipher-alg": "twofish-128", "cipher-mode": "ctr", "format": "luks", "hash-alg": "sha1", "iter-time": 10, "ivgen-alg": "plain64", "ivgen-hash-alg": "md5", "key-secret": "keysec0"}, "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "size": 33554432}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: TEST_IMG + file format: IMGFMT +@@ -144,113 +144,113 @@ Format specific information: + + === Invalid BlockdevRef === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': "this doesn't exist", 'size': 33554432}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "this doesn't exist", "size": 33554432}}} ++{"return": {}} + Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + === Invalid sizes === +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 1234}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 1234}}} ++{"return": {}} + Job failed: Image size must be a multiple of 512 bytes +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 18446744073709551104L}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 18446744073709551104}}} ++{"return": {}} + Job failed: Could not resize image: Image size cannot be negative +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 9223372036854775808L}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 9223372036854775808}}} ++{"return": {}} + Job failed: Could not resize image: Image size cannot be negative +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 9223372036854775296}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 9223372036854775296}}} ++{"return": {}} + Job failed: Could not resize image: Failed to grow the L1 table: File too large +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + === Invalid version === +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'version': 'v1', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +-{u'error': {u'class': u'GenericError', u'desc': u"Invalid parameter 'v1'"}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 67108864, "version": "v1"}}} ++{"error": {"class": "GenericError", "desc": "Invalid parameter 'v1'"}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'lazy-refcounts': True, 'version': 'v2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "lazy-refcounts": true, "size": 67108864, "version": "v2"}}} ++{"return": {}} + Job failed: Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater) +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 8, 'version': 'v2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 8, "size": 67108864, "version": "v2"}}} ++{"return": {}} + Job failed: Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater) +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + === Invalid backing file options === +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'full', 'driver': 'qcow2', 'backing-file': '/dev/null', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"backing-file": "/dev/null", "driver": "qcow2", "file": "node0", "preallocation": "full", "size": 67108864}}} ++{"return": {}} + Job failed: Backing file and preallocation cannot be used at the same time +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'backing-fmt': 'qcow2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"backing-fmt": "qcow2", "driver": "qcow2", "file": "node0", "size": 67108864}}} ++{"return": {}} + Job failed: Backing format cannot be used without backing file +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + === Invalid cluster size === +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1234, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 1234, "driver": "qcow2", "file": "node0", "size": 67108864}}} ++{"return": {}} + Job failed: Cluster size must be a power of two between 512 and 2048k +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 128, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 128, "driver": "qcow2", "file": "node0", "size": 67108864}}} ++{"return": {}} + Job failed: Cluster size must be a power of two between 512 and 2048k +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 4194304, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 4194304, "driver": "qcow2", "file": "node0", "size": 67108864}}} ++{"return": {}} + Job failed: Cluster size must be a power of two between 512 and 2048k +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 0, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 0, "driver": "qcow2", "file": "node0", "size": 67108864}}} ++{"return": {}} + Job failed: Cluster size must be a power of two between 512 and 2048k +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'driver': 'qcow2', 'file': 'node0', 'size': 281474976710656}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 512, "driver": "qcow2", "file": "node0", "size": 281474976710656}}} ++{"return": {}} + Job failed: Could not resize image: Failed to grow the L1 table: File too large +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + === Invalid refcount width === +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 128, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 128, "size": 67108864}}} ++{"return": {}} + Job failed: Refcount width must be a power of two and may not exceed 64 bits +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 0, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 0, "size": 67108864}}} ++{"return": {}} + Job failed: Refcount width must be a power of two and may not exceed 64 bits +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 7, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 7, "size": 67108864}}} ++{"return": {}} + Job failed: Refcount width must be a power of two and may not exceed 64 bits +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207 +index 444ae23..687b2ca 100755 +--- a/tests/qemu-iotests/207 ++++ b/tests/qemu-iotests/207 +@@ -28,7 +28,7 @@ iotests.verify_image_format(supported_fmts=['raw']) + iotests.verify_protocol(supported=['ssh']) + + def filter_hash(msg): +- return re.sub("'hash': '[0-9a-f]+'", "'hash': HASH", msg) ++ return re.sub('"hash": "[0-9a-f]+"', '"hash": HASH', msg) + + def blockdev_create(vm, options): + result = vm.qmp_log('blockdev-create', job_id='job0', options=options, +diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out +index 078b7e6..45ac7c2 100644 +--- a/tests/qemu-iotests/207.out ++++ b/tests/qemu-iotests/207.out +@@ -1,9 +1,9 @@ + === Successful image creation (defaults) === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} + file format: IMGFMT +@@ -16,49 +16,49 @@ virtual size: 4.0M (4194304 bytes) + + === Test host-key-check options === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'none'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 8388608}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 8388608}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} + file format: IMGFMT + virtual size: 8.0M (8388608 bytes) + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'known_hosts'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "known_hosts"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} + file format: IMGFMT + virtual size: 4.0M (4194304 bytes) + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': 'wrong', 'type': 'md5', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 2097152}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "wrong", "mode": "hash", "type": "md5"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 2097152}}} ++{"return": {}} + Job failed: remote host key does not match host_key_check 'wrong' +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': HASH, 'type': 'md5', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 8388608}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": HASH, "mode": "hash", "type": "md5"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 8388608}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} + file format: IMGFMT + virtual size: 8.0M (8388608 bytes) + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': 'wrong', 'type': 'sha1', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 2097152}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "wrong", "mode": "hash", "type": "sha1"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 2097152}}} ++{"return": {}} + Job failed: remote host key does not match host_key_check 'wrong' +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': HASH, 'type': 'sha1', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": HASH, "mode": "hash", "type": "sha1"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} + file format: IMGFMT +@@ -66,15 +66,15 @@ virtual size: 4.0M (4194304 bytes) + + === Invalid path and user === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': '/this/is/not/an/existing/path', 'host-key-check': {'mode': 'none'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "/this/is/not/an/existing/path", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}} ++{"return": {}} + Job failed: failed to open remote file '/this/is/not/an/existing/path': Failed opening remote file (libssh2 error code: -31) +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'none'}, 'user': 'invalid user', 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}, "user": "invalid user"}, "size": 4194304}}} ++{"return": {}} + Job failed: failed to authenticate using publickey authentication and the identities held by your ssh-agent +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +diff --git a/tests/qemu-iotests/208.out b/tests/qemu-iotests/208.out +index 3687e9d..9ff2582 100644 +--- a/tests/qemu-iotests/208.out ++++ b/tests/qemu-iotests/208.out +@@ -1,9 +1,9 @@ + Launching VM... + Starting NBD server... +-{u'return': {}} ++{"return": {}} + Adding NBD export... +-{u'return': {}} ++{"return": {}} + Creating external snapshot... +-{u'return': {}} ++{"return": {}} + Stopping NBD server... +-{u'return': {}} ++{"return": {}} +diff --git a/tests/qemu-iotests/210.out b/tests/qemu-iotests/210.out +index 078ba54..923cb05 100644 +--- a/tests/qemu-iotests/210.out ++++ b/tests/qemu-iotests/210.out +@@ -1,16 +1,16 @@ + === Successful image creation (defaults) === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} +- +-{'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}} +-{u'return': {}} +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'iter-time': 10, 'driver': 'luks', 'file': 'imgfile', 'size': 134217728}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.luks", "size": 0}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} ++ ++{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.luks", "node_name": "imgfile"}} ++{"return": {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "luks", "file": "imgfile", "iter-time": 10, "key-secret": "keysec0", "size": 134217728}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_IMG"}, "key-secret": "keysec0"} + file format: IMGFMT +@@ -54,15 +54,15 @@ Format specific information: + + === Successful image creation (with non-default options) === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.luks", "size": 0}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'hash-alg': 'sha1', 'cipher-mode': 'ctr', 'cipher-alg': 'twofish-128', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}, 'iter-time': 10, 'ivgen-alg': 'plain64', 'ivgen-hash-alg': 'md5', 'driver': 'luks', 'size': 67108864}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cipher-alg": "twofish-128", "cipher-mode": "ctr", "driver": "luks", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.luks"}, "hash-alg": "sha1", "iter-time": 10, "ivgen-alg": "plain64", "ivgen-hash-alg": "md5", "key-secret": "keysec0", "size": 67108864}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_IMG"}, "key-secret": "keysec0"} + file format: IMGFMT +@@ -106,18 +106,18 @@ Format specific information: + + === Invalid BlockdevRef === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'luks', 'file': "this doesn't exist", 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "luks", "file": "this doesn't exist", "size": 67108864}}} ++{"return": {}} + Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + === Zero size === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'iter-time': 10, 'driver': 'luks', 'file': 'node0', 'size': 0}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "luks", "file": "node0", "iter-time": 10, "key-secret": "keysec0", "size": 0}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_IMG"}, "key-secret": "keysec0"} + file format: IMGFMT +@@ -161,34 +161,34 @@ Format specific information: + + === Invalid sizes === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 18446744073709551104L}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "luks", "file": "node0", "key-secret": "keysec0", "size": 18446744073709551104}}} ++{"return": {}} + Job failed: The requested file size is too large +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 9223372036854775808L}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "luks", "file": "node0", "key-secret": "keysec0", "size": 9223372036854775808}}} ++{"return": {}} + Job failed: The requested file size is too large +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 9223372036854775296}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "luks", "file": "node0", "key-secret": "keysec0", "size": 9223372036854775296}}} ++{"return": {}} + Job failed: The requested file size is too large +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + === Resize image with invalid sizes === + +-{'execute': 'block_resize', 'arguments': {'size': 9223372036854775296, 'node_name': 'node1'}} +-{u'error': {u'class': u'GenericError', u'desc': u'The requested file size is too large'}} +-{'execute': 'block_resize', 'arguments': {'size': 9223372036854775808L, 'node_name': 'node1'}} +-{u'error': {u'class': u'GenericError', u'desc': u"Invalid parameter type for 'size', expected: integer"}} +-{'execute': 'block_resize', 'arguments': {'size': 18446744073709551104L, 'node_name': 'node1'}} +-{u'error': {u'class': u'GenericError', u'desc': u"Invalid parameter type for 'size', expected: integer"}} +-{'execute': 'block_resize', 'arguments': {'size': -9223372036854775808, 'node_name': 'node1'}} +-{u'error': {u'class': u'GenericError', u'desc': u"Parameter 'size' expects a >0 size"}} ++{"execute": "block_resize", "arguments": {"node_name": "node1", "size": 9223372036854775296}} ++{"error": {"class": "GenericError", "desc": "The requested file size is too large"}} ++{"execute": "block_resize", "arguments": {"node_name": "node1", "size": 9223372036854775808}} ++{"error": {"class": "GenericError", "desc": "Invalid parameter type for 'size', expected: integer"}} ++{"execute": "block_resize", "arguments": {"node_name": "node1", "size": 18446744073709551104}} ++{"error": {"class": "GenericError", "desc": "Invalid parameter type for 'size', expected: integer"}} ++{"execute": "block_resize", "arguments": {"node_name": "node1", "size": -9223372036854775808}} ++{"error": {"class": "GenericError", "desc": "Parameter 'size' expects a >0 size"}} + image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_IMG"}, "key-secret": "keysec0"} + file format: IMGFMT + virtual size: 0 (0 bytes) +diff --git a/tests/qemu-iotests/211.out b/tests/qemu-iotests/211.out +index 6feaea3..eebb0ea 100644 +--- a/tests/qemu-iotests/211.out ++++ b/tests/qemu-iotests/211.out +@@ -1,16 +1,16 @@ + === Successful image creation (defaults) === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} +- +-{'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}} +-{u'return': {}} +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'imgfile', 'size': 134217728}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} ++ ++{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "node_name": "imgfile"}} ++{"return": {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "imgfile", "size": 134217728}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: TEST_IMG + file format: IMGFMT +@@ -21,15 +21,15 @@ cluster_size: 1048576 + + === Successful image creation (explicit defaults) === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'off', 'driver': 'vdi', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}, 'size': 67108864}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi"}, "preallocation": "off", "size": 67108864}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: TEST_IMG + file format: IMGFMT +@@ -40,15 +40,15 @@ cluster_size: 1048576 + + === Successful image creation (with non-default options) === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'metadata', 'driver': 'vdi', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}, 'size': 33554432}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi"}, "preallocation": "metadata", "size": 33554432}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: TEST_IMG + file format: IMGFMT +@@ -60,18 +60,18 @@ cluster_size: 1048576 + + === Invalid BlockdevRef === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': "this doesn't exist", 'size': 33554432}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "this doesn't exist", "size": 33554432}}} ++{"return": {}} + Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + === Zero size === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 0}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 0}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: TEST_IMG + file format: IMGFMT +@@ -80,10 +80,10 @@ cluster_size: 1048576 + + === Maximum size === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 562949819203584}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 562949819203584}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: TEST_IMG + file format: IMGFMT +@@ -92,21 +92,21 @@ cluster_size: 1048576 + + === Invalid sizes === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 18446744073709551104L}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 18446744073709551104}}} ++{"return": {}} + Job failed: Unsupported VDI image size (size is 0xfffffffffffffe00, max supported is 0x1fffff8000000) +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 9223372036854775808L}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 9223372036854775808}}} ++{"return": {}} + Job failed: Unsupported VDI image size (size is 0x8000000000000000, max supported is 0x1fffff8000000) +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 562949819203585}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 562949819203585}}} ++{"return": {}} + Job failed: Unsupported VDI image size (size is 0x1fffff8000001, max supported is 0x1fffff8000000) +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +diff --git a/tests/qemu-iotests/212.out b/tests/qemu-iotests/212.out +index 9150da7..01da467 100644 +--- a/tests/qemu-iotests/212.out ++++ b/tests/qemu-iotests/212.out +@@ -1,16 +1,16 @@ + === Successful image creation (defaults) === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} +- +-{'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}} +-{u'return': {}} +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'imgfile', 'size': 134217728}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "size": 0}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} ++ ++{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "node_name": "imgfile"}} ++{"return": {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "imgfile", "size": 134217728}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: TEST_IMG + file format: IMGFMT +@@ -18,15 +18,15 @@ virtual size: 128M (134217728 bytes) + + === Successful image creation (explicit defaults) === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "size": 0}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1048576, 'driver': 'parallels', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}, 'size': 67108864}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 1048576, "driver": "parallels", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels"}, "size": 67108864}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: TEST_IMG + file format: IMGFMT +@@ -34,15 +34,15 @@ virtual size: 64M (67108864 bytes) + + === Successful image creation (with non-default options) === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "size": 0}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 65536, 'driver': 'parallels', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}, 'size': 33554432}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 65536, "driver": "parallels", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels"}, "size": 33554432}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: TEST_IMG + file format: IMGFMT +@@ -50,18 +50,18 @@ virtual size: 32M (33554432 bytes) + + === Invalid BlockdevRef === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': "this doesn't exist", 'size': 33554432}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "this doesn't exist", "size": 33554432}}} ++{"return": {}} + Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + === Zero size === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 0}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 0}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: TEST_IMG + file format: IMGFMT +@@ -69,10 +69,10 @@ virtual size: 0 (0 bytes) + + === Maximum size === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 4503599627369984}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 4503599627369984}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: TEST_IMG + file format: IMGFMT +@@ -80,77 +80,77 @@ virtual size: 4096T (4503599627369984 bytes) + + === Invalid sizes === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 1234}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 1234}}} ++{"return": {}} + Job failed: Image size must be a multiple of 512 bytes +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 18446744073709551104L}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 18446744073709551104}}} ++{"return": {}} + Job failed: Image size is too large for this cluster size +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 9223372036854775808L}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 9223372036854775808}}} ++{"return": {}} + Job failed: Image size is too large for this cluster size +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 9223372036854775296}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 9223372036854775296}}} ++{"return": {}} + Job failed: Image size is too large for this cluster size +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 4503599627370497}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 4503599627370497}}} ++{"return": {}} + Job failed: Image size is too large for this cluster size +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + === Invalid cluster size === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1234, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 1234, "driver": "parallels", "file": "node0", "size": 67108864}}} ++{"return": {}} + Job failed: Cluster size must be a multiple of 512 bytes +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 128, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 128, "driver": "parallels", "file": "node0", "size": 67108864}}} ++{"return": {}} + Job failed: Cluster size must be a multiple of 512 bytes +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 4294967296, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 4294967296, "driver": "parallels", "file": "node0", "size": 67108864}}} ++{"return": {}} + Job failed: Cluster size is too large +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 9223372036854775808L, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 9223372036854775808, "driver": "parallels", "file": "node0", "size": 67108864}}} ++{"return": {}} + Job failed: Cluster size is too large +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 18446744073709551104L, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 18446744073709551104, "driver": "parallels", "file": "node0", "size": 67108864}}} ++{"return": {}} + Job failed: Cluster size is too large +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 0, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 0, "driver": "parallels", "file": "node0", "size": 67108864}}} ++{"return": {}} + Job failed: Image size is too large for this cluster size +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'driver': 'parallels', 'file': 'node0', 'size': 281474976710656}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 512, "driver": "parallels", "file": "node0", "size": 281474976710656}}} ++{"return": {}} + Job failed: Image size is too large for this cluster size +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +diff --git a/tests/qemu-iotests/213.out b/tests/qemu-iotests/213.out +index e1dcd47..0c9d65b 100644 +--- a/tests/qemu-iotests/213.out ++++ b/tests/qemu-iotests/213.out +@@ -1,16 +1,16 @@ + === Successful image creation (defaults) === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} +- +-{'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}} +-{u'return': {}} +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'imgfile', 'size': 134217728}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "size": 0}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} ++ ++{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "node_name": "imgfile"}} ++{"return": {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "imgfile", "size": 134217728}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: TEST_IMG + file format: IMGFMT +@@ -19,15 +19,15 @@ cluster_size: 8388608 + + === Successful image creation (explicit defaults) === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "size": 0}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'block-size': 8388608, 'driver': 'vhdx', 'subformat': 'dynamic', 'log-size': 1048576, 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}, 'block-state-zero': True, 'size': 67108864}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 8388608, "block-state-zero": true, "driver": "vhdx", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx"}, "log-size": 1048576, "size": 67108864, "subformat": "dynamic"}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: TEST_IMG + file format: IMGFMT +@@ -36,15 +36,15 @@ cluster_size: 8388608 + + === Successful image creation (with non-default options) === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "size": 0}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'block-size': 268435456, 'driver': 'vhdx', 'subformat': 'fixed', 'log-size': 8388608, 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}, 'block-state-zero': False, 'size': 33554432}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 268435456, "block-state-zero": false, "driver": "vhdx", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx"}, "log-size": 8388608, "size": 33554432, "subformat": "fixed"}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: TEST_IMG + file format: IMGFMT +@@ -53,18 +53,18 @@ cluster_size: 268435456 + + === Invalid BlockdevRef === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': "this doesn't exist", 'size': 33554432}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "this doesn't exist", "size": 33554432}}} ++{"return": {}} + Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + === Zero size === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 0}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 0}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: TEST_IMG + file format: IMGFMT +@@ -73,10 +73,10 @@ cluster_size: 8388608 + + === Maximum size === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 70368744177664}}} +-{u'return': {}} +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 70368744177664}}} ++{"return": {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + image: TEST_IMG + file format: IMGFMT +@@ -85,85 +85,85 @@ cluster_size: 67108864 + + === Invalid sizes === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 18446744073709551104L}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 18446744073709551104}}} ++{"return": {}} + Job failed: Image size too large; max of 64TB +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 9223372036854775808L}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 9223372036854775808}}} ++{"return": {}} + Job failed: Image size too large; max of 64TB +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 9223372036854775296}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 9223372036854775296}}} ++{"return": {}} + Job failed: Image size too large; max of 64TB +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 70368744177665}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 70368744177665}}} ++{"return": {}} + Job failed: Image size too large; max of 64TB +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + === Invalid block size === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 1234567, 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 1234567, "driver": "vhdx", "file": "node0", "size": 67108864}}} ++{"return": {}} + Job failed: Block size must be a multiple of 1 MB +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 128, 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 128, "driver": "vhdx", "file": "node0", "size": 67108864}}} ++{"return": {}} + Job failed: Block size must be a multiple of 1 MB +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 3145728, 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 3145728, "driver": "vhdx", "file": "node0", "size": 67108864}}} ++{"return": {}} + Job failed: Block size must be a power of two +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 536870912, 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 536870912, "driver": "vhdx", "file": "node0", "size": 67108864}}} ++{"return": {}} + Job failed: Block size must not exceed 268435456 +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 0, 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 0, "driver": "vhdx", "file": "node0", "size": 67108864}}} ++{"return": {}} + Job failed: Block size must be a multiple of 1 MB +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + + === Invalid log size === + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 1234567, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 1234567, "size": 67108864}}} ++{"return": {}} + Job failed: Log size must be a multiple of 1 MB +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 128, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 128, "size": 67108864}}} ++{"return": {}} + Job failed: Log size must be a multiple of 1 MB +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 4294967296, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 4294967296, "size": 67108864}}} ++{"return": {}} + Job failed: Log size must be smaller than 4 GB +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +-{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 0, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} +-{u'return': {}} ++{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 0, "size": 67108864}}} ++{"return": {}} + Job failed: Log size must be a multiple of 1 MB +-{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +-{u'return': {}} ++{"execute": "job-dismiss", "arguments": {"id": "job0"}} ++{"return": {}} + +diff --git a/tests/qemu-iotests/216.out b/tests/qemu-iotests/216.out +index 45ea857..a70aa5c 100644 +--- a/tests/qemu-iotests/216.out ++++ b/tests/qemu-iotests/216.out +@@ -7,8 +7,8 @@ Done + + --- Doing COR --- + +-{u'return': {}} +-{u'return': u''} ++{"return": {}} ++{"return": ""} + + --- Checking COR result --- + +diff --git a/tests/qemu-iotests/218.out b/tests/qemu-iotests/218.out +index 7dbf78e..825a657 100644 +--- a/tests/qemu-iotests/218.out ++++ b/tests/qemu-iotests/218.out +@@ -4,27 +4,27 @@ + --- force=false --- + + Cancelling job +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 65536, u'len': 1048576, u'offset': 65536}, u'event': u'BLOCK_JOB_CANCELLED'} ++{"return": {}} ++{"data": {"device": "mirror", "len": 1048576, "offset": 65536, "speed": 65536, "type": "mirror"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} + + --- force=true --- + + Cancelling job +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 65536, u'len': 1048576, u'offset': 65536}, u'event': u'BLOCK_JOB_CANCELLED'} ++{"return": {}} ++{"data": {"device": "mirror", "len": 1048576, "offset": 65536, "speed": 65536, "type": "mirror"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} + + === Cancel mirror job after convergence === + + --- force=false --- + +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_READY'} ++{"data": {"device": "mirror", "len": 1048576, "offset": 1048576, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} + Cancelling job +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_COMPLETED'} ++{"return": {}} ++{"data": {"device": "mirror", "len": 1048576, "offset": 1048576, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} + + --- force=true --- + +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_READY'} ++{"data": {"device": "mirror", "len": 1048576, "offset": 1048576, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} + Cancelling job +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_CANCELLED'} ++{"return": {}} ++{"data": {"device": "mirror", "len": 1048576, "offset": 1048576, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +diff --git a/tests/qemu-iotests/219.out b/tests/qemu-iotests/219.out +index 6dc07bc..8ebd3fe 100644 +--- a/tests/qemu-iotests/219.out ++++ b/tests/qemu-iotests/219.out +@@ -2,326 +2,326 @@ Launching VM... + + + Starting block job: drive-mirror (auto-finalize: True; auto-dismiss: True) +-{u'return': {}} +-{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'mirror'}]} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{"return": {}} ++{"return": [{"current-progress": "FILTERED", "id": "job0", "status": "running", "total-progress": "FILTERED", "type": "mirror"}]} ++{"data": {"id": "job0", "status": "created"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} + + Pause/resume in RUNNING + === Testing block-job-pause/block-job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 65536, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "mirror"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 131072, "id": "job0", "status": "running", "total-progress": 4194304, "type": "mirror"}]} + === Testing block-job-pause/job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 131072, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "mirror"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 196608, "id": "job0", "status": "running", "total-progress": 4194304, "type": "mirror"}]} + === Testing job-pause/block-job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 196608, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "mirror"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 262144, "id": "job0", "status": "running", "total-progress": 4194304, "type": "mirror"}]} + === Testing job-pause/job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +-{u'return': {}} ++{"return": {}} ++{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 262144, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "mirror"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 327680, "id": "job0", "status": "running", "total-progress": 4194304, "type": "mirror"}]} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'complete'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'complete'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} ++{"return": {}} + + Waiting for READY state... +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++{"data": {"id": "job0", "status": "ready"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 4194304, "id": "job0", "status": "ready", "total-progress": 4194304, "type": "mirror"}]} + + Pause/resume in READY + === Testing block-job-pause/block-job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "standby"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 4194304, "id": "job0", "status": "standby", "total-progress": 4194304, "type": "mirror"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "ready"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 4194304, "id": "job0", "status": "ready", "total-progress": 4194304, "type": "mirror"}]} + === Testing block-job-pause/job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "standby"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 4194304, "id": "job0", "status": "standby", "total-progress": 4194304, "type": "mirror"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "ready"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 4194304, "id": "job0", "status": "ready", "total-progress": 4194304, "type": "mirror"}]} + === Testing job-pause/block-job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "standby"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 4194304, "id": "job0", "status": "standby", "total-progress": 4194304, "type": "mirror"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "ready"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 4194304, "id": "job0", "status": "ready", "total-progress": 4194304, "type": "mirror"}]} + === Testing job-pause/job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}} +-{u'return': {}} ++{"return": {}} ++{"data": {"id": "job0", "status": "standby"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 4194304, "id": "job0", "status": "standby", "total-progress": 4194304, "type": "mirror"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "ready"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 4194304, "id": "job0", "status": "ready", "total-progress": 4194304, "type": "mirror"}]} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}} ++{"return": {}} + + Waiting for PENDING state... +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': []} ++{"data": {"id": "job0", "status": "waiting"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"id": "job0", "status": "pending"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"id": "job0", "status": "concluded"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"id": "job0", "status": "null"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": []} + + + Starting block job: drive-backup (auto-finalize: True; auto-dismiss: True) +-{u'return': {}} +-{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{"return": {}} ++{"return": [{"current-progress": "FILTERED", "id": "job0", "status": "running", "total-progress": "FILTERED", "type": "backup"}]} ++{"data": {"id": "job0", "status": "created"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} + + Pause/resume in RUNNING + === Testing block-job-pause/block-job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 65536, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 131072, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} + === Testing block-job-pause/job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 131072, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 196608, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} + === Testing job-pause/block-job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 196608, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 262144, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} + === Testing job-pause/job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +-{u'return': {}} ++{"return": {}} ++{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 262144, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 327680, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'complete'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'complete'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} ++{"return": {}} + + Waiting for PENDING state... +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': []} ++{"data": {"id": "job0", "status": "waiting"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"id": "job0", "status": "pending"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"id": "job0", "status": "concluded"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"id": "job0", "status": "null"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": []} + + + Starting block job: drive-backup (auto-finalize: True; auto-dismiss: False) +-{u'return': {}} +-{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{"return": {}} ++{"return": [{"current-progress": "FILTERED", "id": "job0", "status": "running", "total-progress": "FILTERED", "type": "backup"}]} ++{"data": {"id": "job0", "status": "created"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} + + Pause/resume in RUNNING + === Testing block-job-pause/block-job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 65536, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 131072, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} + === Testing block-job-pause/job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 131072, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 196608, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} + === Testing job-pause/block-job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 196608, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 262144, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} + === Testing job-pause/job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +-{u'return': {}} ++{"return": {}} ++{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 262144, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 327680, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'complete'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'complete'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} ++{"return": {}} + + Waiting for PENDING state... +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'concluded', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': []} ++{"data": {"id": "job0", "status": "waiting"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"id": "job0", "status": "pending"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"id": "job0", "status": "concluded"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 4194304, "id": "job0", "status": "concluded", "total-progress": 4194304, "type": "backup"}]} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} ++{"return": {}} ++{"data": {"id": "job0", "status": "null"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": []} + + + Starting block job: drive-backup (auto-finalize: False; auto-dismiss: True) +-{u'return': {}} +-{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{"return": {}} ++{"return": [{"current-progress": "FILTERED", "id": "job0", "status": "running", "total-progress": "FILTERED", "type": "backup"}]} ++{"data": {"id": "job0", "status": "created"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} + + Pause/resume in RUNNING + === Testing block-job-pause/block-job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 65536, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 131072, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} + === Testing block-job-pause/job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 131072, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 196608, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} + === Testing job-pause/block-job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 196608, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 262144, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} + === Testing job-pause/job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +-{u'return': {}} ++{"return": {}} ++{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 262144, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 327680, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'complete'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'complete'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} ++{"return": {}} + + Waiting for PENDING state... +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'pending', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': []} ++{"data": {"id": "job0", "status": "waiting"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"id": "job0", "status": "pending"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 4194304, "id": "job0", "status": "pending", "total-progress": 4194304, "type": "backup"}]} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} ++{"return": {}} ++{"data": {"id": "job0", "status": "concluded"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"id": "job0", "status": "null"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": []} + + + Starting block job: drive-backup (auto-finalize: False; auto-dismiss: False) +-{u'return': {}} +-{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{"return": {}} ++{"return": [{"current-progress": "FILTERED", "id": "job0", "status": "running", "total-progress": "FILTERED", "type": "backup"}]} ++{"data": {"id": "job0", "status": "created"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} + + Pause/resume in RUNNING + === Testing block-job-pause/block-job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 65536, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 131072, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} + === Testing block-job-pause/job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 131072, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 196608, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} + === Testing job-pause/block-job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 196608, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 262144, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} + === Testing job-pause/job-resume === +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +-{u'return': {}} ++{"return": {}} ++{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 262144, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} ++{"return": {}} ++{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 327680, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'complete'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'complete'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} ++{"return": {}} + + Waiting for PENDING state... +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'pending', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': [{u'status': u'concluded', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} +-{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +-{u'return': []} ++{"data": {"id": "job0", "status": "waiting"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"id": "job0", "status": "pending"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 4194304, "id": "job0", "status": "pending", "total-progress": 4194304, "type": "backup"}]} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} ++{"return": {}} ++{"data": {"id": "job0", "status": "concluded"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": [{"current-progress": 4194304, "id": "job0", "status": "concluded", "total-progress": 4194304, "type": "backup"}]} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} ++{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} ++{"return": {}} ++{"data": {"id": "job0", "status": "null"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": []} +diff --git a/tests/qemu-iotests/222.out b/tests/qemu-iotests/222.out +index 48f336a..16643dd 100644 +--- a/tests/qemu-iotests/222.out ++++ b/tests/qemu-iotests/222.out +@@ -8,13 +8,13 @@ Done + + --- Setting up Fleecing Graph --- + +-{u'return': {}} +-{u'return': {}} ++{"return": {}} ++{"return": {}} + + --- Setting up NBD Export --- + +-{u'return': {}} +-{u'return': {}} ++{"return": {}} ++{"return": {}} + + --- Sanity Check --- + +@@ -29,13 +29,13 @@ read -P0 0x3fe0000 64k + --- Testing COW --- + + write -P0xab 0 64k +-{u'return': u''} ++{"return": ""} + write -P0xad 0x00f8000 64k +-{u'return': u''} ++{"return": ""} + write -P0x1d 0x2008000 64k +-{u'return': u''} ++{"return": ""} + write -P0xea 0x3fe0000 64k +-{u'return': u''} ++{"return": ""} + + --- Verifying Data --- + +@@ -49,10 +49,10 @@ read -P0 0x3fe0000 64k + + --- Cleanup --- + +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'drive0', u'type': u'backup', u'speed': 0, u'len': 67108864, u'offset': 393216}, u'event': u'BLOCK_JOB_CANCELLED'} +-{u'return': {}} +-{u'return': {}} ++{"return": {}} ++{"data": {"device": "drive0", "len": 67108864, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"return": {}} ++{"return": {}} + + --- Confirming writes --- + +diff --git a/tests/qemu-iotests/234.out b/tests/qemu-iotests/234.out +index c7cd95f..592625b 100644 +--- a/tests/qemu-iotests/234.out ++++ b/tests/qemu-iotests/234.out +@@ -1,28 +1,28 @@ + Launching source VM... + Launching destination VM... +-{u'return': {}} +-{u'return': {}} ++{"return": {}} ++{"return": {}} + Enabling migration QMP events on A... +-{u'return': {}} ++{"return": {}} + Starting migration to B... +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'setup'}, u'event': u'MIGRATION'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'active'}, u'event': u'MIGRATION'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'completed'}, u'event': u'MIGRATION'} ++{"return": {}} ++{"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} + completed +-{u'return': {u'status': u'postmigrate', u'singlestep': False, u'running': False}} +-{u'return': {u'status': u'running', u'singlestep': False, u'running': True}} ++{"return": {"running": false, "singlestep": false, "status": "postmigrate"}} ++{"return": {"running": true, "singlestep": false, "status": "running"}} + Add a second parent to drive0-file... +-{u'return': {}} ++{"return": {}} + Restart A with -incoming and second parent... +-{u'return': {}} ++{"return": {}} + Enabling migration QMP events on B... +-{u'return': {}} ++{"return": {}} + Starting migration back to A... +-{u'return': {}} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'setup'}, u'event': u'MIGRATION'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'active'}, u'event': u'MIGRATION'} +-{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'completed'}, u'event': u'MIGRATION'} ++{"return": {}} ++{"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} + completed +-{u'return': {u'status': u'running', u'singlestep': False, u'running': True}} +-{u'return': {u'status': u'postmigrate', u'singlestep': False, u'running': False}} ++{"return": {"running": true, "singlestep": false, "status": "running"}} ++{"return": {"running": false, "singlestep": false, "status": "postmigrate"}} +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index 2f22fab..1b9656a 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -248,7 +248,10 @@ def filter_img_info(output, filename): + def log(msg, filters=[]): + for flt in filters: + msg = flt(msg) +- print msg ++ if type(msg) is dict or type(msg) is list: ++ print(json.dumps(msg, sort_keys=True)) ++ else: ++ print(msg) + + class Timeout: + def __init__(self, seconds, errmsg = "Timeout"): +@@ -436,10 +439,11 @@ class VM(qtest.QEMUQtestMachine): + return result + + def qmp_log(self, cmd, filters=[filter_testfiles], **kwargs): +- logmsg = "{'execute': '%s', 'arguments': %s}" % (cmd, kwargs) ++ logmsg = '{"execute": "%s", "arguments": %s}' % \ ++ (cmd, json.dumps(kwargs, sort_keys=True)) + log(logmsg, filters) + result = self.qmp(cmd, **kwargs) +- log(str(result), filters) ++ log(json.dumps(result, sort_keys=True), filters) + return result + + def run_job(self, job, auto_finalize=True, auto_dismiss=False): +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-Wait-for-qemu-to-end-in-223.patch b/SOURCES/kvm-iotests-Wait-for-qemu-to-end-in-223.patch new file mode 100644 index 0000000..04b68a3 --- /dev/null +++ b/SOURCES/kvm-iotests-Wait-for-qemu-to-end-in-223.patch @@ -0,0 +1,71 @@ +From e5d7e4bd2388cebecc64469ea957676caba9bbe1 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:23:07 +0100 +Subject: [PATCH 129/163] iotests: Wait for qemu to end in 223 + +RH-Author: John Snow +Message-id: <20190327172308.31077-55-jsnow@redhat.com> +Patchwork-id: 85219 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 54/55] iotests: Wait for qemu to end in 223 +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +When iotest 223 was first written, it didn't matter if we waited for +the qemu process to clean up. But with the introduction of a later +qemu-nbd process trying to reuse the same file, there is a race where +even though the asynchronous qemu process has responded to "quit", it +has not yet had time to unlock the file and exit, resulting in: + +-[{ "start": 0, "length": 65536, "depth": 0, "zero": false, "data": false}, +-{ "start": 65536, "length": 2031616, "depth": 0, "zero": false, "data": true}, +-{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}] ++qemu-nbd: Failed to blk_new_open 'tests/qemu-iotests/scratch/t.qcow2': Failed to get shared "write" lock ++Is another process using the image [tests/qemu-iotests/scratch/t.qcow2]? ++qemu-img: Could not open 'driver=nbd,server.type=unix,server.path=tests/qemu-iotests/scratch/qemu-nbd.sock,x-dirty-bitmap=qemu:dirty-bitmap:b': Failed to connect socket tests/qemu-iotests/scratch/qemu-nbd.sock: Connection refused ++./common.nbd: line 33: kill: (11122) - No such process + +Fixes: ddd09448 +Reported-by: Alberto Garcia +Signed-off-by: Eric Blake +Message-Id: <20190305182908.13557-1-eblake@redhat.com> +Tested-by: Alberto Garcia +Reviewed-by: Kevin Wolf +(cherry picked from commit 054be3605459d4342e9ee5a82ae0fcffeeb09e4d) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/223 | 1 + + tests/qemu-iotests/223.out | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 +index f120a01..c0a4f9c 100755 +--- a/tests/qemu-iotests/223 ++++ b/tests/qemu-iotests/223 +@@ -179,6 +179,7 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove", + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-stop"}' "return" + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-stop"}' "error" # Again + _send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' "return" ++wait=yes _cleanup_qemu + + echo + echo "=== Use qemu-nbd as server ===" +diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out +index 963ae28..0524ffb 100644 +--- a/tests/qemu-iotests/223.out ++++ b/tests/qemu-iotests/223.out +@@ -89,6 +89,7 @@ read 2097152/2097152 bytes at offset 2097152 + {"return": {}} + {"error": {"class": "GenericError", "desc": "NBD server not running"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + + === Use qemu-nbd as server === + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-add-222-to-test-basic-fleecing.patch b/SOURCES/kvm-iotests-add-222-to-test-basic-fleecing.patch new file mode 100644 index 0000000..1e57da3 --- /dev/null +++ b/SOURCES/kvm-iotests-add-222-to-test-basic-fleecing.patch @@ -0,0 +1,275 @@ +From 561418d66933d9262f1678fda614dc0bde2a8f45 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Tue, 24 Jul 2018 12:25:31 +0200 +Subject: [PATCH 62/89] iotests: add 222 to test basic fleecing + +RH-Author: John Snow +Message-id: <20180718225511.14878-12-jsnow@redhat.com> +Patchwork-id: 81407 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 11/35] iotests: add 222 to test basic fleecing +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +Signed-off-by: John Snow +Message-Id: <20180702194630.9360-3-jsnow@redhat.com> +Reviewed-by: Eric Blake +Signed-off-by: Eric Blake +(cherry picked from commit bacebdedbf921a2c641a34486ff543089d338f32) +Signed-off-by: John Snow +--- + tests/qemu-iotests/222 | 155 +++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/222.out | 67 ++++++++++++++++++++ + tests/qemu-iotests/group | 1 + + 3 files changed, 223 insertions(+) + create mode 100644 tests/qemu-iotests/222 + create mode 100644 tests/qemu-iotests/222.out + +diff --git a/tests/qemu-iotests/222 b/tests/qemu-iotests/222 +new file mode 100644 +index 0000000..ff3bfc1 +--- /dev/null ++++ b/tests/qemu-iotests/222 +@@ -0,0 +1,155 @@ ++#!/usr/bin/env python ++# ++# This test covers the basic fleecing workflow, which provides a ++# point-in-time snapshot of a node that can be queried over NBD. ++# ++# Copyright (C) 2018 Red Hat, Inc. ++# John helped, too. ++# ++# 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 . ++# ++# Creator/Owner: John Snow ++ ++import iotests ++from iotests import log, qemu_img, qemu_io, qemu_io_silent ++ ++iotests.verify_platform(['linux']) ++ ++patterns = [("0x5d", "0", "64k"), ++ ("0xd5", "1M", "64k"), ++ ("0xdc", "32M", "64k"), ++ ("0xcd", "0x3ff0000", "64k")] # 64M - 64K ++ ++overwrite = [("0xab", "0", "64k"), # Full overwrite ++ ("0xad", "0x00f8000", "64k"), # Partial-left (1M-32K) ++ ("0x1d", "0x2008000", "64k"), # Partial-right (32M+32K) ++ ("0xea", "0x3fe0000", "64k")] # Adjacent-left (64M - 128K) ++ ++zeroes = [("0", "0x00f8000", "32k"), # Left-end of partial-left (1M-32K) ++ ("0", "0x2010000", "32k"), # Right-end of partial-right (32M+64K) ++ ("0", "0x3fe0000", "64k")] # overwrite[3] ++ ++remainder = [("0xd5", "0x108000", "32k"), # Right-end of partial-left [1] ++ ("0xdc", "32M", "32k"), # Left-end of partial-right [2] ++ ("0xcd", "0x3ff0000", "64k")] # patterns[3] ++ ++with iotests.FilePath('base.img') as base_img_path, \ ++ iotests.FilePath('fleece.img') as fleece_img_path, \ ++ iotests.FilePath('nbd.sock') as nbd_sock_path, \ ++ iotests.VM() as vm: ++ ++ log('--- Setting up images ---') ++ log('') ++ ++ assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0 ++ assert qemu_img('create', '-f', "qcow2", fleece_img_path, '64M') == 0 ++ ++ for p in patterns: ++ qemu_io('-f', iotests.imgfmt, ++ '-c', 'write -P%s %s %s' % p, base_img_path) ++ ++ log('Done') ++ ++ log('') ++ log('--- Launching VM ---') ++ log('') ++ ++ vm.add_drive(base_img_path) ++ vm.launch() ++ log('Done') ++ ++ log('') ++ log('--- Setting up Fleecing Graph ---') ++ log('') ++ ++ src_node = "drive0" ++ tgt_node = "fleeceNode" ++ ++ # create tgt_node backed by src_node ++ log(vm.qmp("blockdev-add", **{ ++ "driver": "qcow2", ++ "node-name": tgt_node, ++ "file": { ++ "driver": "file", ++ "filename": fleece_img_path, ++ }, ++ "backing": src_node, ++ })) ++ ++ # Establish COW from source to fleecing node ++ log(vm.qmp("blockdev-backup", ++ device=src_node, ++ target=tgt_node, ++ sync="none")) ++ ++ log('') ++ log('--- Setting up NBD Export ---') ++ log('') ++ ++ nbd_uri = 'nbd+unix:///%s?socket=%s' % (tgt_node, nbd_sock_path) ++ log(vm.qmp("nbd-server-start", ++ **{"addr": { "type": "unix", ++ "data": { "path": nbd_sock_path } } })) ++ ++ log(vm.qmp("nbd-server-add", device=tgt_node)) ++ ++ log('') ++ log('--- Sanity Check ---') ++ log('') ++ ++ for p in (patterns + zeroes): ++ cmd = "read -P%s %s %s" % p ++ log(cmd) ++ assert qemu_io_silent('-r', '-f', 'raw', '-c', cmd, nbd_uri) == 0 ++ ++ log('') ++ log('--- Testing COW ---') ++ log('') ++ ++ for p in overwrite: ++ cmd = "write -P%s %s %s" % p ++ log(cmd) ++ log(vm.hmp_qemu_io(src_node, cmd)) ++ ++ log('') ++ log('--- Verifying Data ---') ++ log('') ++ ++ for p in (patterns + zeroes): ++ cmd = "read -P%s %s %s" % p ++ log(cmd) ++ assert qemu_io_silent('-r', '-f', 'raw', '-c', cmd, nbd_uri) == 0 ++ ++ log('') ++ log('--- Cleanup ---') ++ log('') ++ ++ log(vm.qmp('block-job-cancel', device=src_node)) ++ log(vm.event_wait('BLOCK_JOB_CANCELLED'), ++ filters=[iotests.filter_qmp_event]) ++ log(vm.qmp('nbd-server-stop')) ++ log(vm.qmp('blockdev-del', node_name=tgt_node)) ++ vm.shutdown() ++ ++ log('') ++ log('--- Confirming writes ---') ++ log('') ++ ++ for p in (overwrite + remainder): ++ cmd = "read -P%s %s %s" % p ++ log(cmd) ++ assert qemu_io_silent(base_img_path, '-c', cmd) == 0 ++ ++ log('') ++ log('Done') +diff --git a/tests/qemu-iotests/222.out b/tests/qemu-iotests/222.out +new file mode 100644 +index 0000000..48f336a +--- /dev/null ++++ b/tests/qemu-iotests/222.out +@@ -0,0 +1,67 @@ ++--- Setting up images --- ++ ++Done ++ ++--- Launching VM --- ++ ++Done ++ ++--- Setting up Fleecing Graph --- ++ ++{u'return': {}} ++{u'return': {}} ++ ++--- Setting up NBD Export --- ++ ++{u'return': {}} ++{u'return': {}} ++ ++--- Sanity Check --- ++ ++read -P0x5d 0 64k ++read -P0xd5 1M 64k ++read -P0xdc 32M 64k ++read -P0xcd 0x3ff0000 64k ++read -P0 0x00f8000 32k ++read -P0 0x2010000 32k ++read -P0 0x3fe0000 64k ++ ++--- Testing COW --- ++ ++write -P0xab 0 64k ++{u'return': u''} ++write -P0xad 0x00f8000 64k ++{u'return': u''} ++write -P0x1d 0x2008000 64k ++{u'return': u''} ++write -P0xea 0x3fe0000 64k ++{u'return': u''} ++ ++--- Verifying Data --- ++ ++read -P0x5d 0 64k ++read -P0xd5 1M 64k ++read -P0xdc 32M 64k ++read -P0xcd 0x3ff0000 64k ++read -P0 0x00f8000 32k ++read -P0 0x2010000 32k ++read -P0 0x3fe0000 64k ++ ++--- Cleanup --- ++ ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'drive0', u'type': u'backup', u'speed': 0, u'len': 67108864, u'offset': 393216}, u'event': u'BLOCK_JOB_CANCELLED'} ++{u'return': {}} ++{u'return': {}} ++ ++--- Confirming writes --- ++ ++read -P0xab 0 64k ++read -P0xad 0x00f8000 64k ++read -P0x1d 0x2008000 64k ++read -P0xea 0x3fe0000 64k ++read -P0xd5 0x108000 32k ++read -P0xdc 32M 32k ++read -P0xcd 0x3ff0000 64k ++ ++Done +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index 11498fd..be55905 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -218,4 +218,5 @@ + 217 rw auto quick + 218 rw auto quick + 219 rw auto ++222 rw auto quick + 226 auto quick +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-add-busy-recording-bit-test-to-124.patch b/SOURCES/kvm-iotests-add-busy-recording-bit-test-to-124.patch new file mode 100644 index 0000000..ba3d07b --- /dev/null +++ b/SOURCES/kvm-iotests-add-busy-recording-bit-test-to-124.patch @@ -0,0 +1,172 @@ +From 5252d9e158007252f01fb6de28e22918741e6f8e Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 18:18:48 +0200 +Subject: [PATCH 143/163] iotests: add busy/recording bit test to 124 + +RH-Author: John Snow +Message-id: <20190403181857.9693-13-jsnow@redhat.com> +Patchwork-id: 85423 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 12/21] iotests: add busy/recording bit test to 124 +Bugzilla: 1677073 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Sergio Lopez Pascual + +This adds a simple test that ensures the busy bit works for push backups, +as well as doubling as bonus test for incremental backups that get interrupted +by EIO errors. + +Recording bit tests are already handled sufficiently by 236. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Tested-by: Eric Blake +Message-id: 20190223000614.13894-11-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit c61b198b63219b489908c87371acae8c986ce4d3) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/124 | 113 +++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/124.out | 4 +- + 2 files changed, 115 insertions(+), 2 deletions(-) + +diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124 +index 439a86a..acfe04e 100755 +--- a/tests/qemu-iotests/124 ++++ b/tests/qemu-iotests/124 +@@ -634,6 +634,119 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase): + self.vm.shutdown() + self.check_backups() + ++ def test_incremental_pause(self): ++ """ ++ Test an incremental backup that errors into a pause and is resumed. ++ """ ++ ++ drive0 = self.drives[0] ++ # NB: The blkdebug script here looks for a "flush, read, read" pattern. ++ # The flush occurs in hmp_io_writes, the first read in device_add, and ++ # the last read during the block job. ++ result = self.vm.qmp('blockdev-add', ++ node_name=drive0['id'], ++ driver=drive0['fmt'], ++ file={ ++ 'driver': 'blkdebug', ++ 'image': { ++ 'driver': 'file', ++ 'filename': drive0['file'] ++ }, ++ 'set-state': [{ ++ 'event': 'flush_to_disk', ++ 'state': 1, ++ 'new_state': 2 ++ },{ ++ 'event': 'read_aio', ++ 'state': 2, ++ 'new_state': 3 ++ }], ++ 'inject-error': [{ ++ 'event': 'read_aio', ++ 'errno': 5, ++ 'state': 3, ++ 'immediately': False, ++ 'once': True ++ }], ++ }) ++ self.assert_qmp(result, 'return', {}) ++ self.create_anchor_backup(drive0) ++ bitmap = self.add_bitmap('bitmap0', drive0) ++ ++ # Emulate guest activity ++ self.hmp_io_writes(drive0['id'], (('0xab', 0, 512), ++ ('0xfe', '16M', '256k'), ++ ('0x64', '32736k', '64k'))) ++ ++ # For the purposes of query-block visibility of bitmaps, add a drive ++ # frontend after we've written data; otherwise we can't use hmp-io ++ result = self.vm.qmp("device_add", ++ id="device0", ++ drive=drive0['id'], ++ driver="virtio-blk") ++ self.assert_qmp(result, 'return', {}) ++ ++ # Bitmap Status Check ++ query = self.vm.qmp('query-block') ++ ret = [bmap for bmap in query['return'][0]['dirty-bitmaps'] ++ if bmap.get('name') == bitmap.name][0] ++ self.assert_qmp(ret, 'count', 458752) ++ self.assert_qmp(ret, 'granularity', 65536) ++ self.assert_qmp(ret, 'status', 'active') ++ self.assert_qmp(ret, 'busy', False) ++ self.assert_qmp(ret, 'recording', True) ++ ++ # Start backup ++ parent, _ = bitmap.last_target() ++ target = self.prepare_backup(bitmap, parent) ++ res = self.vm.qmp('drive-backup', ++ job_id=bitmap.drive['id'], ++ device=bitmap.drive['id'], ++ sync='incremental', ++ bitmap=bitmap.name, ++ format=bitmap.drive['fmt'], ++ target=target, ++ mode='existing', ++ on_source_error='stop') ++ self.assert_qmp(res, 'return', {}) ++ ++ # Wait for the error ++ event = self.vm.event_wait(name="BLOCK_JOB_ERROR", ++ match={"data":{"device":bitmap.drive['id']}}) ++ self.assert_qmp(event, 'data', {'device': bitmap.drive['id'], ++ 'action': 'stop', ++ 'operation': 'read'}) ++ ++ # Bitmap Status Check ++ query = self.vm.qmp('query-block') ++ ret = [bmap for bmap in query['return'][0]['dirty-bitmaps'] ++ if bmap.get('name') == bitmap.name][0] ++ self.assert_qmp(ret, 'count', 458752) ++ self.assert_qmp(ret, 'granularity', 65536) ++ self.assert_qmp(ret, 'status', 'frozen') ++ self.assert_qmp(ret, 'busy', True) ++ self.assert_qmp(ret, 'recording', True) ++ ++ # Resume and check incremental backup for consistency ++ res = self.vm.qmp('block-job-resume', device=bitmap.drive['id']) ++ self.assert_qmp(res, 'return', {}) ++ self.wait_qmp_backup(bitmap.drive['id']) ++ ++ # Bitmap Status Check ++ query = self.vm.qmp('query-block') ++ ret = [bmap for bmap in query['return'][0]['dirty-bitmaps'] ++ if bmap.get('name') == bitmap.name][0] ++ self.assert_qmp(ret, 'count', 0) ++ self.assert_qmp(ret, 'granularity', 65536) ++ self.assert_qmp(ret, 'status', 'active') ++ self.assert_qmp(ret, 'busy', False) ++ self.assert_qmp(ret, 'recording', True) ++ ++ # Finalize / Cleanup ++ self.make_reference_backup(bitmap) ++ self.vm.shutdown() ++ self.check_backups() ++ + + if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2']) +diff --git a/tests/qemu-iotests/124.out b/tests/qemu-iotests/124.out +index e56cae0..281b69e 100644 +--- a/tests/qemu-iotests/124.out ++++ b/tests/qemu-iotests/124.out +@@ -1,5 +1,5 @@ +-........... ++............ + ---------------------------------------------------------------------- +-Ran 11 tests ++Ran 12 tests + + OK +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-add-filter_generated_node_ids.patch b/SOURCES/kvm-iotests-add-filter_generated_node_ids.patch new file mode 100644 index 0000000..c94cced --- /dev/null +++ b/SOURCES/kvm-iotests-add-filter_generated_node_ids.patch @@ -0,0 +1,45 @@ +From 08890e6fc7100a01624ed80662d2a7d204e9f3b8 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 16:16:19 +0100 +Subject: [PATCH 021/163] iotests: add filter_generated_node_ids + +RH-Author: John Snow +Message-id: <20190320161631.14841-8-jsnow@redhat.com> +Patchwork-id: 84941 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 07/19] iotests: add filter_generated_node_ids +Bugzilla: 1668956 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +To mimic the common filter of the same name, but for the python tests. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20181221093529.23855-7-jsnow@redhat.com> +Signed-off-by: Eric Blake +(cherry picked from commit fa1151f811ffa1973b6980b09f095d0e2ebea08b) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/iotests.py | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index c5739a6..5334ff4 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -233,6 +233,9 @@ def filter_testfiles(msg): + prefix = os.path.join(test_dir, "%s-" % (os.getpid())) + return msg.replace(prefix, 'TEST_DIR/PID-') + ++def filter_generated_node_ids(msg): ++ return re.sub("#block[0-9]+", "NODE_NAME", msg) ++ + def filter_img_info(output, filename): + lines = [] + for line in output.split('\n'): +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-add-iotest-236-for-testing-bitmap-merge.patch b/SOURCES/kvm-iotests-add-iotest-236-for-testing-bitmap-merge.patch new file mode 100644 index 0000000..c38e1bd --- /dev/null +++ b/SOURCES/kvm-iotests-add-iotest-236-for-testing-bitmap-merge.patch @@ -0,0 +1,570 @@ +From 85a2175420c8c79bf947f1f2af3d544548098b6e Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 16:16:24 +0100 +Subject: [PATCH 026/163] iotests: add iotest 236 for testing bitmap merge + +RH-Author: John Snow +Message-id: <20190320161631.14841-13-jsnow@redhat.com> +Patchwork-id: 84950 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 12/19] iotests: add iotest 236 for testing bitmap merge +Bugzilla: 1668956 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +New interface, new smoke test. + +Signed-off-by: John Snow +Reviewed-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: Eric Blake +Message-Id: <20181221093529.23855-12-jsnow@redhat.com> +[eblake: fix last-minute change to echo text] +Signed-off-by: Eric Blake +(cherry picked from commit 14da540f2a68b3f730b1a7c31de783f3d68f6fc7) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/236 | 161 +++++++++++++++++++++ + tests/qemu-iotests/236.out | 351 +++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/group | 1 + + 3 files changed, 513 insertions(+) + create mode 100755 tests/qemu-iotests/236 + create mode 100644 tests/qemu-iotests/236.out + +diff --git a/tests/qemu-iotests/236 b/tests/qemu-iotests/236 +new file mode 100755 +index 0000000..79a6381 +--- /dev/null ++++ b/tests/qemu-iotests/236 +@@ -0,0 +1,161 @@ ++#!/usr/bin/env python ++# ++# Test bitmap merges. ++# ++# Copyright (c) 2018 John Snow for 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 . ++# ++# owner=jsnow@redhat.com ++ ++import iotests ++from iotests import log ++ ++iotests.verify_image_format(supported_fmts=['generic']) ++size = 64 * 1024 * 1024 ++granularity = 64 * 1024 ++ ++patterns = [("0x5d", "0", "64k"), ++ ("0xd5", "1M", "64k"), ++ ("0xdc", "32M", "64k"), ++ ("0xcd", "0x3ff0000", "64k")] # 64M - 64K ++ ++overwrite = [("0xab", "0", "64k"), # Full overwrite ++ ("0xad", "0x00f8000", "64k"), # Partial-left (1M-32K) ++ ("0x1d", "0x2008000", "64k"), # Partial-right (32M+32K) ++ ("0xea", "0x3fe0000", "64k")] # Adjacent-left (64M - 128K) ++ ++def query_bitmaps(vm): ++ res = vm.qmp("query-block") ++ return { "bitmaps": { device['device']: device.get('dirty-bitmaps', []) for ++ device in res['return'] } } ++ ++with iotests.FilePath('img') as img_path, \ ++ iotests.VM() as vm: ++ ++ log('--- Preparing image & VM ---\n') ++ iotests.qemu_img_create('-f', iotests.imgfmt, img_path, str(size)) ++ vm.add_drive(img_path) ++ vm.launch() ++ ++ log('\n--- Adding preliminary bitmaps A & B ---\n') ++ vm.qmp_log("block-dirty-bitmap-add", node="drive0", ++ name="bitmapA", granularity=granularity) ++ vm.qmp_log("block-dirty-bitmap-add", node="drive0", ++ name="bitmapB", granularity=granularity) ++ ++ # Dirties 4 clusters. count=262144 ++ log('\n--- Emulating writes ---\n') ++ for p in patterns: ++ cmd = "write -P%s %s %s" % p ++ log(cmd) ++ log(vm.hmp_qemu_io("drive0", cmd)) ++ ++ log(query_bitmaps(vm), indent=2) ++ ++ log('\n--- Submitting & Aborting Transaction ---\n') ++ vm.qmp_log("transaction", indent=2, actions=[ ++ { "type": "block-dirty-bitmap-disable", ++ "data": { "node": "drive0", "name": "bitmapB" }}, ++ { "type": "block-dirty-bitmap-add", ++ "data": { "node": "drive0", "name": "bitmapC", ++ "granularity": granularity }}, ++ { "type": "block-dirty-bitmap-clear", ++ "data": { "node": "drive0", "name": "bitmapA" }}, ++ { "type": "abort", "data": {}} ++ ]) ++ log(query_bitmaps(vm), indent=2) ++ ++ log('\n--- Disabling B & Adding C ---\n') ++ vm.qmp_log("transaction", indent=2, actions=[ ++ { "type": "block-dirty-bitmap-disable", ++ "data": { "node": "drive0", "name": "bitmapB" }}, ++ { "type": "block-dirty-bitmap-add", ++ "data": { "node": "drive0", "name": "bitmapC", ++ "granularity": granularity }}, ++ # Purely extraneous, but test that it works: ++ { "type": "block-dirty-bitmap-disable", ++ "data": { "node": "drive0", "name": "bitmapC" }}, ++ { "type": "block-dirty-bitmap-enable", ++ "data": { "node": "drive0", "name": "bitmapC" }}, ++ ]) ++ ++ log('\n--- Emulating further writes ---\n') ++ # Dirties 6 clusters, 3 of which are new in contrast to "A". ++ # A = 64 * 1024 * (4 + 3) = 458752 ++ # C = 64 * 1024 * 6 = 393216 ++ for p in overwrite: ++ cmd = "write -P%s %s %s" % p ++ log(cmd) ++ log(vm.hmp_qemu_io("drive0", cmd)) ++ ++ log('\n--- Disabling A & C ---\n') ++ vm.qmp_log("transaction", indent=2, actions=[ ++ { "type": "block-dirty-bitmap-disable", ++ "data": { "node": "drive0", "name": "bitmapA" }}, ++ { "type": "block-dirty-bitmap-disable", ++ "data": { "node": "drive0", "name": "bitmapC" }} ++ ]) ++ ++ # A: 7 clusters ++ # B: 4 clusters ++ # C: 6 clusters ++ log(query_bitmaps(vm), indent=2) ++ ++ log('\n--- Submitting & Aborting Merge Transaction ---\n') ++ vm.qmp_log("transaction", indent=2, actions=[ ++ { "type": "block-dirty-bitmap-add", ++ "data": { "node": "drive0", "name": "bitmapD", ++ "disabled": True, "granularity": granularity }}, ++ { "type": "block-dirty-bitmap-merge", ++ "data": { "node": "drive0", "target": "bitmapD", ++ "bitmaps": ["bitmapB", "bitmapC"] }}, ++ { "type": "abort", "data": {}} ++ ]) ++ log(query_bitmaps(vm), indent=2) ++ ++ log('\n--- Creating D as a merge of B & C ---\n') ++ # Good hygiene: create a disabled bitmap as a merge target. ++ vm.qmp_log("transaction", indent=2, actions=[ ++ { "type": "block-dirty-bitmap-add", ++ "data": { "node": "drive0", "name": "bitmapD", ++ "disabled": True, "granularity": granularity }}, ++ { "type": "block-dirty-bitmap-merge", ++ "data": { "node": "drive0", "target": "bitmapD", ++ "bitmaps": ["bitmapB", "bitmapC"] }} ++ ]) ++ ++ # A and D should now both have 7 clusters apiece. ++ # B and C remain unchanged with 4 and 6 respectively. ++ log(query_bitmaps(vm), indent=2) ++ ++ # A and D should be equivalent. ++ # Some formats round the size of the disk, so don't print the checksums. ++ check_a = vm.qmp('x-debug-block-dirty-bitmap-sha256', ++ node="drive0", name="bitmapA")['return']['sha256'] ++ check_d = vm.qmp('x-debug-block-dirty-bitmap-sha256', ++ node="drive0", name="bitmapD")['return']['sha256'] ++ assert(check_a == check_d) ++ ++ log('\n--- Removing bitmaps A, B, C, and D ---\n') ++ vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapA") ++ vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapB") ++ vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapC") ++ vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapD") ++ ++ log('\n--- Final Query ---\n') ++ log(query_bitmaps(vm), indent=2) ++ ++ log('\n--- Done ---\n') ++ vm.shutdown() +diff --git a/tests/qemu-iotests/236.out b/tests/qemu-iotests/236.out +new file mode 100644 +index 0000000..1dad24d +--- /dev/null ++++ b/tests/qemu-iotests/236.out +@@ -0,0 +1,351 @@ ++--- Preparing image & VM --- ++ ++ ++--- Adding preliminary bitmaps A & B --- ++ ++{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 65536, "name": "bitmapA", "node": "drive0"}} ++{"return": {}} ++{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 65536, "name": "bitmapB", "node": "drive0"}} ++{"return": {}} ++ ++--- Emulating writes --- ++ ++write -P0x5d 0 64k ++{"return": ""} ++write -P0xd5 1M 64k ++{"return": ""} ++write -P0xdc 32M 64k ++{"return": ""} ++write -P0xcd 0x3ff0000 64k ++{"return": ""} ++{ ++ "bitmaps": { ++ "drive0": [ ++ { ++ "count": 262144, ++ "granularity": 65536, ++ "name": "bitmapB", ++ "status": "active" ++ }, ++ { ++ "count": 262144, ++ "granularity": 65536, ++ "name": "bitmapA", ++ "status": "active" ++ } ++ ] ++ } ++} ++ ++--- Submitting & Aborting Transaction --- ++ ++{ ++ "execute": "transaction", ++ "arguments": { ++ "actions": [ ++ { ++ "data": { ++ "node": "drive0", ++ "name": "bitmapB" ++ }, ++ "type": "block-dirty-bitmap-disable" ++ }, ++ { ++ "data": { ++ "node": "drive0", ++ "name": "bitmapC", ++ "granularity": 65536 ++ }, ++ "type": "block-dirty-bitmap-add" ++ }, ++ { ++ "data": { ++ "node": "drive0", ++ "name": "bitmapA" ++ }, ++ "type": "block-dirty-bitmap-clear" ++ }, ++ { ++ "data": {}, ++ "type": "abort" ++ } ++ ] ++ } ++} ++{ ++ "error": { ++ "class": "GenericError", ++ "desc": "Transaction aborted using Abort action" ++ } ++} ++{ ++ "bitmaps": { ++ "drive0": [ ++ { ++ "count": 262144, ++ "granularity": 65536, ++ "name": "bitmapB", ++ "status": "active" ++ }, ++ { ++ "count": 262144, ++ "granularity": 65536, ++ "name": "bitmapA", ++ "status": "active" ++ } ++ ] ++ } ++} ++ ++--- Disabling B & Adding C --- ++ ++{ ++ "execute": "transaction", ++ "arguments": { ++ "actions": [ ++ { ++ "data": { ++ "node": "drive0", ++ "name": "bitmapB" ++ }, ++ "type": "block-dirty-bitmap-disable" ++ }, ++ { ++ "data": { ++ "node": "drive0", ++ "name": "bitmapC", ++ "granularity": 65536 ++ }, ++ "type": "block-dirty-bitmap-add" ++ }, ++ { ++ "data": { ++ "node": "drive0", ++ "name": "bitmapC" ++ }, ++ "type": "block-dirty-bitmap-disable" ++ }, ++ { ++ "data": { ++ "node": "drive0", ++ "name": "bitmapC" ++ }, ++ "type": "block-dirty-bitmap-enable" ++ } ++ ] ++ } ++} ++{ ++ "return": {} ++} ++ ++--- Emulating further writes --- ++ ++write -P0xab 0 64k ++{"return": ""} ++write -P0xad 0x00f8000 64k ++{"return": ""} ++write -P0x1d 0x2008000 64k ++{"return": ""} ++write -P0xea 0x3fe0000 64k ++{"return": ""} ++ ++--- Disabling A & C --- ++ ++{ ++ "execute": "transaction", ++ "arguments": { ++ "actions": [ ++ { ++ "data": { ++ "node": "drive0", ++ "name": "bitmapA" ++ }, ++ "type": "block-dirty-bitmap-disable" ++ }, ++ { ++ "data": { ++ "node": "drive0", ++ "name": "bitmapC" ++ }, ++ "type": "block-dirty-bitmap-disable" ++ } ++ ] ++ } ++} ++{ ++ "return": {} ++} ++{ ++ "bitmaps": { ++ "drive0": [ ++ { ++ "count": 393216, ++ "granularity": 65536, ++ "name": "bitmapC", ++ "status": "disabled" ++ }, ++ { ++ "count": 262144, ++ "granularity": 65536, ++ "name": "bitmapB", ++ "status": "disabled" ++ }, ++ { ++ "count": 458752, ++ "granularity": 65536, ++ "name": "bitmapA", ++ "status": "disabled" ++ } ++ ] ++ } ++} ++ ++--- Submitting & Aborting Merge Transaction --- ++ ++{ ++ "execute": "transaction", ++ "arguments": { ++ "actions": [ ++ { ++ "data": { ++ "node": "drive0", ++ "disabled": true, ++ "name": "bitmapD", ++ "granularity": 65536 ++ }, ++ "type": "block-dirty-bitmap-add" ++ }, ++ { ++ "data": { ++ "node": "drive0", ++ "target": "bitmapD", ++ "bitmaps": [ ++ "bitmapB", ++ "bitmapC" ++ ] ++ }, ++ "type": "block-dirty-bitmap-merge" ++ }, ++ { ++ "data": {}, ++ "type": "abort" ++ } ++ ] ++ } ++} ++{ ++ "error": { ++ "class": "GenericError", ++ "desc": "Transaction aborted using Abort action" ++ } ++} ++{ ++ "bitmaps": { ++ "drive0": [ ++ { ++ "count": 393216, ++ "granularity": 65536, ++ "name": "bitmapC", ++ "status": "disabled" ++ }, ++ { ++ "count": 262144, ++ "granularity": 65536, ++ "name": "bitmapB", ++ "status": "disabled" ++ }, ++ { ++ "count": 458752, ++ "granularity": 65536, ++ "name": "bitmapA", ++ "status": "disabled" ++ } ++ ] ++ } ++} ++ ++--- Creating D as a merge of B & C --- ++ ++{ ++ "execute": "transaction", ++ "arguments": { ++ "actions": [ ++ { ++ "data": { ++ "node": "drive0", ++ "disabled": true, ++ "name": "bitmapD", ++ "granularity": 65536 ++ }, ++ "type": "block-dirty-bitmap-add" ++ }, ++ { ++ "data": { ++ "node": "drive0", ++ "target": "bitmapD", ++ "bitmaps": [ ++ "bitmapB", ++ "bitmapC" ++ ] ++ }, ++ "type": "block-dirty-bitmap-merge" ++ } ++ ] ++ } ++} ++{ ++ "return": {} ++} ++{ ++ "bitmaps": { ++ "drive0": [ ++ { ++ "count": 458752, ++ "granularity": 65536, ++ "name": "bitmapD", ++ "status": "disabled" ++ }, ++ { ++ "count": 393216, ++ "granularity": 65536, ++ "name": "bitmapC", ++ "status": "disabled" ++ }, ++ { ++ "count": 262144, ++ "granularity": 65536, ++ "name": "bitmapB", ++ "status": "disabled" ++ }, ++ { ++ "count": 458752, ++ "granularity": 65536, ++ "name": "bitmapA", ++ "status": "disabled" ++ } ++ ] ++ } ++} ++ ++--- Removing bitmaps A, B, C, and D --- ++ ++{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmapA", "node": "drive0"}} ++{"return": {}} ++{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmapB", "node": "drive0"}} ++{"return": {}} ++{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmapC", "node": "drive0"}} ++{"return": {}} ++{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmapD", "node": "drive0"}} ++{"return": {}} ++ ++--- Final Query --- ++ ++{ ++ "bitmaps": { ++ "drive0": [] ++ } ++} ++ ++--- Done --- ++ +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index 0998dcd..8662839 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -227,3 +227,4 @@ + 231 auto quick + 232 auto quick + 234 auto quick migration ++236 auto quick +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-add-qmp-recursive-sorting-function.patch b/SOURCES/kvm-iotests-add-qmp-recursive-sorting-function.patch new file mode 100644 index 0000000..a98ebd7 --- /dev/null +++ b/SOURCES/kvm-iotests-add-qmp-recursive-sorting-function.patch @@ -0,0 +1,110 @@ +From c455d2804d7ef6c379b815b6787baf69d3cb760a Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 16:16:20 +0100 +Subject: [PATCH 022/163] iotests: add qmp recursive sorting function + +RH-Author: John Snow +Message-id: <20190320161631.14841-9-jsnow@redhat.com> +Patchwork-id: 84944 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 08/19] iotests: add qmp recursive sorting function +Bugzilla: 1668956 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +Python before 3.6 does not sort dictionaries (including kwargs). +Therefore, printing QMP objects involves sorting the keys to have +a predictable ordering in the iotests output. This means that +iotests output will sometimes show arguments in an order not +specified by the test author. + +Presently, we accomplish this by using json.dumps' sort_keys argument, +where we only serialize the arguments dictionary, but not the command. + +However, if we want to pretty-print QMP objects being sent to the +QEMU process, we need to build the entire command before logging it. +Ordinarily, this would then involve "arguments" being sorted above +"execute", which would necessitate a rather ugly and harder-to-read +change to many iotests outputs. + +To facilitate pretty-printing AND maintaining predictable output AND +having "arguments" sort after "execute", add a custom sort function +that takes a dictionary and recursively builds an OrderedDict that +maintains the specific key order we wish to see in iotests output. + +The qmp_log function uses this to build a QMP object that keeps +"execute" above "arguments", but sorts all keys and keys in any +subdicts in "arguments" lexicographically to maintain consistent +iotests output, with no incompatible changes to any current test. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20181221093529.23855-8-jsnow@redhat.com> +Signed-off-by: Eric Blake +(cherry picked from commit 0706e87d72b02f28bfa04400388f9c9df1b9c943) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/iotests.py | 24 ++++++++++++++++++++---- + 1 file changed, 20 insertions(+), 4 deletions(-) + +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index 5334ff4..bd508be 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -28,6 +28,7 @@ import json + import signal + import logging + import atexit ++from collections import OrderedDict + + sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts')) + import qtest +@@ -73,6 +74,16 @@ def qemu_img(*args): + sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args)))) + return exitcode + ++def ordered_kwargs(kwargs): ++ # kwargs prior to 3.6 are not ordered, so: ++ od = OrderedDict() ++ for k, v in sorted(kwargs.items()): ++ if isinstance(v, dict): ++ od[k] = ordered_kwargs(v) ++ else: ++ od[k] = v ++ return od ++ + def qemu_img_create(*args): + args = list(args) + +@@ -251,8 +262,10 @@ def filter_img_info(output, filename): + def log(msg, filters=[]): + for flt in filters: + msg = flt(msg) +- if type(msg) is dict or type(msg) is list: +- print(json.dumps(msg, sort_keys=True)) ++ if isinstance(msg, dict) or isinstance(msg, list): ++ # Don't sort if it's already sorted ++ do_sort = not isinstance(msg, OrderedDict) ++ print(json.dumps(msg, sort_keys=do_sort)) + else: + print(msg) + +@@ -442,8 +455,11 @@ class VM(qtest.QEMUQtestMachine): + return result + + def qmp_log(self, cmd, filters=[filter_testfiles], **kwargs): +- logmsg = '{"execute": "%s", "arguments": %s}' % \ +- (cmd, json.dumps(kwargs, sort_keys=True)) ++ full_cmd = OrderedDict(( ++ ("execute", cmd), ++ ("arguments", ordered_kwargs(kwargs)) ++ )) ++ logmsg = json.dumps(full_cmd) + log(logmsg, filters) + result = self.qmp(cmd, **kwargs) + log(json.dumps(result, sort_keys=True), filters) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-add-test-226-for-file-driver-types.patch b/SOURCES/kvm-iotests-add-test-226-for-file-driver-types.patch new file mode 100644 index 0000000..2fb536b --- /dev/null +++ b/SOURCES/kvm-iotests-add-test-226-for-file-driver-types.patch @@ -0,0 +1,150 @@ +From 60808b0c672add535f7381785faa6e5609fb20ce Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 13 Jul 2018 14:50:02 +0200 +Subject: [PATCH 44/89] iotests: add test 226 for file driver types + +RH-Author: Kevin Wolf +Message-id: <20180713145002.20953-3-kwolf@redhat.com> +Patchwork-id: 81351 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 2/2] iotests: add test 226 for file driver types +Bugzilla: 1525829 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +From: John Snow + +Test that we're rejecting what we ought to for file, +host_driver and host_cdrom drivers. Test that we're +seeing the deprecated message for block and chardevs +on the file driver. + +Signed-off-by: John Snow +Signed-off-by: Kevin Wolf +(cherry picked from commit 2d4cb49ddaa219ed3f5a985ecf8b188aeb2b3d6b) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/226 | 66 ++++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/226.out | 26 ++++++++++++++++++ + tests/qemu-iotests/group | 1 + + 3 files changed, 93 insertions(+) + create mode 100755 tests/qemu-iotests/226 + create mode 100644 tests/qemu-iotests/226.out + +diff --git a/tests/qemu-iotests/226 b/tests/qemu-iotests/226 +new file mode 100755 +index 0000000..460aea2 +--- /dev/null ++++ b/tests/qemu-iotests/226 +@@ -0,0 +1,66 @@ ++#!/bin/bash ++# ++# This test covers expected filetypes for the file, host_cdrom and ++# host_device drivers. ++# ++# Copyright (C) 2018 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 . ++# ++ ++# creator ++owner=jsnow@redhat.com ++ ++seq=`basename $0` ++echo "QA output created by $seq" ++ ++here=`pwd` ++status=1 # failure is the default! ++ ++_cleanup() ++{ ++ rmdir "$TEST_IMG" ++} ++trap "_cleanup; exit \$status" 0 1 2 3 15 ++ ++# get standard environment, filters and checks ++. ./common.rc ++. ./common.filter ++. ./common.pattern ++ ++# Generic format, but tests file-protocol specific error handling ++_supported_fmt generic ++_supported_proto file ++_supported_os Linux ++ ++# Create something decidedly not a file, blockdev or chardev... ++mkdir "$TEST_IMG" ++ ++for PROTO in "file" "host_device" "host_cdrom"; do ++ echo ++ echo "=== Testing with driver:$PROTO ===" ++ echo ++ echo "== Testing RO ==" ++ $QEMU_IO -c "open -r -o driver=$PROTO,filename=$TEST_IMG" 2>&1 | _filter_imgfmt | _filter_testdir ++ $QEMU_IO -c "open -r -o driver=$PROTO,filename=/dev/null" 2>&1 | _filter_imgfmt ++ echo "== Testing RW ==" ++ $QEMU_IO -c "open -o driver=$PROTO,filename=$TEST_IMG" 2>&1 | _filter_imgfmt | _filter_testdir ++ $QEMU_IO -c "open -o driver=$PROTO,filename=/dev/null" 2>&1 | _filter_imgfmt ++done ++ ++# success, all done ++echo ++echo "*** done" ++rm -f $seq.full ++status=0 +diff --git a/tests/qemu-iotests/226.out b/tests/qemu-iotests/226.out +new file mode 100644 +index 0000000..8c0d060 +--- /dev/null ++++ b/tests/qemu-iotests/226.out +@@ -0,0 +1,26 @@ ++QA output created by 226 ++ ++=== Testing with driver:file === ++ ++== Testing RO == ++can't open: A regular file was expected by the 'file' driver, but something else was given ++warning: Opening a character device as a file using the 'file' driver is deprecated ++== Testing RW == ++can't open: Could not open 'TEST_DIR/t.IMGFMT': Is a directory ++warning: Opening a character device as a file using the 'file' driver is deprecated ++ ++=== Testing with driver:host_device === ++ ++== Testing RO == ++can't open: 'host_device' driver expects either a character or block device ++== Testing RW == ++can't open: Could not open 'TEST_DIR/t.IMGFMT': Is a directory ++ ++=== Testing with driver:host_cdrom === ++ ++== Testing RO == ++can't open: 'host_cdrom' driver expects either a character or block device ++== Testing RW == ++can't open: Could not open 'TEST_DIR/t.IMGFMT': Is a directory ++ ++*** done +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index 5c55adc..11498fd 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -218,3 +218,4 @@ + 217 rw auto quick + 218 rw auto quick + 219 rw auto ++226 auto quick +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-avoid-broken-pipe-with-certtool.patch b/SOURCES/kvm-iotests-avoid-broken-pipe-with-certtool.patch new file mode 100644 index 0000000..739b88a --- /dev/null +++ b/SOURCES/kvm-iotests-avoid-broken-pipe-with-certtool.patch @@ -0,0 +1,126 @@ +From f4ab0fe995c89692656af2c792a66892fcfbb862 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:23:06 +0100 +Subject: [PATCH 128/163] iotests: avoid broken pipe with certtool +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190327172308.31077-54-jsnow@redhat.com> +Patchwork-id: 85220 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 53/55] iotests: avoid broken pipe with certtool +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Daniel P. Berrangé + +When we run "certtool 2>&1 | head -1" the latter command is likely to +complete and exit before certtool has written everything it wants to +stderr. In at least the RHEL-7 gnutls 3.3.29 this causes certtool to +quit with broken pipe before it has finished writing the desired +output file to disk. This causes non-deterministic failures of the +iotest 233 because the certs are sometimes zero length files. +If certtool fails the "head -1" means we also lose any useful error +message it would have printed. + +Thus this patch gets rid of the pipe and post-processes the output in a +more flexible & reliable manner. + +Reported-by: Thomas Huth +Signed-off-by: Daniel P. Berrangé +Message-Id: <20190220145819.30969-3-berrange@redhat.com> +Reviewed-by: Eric Blake +Signed-off-by: Eric Blake +(cherry picked from commit 3e6f45446b11ccc20b4b751f70331f03d70369b8) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/common.tls | 48 ++++++++++++++++++++++++++++--------------- + 1 file changed, 32 insertions(+), 16 deletions(-) + +diff --git a/tests/qemu-iotests/common.tls b/tests/qemu-iotests/common.tls +index eae8178..3caf989 100644 +--- a/tests/qemu-iotests/common.tls ++++ b/tests/qemu-iotests/common.tls +@@ -29,6 +29,17 @@ tls_x509_cleanup() + } + + ++tls_certtool() ++{ ++ certtool "$@" 1>"${tls_dir}"/certtool.log 2>&1 ++ if test "$?" = 0; then ++ head -1 "${tls_dir}"/certtool.log ++ else ++ cat "${tls_dir}"/certtool.log ++ fi ++ rm -f "${tls_dir}"/certtool.log ++} ++ + tls_x509_init() + { + (certtool --help) >/dev/null 2>&1 || \ +@@ -71,10 +82,11 @@ ca + cert_signing_key + EOF + +- certtool --generate-self-signed \ +- --load-privkey "${tls_dir}/key.pem" \ +- --template "${tls_dir}/ca.info" \ +- --outfile "${tls_dir}/$name-cert.pem" 2>&1 | head -1 ++ tls_certtool \ ++ --generate-self-signed \ ++ --load-privkey "${tls_dir}/key.pem" \ ++ --template "${tls_dir}/ca.info" \ ++ --outfile "${tls_dir}/$name-cert.pem" + + rm -f "${tls_dir}/ca.info" + } +@@ -98,12 +110,14 @@ encryption_key + signing_key + EOF + +- certtool --generate-certificate \ +- --load-ca-privkey "${tls_dir}/key.pem" \ +- --load-ca-certificate "${tls_dir}/$caname-cert.pem" \ +- --load-privkey "${tls_dir}/key.pem" \ +- --template "${tls_dir}/cert.info" \ +- --outfile "${tls_dir}/$name/server-cert.pem" 2>&1 | head -1 ++ tls_certtool \ ++ --generate-certificate \ ++ --load-ca-privkey "${tls_dir}/key.pem" \ ++ --load-ca-certificate "${tls_dir}/$caname-cert.pem" \ ++ --load-privkey "${tls_dir}/key.pem" \ ++ --template "${tls_dir}/cert.info" \ ++ --outfile "${tls_dir}/$name/server-cert.pem" ++ + ln -s "${tls_dir}/$caname-cert.pem" "${tls_dir}/$name/ca-cert.pem" + ln -s "${tls_dir}/key.pem" "${tls_dir}/$name/server-key.pem" + +@@ -127,12 +141,14 @@ encryption_key + signing_key + EOF + +- certtool --generate-certificate \ +- --load-ca-privkey "${tls_dir}/key.pem" \ +- --load-ca-certificate "${tls_dir}/$caname-cert.pem" \ +- --load-privkey "${tls_dir}/key.pem" \ +- --template "${tls_dir}/cert.info" \ +- --outfile "${tls_dir}/$name/client-cert.pem" 2>&1 | head -1 ++ tls_certtool \ ++ --generate-certificate \ ++ --load-ca-privkey "${tls_dir}/key.pem" \ ++ --load-ca-certificate "${tls_dir}/$caname-cert.pem" \ ++ --load-privkey "${tls_dir}/key.pem" \ ++ --template "${tls_dir}/cert.info" \ ++ --outfile "${tls_dir}/$name/client-cert.pem" ++ + ln -s "${tls_dir}/$caname-cert.pem" "${tls_dir}/$name/ca-cert.pem" + ln -s "${tls_dir}/key.pem" "${tls_dir}/$name/client-key.pem" + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-change-qmp_log-filters-to-expect-QMP-objects.patch b/SOURCES/kvm-iotests-change-qmp_log-filters-to-expect-QMP-objects.patch new file mode 100644 index 0000000..20536f8 --- /dev/null +++ b/SOURCES/kvm-iotests-change-qmp_log-filters-to-expect-QMP-objects.patch @@ -0,0 +1,128 @@ +From 2a6c4e6212c3f342132a2aca22c84bbf886dc79b Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 16:16:22 +0100 +Subject: [PATCH 024/163] iotests: change qmp_log filters to expect QMP objects + only + +RH-Author: John Snow +Message-id: <20190320161631.14841-11-jsnow@redhat.com> +Patchwork-id: 84946 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 10/19] iotests: change qmp_log filters to expect QMP objects only +Bugzilla: 1668956 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +As laid out in the previous commit's message: + +``` +Several places in iotests deal with serializing objects into JSON +strings, but to add pretty-printing it seems desirable to localize +all of those cases. + +log() seems like a good candidate for that centralized behavior. +log() can already serialize json objects, but when it does so, +it assumes filters=[] operates on QMP objects, not strings. + +qmp_log currently operates by dumping outgoing and incoming QMP +objects into strings and filtering them assuming that filters=[] +are string filters. +``` + +Therefore: + +Change qmp_log to treat filters as if they're always qmp object filters, +then change the logging call to rely on log()'s ability to serialize QMP +objects, so we're not duplicating that effort. + +Add a qmp version of filter_testfiles and adjust the only caller using +it for qmp_log to use the qmp version. + +Signed-off-by: John Snow +Message-Id: <20181221093529.23855-10-jsnow@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +Signed-off-by: Eric Blake +(cherry picked from commit 08fcd6111e1949f456e1b232ebeeb0cc17019a92) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/206 | 4 ++-- + tests/qemu-iotests/iotests.py | 28 +++++++++++++++++++++++++--- + 2 files changed, 27 insertions(+), 5 deletions(-) + +diff --git a/tests/qemu-iotests/206 b/tests/qemu-iotests/206 +index e92550f..5bb738b 100755 +--- a/tests/qemu-iotests/206 ++++ b/tests/qemu-iotests/206 +@@ -27,7 +27,7 @@ iotests.verify_image_format(supported_fmts=['qcow2']) + + def blockdev_create(vm, options): + result = vm.qmp_log('blockdev-create', +- filters=[iotests.filter_testfiles], ++ filters=[iotests.filter_qmp_testfiles], + job_id='job0', options=options) + + if 'return' in result: +@@ -55,7 +55,7 @@ with iotests.FilePath('t.qcow2') as disk_path, \ + 'size': 0 }) + + vm.qmp_log('blockdev-add', +- filters=[iotests.filter_testfiles], ++ filters=[iotests.filter_qmp_testfiles], + driver='file', filename=disk_path, + node_name='imgfile') + +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index 30d198a..d2a8fbd 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -240,10 +240,33 @@ def filter_qmp_event(event): + event['timestamp']['microseconds'] = 'USECS' + return event + ++def filter_qmp(qmsg, filter_fn): ++ '''Given a string filter, filter a QMP object's values. ++ filter_fn takes a (key, value) pair.''' ++ # Iterate through either lists or dicts; ++ if isinstance(qmsg, list): ++ items = enumerate(qmsg) ++ else: ++ items = qmsg.items() ++ ++ for k, v in items: ++ if isinstance(v, list) or isinstance(v, dict): ++ qmsg[k] = filter_qmp(v, filter_fn) ++ else: ++ qmsg[k] = filter_fn(k, v) ++ return qmsg ++ + def filter_testfiles(msg): + prefix = os.path.join(test_dir, "%s-" % (os.getpid())) + return msg.replace(prefix, 'TEST_DIR/PID-') + ++def filter_qmp_testfiles(qmsg): ++ def _filter(key, value): ++ if key == 'filename' or key == 'backing-file': ++ return filter_testfiles(value) ++ return value ++ return filter_qmp(qmsg, _filter) ++ + def filter_generated_node_ids(msg): + return re.sub("#block[0-9]+", "NODE_NAME", msg) + +@@ -459,10 +482,9 @@ class VM(qtest.QEMUQtestMachine): + ("execute", cmd), + ("arguments", ordered_kwargs(kwargs)) + )) +- logmsg = json.dumps(full_cmd) +- log(logmsg, filters) ++ log(full_cmd, filters) + result = self.qmp(cmd, **kwargs) +- log(json.dumps(result, sort_keys=True), filters) ++ log(result, filters) + return result + + def run_job(self, job, auto_finalize=True, auto_dismiss=False): +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-ensure-we-print-nbd-server-log-on-error.patch b/SOURCES/kvm-iotests-ensure-we-print-nbd-server-log-on-error.patch new file mode 100644 index 0000000..3e14c99 --- /dev/null +++ b/SOURCES/kvm-iotests-ensure-we-print-nbd-server-log-on-error.patch @@ -0,0 +1,60 @@ +From c5f12193572b8d67f30a7a2b4e33ff6eef97558d Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:23:05 +0100 +Subject: [PATCH 127/163] iotests: ensure we print nbd server log on error +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190327172308.31077-53-jsnow@redhat.com> +Patchwork-id: 85222 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 52/55] iotests: ensure we print nbd server log on error +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Daniel P. Berrangé + +If we abort the iotest early the server.log file might contain useful +information for diagnosing the problem. Ensure its contents are +displayed in this case. + +Reviewed-by: Eric Blake +Signed-off-by: Daniel P. Berrangé +Message-Id: <20190220145819.30969-2-berrange@redhat.com> +[eblake: fix shell quoting] +Signed-off-by: Eric Blake +(cherry picked from commit 84f8b840a2d9ed248c80b3601d2d212cdf60cecf) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/233 | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/tests/qemu-iotests/233 b/tests/qemu-iotests/233 +index fc345a1..adb742f 100755 +--- a/tests/qemu-iotests/233 ++++ b/tests/qemu-iotests/233 +@@ -30,6 +30,8 @@ _cleanup() + { + nbd_server_stop + _cleanup_test_img ++ # If we aborted early we want to see this log for diagnosis ++ test -f "$TEST_DIR/server.log" && cat "$TEST_DIR/server.log" + rm -f "$TEST_DIR/server.log" + tls_x509_cleanup + } +@@ -120,6 +122,7 @@ $QEMU_IO -f $IMGFMT -r -U -c 'r -P 0x22 1m 1m' "$TEST_IMG" | _filter_qemu_io + echo + echo "== final server log ==" + cat "$TEST_DIR/server.log" ++rm -f "$TEST_DIR/server.log" + + # success, all done + echo "*** done" +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-fix-nbd-test-233-to-work-correctly-with-raw-.patch b/SOURCES/kvm-iotests-fix-nbd-test-233-to-work-correctly-with-raw-.patch new file mode 100644 index 0000000..436fee8 --- /dev/null +++ b/SOURCES/kvm-iotests-fix-nbd-test-233-to-work-correctly-with-raw-.patch @@ -0,0 +1,71 @@ +From e33997285c334322f45b36900f63786ba4de8cc9 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:41 +0100 +Subject: [PATCH 074/163] iotests: fix nbd test 233 to work correctly with raw + images +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190322032241.8111-29-jsnow@redhat.com> +Patchwork-id: 85115 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 28/28] iotests: fix nbd test 233 to work correctly with raw images +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Daniel P. Berrangé + +The first qemu-io command must honour the $IMGFMT that is set rather +than hardcoding qcow2. The qemu-nbd commands should also set $IMGFMT +to avoid the insecure format probe warning. + +Signed-off-by: Daniel P. Berrangé +Reviewed-by: Eric Blake +Signed-off-by: Kevin Wolf +(cherry picked from commit e4c8f2925d22584b2008aadea5c70e1e05c2a522) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/233 | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/tests/qemu-iotests/233 b/tests/qemu-iotests/233 +index a4da60d..1814efe 100755 +--- a/tests/qemu-iotests/233 ++++ b/tests/qemu-iotests/233 +@@ -66,7 +66,7 @@ $QEMU_IO -c 'w -P 0x11 1m 1m' "$TEST_IMG" | _filter_qemu_io + + echo + echo "== check TLS client to plain server fails ==" +-nbd_server_start_tcp_socket "$TEST_IMG" ++nbd_server_start_tcp_socket -f $IMGFMT "$TEST_IMG" + + $QEMU_IMG info --image-opts \ + --object tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 \ +@@ -78,7 +78,10 @@ nbd_server_stop + echo + echo "== check plain client to TLS server fails ==" + +-nbd_server_start_tcp_socket --object tls-creds-x509,dir=${tls_dir}/server1,endpoint=server,id=tls0,verify-peer=yes --tls-creds tls0 "$TEST_IMG" ++nbd_server_start_tcp_socket \ ++ --object tls-creds-x509,dir=${tls_dir}/server1,endpoint=server,id=tls0,verify-peer=yes \ ++ --tls-creds tls0 \ ++ -f $IMGFMT "$TEST_IMG" + + $QEMU_IMG info nbd://localhost:$nbd_tcp_port 2>&1 | sed "s/$nbd_tcp_port/PORT/g" + +@@ -104,7 +107,7 @@ $QEMU_IO -c 'r -P 0x11 1m 1m' -c 'w -P 0x22 1m 1m' --image-opts \ + driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ + 2>&1 | _filter_qemu_io + +-$QEMU_IO -f qcow2 -r -U -c 'r -P 0x22 1m 1m' "$TEST_IMG" | _filter_qemu_io ++$QEMU_IO -f $IMGFMT -r -U -c 'r -P 0x22 1m 1m' "$TEST_IMG" | _filter_qemu_io + + # success, all done + echo "*** done" +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-implement-pretty-print-for-log-and-qmp_log.patch b/SOURCES/kvm-iotests-implement-pretty-print-for-log-and-qmp_log.patch new file mode 100644 index 0000000..f7f48cd --- /dev/null +++ b/SOURCES/kvm-iotests-implement-pretty-print-for-log-and-qmp_log.patch @@ -0,0 +1,79 @@ +From ec929cb5c98bd8148bff965f80cd0472348199ba Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 16:16:23 +0100 +Subject: [PATCH 025/163] iotests: implement pretty-print for log and qmp_log + +RH-Author: John Snow +Message-id: <20190320161631.14841-12-jsnow@redhat.com> +Patchwork-id: 84952 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 11/19] iotests: implement pretty-print for log and qmp_log +Bugzilla: 1668956 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +If iotests have lines exceeding >998 characters long, git doesn't +want to send it plaintext to the list. We can solve this by allowing +the iotests to use pretty printed QMP output that we can match against +instead. + +As a bonus, it's much nicer for human eyes too. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20181221093529.23855-11-jsnow@redhat.com> +Signed-off-by: Eric Blake +(cherry picked from commit 55cd64eab5cb7958c629edbf5f2233b87dfbd1b0) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/iotests.py | 15 ++++++++++----- + 1 file changed, 10 insertions(+), 5 deletions(-) + +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index d2a8fbd..d178469 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -282,13 +282,18 @@ def filter_img_info(output, filename): + lines.append(line) + return '\n'.join(lines) + +-def log(msg, filters=[]): ++def log(msg, filters=[], indent=None): ++ '''Logs either a string message or a JSON serializable message (like QMP). ++ If indent is provided, JSON serializable messages are pretty-printed.''' + for flt in filters: + msg = flt(msg) + if isinstance(msg, dict) or isinstance(msg, list): ++ # Python < 3.4 needs to know not to add whitespace when pretty-printing: ++ separators = (', ', ': ') if indent is None else (',', ': ') + # Don't sort if it's already sorted + do_sort = not isinstance(msg, OrderedDict) +- print(json.dumps(msg, sort_keys=do_sort)) ++ print(json.dumps(msg, sort_keys=do_sort, ++ indent=indent, separators=separators)) + else: + print(msg) + +@@ -477,14 +482,14 @@ class VM(qtest.QEMUQtestMachine): + result.append(filter_qmp_event(ev)) + return result + +- def qmp_log(self, cmd, filters=[], **kwargs): ++ def qmp_log(self, cmd, filters=[], indent=None, **kwargs): + full_cmd = OrderedDict(( + ("execute", cmd), + ("arguments", ordered_kwargs(kwargs)) + )) +- log(full_cmd, filters) ++ log(full_cmd, filters, indent=indent) + result = self.qmp(cmd, **kwargs) +- log(result, filters) ++ log(result, filters, indent=indent) + return result + + def run_job(self, job, auto_finalize=True, auto_dismiss=False): +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-improve-169.patch b/SOURCES/kvm-iotests-improve-169.patch new file mode 100644 index 0000000..9b67d86 --- /dev/null +++ b/SOURCES/kvm-iotests-improve-169.patch @@ -0,0 +1,70 @@ +From 2002520b4c5e6ca74367f9db22a53d5a463b9de2 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:42 +0100 +Subject: [PATCH 32/33] iotests: improve 169 + +RH-Author: John Snow +Message-id: <20190206221243.7407-23-jsnow@redhat.com> +Patchwork-id: 84281 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 22/23] iotests: improve 169 +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +From: Vladimir Sementsov-Ogievskiy + +Before previous patch, iotest 169 was actually broken for the case +test_persistent__not_migbitmap__offline_shared, while formally +passing. + +After migration log of vm_b had message: + + qemu-system-x86_64: Could not reopen qcow2 layer: Bitmap already + exists: bitmap0 + +which means that invalidation failed and bs->drv = NULL. + +It was because we've loaded bitmap twice: on open and on invalidation. + +Add code to 169, to catch such fails. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Signed-off-by: John Snow +(cherry picked from commit b9247fc1a8ffe5c367fa049f295fbb58c8ca9d05) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/169 | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/tests/qemu-iotests/169 b/tests/qemu-iotests/169 +index df408f8..8b7947d 100755 +--- a/tests/qemu-iotests/169 ++++ b/tests/qemu-iotests/169 +@@ -24,6 +24,7 @@ import time + import itertools + import operator + import new ++import re + from iotests import qemu_img + + +@@ -133,6 +134,14 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): + + if should_migrate: + self.vm_b.shutdown() ++ ++ # catch 'Could not reopen qcow2 layer: Bitmap already exists' ++ # possible error ++ log = self.vm_b.get_log() ++ log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) ++ log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) ++ self.assertEqual(log, '') ++ + # recreate vm_b, as we don't want -incoming option (this will lead + # to "cat" process left alive after test finish) + self.vm_b = iotests.VM(path_suffix='b') +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-improve-pause_job.patch b/SOURCES/kvm-iotests-improve-pause_job.patch new file mode 100644 index 0000000..481172f --- /dev/null +++ b/SOURCES/kvm-iotests-improve-pause_job.patch @@ -0,0 +1,55 @@ +From 438c9fb5fd117816c960ad2995e53a627d2df4c5 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:56 +0200 +Subject: [PATCH 87/89] iotests: improve pause_job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-74-kwolf@redhat.com> +Patchwork-id: 81118 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 73/73] iotests: improve pause_job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +It's possible, that job was finished during waiting. In this case we +will see error message "Timeout waiting for job to pause" which is not +very informative. So, let's check during waiting iteration that the job +exists. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-id: 20180601115923.17159-1-vsementsov@virtuozzo.com +Signed-off-by: Max Reitz +(cherry picked from commit c1bac161bb7ad27243776e90971c51cc38c2e1b6) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/iotests.py | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index 0b204dc..2f22fab 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -590,9 +590,14 @@ class QMPTestCase(unittest.TestCase): + with Timeout(1, "Timeout waiting for job to pause"): + while True: + result = self.vm.qmp('query-block-jobs') ++ found = False + for job in result['return']: +- if job['device'] == job_id and job['paused'] == True and job['busy'] == False: +- return job ++ if job['device'] == job_id: ++ found = True ++ if job['paused'] == True and job['busy'] == False: ++ return job ++ break ++ assert found + + def pause_job(self, job_id='job0', wait=True): + result = self.vm.qmp('block-job-pause', device=job_id) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-make-083-specific-to-raw.patch b/SOURCES/kvm-iotests-make-083-specific-to-raw.patch new file mode 100644 index 0000000..a257aef --- /dev/null +++ b/SOURCES/kvm-iotests-make-083-specific-to-raw.patch @@ -0,0 +1,77 @@ +From aa707f9fe5e43d297d3ebc201087fba331373993 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:16 +0100 +Subject: [PATCH 077/163] iotests: make 083 specific to raw + +RH-Author: John Snow +Message-id: <20190327172308.31077-4-jsnow@redhat.com> +Patchwork-id: 85179 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 03/55] iotests: make 083 specific to raw +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Cleber Rosa + +While testing the Python 3 changes which touch the 083 test, I noticed +that it would fail with qcow2. Expanding the testing, I noticed it +had nothing to do with the Python 3 changes, and in fact, it would not +pass on anything but raw: + + raw: pass + bochs: not generic + cloop: not generic + parallels: fail + qcow: fail + qcow2: fail + qed: fail + vdi: fail + vhdx: fail + vmdk: fail + vpc: fail + luks: fail + +The errors are a mixture I/O and "image not in xxx format", such as: + + === Check disconnect before data === + + Unexpected end-of-file before all bytes were read + -read failed: Input/output error + +can't open device nbd+tcp://127.0.0.1:PORT/foo: Could not open 'nbd://127.0.0.1:PORT/foo': Input/output error + + === Check disconnect after data === + + -read 512/512 bytes at offset 0 + -512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +can't open device nbd+tcp://127.0.0.1:PORT/foo: Image not in qcow format + +I'm not aware if there's a quick fix, so, for the time being, it looks +like the honest approach is to make the test known to work on raw +only. + +Signed-off-by: Cleber Rosa +Signed-off-by: Kevin Wolf +(cherry picked from commit d98205c58667ca6750658834e8466ac41a3e572e) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/083 | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/qemu-iotests/083 b/tests/qemu-iotests/083 +index 982adec..89f67db 100755 +--- a/tests/qemu-iotests/083 ++++ b/tests/qemu-iotests/083 +@@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 + . ./common.rc + . ./common.filter + +-_supported_fmt generic ++_supported_fmt raw + _supported_proto nbd + _supported_os Linux + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-nbd-Stop-qemu-nbd-before-remaking-image.patch b/SOURCES/kvm-iotests-nbd-Stop-qemu-nbd-before-remaking-image.patch new file mode 100644 index 0000000..c137382 --- /dev/null +++ b/SOURCES/kvm-iotests-nbd-Stop-qemu-nbd-before-remaking-image.patch @@ -0,0 +1,90 @@ +From 1e5bf346765701c7301d3f86311b339288261558 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:18 +0100 +Subject: [PATCH 051/163] iotests: nbd: Stop qemu-nbd before remaking image + +RH-Author: John Snow +Message-id: <20190322032241.8111-6-jsnow@redhat.com> +Patchwork-id: 85093 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 05/28] iotests: nbd: Stop qemu-nbd before remaking image +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Fam Zheng + +197 is one example where _make_test_img is used twice without stopping +the NBD server in between. An error will occur like this: + + @@ -26,9 +26,13 @@ + + === Partial final cluster === + + +qemu-img: TEST_DIR/t.IMGFMT: Failed to get "resize" lock + +Is another process using the image? + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1024 + +Failed to find an available port: Address already in use + read 1024/1024 bytes at offset 0 + +Patch _make_test_img to stop the old qemu-nbd before starting a new one, +which fixes this problem, and similarly 215. + +Signed-off-by: Fam Zheng +Signed-off-by: Kevin Wolf +(cherry picked from commit 2f9d4083f7fdafe82138e983a24ef30b81b029d7) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/common.rc | 21 +++++++++++++++------ + 1 file changed, 15 insertions(+), 6 deletions(-) + +diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc +index 9a65a11..1e567b0 100644 +--- a/tests/qemu-iotests/common.rc ++++ b/tests/qemu-iotests/common.rc +@@ -190,6 +190,16 @@ _use_sample_img() + fi + } + ++_stop_nbd_server() ++{ ++ if [ -f "${QEMU_TEST_DIR}/qemu-nbd.pid" ]; then ++ local QEMU_NBD_PID ++ read QEMU_NBD_PID < "${QEMU_TEST_DIR}/qemu-nbd.pid" ++ kill ${QEMU_NBD_PID} ++ rm -f "${QEMU_TEST_DIR}/qemu-nbd.pid" ++ fi ++} ++ + _make_test_img() + { + # extra qemu-img options can be added by tests +@@ -229,6 +239,10 @@ _make_test_img() + extra_img_options="-o $optstr $extra_img_options" + fi + ++ if [ $IMGPROTO = "nbd" ]; then ++ _stop_nbd_server ++ fi ++ + # XXX(hch): have global image options? + ( + if [ $use_backing = 1 ]; then +@@ -269,12 +283,7 @@ _cleanup_test_img() + case "$IMGPROTO" in + + nbd) +- if [ -f "${QEMU_TEST_DIR}/qemu-nbd.pid" ]; then +- local QEMU_NBD_PID +- read QEMU_NBD_PID < "${QEMU_TEST_DIR}/qemu-nbd.pid" +- kill ${QEMU_NBD_PID} +- rm -f "${QEMU_TEST_DIR}/qemu-nbd.pid" +- fi ++ _stop_nbd_server + rm -f "$TEST_IMG_FILE" + ;; + vxhs) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests-remove-default-filters-from-qmp_log.patch b/SOURCES/kvm-iotests-remove-default-filters-from-qmp_log.patch new file mode 100644 index 0000000..658bd8d --- /dev/null +++ b/SOURCES/kvm-iotests-remove-default-filters-from-qmp_log.patch @@ -0,0 +1,92 @@ +From ca5abb0f6123f6699f84125c7c358e29c5cce2a7 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 16:16:21 +0100 +Subject: [PATCH 023/163] iotests: remove default filters from qmp_log + +RH-Author: John Snow +Message-id: <20190320161631.14841-10-jsnow@redhat.com> +Patchwork-id: 84942 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 09/19] iotests: remove default filters from qmp_log +Bugzilla: 1668956 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +Several places in iotests deal with serializing objects into JSON +strings, but to add pretty-printing it seems desirable to localize +all of those cases. + +log() seems like a good candidate for that centralized behavior. +log() can already serialize json objects, but when it does so, +it assumes filters=[] operates on QMP objects, not strings. + +qmp_log currently operates by dumping outgoing and incoming QMP +objects into strings and filtering them assuming that filters=[] +are string filters. + +To have qmp_log use log's serialization, qmp_log will need to +accept only qmp filters, not text filters. + +However, only a single caller of qmp_log actually requires any +filters at all. I remove the default filter and add it explicitly +to the caller in preparation for refactoring qmp_log to use rich +filters instead. + +test 206 is amended to name the filter explicitly and the default +is removed. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20181221093529.23855-9-jsnow@redhat.com> +Signed-off-by: Eric Blake +(cherry picked from commit f8ca8609d8549def45b28e82ecac64adaeee9f12) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/206 | 8 ++++++-- + tests/qemu-iotests/iotests.py | 2 +- + 2 files changed, 7 insertions(+), 3 deletions(-) + +diff --git a/tests/qemu-iotests/206 b/tests/qemu-iotests/206 +index 128c334..e92550f 100755 +--- a/tests/qemu-iotests/206 ++++ b/tests/qemu-iotests/206 +@@ -26,7 +26,9 @@ from iotests import imgfmt + iotests.verify_image_format(supported_fmts=['qcow2']) + + def blockdev_create(vm, options): +- result = vm.qmp_log('blockdev-create', job_id='job0', options=options) ++ result = vm.qmp_log('blockdev-create', ++ filters=[iotests.filter_testfiles], ++ job_id='job0', options=options) + + if 'return' in result: + assert result['return'] == {} +@@ -52,7 +54,9 @@ with iotests.FilePath('t.qcow2') as disk_path, \ + 'filename': disk_path, + 'size': 0 }) + +- vm.qmp_log('blockdev-add', driver='file', filename=disk_path, ++ vm.qmp_log('blockdev-add', ++ filters=[iotests.filter_testfiles], ++ driver='file', filename=disk_path, + node_name='imgfile') + + blockdev_create(vm, { 'driver': imgfmt, +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index bd508be..30d198a 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -454,7 +454,7 @@ class VM(qtest.QEMUQtestMachine): + result.append(filter_qmp_event(ev)) + return result + +- def qmp_log(self, cmd, filters=[filter_testfiles], **kwargs): ++ def qmp_log(self, cmd, filters=[], **kwargs): + full_cmd = OrderedDict(( + ("execute", cmd), + ("arguments", ordered_kwargs(kwargs)) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests.py-Add-is_str.patch b/SOURCES/kvm-iotests.py-Add-is_str.patch new file mode 100644 index 0000000..6a86292 --- /dev/null +++ b/SOURCES/kvm-iotests.py-Add-is_str.patch @@ -0,0 +1,51 @@ +From f32cd24bac5efaf4331b864ad55eed80b0b0d89c Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 16:16:30 +0100 +Subject: [PATCH 032/163] iotests.py: Add is_str() + +RH-Author: John Snow +Message-id: <20190320161631.14841-19-jsnow@redhat.com> +Patchwork-id: 84956 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 18/19] iotests.py: Add is_str() +Bugzilla: 1668956 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Max Reitz + +On Python 2.x, strings are not always unicode strings. This function +checks whether a given value is a plain string, or a unicode string (if +there is a difference). + +Signed-off-by: Max Reitz +Reviewed-by: John Snow +Message-id: 20190210145736.1486-7-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit 011a576113000f9c90a8450ef9116cca8d6f2523) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/iotests.py | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index 4e9b2c4..64bb718 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -216,6 +216,12 @@ def image_size(img): + r = qemu_img_pipe('info', '--output=json', '-f', imgfmt, img) + return json.loads(r)['virtual-size'] + ++def is_str(val): ++ if sys.version_info.major >= 3: ++ return isinstance(val, str) ++ else: ++ return isinstance(val, str) or isinstance(val, unicode) ++ + test_dir_re = re.compile(r"%s" % test_dir) + def filter_test_dir(msg): + return test_dir_re.sub("TEST_DIR", msg) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests.py-Add-qemu_io_silent.patch b/SOURCES/kvm-iotests.py-Add-qemu_io_silent.patch new file mode 100644 index 0000000..90384f0 --- /dev/null +++ b/SOURCES/kvm-iotests.py-Add-qemu_io_silent.patch @@ -0,0 +1,52 @@ +From fc5822a57d255dd35ccb789dc03ac11d699007a1 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 16:43:11 +0200 +Subject: [PATCH 43/54] iotests.py: Add qemu_io_silent + +RH-Author: Max Reitz +Message-id: <20180618164312.24423-5-mreitz@redhat.com> +Patchwork-id: 80777 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 4/5] iotests.py: Add qemu_io_silent +Bugzilla: 1519617 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Miroslav Rezanina + +With qemu-io now returning a useful exit code, some tests may find it +sufficient to just query that instead of logging (and filtering) the +whole output. + +Signed-off-by: Max Reitz +Reviewed-by: Eric Blake +Message-id: 20180509194302.21585-5-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit 745f2bf4a5b973d1a69b21db97f9e4219da60624) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/iotests.py | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index b25d48a..9747ca9 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -119,6 +119,15 @@ def qemu_io(*args): + sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args))) + return subp.communicate()[0] + ++def qemu_io_silent(*args): ++ '''Run qemu-io and return the exit code, suppressing stdout''' ++ args = qemu_io_args + list(args) ++ exitcode = subprocess.call(args, stdout=open('/dev/null', 'w')) ++ if exitcode < 0: ++ sys.stderr.write('qemu-io received signal %i: %s\n' % ++ (-exitcode, ' '.join(args))) ++ return exitcode ++ + + class QemuIoInteractive: + def __init__(self, *args): +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests.py-Filter-filename-in-any-string-value.patch b/SOURCES/kvm-iotests.py-Filter-filename-in-any-string-value.patch new file mode 100644 index 0000000..f911146 --- /dev/null +++ b/SOURCES/kvm-iotests.py-Filter-filename-in-any-string-value.patch @@ -0,0 +1,49 @@ +From 8e28b84e9502f95c2f08e20ec44b1f161193c2d8 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 16:16:31 +0100 +Subject: [PATCH 033/163] iotests.py: Filter filename in any string value + +RH-Author: John Snow +Message-id: <20190320161631.14841-20-jsnow@redhat.com> +Patchwork-id: 84954 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 19/19] iotests.py: Filter filename in any string value +Bugzilla: 1668956 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Max Reitz + +filter_qmp_testfiles() currently filters the filename only for specific +keys. However, there are more keys that take filenames (such as +block-commit's @top and @base, or ssh's @path), and it does not make +sense to list them all here. "$TEST_DIR/$PID-" should have enough +entropy not to appear anywhere randomly. + +Signed-off-by: Max Reitz +Reviewed-by: John Snow +Message-id: 20190210145736.1486-8-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit 56a6e5d0ca61f746577ea6223bcabbf7d6c576af) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/iotests.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index 64bb718..a796ad2 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -269,7 +269,7 @@ def filter_testfiles(msg): + + def filter_qmp_testfiles(qmsg): + def _filter(key, value): +- if key == 'filename' or key == 'backing-file': ++ if is_str(value): + return filter_testfiles(value) + return value + return filter_qmp(qmsg, _filter) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iotests.py-don-t-abort-if-IMGKEYSECRET-is-undefined.patch b/SOURCES/kvm-iotests.py-don-t-abort-if-IMGKEYSECRET-is-undefined.patch new file mode 100644 index 0000000..60831cb --- /dev/null +++ b/SOURCES/kvm-iotests.py-don-t-abort-if-IMGKEYSECRET-is-undefined.patch @@ -0,0 +1,47 @@ +From ffb202539710de98044f20d13bd05843ed1dc94b Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 16:16:18 +0100 +Subject: [PATCH 020/163] iotests.py: don't abort if IMGKEYSECRET is undefined + +RH-Author: John Snow +Message-id: <20190320161631.14841-7-jsnow@redhat.com> +Patchwork-id: 84943 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 06/19] iotests.py: don't abort if IMGKEYSECRET is undefined +Bugzilla: 1668956 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +Instead of using os.environ[], use .get with a default of empty string +to match the setup in check to allow us to import the iotests module +(for debugging, say) without needing a crafted environment just to +import the module. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20181221093529.23855-6-jsnow@redhat.com> +Signed-off-by: Eric Blake +(cherry picked from commit 58ebcb65d8e38fd693bf1e58dd941a4d7a8dfdef) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/iotests.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index 1b9656a..c5739a6 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -61,7 +61,7 @@ socket_scm_helper = os.environ.get('SOCKET_SCM_HELPER', 'socket_scm_helper') + debug = False + + luks_default_secret_object = 'secret,id=keysec0,data=' + \ +- os.environ['IMGKEYSECRET'] ++ os.environ.get('IMGKEYSECRET', '') + luks_default_key_secret_opt = 'key-secret=keysec0' + + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iothread-fix-crash-with-invalid-properties.patch b/SOURCES/kvm-iothread-fix-crash-with-invalid-properties.patch new file mode 100644 index 0000000..a2995cd --- /dev/null +++ b/SOURCES/kvm-iothread-fix-crash-with-invalid-properties.patch @@ -0,0 +1,81 @@ +From 6470ace7aae0d6e76e8ca8cc450085a502877250 Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +Date: Wed, 28 Nov 2018 16:04:27 +0100 +Subject: [PATCH 17/34] iothread: fix crash with invalid properties +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Stefan Hajnoczi +Message-id: <20181128160427.7389-2-stefanha@redhat.com> +Patchwork-id: 83183 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/1] iothread: fix crash with invalid properties +Bugzilla: 1607768 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Peter Xu +RH-Acked-by: Marc-André Lureau +RH-Acked-by: Pankaj Gupta + +From: Marc-André Lureau + +-object iothread,id=foo,? will crash qemu: + +qemu-system-x86_64:qemu-thread-posix.c:128: qemu_cond_destroy: Assertion `cond->initialized' failed. + +Use thread_id != -1 to check if iothread_complete() finished +successfully and the mutex/cond have been initialized. + +Signed-off-by: Marc-André Lureau +Message-Id: <20180821100716.13803-1-marcandre.lureau@redhat.com> +Signed-off-by: Fam Zheng +(cherry picked from commit 14a2d11825ddc37d6547a80704ae6450e9e376c7) +Signed-off-by: Stefan Hajnoczi +Signed-off-by: Miroslav Rezanina +--- + iothread.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/iothread.c b/iothread.c +index aff1281..2fb1cdf 100644 +--- a/iothread.c ++++ b/iothread.c +@@ -110,6 +110,7 @@ static void iothread_instance_init(Object *obj) + IOThread *iothread = IOTHREAD(obj); + + iothread->poll_max_ns = IOTHREAD_POLL_MAX_NS_DEFAULT; ++ iothread->thread_id = -1; + } + + static void iothread_instance_finalize(Object *obj) +@@ -117,6 +118,11 @@ static void iothread_instance_finalize(Object *obj) + IOThread *iothread = IOTHREAD(obj); + + iothread_stop(iothread); ++ ++ if (iothread->thread_id != -1) { ++ qemu_cond_destroy(&iothread->init_done_cond); ++ qemu_mutex_destroy(&iothread->init_done_lock); ++ } + /* + * Before glib2 2.33.10, there is a glib2 bug that GSource context + * pointer may not be cleared even if the context has already been +@@ -135,8 +141,6 @@ static void iothread_instance_finalize(Object *obj) + g_main_context_unref(iothread->worker_context); + iothread->worker_context = NULL; + } +- qemu_cond_destroy(&iothread->init_done_cond); +- qemu_mutex_destroy(&iothread->init_done_lock); + } + + static void iothread_complete(UserCreatable *obj, Error **errp) +@@ -147,7 +151,6 @@ static void iothread_complete(UserCreatable *obj, Error **errp) + + iothread->stopping = false; + iothread->running = true; +- iothread->thread_id = -1; + iothread->ctx = aio_context_new(&local_error); + if (!iothread->ctx) { + error_propagate(errp, local_error); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iscsi-Create-and-use-iscsi_co_wait_for_task.patch b/SOURCES/kvm-iscsi-Create-and-use-iscsi_co_wait_for_task.patch new file mode 100644 index 0000000..713dce5 --- /dev/null +++ b/SOURCES/kvm-iscsi-Create-and-use-iscsi_co_wait_for_task.patch @@ -0,0 +1,139 @@ +From b528c59ca234b23c7bdcb9d3bcff20cdb111fd46 Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Fri, 29 Jun 2018 06:11:47 +0200 +Subject: [PATCH 43/57] iscsi: Create and use iscsi_co_wait_for_task + +RH-Author: Fam Zheng +Message-id: <20180629061153.12687-8-famz@redhat.com> +Patchwork-id: 81160 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 07/13] iscsi: Create and use iscsi_co_wait_for_task +Bugzilla: 1482537 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Kevin Wolf + +This loop is repeated a growing number times. Make a helper. + +Signed-off-by: Fam Zheng +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Eric Blake +Message-id: 20180601092648.24614-8-famz@redhat.com +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit 66e75c03b2247bda6dcaa883b700291bc0f7f0ef) +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + block/iscsi.c | 54 +++++++++++++++++------------------------------------- + 1 file changed, 17 insertions(+), 37 deletions(-) + +diff --git a/block/iscsi.c b/block/iscsi.c +index a1a0044..338f3dd 100644 +--- a/block/iscsi.c ++++ b/block/iscsi.c +@@ -557,6 +557,17 @@ static inline bool iscsi_allocmap_is_valid(IscsiLun *iscsilun, + offset / iscsilun->cluster_size) == size); + } + ++static void coroutine_fn iscsi_co_wait_for_task(IscsiTask *iTask, ++ IscsiLun *iscsilun) ++{ ++ while (!iTask->complete) { ++ iscsi_set_events(iscsilun); ++ qemu_mutex_unlock(&iscsilun->mutex); ++ qemu_coroutine_yield(); ++ qemu_mutex_lock(&iscsilun->mutex); ++ } ++} ++ + static int coroutine_fn + iscsi_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors, + QEMUIOVector *iov, int flags) +@@ -618,12 +629,7 @@ retry: + scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov, + iov->niov); + #endif +- while (!iTask.complete) { +- iscsi_set_events(iscsilun); +- qemu_mutex_unlock(&iscsilun->mutex); +- qemu_coroutine_yield(); +- qemu_mutex_lock(&iscsilun->mutex); +- } ++ iscsi_co_wait_for_task(&iTask, iscsilun); + + if (iTask.task != NULL) { + scsi_free_scsi_task(iTask.task); +@@ -694,13 +700,7 @@ retry: + ret = -ENOMEM; + goto out_unlock; + } +- +- while (!iTask.complete) { +- iscsi_set_events(iscsilun); +- qemu_mutex_unlock(&iscsilun->mutex); +- qemu_coroutine_yield(); +- qemu_mutex_lock(&iscsilun->mutex); +- } ++ iscsi_co_wait_for_task(&iTask, iscsilun); + + if (iTask.do_retry) { + if (iTask.task != NULL) { +@@ -864,13 +864,8 @@ retry: + #if LIBISCSI_API_VERSION < (20160603) + scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov); + #endif +- while (!iTask.complete) { +- iscsi_set_events(iscsilun); +- qemu_mutex_unlock(&iscsilun->mutex); +- qemu_coroutine_yield(); +- qemu_mutex_lock(&iscsilun->mutex); +- } + ++ iscsi_co_wait_for_task(&iTask, iscsilun); + if (iTask.task != NULL) { + scsi_free_scsi_task(iTask.task); + iTask.task = NULL; +@@ -907,12 +902,7 @@ retry: + return -ENOMEM; + } + +- while (!iTask.complete) { +- iscsi_set_events(iscsilun); +- qemu_mutex_unlock(&iscsilun->mutex); +- qemu_coroutine_yield(); +- qemu_mutex_lock(&iscsilun->mutex); +- } ++ iscsi_co_wait_for_task(&iTask, iscsilun); + + if (iTask.task != NULL) { + scsi_free_scsi_task(iTask.task); +@@ -1144,12 +1134,7 @@ retry: + goto out_unlock; + } + +- while (!iTask.complete) { +- iscsi_set_events(iscsilun); +- qemu_mutex_unlock(&iscsilun->mutex); +- qemu_coroutine_yield(); +- qemu_mutex_lock(&iscsilun->mutex); +- } ++ iscsi_co_wait_for_task(&iTask, iscsilun); + + if (iTask.task != NULL) { + scsi_free_scsi_task(iTask.task); +@@ -1245,12 +1230,7 @@ retry: + return -ENOMEM; + } + +- while (!iTask.complete) { +- iscsi_set_events(iscsilun); +- qemu_mutex_unlock(&iscsilun->mutex); +- qemu_coroutine_yield(); +- qemu_mutex_lock(&iscsilun->mutex); +- } ++ iscsi_co_wait_for_task(&iTask, iscsilun); + + if (iTask.status == SCSI_STATUS_CHECK_CONDITION && + iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iscsi-Don-t-blindly-use-designator-length-in-respons.patch b/SOURCES/kvm-iscsi-Don-t-blindly-use-designator-length-in-respons.patch new file mode 100644 index 0000000..6533b8d --- /dev/null +++ b/SOURCES/kvm-iscsi-Don-t-blindly-use-designator-length-in-respons.patch @@ -0,0 +1,42 @@ +From cb9d3b91772324bb697a0f3b89ecc181f59629e2 Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Fri, 29 Jun 2018 06:11:52 +0200 +Subject: [PATCH 48/57] iscsi: Don't blindly use designator length in response + for memcpy + +RH-Author: Fam Zheng +Message-id: <20180629061153.12687-13-famz@redhat.com> +Patchwork-id: 81162 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 12/13] iscsi: Don't blindly use designator length in response for memcpy +Bugzilla: 1482537 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Kevin Wolf + +Per SCSI definition the designator_length we receive from INQUIRY is 8, +12 or at most 16, but we should be careful because the remote iscsi +target may misbehave, otherwise we could have a buffer overflow. + +Reported-by: Max Reitz +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + block/iscsi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/block/iscsi.c b/block/iscsi.c +index fbcd5bb..751884d 100644 +--- a/block/iscsi.c ++++ b/block/iscsi.c +@@ -2226,7 +2226,7 @@ static void iscsi_populate_target_desc(unsigned char *desc, IscsiLun *lun) + desc[5] = (dd->designator_type & 0xF) + | ((dd->association & 3) << 4); + desc[7] = dd->designator_length; +- memcpy(desc + 8, dd->designator, dd->designator_length); ++ memcpy(desc + 8, dd->designator, MIN(dd->designator_length, 20)); + + desc[28] = 0; + desc[29] = (lun->block_size >> 16) & 0xFF; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iscsi-Drop-deprecated-drive-parameter-filename.patch b/SOURCES/kvm-iscsi-Drop-deprecated-drive-parameter-filename.patch new file mode 100644 index 0000000..99880c7 --- /dev/null +++ b/SOURCES/kvm-iscsi-Drop-deprecated-drive-parameter-filename.patch @@ -0,0 +1,82 @@ +From 975a41d8556024bed3fafc5118f084adf4cb391e Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:14 +0200 +Subject: [PATCH 07/54] iscsi: Drop deprecated -drive parameter "filename" + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-8-armbru@redhat.com> +Patchwork-id: 80740 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 07/23] iscsi: Drop deprecated -drive parameter "filename" +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Parameter "filename" is deprecated since commit 5c3ad1a6a8f, v2.10.0. +Time to get rid of it. + +Signed-off-by: Markus Armbruster +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit deadbb8ebb5c253da9b8ed02ab51a0fadf60edc7) +Signed-off-by: Miroslav Rezanina +--- + block/iscsi.c | 23 ++--------------------- + 1 file changed, 2 insertions(+), 21 deletions(-) + +diff --git a/block/iscsi.c b/block/iscsi.c +index d19ae0e..658462b 100644 +--- a/block/iscsi.c ++++ b/block/iscsi.c +@@ -1732,10 +1732,6 @@ static QemuOptsList runtime_opts = { + .name = "timeout", + .type = QEMU_OPT_NUMBER, + }, +- { +- .name = "filename", +- .type = QEMU_OPT_STRING, +- }, + { /* end of list */ } + }, + }; +@@ -1751,27 +1747,12 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, + char *initiator_name = NULL; + QemuOpts *opts; + Error *local_err = NULL; +- const char *transport_name, *portal, *target, *filename; ++ const char *transport_name, *portal, *target; + #if LIBISCSI_API_VERSION >= (20160603) + enum iscsi_transport_type transport; + #endif + int i, ret = 0, timeout = 0, lun; + +- /* If we are given a filename, parse the filename, with precedence given to +- * filename encoded options */ +- filename = qdict_get_try_str(options, "filename"); +- if (filename) { +- warn_report("'filename' option specified. " +- "This is an unsupported option, and may be deprecated " +- "in the future"); +- iscsi_parse_filename(filename, options, &local_err); +- if (local_err) { +- ret = -EINVAL; +- error_propagate(errp, local_err); +- goto exit; +- } +- } +- + opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { +@@ -1989,7 +1970,7 @@ out: + } + memset(iscsilun, 0, sizeof(IscsiLun)); + } +-exit: ++ + return ret; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iscsi-Implement-copy-offloading.patch b/SOURCES/kvm-iscsi-Implement-copy-offloading.patch new file mode 100644 index 0000000..a63e770 --- /dev/null +++ b/SOURCES/kvm-iscsi-Implement-copy-offloading.patch @@ -0,0 +1,291 @@ +From e059872c2b30d6065411e7b11d6841efa69d57c4 Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Fri, 29 Jun 2018 06:11:48 +0200 +Subject: [PATCH 44/57] iscsi: Implement copy offloading + +RH-Author: Fam Zheng +Message-id: <20180629061153.12687-9-famz@redhat.com> +Patchwork-id: 81159 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 08/13] iscsi: Implement copy offloading +Bugzilla: 1482537 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Kevin Wolf + +Issue EXTENDED COPY (LID1) command to implement the copy_range API. + +The parameter data construction code is modified from libiscsi's +iscsi-dd.c. + +Signed-off-by: Fam Zheng +Reviewed-by: Stefan Hajnoczi +Message-id: 20180601092648.24614-9-famz@redhat.com +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit 604dfaaa3270081da689991afe83d94d3e8231df) +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + block/iscsi.c | 219 +++++++++++++++++++++++++++++++++++++++++++++++ + include/scsi/constants.h | 4 + + 2 files changed, 223 insertions(+) + +diff --git a/block/iscsi.c b/block/iscsi.c +index 338f3dd..fbcd5bb 100644 +--- a/block/iscsi.c ++++ b/block/iscsi.c +@@ -2187,6 +2187,221 @@ static void coroutine_fn iscsi_co_invalidate_cache(BlockDriverState *bs, + iscsi_allocmap_invalidate(iscsilun); + } + ++static int coroutine_fn iscsi_co_copy_range_from(BlockDriverState *bs, ++ BdrvChild *src, ++ uint64_t src_offset, ++ BdrvChild *dst, ++ uint64_t dst_offset, ++ uint64_t bytes, ++ BdrvRequestFlags flags) ++{ ++ return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, flags); ++} ++ ++static struct scsi_task *iscsi_xcopy_task(int param_len) ++{ ++ struct scsi_task *task; ++ ++ task = g_new0(struct scsi_task, 1); ++ ++ task->cdb[0] = EXTENDED_COPY; ++ task->cdb[10] = (param_len >> 24) & 0xFF; ++ task->cdb[11] = (param_len >> 16) & 0xFF; ++ task->cdb[12] = (param_len >> 8) & 0xFF; ++ task->cdb[13] = param_len & 0xFF; ++ task->cdb_size = 16; ++ task->xfer_dir = SCSI_XFER_WRITE; ++ task->expxferlen = param_len; ++ ++ return task; ++} ++ ++static void iscsi_populate_target_desc(unsigned char *desc, IscsiLun *lun) ++{ ++ struct scsi_inquiry_device_designator *dd = lun->dd; ++ ++ memset(desc, 0, 32); ++ desc[0] = 0xE4; /* IDENT_DESCR_TGT_DESCR */ ++ desc[4] = dd->code_set; ++ desc[5] = (dd->designator_type & 0xF) ++ | ((dd->association & 3) << 4); ++ desc[7] = dd->designator_length; ++ memcpy(desc + 8, dd->designator, dd->designator_length); ++ ++ desc[28] = 0; ++ desc[29] = (lun->block_size >> 16) & 0xFF; ++ desc[30] = (lun->block_size >> 8) & 0xFF; ++ desc[31] = lun->block_size & 0xFF; ++} ++ ++static void iscsi_xcopy_desc_hdr(uint8_t *hdr, int dc, int cat, int src_index, ++ int dst_index) ++{ ++ hdr[0] = 0x02; /* BLK_TO_BLK_SEG_DESCR */ ++ hdr[1] = ((dc << 1) | cat) & 0xFF; ++ hdr[2] = (XCOPY_BLK2BLK_SEG_DESC_SIZE >> 8) & 0xFF; ++ /* don't account for the first 4 bytes in descriptor header*/ ++ hdr[3] = (XCOPY_BLK2BLK_SEG_DESC_SIZE - 4 /* SEG_DESC_SRC_INDEX_OFFSET */) & 0xFF; ++ hdr[4] = (src_index >> 8) & 0xFF; ++ hdr[5] = src_index & 0xFF; ++ hdr[6] = (dst_index >> 8) & 0xFF; ++ hdr[7] = dst_index & 0xFF; ++} ++ ++static void iscsi_xcopy_populate_desc(uint8_t *desc, int dc, int cat, ++ int src_index, int dst_index, int num_blks, ++ uint64_t src_lba, uint64_t dst_lba) ++{ ++ iscsi_xcopy_desc_hdr(desc, dc, cat, src_index, dst_index); ++ ++ /* The caller should verify the request size */ ++ assert(num_blks < 65536); ++ desc[10] = (num_blks >> 8) & 0xFF; ++ desc[11] = num_blks & 0xFF; ++ desc[12] = (src_lba >> 56) & 0xFF; ++ desc[13] = (src_lba >> 48) & 0xFF; ++ desc[14] = (src_lba >> 40) & 0xFF; ++ desc[15] = (src_lba >> 32) & 0xFF; ++ desc[16] = (src_lba >> 24) & 0xFF; ++ desc[17] = (src_lba >> 16) & 0xFF; ++ desc[18] = (src_lba >> 8) & 0xFF; ++ desc[19] = src_lba & 0xFF; ++ desc[20] = (dst_lba >> 56) & 0xFF; ++ desc[21] = (dst_lba >> 48) & 0xFF; ++ desc[22] = (dst_lba >> 40) & 0xFF; ++ desc[23] = (dst_lba >> 32) & 0xFF; ++ desc[24] = (dst_lba >> 24) & 0xFF; ++ desc[25] = (dst_lba >> 16) & 0xFF; ++ desc[26] = (dst_lba >> 8) & 0xFF; ++ desc[27] = dst_lba & 0xFF; ++} ++ ++static void iscsi_xcopy_populate_header(unsigned char *buf, int list_id, int str, ++ int list_id_usage, int prio, ++ int tgt_desc_len, ++ int seg_desc_len, int inline_data_len) ++{ ++ buf[0] = list_id; ++ buf[1] = ((str & 1) << 5) | ((list_id_usage & 3) << 3) | (prio & 7); ++ buf[2] = (tgt_desc_len >> 8) & 0xFF; ++ buf[3] = tgt_desc_len & 0xFF; ++ buf[8] = (seg_desc_len >> 24) & 0xFF; ++ buf[9] = (seg_desc_len >> 16) & 0xFF; ++ buf[10] = (seg_desc_len >> 8) & 0xFF; ++ buf[11] = seg_desc_len & 0xFF; ++ buf[12] = (inline_data_len >> 24) & 0xFF; ++ buf[13] = (inline_data_len >> 16) & 0xFF; ++ buf[14] = (inline_data_len >> 8) & 0xFF; ++ buf[15] = inline_data_len & 0xFF; ++} ++ ++static void iscsi_xcopy_data(struct iscsi_data *data, ++ IscsiLun *src, int64_t src_lba, ++ IscsiLun *dst, int64_t dst_lba, ++ uint16_t num_blocks) ++{ ++ uint8_t *buf; ++ const int src_offset = XCOPY_DESC_OFFSET; ++ const int dst_offset = XCOPY_DESC_OFFSET + IDENT_DESCR_TGT_DESCR_SIZE; ++ const int seg_offset = dst_offset + IDENT_DESCR_TGT_DESCR_SIZE; ++ ++ data->size = XCOPY_DESC_OFFSET + ++ IDENT_DESCR_TGT_DESCR_SIZE * 2 + ++ XCOPY_BLK2BLK_SEG_DESC_SIZE; ++ data->data = g_malloc0(data->size); ++ buf = data->data; ++ ++ /* Initialise the parameter list header */ ++ iscsi_xcopy_populate_header(buf, 1, 0, 2 /* LIST_ID_USAGE_DISCARD */, ++ 0, 2 * IDENT_DESCR_TGT_DESCR_SIZE, ++ XCOPY_BLK2BLK_SEG_DESC_SIZE, ++ 0); ++ ++ /* Initialise CSCD list with one src + one dst descriptor */ ++ iscsi_populate_target_desc(&buf[src_offset], src); ++ iscsi_populate_target_desc(&buf[dst_offset], dst); ++ ++ /* Initialise one segment descriptor */ ++ iscsi_xcopy_populate_desc(&buf[seg_offset], 0, 0, 0, 1, num_blocks, ++ src_lba, dst_lba); ++} ++ ++static int coroutine_fn iscsi_co_copy_range_to(BlockDriverState *bs, ++ BdrvChild *src, ++ uint64_t src_offset, ++ BdrvChild *dst, ++ uint64_t dst_offset, ++ uint64_t bytes, ++ BdrvRequestFlags flags) ++{ ++ IscsiLun *dst_lun = dst->bs->opaque; ++ IscsiLun *src_lun; ++ struct IscsiTask iscsi_task; ++ struct iscsi_data data; ++ int r = 0; ++ int block_size; ++ ++ if (src->bs->drv->bdrv_co_copy_range_to != iscsi_co_copy_range_to) { ++ return -ENOTSUP; ++ } ++ src_lun = src->bs->opaque; ++ ++ if (!src_lun->dd || !dst_lun->dd) { ++ return -ENOTSUP; ++ } ++ if (!is_byte_request_lun_aligned(dst_offset, bytes, dst_lun)) { ++ return -ENOTSUP; ++ } ++ if (!is_byte_request_lun_aligned(src_offset, bytes, src_lun)) { ++ return -ENOTSUP; ++ } ++ if (dst_lun->block_size != src_lun->block_size || ++ !dst_lun->block_size) { ++ return -ENOTSUP; ++ } ++ ++ block_size = dst_lun->block_size; ++ if (bytes / block_size > 65535) { ++ return -ENOTSUP; ++ } ++ ++ iscsi_xcopy_data(&data, ++ src_lun, src_offset / block_size, ++ dst_lun, dst_offset / block_size, ++ bytes / block_size); ++ ++ iscsi_co_init_iscsitask(dst_lun, &iscsi_task); ++ ++ qemu_mutex_lock(&dst_lun->mutex); ++ iscsi_task.task = iscsi_xcopy_task(data.size); ++retry: ++ if (iscsi_scsi_command_async(dst_lun->iscsi, dst_lun->lun, ++ iscsi_task.task, iscsi_co_generic_cb, ++ &data, ++ &iscsi_task) != 0) { ++ r = -EIO; ++ goto out_unlock; ++ } ++ ++ iscsi_co_wait_for_task(&iscsi_task, dst_lun); ++ ++ if (iscsi_task.do_retry) { ++ iscsi_task.complete = 0; ++ goto retry; ++ } ++ ++ if (iscsi_task.status != SCSI_STATUS_GOOD) { ++ r = iscsi_task.err_code; ++ goto out_unlock; ++ } ++ ++out_unlock: ++ g_free(iscsi_task.task); ++ qemu_mutex_unlock(&dst_lun->mutex); ++ g_free(iscsi_task.err_str); ++ return r; ++} ++ + static QemuOptsList iscsi_create_opts = { + .name = "iscsi-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head), +@@ -2221,6 +2436,8 @@ static BlockDriver bdrv_iscsi = { + + .bdrv_co_block_status = iscsi_co_block_status, + .bdrv_co_pdiscard = iscsi_co_pdiscard, ++ .bdrv_co_copy_range_from = iscsi_co_copy_range_from, ++ .bdrv_co_copy_range_to = iscsi_co_copy_range_to, + .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, + .bdrv_co_readv = iscsi_co_readv, + .bdrv_co_writev_flags = iscsi_co_writev_flags, +@@ -2256,6 +2473,8 @@ static BlockDriver bdrv_iser = { + + .bdrv_co_block_status = iscsi_co_block_status, + .bdrv_co_pdiscard = iscsi_co_pdiscard, ++ .bdrv_co_copy_range_from = iscsi_co_copy_range_from, ++ .bdrv_co_copy_range_to = iscsi_co_copy_range_to, + .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, + .bdrv_co_readv = iscsi_co_readv, + .bdrv_co_writev_flags = iscsi_co_writev_flags, +diff --git a/include/scsi/constants.h b/include/scsi/constants.h +index a141dd7..083a8e8 100644 +--- a/include/scsi/constants.h ++++ b/include/scsi/constants.h +@@ -311,4 +311,8 @@ + #define MMC_PROFILE_HDDVD_RW_DL 0x005A + #define MMC_PROFILE_INVALID 0xFFFF + ++#define XCOPY_DESC_OFFSET 16 ++#define IDENT_DESCR_TGT_DESCR_SIZE 32 ++#define XCOPY_BLK2BLK_SEG_DESC_SIZE 28 ++ + #endif +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iscsi-Query-and-save-device-designator-when-opening.patch b/SOURCES/kvm-iscsi-Query-and-save-device-designator-when-opening.patch new file mode 100644 index 0000000..8951b07 --- /dev/null +++ b/SOURCES/kvm-iscsi-Query-and-save-device-designator-when-opening.patch @@ -0,0 +1,112 @@ +From 20dcbb9776926fea51e6178298ed76f2c64a1d03 Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Fri, 29 Jun 2018 06:11:46 +0200 +Subject: [PATCH 42/57] iscsi: Query and save device designator when opening + +RH-Author: Fam Zheng +Message-id: <20180629061153.12687-7-famz@redhat.com> +Patchwork-id: 81157 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 06/13] iscsi: Query and save device designator when opening +Bugzilla: 1482537 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Kevin Wolf + +The device designator data returned in INQUIRY command will be useful to +fill in source/target fields during copy offloading. Do this when +connecting to the target and save the data for later use. + +Signed-off-by: Fam Zheng +Reviewed-by: Stefan Hajnoczi +Message-id: 20180601092648.24614-7-famz@redhat.com +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit cc9743c236cce8a35449e3ef67140287b68bb705) +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + block/iscsi.c | 41 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 41 insertions(+) + +diff --git a/block/iscsi.c b/block/iscsi.c +index 1705187..a1a0044 100644 +--- a/block/iscsi.c ++++ b/block/iscsi.c +@@ -69,6 +69,7 @@ typedef struct IscsiLun { + QemuMutex mutex; + struct scsi_inquiry_logical_block_provisioning lbp; + struct scsi_inquiry_block_limits bl; ++ struct scsi_inquiry_device_designator *dd; + unsigned char *zeroblock; + /* The allocmap tracks which clusters (pages) on the iSCSI target are + * allocated and which are not. In case a target returns zeros for +@@ -1737,6 +1738,30 @@ static QemuOptsList runtime_opts = { + }, + }; + ++static void iscsi_save_designator(IscsiLun *lun, ++ struct scsi_inquiry_device_identification *inq_di) ++{ ++ struct scsi_inquiry_device_designator *desig, *copy = NULL; ++ ++ for (desig = inq_di->designators; desig; desig = desig->next) { ++ if (desig->association || ++ desig->designator_type > SCSI_DESIGNATOR_TYPE_NAA) { ++ continue; ++ } ++ /* NAA works better than T10 vendor ID based designator. */ ++ if (!copy || copy->designator_type < desig->designator_type) { ++ copy = desig; ++ } ++ } ++ if (copy) { ++ lun->dd = g_new(struct scsi_inquiry_device_designator, 1); ++ *lun->dd = *copy; ++ lun->dd->next = NULL; ++ lun->dd->designator = g_malloc(copy->designator_length); ++ memcpy(lun->dd->designator, copy->designator, copy->designator_length); ++ } ++} ++ + static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) + { +@@ -1904,6 +1929,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, + struct scsi_task *inq_task; + struct scsi_inquiry_logical_block_provisioning *inq_lbp; + struct scsi_inquiry_block_limits *inq_bl; ++ struct scsi_inquiry_device_identification *inq_di; + switch (inq_vpd->pages[i]) { + case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING: + inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, +@@ -1929,6 +1955,17 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, + sizeof(struct scsi_inquiry_block_limits)); + scsi_free_scsi_task(inq_task); + break; ++ case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION: ++ inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, ++ SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION, ++ (void **) &inq_di, errp); ++ if (inq_task == NULL) { ++ ret = -EINVAL; ++ goto out; ++ } ++ iscsi_save_designator(iscsilun, inq_di); ++ scsi_free_scsi_task(inq_task); ++ break; + default: + break; + } +@@ -1985,6 +2022,10 @@ static void iscsi_close(BlockDriverState *bs) + iscsi_logout_sync(iscsi); + } + iscsi_destroy_context(iscsi); ++ if (iscsilun->dd) { ++ g_free(iscsilun->dd->designator); ++ g_free(iscsilun->dd); ++ } + g_free(iscsilun->zeroblock); + iscsi_allocmap_free(iscsilun); + qemu_mutex_destroy(&iscsilun->mutex); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-iscsi-Support-auto-read-only-option.patch b/SOURCES/kvm-iscsi-Support-auto-read-only-option.patch new file mode 100644 index 0000000..da2b179 --- /dev/null +++ b/SOURCES/kvm-iscsi-Support-auto-read-only-option.patch @@ -0,0 +1,50 @@ +From 546f992fdc25fd65f050efff3b4c8bf8f22d5a05 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 23 Nov 2018 10:41:51 +0100 +Subject: [PATCH 10/34] iscsi: Support auto-read-only option + +RH-Author: Kevin Wolf +Message-id: <20181123104154.13541-10-kwolf@redhat.com> +Patchwork-id: 83120 +O-Subject: [RHEL-7.7/7.6.z qemu-kvm-rhev PATCH v2 09/12] iscsi: Support auto-read-only option +Bugzilla: 1623986 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina +RH-Acked-by: John Snow + +If read-only=off, but auto-read-only=on is given, open the volume +read-write if we have the permissions, but instead of erroring out for +read-only volumes, just degrade to read-only. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +(cherry picked from commit 8f3bf50d340372662a35564c669e567c6c846943) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/iscsi.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/block/iscsi.c b/block/iscsi.c +index 2b45458..c412b12 100644 +--- a/block/iscsi.c ++++ b/block/iscsi.c +@@ -1877,9 +1877,11 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, + /* Check the write protect flag of the LUN if we want to write */ + if (iscsilun->type == TYPE_DISK && (flags & BDRV_O_RDWR) && + iscsilun->write_protected) { +- error_setg(errp, "Cannot open a write protected LUN as read-write"); +- ret = -EACCES; +- goto out; ++ ret = bdrv_apply_auto_read_only(bs, "LUN is write protected", errp); ++ if (ret < 0) { ++ goto out; ++ } ++ flags &= ~BDRV_O_RDWR; + } + + iscsi_readcapacity_sync(iscsilun, &local_err); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-ivshmem-Fix-unplug-of-device-ivshmem-plain.patch b/SOURCES/kvm-ivshmem-Fix-unplug-of-device-ivshmem-plain.patch new file mode 100644 index 0000000..2c4acb7 --- /dev/null +++ b/SOURCES/kvm-ivshmem-Fix-unplug-of-device-ivshmem-plain.patch @@ -0,0 +1,94 @@ +From 1342ea3b7a14657c3e9f6e38ea3dcc0c2f42939e Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 29 Oct 2018 07:01:37 +0100 +Subject: [PATCH 07/22] ivshmem: Fix unplug of device "ivshmem-plain" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20181029070137.21196-4-armbru@redhat.com> +Patchwork-id: 82900 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 3/3] ivshmem: Fix unplug of device "ivshmem-plain" +Bugzilla: 1620373 +RH-Acked-by: David Hildenbrand +RH-Acked-by: Igor Mammedov +RH-Acked-by: Laurent Vivier +RH-Acked-by: Marc-André Lureau + +Commit 2aece63c8a "hostmem: detect host backend memory is being used +properly" fixed "ivshmem-plain" to reject memory backends that are +already in use, and to block their deletion while in use. Two bugs +escaped review: + +* New ivshmem_plain_exit() fails to call ivshmem_exit(). This breaks + unplug. Reproducer: migration after unplug still fails with + "Migration is disabled when using feature 'peer mode' in device + 'ivshmem'". + +* It failed to update legacy "ivshmem". Harmless, because it creates + the memory backend itself, and nothing else should use it. + +Fix by moving the two host_memory_backend_set_mapped() calls into +ivshmem_common_realize() and ivshmem_exit(), guarded by s->hostmem. + +Fixes: 2aece63c8a9d2c3a8ff41d2febc4cdeff2633331 +Signed-off-by: Markus Armbruster +Message-Id: <20180926163709.22876-1-armbru@redhat.com> +Reviewed-by: Marc-André Lureau +Reviewed-by: Paolo Bonzini +(cherry picked from commit b266f1d1123396f9f5df865508f7555ab0c9582a) +Signed-off-by: Miroslav Rezanina +--- + hw/misc/ivshmem.c | 14 +++++--------- + 1 file changed, 5 insertions(+), 9 deletions(-) + +diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c +index 47456b8..28e55d4 100644 +--- a/hw/misc/ivshmem.c ++++ b/hw/misc/ivshmem.c +@@ -917,6 +917,7 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp) + IVSHMEM_DPRINTF("using hostmem\n"); + + s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem); ++ host_memory_backend_set_mapped(s->hostmem, true); + } else { + Chardev *chr = qemu_chr_fe_get_driver(&s->server_chr); + assert(chr); +@@ -999,6 +1000,10 @@ static void ivshmem_exit(PCIDevice *dev) + vmstate_unregister_ram(s->ivshmem_bar2, DEVICE(dev)); + } + ++ if (s->hostmem) { ++ host_memory_backend_set_mapped(s->hostmem, false); ++ } ++ + if (s->peers) { + for (i = 0; i < s->nb_peers; i++) { + close_peer_eventfds(s, i); +@@ -1107,14 +1112,6 @@ static void ivshmem_plain_realize(PCIDevice *dev, Error **errp) + } + + ivshmem_common_realize(dev, errp); +- host_memory_backend_set_mapped(s->hostmem, true); +-} +- +-static void ivshmem_plain_exit(PCIDevice *pci_dev) +-{ +- IVShmemState *s = IVSHMEM_COMMON(pci_dev); +- +- host_memory_backend_set_mapped(s->hostmem, false); + } + + static void ivshmem_plain_class_init(ObjectClass *klass, void *data) +@@ -1123,7 +1120,6 @@ static void ivshmem_plain_class_init(ObjectClass *klass, void *data) + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->realize = ivshmem_plain_realize; +- k->exit = ivshmem_plain_exit; + dc->props = ivshmem_plain_properties; + dc->vmsd = &ivshmem_plain_vmsd; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Add-JOB_STATUS_CHANGE-QMP-event.patch b/SOURCES/kvm-job-Add-JOB_STATUS_CHANGE-QMP-event.patch new file mode 100644 index 0000000..5d0d177 --- /dev/null +++ b/SOURCES/kvm-job-Add-JOB_STATUS_CHANGE-QMP-event.patch @@ -0,0 +1,1333 @@ +From 72b18613852189fb6b2c1a8e88f5bd83749dfe2e Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:31 +0200 +Subject: [PATCH 62/89] job: Add JOB_STATUS_CHANGE QMP event + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-49-kwolf@redhat.com> +Patchwork-id: 81121 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 48/73] job: Add JOB_STATUS_CHANGE QMP event +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This adds a QMP event that is emitted whenever a job transitions from +one status to another. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +(cherry picked from commit 1dac83f1a10c4c66858075970e199f4e4a8d8b71) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + job.c | 10 +++ + qapi/job.json | 14 ++++ + tests/qemu-iotests/030 | 17 +++- + tests/qemu-iotests/040 | 2 + + tests/qemu-iotests/041 | 17 +++- + tests/qemu-iotests/094.out | 7 ++ + tests/qemu-iotests/095 | 2 +- + tests/qemu-iotests/095.out | 6 ++ + tests/qemu-iotests/109 | 2 +- + tests/qemu-iotests/109.out | 178 ++++++++++++++++++++++++++++++++++++------ + tests/qemu-iotests/124 | 8 ++ + tests/qemu-iotests/127.out | 7 ++ + tests/qemu-iotests/141 | 13 ++- + tests/qemu-iotests/141.out | 29 +++++++ + tests/qemu-iotests/144 | 2 +- + tests/qemu-iotests/144.out | 7 ++ + tests/qemu-iotests/156 | 2 +- + tests/qemu-iotests/156.out | 7 ++ + tests/qemu-iotests/185 | 12 +-- + tests/qemu-iotests/185.out | 10 +++ + tests/qemu-iotests/191 | 4 +- + tests/qemu-iotests/191.out | 132 +++++++++++++++++++++++++++++++ + tests/qemu-iotests/iotests.py | 5 ++ + 23 files changed, 449 insertions(+), 44 deletions(-) + +diff --git a/job.c b/job.c +index 2046d2f..599a104 100644 +--- a/job.c ++++ b/job.c +@@ -30,6 +30,7 @@ + #include "qemu/id.h" + #include "qemu/main-loop.h" + #include "trace-root.h" ++#include "qapi/qapi-events-job.h" + + static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs); + +@@ -157,6 +158,11 @@ static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock) + return rc; + } + ++static bool job_is_internal(Job *job) ++{ ++ return (job->id == NULL); ++} ++ + static void job_state_transition(Job *job, JobStatus s1) + { + JobStatus s0 = job->status; +@@ -166,6 +172,10 @@ static void job_state_transition(Job *job, JobStatus s1) + 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, &error_abort); ++ } + } + + int job_apply_verb(Job *job, JobVerb verb, Error **errp) +diff --git a/qapi/job.json b/qapi/job.json +index a472c0c..9fbdd0c 100644 +--- a/qapi/job.json ++++ b/qapi/job.json +@@ -92,3 +92,17 @@ + { 'enum': 'JobVerb', + 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss', + 'finalize' ] } ++ ++## ++# @JOB_STATUS_CHANGE: ++# ++# Emitted when a job transitions to a different status. ++# ++# @id: The job identifier ++# @status: The new job status ++# ++# Since: 2.13 ++## ++{ 'event': 'JOB_STATUS_CHANGE', ++ 'data': { 'id': 'str', ++ 'status': 'JobStatus' } } +diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 +index 640a6df..1dbc2dd 100755 +--- a/tests/qemu-iotests/030 ++++ b/tests/qemu-iotests/030 +@@ -304,8 +304,7 @@ class TestParallelOps(iotests.QMPTestCase): + result = self.vm.qmp('block-stream', device='node5', base=self.imgs[3], job_id='stream-node6') + self.assert_qmp(result, 'error/class', 'GenericError') + +- event = self.vm.get_qmp_event(wait=True) +- self.assertEqual(event['event'], 'BLOCK_JOB_READY') ++ event = self.vm.event_wait(name='BLOCK_JOB_READY') + self.assert_qmp(event, 'data/device', 'commit-drive0') + self.assert_qmp(event, 'data/type', 'commit') + self.assert_qmp_absent(event, 'data/error') +@@ -565,6 +564,8 @@ class TestEIO(TestErrors): + self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE) + self.assert_qmp(event, 'data/len', self.image_len) + completed = True ++ elif event['event'] == 'JOB_STATUS_CHANGE': ++ self.assert_qmp(event, 'data/id', 'drive0') + + self.assert_no_active_block_jobs() + self.vm.shutdown() +@@ -596,6 +597,8 @@ class TestEIO(TestErrors): + self.assert_qmp(event, 'data/offset', self.image_len) + self.assert_qmp(event, 'data/len', self.image_len) + completed = True ++ elif event['event'] == 'JOB_STATUS_CHANGE': ++ self.assert_qmp(event, 'data/id', 'drive0') + + self.assert_no_active_block_jobs() + self.vm.shutdown() +@@ -637,6 +640,8 @@ class TestEIO(TestErrors): + self.assert_qmp(event, 'data/offset', self.image_len) + self.assert_qmp(event, 'data/len', self.image_len) + completed = True ++ elif event['event'] == 'JOB_STATUS_CHANGE': ++ self.assert_qmp(event, 'data/id', 'drive0') + + self.assert_no_active_block_jobs() + self.vm.shutdown() +@@ -663,6 +668,8 @@ class TestEIO(TestErrors): + self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE) + self.assert_qmp(event, 'data/len', self.image_len) + completed = True ++ elif event['event'] == 'JOB_STATUS_CHANGE': ++ self.assert_qmp(event, 'data/id', 'drive0') + + self.assert_no_active_block_jobs() + self.vm.shutdown() +@@ -722,6 +729,8 @@ class TestENOSPC(TestErrors): + self.assert_qmp(event, 'data/offset', self.image_len) + self.assert_qmp(event, 'data/len', self.image_len) + completed = True ++ elif event['event'] == 'JOB_STATUS_CHANGE': ++ self.assert_qmp(event, 'data/id', 'drive0') + + self.assert_no_active_block_jobs() + self.vm.shutdown() +@@ -751,7 +760,9 @@ class TestStreamStop(iotests.QMPTestCase): + + time.sleep(0.1) + events = self.vm.get_qmp_events(wait=False) +- self.assertEqual(events, [], 'unexpected QMP event: %s' % events) ++ for e in events: ++ self.assert_qmp(e, 'event', 'JOB_STATUS_CHANGE') ++ self.assert_qmp(e, 'data/id', 'drive0') + + self.cancel_and_wait(resume=True) + +diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040 +index 90b5b4f..1beb5e6 100755 +--- a/tests/qemu-iotests/040 ++++ b/tests/qemu-iotests/040 +@@ -162,6 +162,8 @@ class TestSingleDrive(ImageCommitTestCase): + elif event['event'] == 'BLOCK_JOB_CANCELLED': + self.assert_qmp(event, 'data/device', 'drive0') + cancelled = True ++ elif event['event'] == 'JOB_STATUS_CHANGE': ++ self.assert_qmp(event, 'data/id', 'drive0') + else: + self.fail("Unexpected event %s" % (event['event'])) + +diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 +index a860a31..e945879 100755 +--- a/tests/qemu-iotests/041 ++++ b/tests/qemu-iotests/041 +@@ -445,6 +445,8 @@ new_state = "1" + self.assert_qmp(event, 'data/device', 'drive0') + self.assert_qmp(event, 'data/error', 'Input/output error') + completed = True ++ elif event['event'] == 'JOB_STATUS_CHANGE': ++ self.assert_qmp(event, 'data/id', 'drive0') + + self.assert_no_active_block_jobs() + self.vm.shutdown() +@@ -457,6 +459,10 @@ new_state = "1" + self.assert_qmp(result, 'return', {}) + + event = self.vm.get_qmp_event(wait=True) ++ while event['event'] == 'JOB_STATUS_CHANGE': ++ self.assert_qmp(event, 'data/id', 'drive0') ++ event = self.vm.get_qmp_event(wait=True) ++ + self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') + self.assert_qmp(event, 'data/device', 'drive0') + self.assert_qmp(event, 'data/operation', 'read') +@@ -478,6 +484,10 @@ new_state = "1" + self.assert_qmp(result, 'return', {}) + + event = self.vm.get_qmp_event(wait=True) ++ while event['event'] == 'JOB_STATUS_CHANGE': ++ self.assert_qmp(event, 'data/id', 'drive0') ++ event = self.vm.get_qmp_event(wait=True) ++ + self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') + self.assert_qmp(event, 'data/device', 'drive0') + self.assert_qmp(event, 'data/operation', 'read') +@@ -608,7 +618,7 @@ new_state = "1" + on_target_error='ignore') + self.assert_qmp(result, 'return', {}) + +- event = self.vm.get_qmp_event(wait=True) ++ event = self.vm.event_wait(name='BLOCK_JOB_ERROR') + self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') + self.assert_qmp(event, 'data/device', 'drive0') + self.assert_qmp(event, 'data/operation', 'write') +@@ -784,7 +794,12 @@ class TestGranularity(iotests.QMPTestCase): + sync='full', target=target_img, + mode='absolute-paths', granularity=8192) + self.assert_qmp(result, 'return', {}) ++ + event = self.vm.get_qmp_event(wait=60.0) ++ while event['event'] == 'JOB_STATUS_CHANGE': ++ self.assert_qmp(event, 'data/id', 'drive0') ++ event = self.vm.get_qmp_event(wait=60.0) ++ + # Failures will manifest as COMPLETED/ERROR. + self.assert_qmp(event, 'event', 'BLOCK_JOB_READY') + self.complete_and_wait(drive='drive0', wait_ready=False) +diff --git a/tests/qemu-iotests/094.out b/tests/qemu-iotests/094.out +index f52baff..665b630 100644 +--- a/tests/qemu-iotests/094.out ++++ b/tests/qemu-iotests/094.out +@@ -2,10 +2,17 @@ QA output created by 094 + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + Formatting 'TEST_DIR/source.IMGFMT', fmt=IMGFMT size=67108864 + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + *** done +diff --git a/tests/qemu-iotests/095 b/tests/qemu-iotests/095 +index 030adb2..72ecc22 100755 +--- a/tests/qemu-iotests/095 ++++ b/tests/qemu-iotests/095 +@@ -72,7 +72,7 @@ _send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" "return" + + _send_qemu_cmd $h "{ 'execute': 'block-commit', + 'arguments': { 'device': 'test', +- 'top': '"${TEST_IMG}.snp1"' } }" "BLOCK_JOB_COMPLETED" ++ 'top': '"${TEST_IMG}.snp1"' } }" '"status": "null"' + + _cleanup_qemu + +diff --git a/tests/qemu-iotests/095.out b/tests/qemu-iotests/095.out +index 73875ca..8c093df 100644 +--- a/tests/qemu-iotests/095.out ++++ b/tests/qemu-iotests/095.out +@@ -11,8 +11,14 @@ virtual size: 5.0M (5242880 bytes) + === Running QEMU Live Commit Test === + + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "test"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "test"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "test"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "test"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "test", "len": 104857600, "offset": 104857600, "speed": 0, "type": "commit"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "test"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "test"}} + + === Base image info after commit and resize === + image: TEST_DIR/t.IMGFMT.base +diff --git a/tests/qemu-iotests/109 b/tests/qemu-iotests/109 +index d70b574..acbd079 100755 +--- a/tests/qemu-iotests/109 ++++ b/tests/qemu-iotests/109 +@@ -64,7 +64,7 @@ function run_qemu() + + _send_qemu_cmd $QEMU_HANDLE '' "$qmp_event" + if test "$qmp_event" = BLOCK_JOB_ERROR; then +- _send_qemu_cmd $QEMU_HANDLE '' "BLOCK_JOB_COMPLETED" ++ _send_qemu_cmd $QEMU_HANDLE '' '"status": "null"' + fi + _send_qemu_cmd $QEMU_HANDLE '{"execute":"query-block-jobs"}' "return" + _send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' "return" +diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out +index 8a9b936..ad0ee6f 100644 +--- a/tests/qemu-iotests/109.out ++++ b/tests/qemu-iotests/109.out +@@ -6,23 +6,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 + {"return": {}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +-Specify the 'raw' format explicitly to remove the restrictions. ++ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. ++ Specify the 'raw' format explicitly to remove the restrictions. ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + {"return": []} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + read 65536/65536 bytes at offset 0 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + Warning: Image size mismatch! + Images are identical. + +@@ -32,23 +44,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 + {"return": {}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +-Specify the 'raw' format explicitly to remove the restrictions. ++ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. ++ Specify the 'raw' format explicitly to remove the restrictions. ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 512, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + {"return": []} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + read 65536/65536 bytes at offset 0 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 197120, "offset": 197120, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + Warning: Image size mismatch! + Images are identical. + +@@ -58,23 +82,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 + {"return": {}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +-Specify the 'raw' format explicitly to remove the restrictions. ++ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. ++ Specify the 'raw' format explicitly to remove the restrictions. ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 262144, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + {"return": []} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + read 65536/65536 bytes at offset 0 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + Warning: Image size mismatch! + Images are identical. + +@@ -84,23 +120,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 + {"return": {}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +-Specify the 'raw' format explicitly to remove the restrictions. ++ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. ++ Specify the 'raw' format explicitly to remove the restrictions. ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + {"return": []} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + read 65536/65536 bytes at offset 0 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + Warning: Image size mismatch! + Images are identical. + +@@ -110,23 +158,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 + {"return": {}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +-Specify the 'raw' format explicitly to remove the restrictions. ++ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. ++ Specify the 'raw' format explicitly to remove the restrictions. ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + {"return": []} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + read 65536/65536 bytes at offset 0 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 65536, "offset": 65536, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + Warning: Image size mismatch! + Images are identical. + +@@ -136,23 +196,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 + {"return": {}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +-Specify the 'raw' format explicitly to remove the restrictions. ++ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. ++ Specify the 'raw' format explicitly to remove the restrictions. ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + {"return": []} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + read 65536/65536 bytes at offset 0 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + Warning: Image size mismatch! + Images are identical. + +@@ -161,23 +233,35 @@ Images are identical. + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + {"return": {}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +-Specify the 'raw' format explicitly to remove the restrictions. ++ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. ++ Specify the 'raw' format explicitly to remove the restrictions. ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + {"return": []} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + read 65536/65536 bytes at offset 0 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + Warning: Image size mismatch! + Images are identical. + +@@ -186,23 +270,35 @@ Images are identical. + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + {"return": {}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +-Specify the 'raw' format explicitly to remove the restrictions. ++ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. ++ Specify the 'raw' format explicitly to remove the restrictions. ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + {"return": []} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + read 65536/65536 bytes at offset 0 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 31457280, "offset": 31457280, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + Warning: Image size mismatch! + Images are identical. + +@@ -211,23 +307,35 @@ Images are identical. + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + {"return": {}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +-Specify the 'raw' format explicitly to remove the restrictions. ++ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. ++ Specify the 'raw' format explicitly to remove the restrictions. ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + {"return": []} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + read 65536/65536 bytes at offset 0 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + Warning: Image size mismatch! + Images are identical. + +@@ -236,23 +344,35 @@ Images are identical. + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + {"return": {}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +-Specify the 'raw' format explicitly to remove the restrictions. ++ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. ++ Specify the 'raw' format explicitly to remove the restrictions. ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + {"return": []} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + read 65536/65536 bytes at offset 0 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2048, "offset": 2048, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + Warning: Image size mismatch! + Images are identical. + +@@ -261,23 +381,37 @@ Images are identical. + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + {"return": {}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +-Specify the 'raw' format explicitly to remove the restrictions. ++ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. ++ Specify the 'raw' format explicitly to remove the restrictions. ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + Warning: Image size mismatch! + Images are identical. + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} + Warning: Image size mismatch! + Images are identical. + *** done +diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124 +index 8e76e62..3ea4ac5 100755 +--- a/tests/qemu-iotests/124 ++++ b/tests/qemu-iotests/124 +@@ -151,10 +151,17 @@ class TestIncrementalBackupBase(iotests.QMPTestCase): + return self.wait_qmp_backup(kwargs['device'], error) + + ++ def ignore_job_status_change_events(self): ++ while True: ++ e = self.vm.event_wait(name="JOB_STATUS_CHANGE") ++ if e['data']['status'] == 'null': ++ break ++ + def wait_qmp_backup(self, device, error='Input/output error'): + event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED", + match={'data': {'device': device}}) + self.assertNotEqual(event, None) ++ self.ignore_job_status_change_events() + + try: + failure = self.dictpath(event, 'data/error') +@@ -172,6 +179,7 @@ class TestIncrementalBackupBase(iotests.QMPTestCase): + event = self.vm.event_wait(name='BLOCK_JOB_CANCELLED', + match={'data': {'device': device}}) + self.assertNotEqual(event, None) ++ self.ignore_job_status_change_events() + + + def create_anchor_backup(self, drive=None): +diff --git a/tests/qemu-iotests/127.out b/tests/qemu-iotests/127.out +index 543d075..83b522d 100644 +--- a/tests/qemu-iotests/127.out ++++ b/tests/qemu-iotests/127.out +@@ -5,10 +5,17 @@ Formatting 'TEST_DIR/t.IMGFMT.overlay1', fmt=IMGFMT size=65536 backing_file=TEST + wrote 42/42 bytes at offset 0 + 42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "mirror"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "mirror"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "mirror", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "mirror"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "mirror", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "mirror"}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + *** done +diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141 +index 2f9d7b9..4246d38 100755 +--- a/tests/qemu-iotests/141 ++++ b/tests/qemu-iotests/141 +@@ -107,7 +107,7 @@ test_blockjob \ + 'format': '$IMGFMT', + 'sync': 'none'}}" \ + 'return' \ +- 'BLOCK_JOB_CANCELLED' ++ '"status": "null"' + + echo + echo '=== Testing drive-mirror ===' +@@ -124,7 +124,7 @@ test_blockjob \ + 'format': '$IMGFMT', + 'sync': 'none'}}" \ + 'BLOCK_JOB_READY' \ +- 'BLOCK_JOB_COMPLETED' ++ '"status": "null"' + + echo + echo '=== Testing active block-commit ===' +@@ -138,7 +138,7 @@ test_blockjob \ + "{'execute': 'block-commit', + 'arguments': {'job-id': 'job0', 'device': 'drv0'}}" \ + 'BLOCK_JOB_READY' \ +- 'BLOCK_JOB_COMPLETED' ++ '"status": "null"' + + echo + echo '=== Testing non-active block-commit ===' +@@ -157,7 +157,7 @@ test_blockjob \ + 'top': '$TEST_DIR/m.$IMGFMT', + 'speed': 1}}" \ + 'return' \ +- 'BLOCK_JOB_CANCELLED' ++ '"status": "null"' + + echo + echo '=== Testing block-stream ===' +@@ -170,8 +170,7 @@ echo + $QEMU_IO -c 'write 0 1M' "$TEST_DIR/b.$IMGFMT" | _filter_qemu_io + + # With some data to stream (and @speed set to 1), block-stream will not complete +-# until we send the block-job-cancel command. Therefore, no event other than +-# BLOCK_JOB_CANCELLED will be emitted. ++# until we send the block-job-cancel command. + + test_blockjob \ + "{'execute': 'block-stream', +@@ -179,7 +178,7 @@ test_blockjob \ + 'device': 'drv0', + 'speed': 1}}" \ + 'return' \ +- 'BLOCK_JOB_CANCELLED' ++ '"status": "null"' + + _cleanup_qemu + +diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out +index 82e763b..f252c86 100644 +--- a/tests/qemu-iotests/141.out ++++ b/tests/qemu-iotests/141.out +@@ -8,31 +8,50 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/m. + + {"return": {}} + Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} + {"return": {}} + {"error": {"class": "GenericError", "desc": "Node drv0 is in use"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 0, "speed": 0, "type": "backup"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} + {"return": {}} + + === Testing drive-mirror === + + {"return": {}} + Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}} + {"return": {}} + {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} + {"return": {}} + + === Testing active block-commit === + + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} + {"return": {}} + {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} + {"return": {}} + + === Testing non-active block-commit === +@@ -40,10 +59,15 @@ Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t. + wrote 1048576/1048576 bytes at offset 0 + 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} + {"return": {}} + {"error": {"class": "GenericError", "desc": "Node drv0 is in use"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "commit"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} + {"return": {}} + + === Testing block-stream === +@@ -51,9 +75,14 @@ wrote 1048576/1048576 bytes at offset 0 + wrote 1048576/1048576 bytes at offset 0 + 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} + {"return": {}} + {"error": {"class": "GenericError", "desc": "Node drv0 is in use"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "stream"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} + {"return": {}} + *** done +diff --git a/tests/qemu-iotests/144 b/tests/qemu-iotests/144 +index 00de3c33..4b91571 100755 +--- a/tests/qemu-iotests/144 ++++ b/tests/qemu-iotests/144 +@@ -93,7 +93,7 @@ _send_qemu_cmd $h "{ 'execute': 'block-job-complete', + 'arguments': { + 'device': 'virtio0' + } +- }" "COMPLETED" ++ }" '"status": "null"' + + echo + echo === Performing Live Snapshot 2 === +diff --git a/tests/qemu-iotests/144.out b/tests/qemu-iotests/144.out +index 014b281..5529920 100644 +--- a/tests/qemu-iotests/144.out ++++ b/tests/qemu-iotests/144.out +@@ -12,10 +12,17 @@ Formatting 'TEST_DIR/tmp.qcow2', fmt=qcow2 size=536870912 backing_file=TEST_DIR/ + + === Performing block-commit on active layer === + ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "virtio0"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "virtio0"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "virtio0"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} + {"return": {}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "virtio0"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "virtio0"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "virtio0"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "virtio0"}} + + === Performing Live Snapshot 2 === + +diff --git a/tests/qemu-iotests/156 b/tests/qemu-iotests/156 +index e75dc4d..0a9a098 100755 +--- a/tests/qemu-iotests/156 ++++ b/tests/qemu-iotests/156 +@@ -119,7 +119,7 @@ _send_qemu_cmd $QEMU_HANDLE \ + + _send_qemu_cmd $QEMU_HANDLE \ + '' \ +- 'BLOCK_JOB_COMPLETED' ++ '"status": "null"' + + # Remove the source images + rm -f "$TEST_IMG{,.backing,.overlay}" +diff --git a/tests/qemu-iotests/156.out b/tests/qemu-iotests/156.out +index f96a564..34c057b 100644 +--- a/tests/qemu-iotests/156.out ++++ b/tests/qemu-iotests/156.out +@@ -12,13 +12,20 @@ wrote 131072/131072 bytes at offset 131072 + 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + {"return": ""} + Formatting 'TEST_DIR/t.IMGFMT.target.overlay', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.target ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "source"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "source"}} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "source"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "source", "len": 131072, "offset": 131072, "speed": 0, "type": "mirror"}} + wrote 65536/65536 bytes at offset 196608 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + {"return": ""} + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "source"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "source"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "source", "len": 196608, "offset": 196608, "speed": 0, "type": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "source"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "source"}} + + read 65536/65536 bytes at offset 0 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185 +index 9a2d317..567ba67 100755 +--- a/tests/qemu-iotests/185 ++++ b/tests/qemu-iotests/185 +@@ -118,8 +118,10 @@ _send_qemu_cmd $h \ + # If we don't sleep here 'quit' command races with disk I/O + sleep 0.5 + ++# Ignore the JOB_STATUS_CHANGE events while shutting down the VM. Depending on ++# the timing, jobs may or may not transition through a paused state. + _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" +-wait=1 _cleanup_qemu ++wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' + + echo + echo === Start active commit job and exit qemu === +@@ -141,7 +143,7 @@ _send_qemu_cmd $h \ + sleep 0.5 + + _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" +-wait=1 _cleanup_qemu ++wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' + + echo + echo === Start mirror job and exit qemu === +@@ -166,7 +168,7 @@ _send_qemu_cmd $h \ + sleep 0.5 + + _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" +-wait=1 _cleanup_qemu ++wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' + + echo + echo === Start backup job and exit qemu === +@@ -190,7 +192,7 @@ _send_qemu_cmd $h \ + sleep 0.5 + + _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" +-wait=1 _cleanup_qemu ++wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' + + echo + echo === Start streaming job and exit qemu === +@@ -211,7 +213,7 @@ _send_qemu_cmd $h \ + sleep 0.5 + + _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" +-wait=1 _cleanup_qemu ++wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' + + _check_test_img + +diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out +index 57eaf8d..4e0ca0d 100644 +--- a/tests/qemu-iotests/185.out ++++ b/tests/qemu-iotests/185.out +@@ -17,6 +17,8 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q + + === Start commit job and exit qemu === + ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} + {"return": {}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +@@ -25,6 +27,8 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q + === Start active commit job and exit qemu === + + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} + {"return": {}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +@@ -34,6 +38,8 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q + + {"return": {}} + Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} + {"return": {}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +@@ -43,6 +49,8 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l + + {"return": {}} + Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} + {"return": {}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +@@ -51,6 +59,8 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l + === Start streaming job and exit qemu === + + {"return": {}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} + {"return": {}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +diff --git a/tests/qemu-iotests/191 b/tests/qemu-iotests/191 +index dfad655..b3629ff 100755 +--- a/tests/qemu-iotests/191 ++++ b/tests/qemu-iotests/191 +@@ -83,7 +83,7 @@ _send_qemu_cmd $h \ + 'device': 'top', + 'base':'$TEST_IMG.base', + 'top': '$TEST_IMG.mid' } }" \ +- "BLOCK_JOB_COMPLETED" ++ '"status": "null"' + _send_qemu_cmd $h "" "^}" + + echo +@@ -131,7 +131,7 @@ _send_qemu_cmd $h \ + 'device': 'top', + 'base':'$TEST_IMG.base', + 'top': '$TEST_IMG.mid' } }" \ +- "BLOCK_JOB_COMPLETED" ++ '"status": "null"' + _send_qemu_cmd $h "" "^}" + + echo +diff --git a/tests/qemu-iotests/191.out b/tests/qemu-iotests/191.out +index 190c5f0..31a0c7d 100644 +--- a/tests/qemu-iotests/191.out ++++ b/tests/qemu-iotests/191.out +@@ -16,6 +16,28 @@ wrote 65536/65536 bytes at offset 1048576 + === Perform commit job === + + { ++ "timestamp": { ++ "seconds": TIMESTAMP, ++ "microseconds": TIMESTAMP ++ }, ++ "event": "JOB_STATUS_CHANGE", ++ "data": { ++ "status": "created", ++ "id": "commit0" ++ } ++} ++{ ++ "timestamp": { ++ "seconds": TIMESTAMP, ++ "microseconds": TIMESTAMP ++ }, ++ "event": "JOB_STATUS_CHANGE", ++ "data": { ++ "status": "running", ++ "id": "commit0" ++ } ++} ++{ + "return": { + } + } +@@ -24,6 +46,28 @@ wrote 65536/65536 bytes at offset 1048576 + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, ++ "event": "JOB_STATUS_CHANGE", ++ "data": { ++ "status": "waiting", ++ "id": "commit0" ++ } ++} ++{ ++ "timestamp": { ++ "seconds": TIMESTAMP, ++ "microseconds": TIMESTAMP ++ }, ++ "event": "JOB_STATUS_CHANGE", ++ "data": { ++ "status": "pending", ++ "id": "commit0" ++ } ++} ++{ ++ "timestamp": { ++ "seconds": TIMESTAMP, ++ "microseconds": TIMESTAMP ++ }, + "event": "BLOCK_JOB_COMPLETED", + "data": { + "device": "commit0", +@@ -33,6 +77,28 @@ wrote 65536/65536 bytes at offset 1048576 + "type": "commit" + } + } ++{ ++ "timestamp": { ++ "seconds": TIMESTAMP, ++ "microseconds": TIMESTAMP ++ }, ++ "event": "JOB_STATUS_CHANGE", ++ "data": { ++ "status": "concluded", ++ "id": "commit0" ++ } ++} ++{ ++ "timestamp": { ++ "seconds": TIMESTAMP, ++ "microseconds": TIMESTAMP ++ }, ++ "event": "JOB_STATUS_CHANGE", ++ "data": { ++ "status": "null", ++ "id": "commit0" ++ } ++} + + === Check that both top and top2 point to base now === + +@@ -356,6 +422,28 @@ wrote 65536/65536 bytes at offset 1048576 + === Perform commit job === + + { ++ "timestamp": { ++ "seconds": TIMESTAMP, ++ "microseconds": TIMESTAMP ++ }, ++ "event": "JOB_STATUS_CHANGE", ++ "data": { ++ "status": "created", ++ "id": "commit0" ++ } ++} ++{ ++ "timestamp": { ++ "seconds": TIMESTAMP, ++ "microseconds": TIMESTAMP ++ }, ++ "event": "JOB_STATUS_CHANGE", ++ "data": { ++ "status": "running", ++ "id": "commit0" ++ } ++} ++{ + "return": { + } + } +@@ -364,6 +452,28 @@ wrote 65536/65536 bytes at offset 1048576 + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, ++ "event": "JOB_STATUS_CHANGE", ++ "data": { ++ "status": "waiting", ++ "id": "commit0" ++ } ++} ++{ ++ "timestamp": { ++ "seconds": TIMESTAMP, ++ "microseconds": TIMESTAMP ++ }, ++ "event": "JOB_STATUS_CHANGE", ++ "data": { ++ "status": "pending", ++ "id": "commit0" ++ } ++} ++{ ++ "timestamp": { ++ "seconds": TIMESTAMP, ++ "microseconds": TIMESTAMP ++ }, + "event": "BLOCK_JOB_COMPLETED", + "data": { + "device": "commit0", +@@ -373,6 +483,28 @@ wrote 65536/65536 bytes at offset 1048576 + "type": "commit" + } + } ++{ ++ "timestamp": { ++ "seconds": TIMESTAMP, ++ "microseconds": TIMESTAMP ++ }, ++ "event": "JOB_STATUS_CHANGE", ++ "data": { ++ "status": "concluded", ++ "id": "commit0" ++ } ++} ++{ ++ "timestamp": { ++ "seconds": TIMESTAMP, ++ "microseconds": TIMESTAMP ++ }, ++ "event": "JOB_STATUS_CHANGE", ++ "data": { ++ "status": "null", ++ "id": "commit0" ++ } ++} + + === Check that both top and top2 point to base now === + +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index 9747ca9..3dab1bf 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -473,6 +473,9 @@ class QMPTestCase(unittest.TestCase): + self.assert_qmp(event, 'data/device', drive) + result = event + cancelled = True ++ elif event['event'] == 'JOB_STATUS_CHANGE': ++ self.assert_qmp(event, 'data/id', drive) ++ + + self.assert_no_active_block_jobs() + return result +@@ -488,6 +491,8 @@ class QMPTestCase(unittest.TestCase): + self.assert_qmp(event, 'data/offset', event['data']['len']) + self.assert_no_active_block_jobs() + return event ++ elif event['event'] == 'JOB_STATUS_CHANGE': ++ self.assert_qmp(event, 'data/id', drive) + + def wait_ready(self, drive='drive0'): + '''Wait until a block job BLOCK_JOB_READY event''' +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Add-Job.aio_context.patch b/SOURCES/kvm-job-Add-Job.aio_context.patch new file mode 100644 index 0000000..6f2d9be --- /dev/null +++ b/SOURCES/kvm-job-Add-Job.aio_context.patch @@ -0,0 +1,112 @@ +From 25511b69b93fae007b86ccf1410585e6288c2b78 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:07 +0200 +Subject: [PATCH 38/89] job: Add Job.aio_context + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-25-kwolf@redhat.com> +Patchwork-id: 81075 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 24/73] job: Add Job.aio_context +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +When block jobs need an AioContext, they just take it from their main +block node. Generic jobs don't have a main block node, so we need to +assign them an AioContext explicitly. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: John Snow +(cherry picked from commit 08be6fe26f6c76d900fc987f58d322b94bc4e248) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + blockjob.c | 5 ++++- + include/qemu/job.h | 7 ++++++- + job.c | 4 +++- + 3 files changed, 13 insertions(+), 3 deletions(-) + +diff --git a/blockjob.c b/blockjob.c +index f4f9956..0a0b1c4 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -216,6 +216,7 @@ static void block_job_attached_aio_context(AioContext *new_context, + { + BlockJob *job = opaque; + ++ job->job.aio_context = new_context; + if (job->driver->attached_aio_context) { + job->driver->attached_aio_context(job, new_context); + } +@@ -247,6 +248,7 @@ static void block_job_detach_aio_context(void *opaque) + block_job_drain(job); + } + ++ job->job.aio_context = NULL; + job_unref(&job->job); + } + +@@ -899,7 +901,8 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + return NULL; + } + +- job = job_create(job_id, &driver->job_driver, errp); ++ job = job_create(job_id, &driver->job_driver, blk_get_aio_context(blk), ++ errp); + if (job == NULL) { + blk_unref(blk); + return NULL; +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 5dfbec5..01e083f 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -47,6 +47,9 @@ typedef struct Job { + /** Current state; See @JobStatus for details. */ + JobStatus status; + ++ /** AioContext to run the job coroutine in */ ++ AioContext *aio_context; ++ + /** + * Set to true if the job should cancel itself. The flag must + * always be tested just before toggling the busy flag from false +@@ -79,9 +82,11 @@ struct JobDriver { + * + * @job_id: The id of the newly-created job, or %NULL for internal jobs + * @driver: The class object for the newly-created job. ++ * @ctx: The AioContext to run the job coroutine in. + * @errp: Error object. + */ +-void *job_create(const char *job_id, const JobDriver *driver, Error **errp); ++void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, ++ Error **errp); + + /** + * Add a reference to Job refcnt, it will be decreased with job_unref, and then +diff --git a/job.c b/job.c +index 1abca6a..01074d0 100644 +--- a/job.c ++++ b/job.c +@@ -121,7 +121,8 @@ Job *job_get(const char *id) + return NULL; + } + +-void *job_create(const char *job_id, const JobDriver *driver, Error **errp) ++void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, ++ Error **errp) + { + Job *job; + +@@ -140,6 +141,7 @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp) + job->driver = driver; + job->id = g_strdup(job_id); + job->refcnt = 1; ++ job->aio_context = ctx; + + job_state_transition(job, JOB_STATUS_CREATED); + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Add-JobDriver.job_type.patch b/SOURCES/kvm-job-Add-JobDriver.job_type.patch new file mode 100644 index 0000000..9c376f9 --- /dev/null +++ b/SOURCES/kvm-job-Add-JobDriver.job_type.patch @@ -0,0 +1,240 @@ +From afe531712d6465c1a850a8538c1756dbad10135a Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:01 +0200 +Subject: [PATCH 32/89] job: Add JobDriver.job_type + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-19-kwolf@redhat.com> +Patchwork-id: 81063 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 18/73] job: Add JobDriver.job_type +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This moves the job_type field from BlockJobDriver to JobDriver. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: John Snow +(cherry picked from commit 252291eaeafcd234a602d71cdf9415dbfc7bc867) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 2 +- + block/commit.c | 2 +- + block/mirror.c | 4 ++-- + block/stream.c | 2 +- + blockjob.c | 16 +++++++--------- + include/block/blockjob_int.h | 3 --- + include/qemu/job.h | 11 +++++++++++ + job.c | 10 ++++++++++ + 8 files changed, 33 insertions(+), 17 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index c49ea92..baf8d43 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -525,8 +525,8 @@ static void coroutine_fn backup_run(void *opaque) + static const BlockJobDriver backup_job_driver = { + .job_driver = { + .instance_size = sizeof(BackupBlockJob), ++ .job_type = JOB_TYPE_BACKUP, + }, +- .job_type = JOB_TYPE_BACKUP, + .start = backup_run, + .commit = backup_commit, + .abort = backup_abort, +diff --git a/block/commit.c b/block/commit.c +index afa2b2b..32d29c8 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -217,8 +217,8 @@ out: + static const BlockJobDriver commit_job_driver = { + .job_driver = { + .instance_size = sizeof(CommitBlockJob), ++ .job_type = JOB_TYPE_COMMIT, + }, +- .job_type = JOB_TYPE_COMMIT, + .start = commit_run, + }; + +diff --git a/block/mirror.c b/block/mirror.c +index ed72656..35fcc1f 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -988,8 +988,8 @@ static void mirror_drain(BlockJob *job) + static const BlockJobDriver mirror_job_driver = { + .job_driver = { + .instance_size = sizeof(MirrorBlockJob), ++ .job_type = JOB_TYPE_MIRROR, + }, +- .job_type = JOB_TYPE_MIRROR, + .start = mirror_run, + .complete = mirror_complete, + .pause = mirror_pause, +@@ -1000,8 +1000,8 @@ static const BlockJobDriver mirror_job_driver = { + static const BlockJobDriver commit_active_job_driver = { + .job_driver = { + .instance_size = sizeof(MirrorBlockJob), ++ .job_type = JOB_TYPE_COMMIT, + }, +- .job_type = JOB_TYPE_COMMIT, + .start = mirror_run, + .complete = mirror_complete, + .pause = mirror_pause, +diff --git a/block/stream.c b/block/stream.c +index 048bceb..cb723f1 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -211,8 +211,8 @@ out: + static const BlockJobDriver stream_job_driver = { + .job_driver = { + .instance_size = sizeof(StreamBlockJob), ++ .job_type = JOB_TYPE_STREAM, + }, +- .job_type = JOB_TYPE_STREAM, + .start = stream_run, + }; + +diff --git a/blockjob.c b/blockjob.c +index 2a38447..ea71ec0 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -309,9 +309,7 @@ static void block_job_detach_aio_context(void *opaque) + static char *child_job_get_parent_desc(BdrvChild *c) + { + BlockJob *job = c->opaque; +- return g_strdup_printf("%s job '%s'", +- JobType_str(job->driver->job_type), +- job->job.id); ++ return g_strdup_printf("%s job '%s'", job_type_str(&job->job), job->job.id); + } + + static void child_job_drained_begin(BdrvChild *c) +@@ -847,7 +845,7 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) + return NULL; + } + info = g_new0(BlockJobInfo, 1); +- info->type = g_strdup(JobType_str(job->driver->job_type)); ++ info->type = g_strdup(job_type_str(&job->job)); + info->device = g_strdup(job->job.id); + info->len = job->len; + info->busy = atomic_read(&job->busy); +@@ -878,7 +876,7 @@ static void block_job_event_cancelled(BlockJob *job) + return; + } + +- qapi_event_send_block_job_cancelled(job->driver->job_type, ++ qapi_event_send_block_job_cancelled(job_type(&job->job), + job->job.id, + job->len, + job->offset, +@@ -892,7 +890,7 @@ static void block_job_event_completed(BlockJob *job, const char *msg) + return; + } + +- qapi_event_send_block_job_completed(job->driver->job_type, ++ qapi_event_send_block_job_completed(job_type(&job->job), + job->job.id, + job->len, + job->offset, +@@ -906,7 +904,7 @@ static int block_job_event_pending(BlockJob *job) + { + block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING); + if (!job->auto_finalize && !block_job_is_internal(job)) { +- qapi_event_send_block_job_pending(job->driver->job_type, ++ qapi_event_send_block_job_pending(job_type(&job->job), + job->job.id, + &error_abort); + } +@@ -980,7 +978,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + block_job_sleep_timer_cb, job); + + error_setg(&job->blocker, "block device is in use by block job: %s", +- JobType_str(driver->job_type)); ++ job_type_str(&job->job)); + block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort); + bs->job = job; + +@@ -1184,7 +1182,7 @@ void block_job_event_ready(BlockJob *job) + return; + } + +- qapi_event_send_block_job_ready(job->driver->job_type, ++ qapi_event_send_block_job_ready(job_type(&job->job), + job->job.id, + job->len, + job->offset, +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index 8e7e0a2..1e62d6d 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -38,9 +38,6 @@ struct BlockJobDriver { + /** Generic JobDriver callbacks and settings */ + JobDriver job_driver; + +- /** String describing the operation, part of query-block-jobs QMP API */ +- JobType job_type; +- + /** Mandatory: Entrypoint for the Coroutine. */ + CoroutineEntry *start; + +diff --git a/include/qemu/job.h b/include/qemu/job.h +index b4b49f1..279ce68 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -26,6 +26,8 @@ + #ifndef JOB_H + #define JOB_H + ++#include "qapi/qapi-types-block-core.h" ++ + typedef struct JobDriver JobDriver; + + /** +@@ -45,6 +47,9 @@ typedef struct Job { + struct JobDriver { + /** Derived Job struct size */ + size_t instance_size; ++ ++ /** Enum describing the operation */ ++ JobType job_type; + }; + + +@@ -57,4 +62,10 @@ struct JobDriver { + */ + void *job_create(const char *job_id, const JobDriver *driver, Error **errp); + ++/** 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); ++ + #endif +diff --git a/job.c b/job.c +index 87fd484..83724a4 100644 +--- a/job.c ++++ b/job.c +@@ -29,6 +29,16 @@ + #include "qemu/job.h" + #include "qemu/id.h" + ++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)); ++} ++ + void *job_create(const char *job_id, const JobDriver *driver, Error **errp) + { + Job *job; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Add-error-message-for-failing-jobs.patch b/SOURCES/kvm-job-Add-error-message-for-failing-jobs.patch new file mode 100644 index 0000000..ebca08b --- /dev/null +++ b/SOURCES/kvm-job-Add-error-message-for-failing-jobs.patch @@ -0,0 +1,257 @@ +From 864c5b5a3146aac264065f9ef5cf397451747440 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:39 +0200 +Subject: [PATCH 70/89] job: Add error message for failing jobs + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-57-kwolf@redhat.com> +Patchwork-id: 81110 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 56/73] job: Add error message for failing jobs +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +So far we relied on job->ret and strerror() to produce an error message +for failed jobs. Not surprisingly, this tends to result in completely +useless messages. + +This adds a Job.error field that can contain an error string for a +failing job, and a parameter to job_completed() that sets the field. As +a default, if NULL is passed, we continue to use strerror(job->ret). + +All existing callers are changed to pass NULL. They can be improved in +separate patches. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: Jeff Cody +(cherry picked from commit 1266c9b9f5fa05877b979eece5963a2bd99c3bfd) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 2 +- + block/commit.c | 2 +- + block/mirror.c | 2 +- + block/stream.c | 2 +- + include/qemu/job.h | 7 ++++++- + job-qmp.c | 9 ++------- + job.c | 16 ++++++++++++++-- + tests/test-bdrv-drain.c | 2 +- + tests/test-blockjob-txn.c | 2 +- + tests/test-blockjob.c | 2 +- + 10 files changed, 29 insertions(+), 17 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index 4e228e9..5661435 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -321,7 +321,7 @@ static void backup_complete(Job *job, void *opaque) + { + BackupCompleteData *data = opaque; + +- job_completed(job, data->ret); ++ job_completed(job, data->ret, NULL); + g_free(data); + } + +diff --git a/block/commit.c b/block/commit.c +index 6206661..e1814d9 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -117,7 +117,7 @@ static void commit_complete(Job *job, void *opaque) + * bdrv_set_backing_hd() to fail. */ + block_job_remove_all_bdrv(bjob); + +- job_completed(job, ret); ++ job_completed(job, ret, NULL); + g_free(data); + + /* If bdrv_drop_intermediate() didn't already do that, remove the commit +diff --git a/block/mirror.c b/block/mirror.c +index dcb66ec..435268b 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -581,7 +581,7 @@ static void mirror_exit(Job *job, void *opaque) + blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort); + blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort); + +- job_completed(job, data->ret); ++ job_completed(job, data->ret, NULL); + + g_free(data); + bdrv_drained_end(src); +diff --git a/block/stream.c b/block/stream.c +index a5d6e0c..9264b68 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -93,7 +93,7 @@ out: + } + + g_free(s->backing_file_str); +- job_completed(job, data->ret); ++ job_completed(job, data->ret, NULL); + g_free(data); + } + +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 8c8badf..1d82053 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -124,6 +124,9 @@ typedef struct Job { + /** Estimated progress_current value at the completion of the job */ + int64_t progress_total; + ++ /** Error string for a failed job (NULL if, and only if, job->ret == 0) */ ++ char *error; ++ + /** ret code passed to job_completed. */ + int ret; + +@@ -466,13 +469,15 @@ void job_transition_to_ready(Job *job); + /** + * @job: The job being completed. + * @ret: The status code. ++ * @error: The error message for a failing job (only with @ret < 0). If @ret is ++ * negative, but NULL is given for @error, strerror() is used. + * + * Marks @job as completed. If @ret is non-zero, the job transaction it is part + * of is aborted. If @ret is 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. + */ +-void job_completed(Job *job, int ret); ++void job_completed(Job *job, int ret, Error *error); + + /** Asynchronously complete the specified @job. */ + void job_complete(Job *job, Error **errp); +diff --git a/job-qmp.c b/job-qmp.c +index 7f38f63..410775d 100644 +--- a/job-qmp.c ++++ b/job-qmp.c +@@ -136,14 +136,9 @@ void qmp_job_dismiss(const char *id, Error **errp) + static JobInfo *job_query_single(Job *job, Error **errp) + { + JobInfo *info; +- const char *errmsg = NULL; + + assert(!job_is_internal(job)); + +- if (job->ret < 0) { +- errmsg = strerror(-job->ret); +- } +- + info = g_new(JobInfo, 1); + *info = (JobInfo) { + .id = g_strdup(job->id), +@@ -151,8 +146,8 @@ static JobInfo *job_query_single(Job *job, Error **errp) + .status = job->status, + .current_progress = job->progress_current, + .total_progress = job->progress_total, +- .has_error = !!errmsg, +- .error = g_strdup(errmsg), ++ .has_error = !!job->error, ++ .error = g_strdup(job->error), + }; + + return info; +diff --git a/job.c b/job.c +index f026661..84e1402 100644 +--- a/job.c ++++ b/job.c +@@ -369,6 +369,7 @@ void job_unref(Job *job) + + QLIST_REMOVE(job, job_list); + ++ g_free(job->error); + g_free(job->id); + g_free(job); + } +@@ -660,6 +661,9 @@ static void job_update_rc(Job *job) + job->ret = -ECANCELED; + } + if (job->ret) { ++ if (!job->error) { ++ job->error = g_strdup(strerror(-job->ret)); ++ } + job_state_transition(job, JOB_STATUS_ABORTING); + } + } +@@ -782,6 +786,7 @@ static int job_prepare(Job *job) + { + if (job->ret == 0 && job->driver->prepare) { + job->ret = job->driver->prepare(job); ++ job_update_rc(job); + } + return job->ret; + } +@@ -855,10 +860,17 @@ static void job_completed_txn_success(Job *job) + } + } + +-void job_completed(Job *job, int ret) ++void job_completed(Job *job, int ret, Error *error) + { + assert(job && job->txn && !job_is_completed(job)); ++ + job->ret = ret; ++ if (error) { ++ assert(job->ret < 0); ++ job->error = g_strdup(error_get_pretty(error)); ++ error_free(error); ++ } ++ + job_update_rc(job); + trace_job_completed(job, ret, job->ret); + if (job->ret) { +@@ -876,7 +888,7 @@ void job_cancel(Job *job, bool force) + } + job_cancel_async(job, force); + if (!job_started(job)) { +- job_completed(job, -ECANCELED); ++ job_completed(job, -ECANCELED, NULL); + } else if (job->deferred_to_main_loop) { + job_completed_txn_abort(job); + } else { +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index 2cba63b..a11c4cf 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -498,7 +498,7 @@ typedef struct TestBlockJob { + + static void test_job_completed(Job *job, void *opaque) + { +- job_completed(job, 0); ++ job_completed(job, 0, NULL); + } + + static void coroutine_fn test_job_start(void *opaque) +diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c +index fce8366..58d9b87 100644 +--- a/tests/test-blockjob-txn.c ++++ b/tests/test-blockjob-txn.c +@@ -34,7 +34,7 @@ static void test_block_job_complete(Job *job, void *opaque) + rc = -ECANCELED; + } + +- job_completed(job, rc); ++ job_completed(job, rc, NULL); + bdrv_unref(bs); + } + +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index e408d52..cb42f06 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -167,7 +167,7 @@ static void cancel_job_completed(Job *job, void *opaque) + { + CancelJob *s = opaque; + s->completed = true; +- job_completed(job, 0); ++ job_completed(job, 0, NULL); + } + + static void cancel_job_complete(Job *job, Error **errp) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Add-job_delete.patch b/SOURCES/kvm-job-Add-job_delete.patch new file mode 100644 index 0000000..bbc4a98 --- /dev/null +++ b/SOURCES/kvm-job-Add-job_delete.patch @@ -0,0 +1,75 @@ +From 6828f21b1fda85e52bbbe92b4a37f1fde5de5ff0 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:02 +0200 +Subject: [PATCH 33/89] job: Add job_delete() + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-20-kwolf@redhat.com> +Patchwork-id: 81065 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 19/73] job: Add job_delete() +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This moves freeing the Job object and its fields from block_job_unref() +to job_delete(). + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: John Snow +(cherry picked from commit fd61a701f1de8e4c1d89b3716ba9ca749cf5c724) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + blockjob.c | 3 +-- + include/qemu/job.h | 3 +++ + job.c | 6 ++++++ + 3 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/blockjob.c b/blockjob.c +index ea71ec0..430a67b 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -261,9 +261,8 @@ void block_job_unref(BlockJob *job) + block_job_detach_aio_context, job); + blk_unref(job->blk); + error_free(job->blocker); +- g_free(job->job.id); + assert(!timer_pending(&job->sleep_timer)); +- g_free(job); ++ job_delete(&job->job); + } + } + +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 279ce68..43dc2e4 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -62,6 +62,9 @@ struct JobDriver { + */ + void *job_create(const char *job_id, const JobDriver *driver, Error **errp); + ++/** Frees the @job object. */ ++void job_delete(Job *job); ++ + /** Returns the JobType of a given Job. */ + JobType job_type(const Job *job); + +diff --git a/job.c b/job.c +index 83724a4..cfdd008 100644 +--- a/job.c ++++ b/job.c +@@ -56,3 +56,9 @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp) + + return job; + } ++ ++void job_delete(Job *job) ++{ ++ g_free(job->id); ++ g_free(job); ++} +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Add-job_dismiss.patch b/SOURCES/kvm-job-Add-job_dismiss.patch new file mode 100644 index 0000000..65d8035 --- /dev/null +++ b/SOURCES/kvm-job-Add-job_dismiss.patch @@ -0,0 +1,174 @@ +From ba27464d8206fba129745e7b7eb38d59ef05fd5c Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:26 +0200 +Subject: [PATCH 57/89] job: Add job_dismiss() + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-44-kwolf@redhat.com> +Patchwork-id: 81125 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 43/73] job: Add job_dismiss() +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This moves block_job_dismiss() to the Job layer. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit 5f9a6a08e8f65e01746d2485fc65a3a78e74865f) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 10 ++++++---- + blockjob.c | 13 ------------- + include/block/blockjob.h | 9 --------- + include/qemu/job.h | 7 ++++++- + job.c | 15 ++++++++++++++- + tests/test-blockjob.c | 4 ++-- + 6 files changed, 28 insertions(+), 30 deletions(-) + +diff --git a/blockdev.c b/blockdev.c +index d6cd8ed..d8b6520 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3962,14 +3962,16 @@ void qmp_block_job_finalize(const char *id, Error **errp) + void qmp_block_job_dismiss(const char *id, Error **errp) + { + AioContext *aio_context; +- BlockJob *job = find_block_job(id, &aio_context, errp); ++ BlockJob *bjob = find_block_job(id, &aio_context, errp); ++ Job *job; + +- if (!job) { ++ if (!bjob) { + return; + } + +- trace_qmp_block_job_dismiss(job); +- block_job_dismiss(&job, errp); ++ trace_qmp_block_job_dismiss(bjob); ++ job = &bjob->job; ++ job_dismiss(&job, errp); + aio_context_release(aio_context); + } + +diff --git a/blockjob.c b/blockjob.c +index f146fe0..3ca009b 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -242,19 +242,6 @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) + return ratelimit_calculate_delay(&job->limit, n); + } + +-void block_job_dismiss(BlockJob **jobptr, Error **errp) +-{ +- BlockJob *job = *jobptr; +- /* similarly to _complete, this is QMP-interface only. */ +- assert(job->job.id); +- if (job_apply_verb(&job->job, JOB_VERB_DISMISS, errp)) { +- return; +- } +- +- job_do_dismiss(&job->job); +- *jobptr = NULL; +-} +- + void block_job_progress_update(BlockJob *job, uint64_t done) + { + job->offset += done; +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index e9ed7b8..5a81afc 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -141,15 +141,6 @@ void block_job_remove_all_bdrv(BlockJob *job); + void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp); + + /** +- * block_job_dismiss: +- * @job: The job to be dismissed. +- * @errp: Error object. +- * +- * Remove a concluded job from the query list. +- */ +-void block_job_dismiss(BlockJob **job, Error **errp); +- +-/** + * block_job_progress_update: + * @job: The job that has made progress + * @done: How much progress the job made +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 94900ec..1e8050c 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -487,6 +487,12 @@ int job_complete_sync(Job *job, Error **errp); + */ + void job_finalize(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. ++ */ ++void job_dismiss(Job **job, Error **errp); ++ + typedef void JobDeferToMainLoopFn(Job *job, void *opaque); + + /** +@@ -515,6 +521,5 @@ int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) + + /* TODO To be removed from the public interface */ + void job_state_transition(Job *job, JobStatus s1); +-void job_do_dismiss(Job *job); + + #endif +diff --git a/job.c b/job.c +index eede680..7cd3602 100644 +--- a/job.c ++++ b/job.c +@@ -568,7 +568,7 @@ void job_user_resume(Job *job, Error **errp) + job_resume(job); + } + +-void job_do_dismiss(Job *job) ++static void job_do_dismiss(Job *job) + { + assert(job); + job->busy = false; +@@ -581,6 +581,19 @@ void job_do_dismiss(Job *job) + job_unref(job); + } + ++void job_dismiss(Job **jobptr, Error **errp) ++{ ++ Job *job = *jobptr; ++ /* similarly to _complete, this is QMP-interface only. */ ++ assert(job->id); ++ if (job_apply_verb(job, JOB_VERB_DISMISS, errp)) { ++ return; ++ } ++ ++ job_do_dismiss(job); ++ *jobptr = NULL; ++} ++ + void job_early_fail(Job *job) + { + assert(job->status == JOB_STATUS_CREATED); +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index 46a7873..7131cab 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -233,8 +233,8 @@ static void cancel_common(CancelJob *s) + + job_cancel_sync(&job->job); + if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) { +- BlockJob *dummy = job; +- block_job_dismiss(&dummy, &error_abort); ++ Job *dummy = &job->job; ++ job_dismiss(&dummy, &error_abort); + } + assert(job->job.status == JOB_STATUS_NULL); + job_unref(&job->job); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Add-job_drain.patch b/SOURCES/kvm-job-Add-job_drain.patch new file mode 100644 index 0000000..386908f --- /dev/null +++ b/SOURCES/kvm-job-Add-job_drain.patch @@ -0,0 +1,281 @@ +From eb408c486b9f287df241f65b8abcb05b3c8772da Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:18 +0200 +Subject: [PATCH 49/89] job: Add job_drain() + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-36-kwolf@redhat.com> +Patchwork-id: 81119 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 35/73] job: Add job_drain() +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +block_job_drain() contains a blk_drain() call which cannot be moved to +Job, so add a new JobDriver callback JobDriver.drain which has a common +implementation for all BlockJobs. In addition to this we keep the +existing BlockJobDriver.drain callback that is called by the common +drain implementation for all block jobs. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit b69f777dd9ba992fdd35828a90eefcd88c0ec332) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 1 + + block/commit.c | 1 + + block/mirror.c | 2 ++ + block/stream.c | 1 + + blockjob.c | 20 ++++++++++---------- + include/block/blockjob_int.h | 12 ++++++++++++ + include/qemu/job.h | 13 +++++++++++++ + job.c | 11 +++++++++++ + tests/test-bdrv-drain.c | 1 + + tests/test-blockjob-txn.c | 1 + + tests/test-blockjob.c | 2 ++ + 11 files changed, 55 insertions(+), 10 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index bd31282..ca7d990 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -529,6 +529,7 @@ static const BlockJobDriver backup_job_driver = { + .job_type = JOB_TYPE_BACKUP, + .free = block_job_free, + .user_resume = block_job_user_resume, ++ .drain = block_job_drain, + .start = backup_run, + .commit = backup_commit, + .abort = backup_abort, +diff --git a/block/commit.c b/block/commit.c +index e53b2d7..02a8af9 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -221,6 +221,7 @@ static const BlockJobDriver commit_job_driver = { + .job_type = JOB_TYPE_COMMIT, + .free = block_job_free, + .user_resume = block_job_user_resume, ++ .drain = block_job_drain, + .start = commit_run, + }, + }; +diff --git a/block/mirror.c b/block/mirror.c +index c3951d1..a579bd8 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -992,6 +992,7 @@ static const BlockJobDriver mirror_job_driver = { + .job_type = JOB_TYPE_MIRROR, + .free = block_job_free, + .user_resume = block_job_user_resume, ++ .drain = block_job_drain, + .start = mirror_run, + .pause = mirror_pause, + }, +@@ -1006,6 +1007,7 @@ static const BlockJobDriver commit_active_job_driver = { + .job_type = JOB_TYPE_COMMIT, + .free = block_job_free, + .user_resume = block_job_user_resume, ++ .drain = block_job_drain, + .start = mirror_run, + .pause = mirror_pause, + }, +diff --git a/block/stream.c b/block/stream.c +index eee0253..b996278 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -215,6 +215,7 @@ static const BlockJobDriver stream_job_driver = { + .free = block_job_free, + .start = stream_run, + .user_resume = block_job_user_resume, ++ .drain = block_job_drain, + }, + }; + +diff --git a/blockjob.c b/blockjob.c +index 4cac367..63e1669 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -169,14 +169,13 @@ static void block_job_attached_aio_context(AioContext *new_context, + job_resume(&job->job); + } + +-static void block_job_drain(BlockJob *job) ++void block_job_drain(Job *job) + { +- /* If job is !job->job.busy this kicks it into the next pause point. */ +- block_job_enter(job); ++ BlockJob *bjob = container_of(job, BlockJob, job); + +- blk_drain(job->blk); +- if (job->driver->drain) { +- job->driver->drain(job); ++ blk_drain(bjob->blk); ++ if (bjob->driver->drain) { ++ bjob->driver->drain(bjob); + } + } + +@@ -190,7 +189,7 @@ static void block_job_detach_aio_context(void *opaque) + job_pause(&job->job); + + while (!job->job.paused && !job_is_completed(&job->job)) { +- block_job_drain(job); ++ job_drain(&job->job); + } + + job->job.aio_context = NULL; +@@ -327,11 +326,11 @@ static int block_job_finish_sync(BlockJob *job, + job_unref(&job->job); + return -EBUSY; + } +- /* block_job_drain calls block_job_enter, and it should be enough to +- * induce progress until the job completes or moves to the main thread. ++ /* job_drain calls job_enter, and it should be enough to induce progress ++ * until the job completes or moves to the main thread. + */ + while (!job->job.deferred_to_main_loop && !job_is_completed(&job->job)) { +- block_job_drain(job); ++ job_drain(&job->job); + } + while (!job_is_completed(&job->job)) { + aio_poll(qemu_get_aio_context(), true); +@@ -713,6 +712,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + assert(is_block_job(&job->job)); + assert(job->job.driver->free == &block_job_free); + assert(job->job.driver->user_resume == &block_job_user_resume); ++ assert(job->job.driver->drain == &block_job_drain); + + job->driver = driver; + job->blk = blk; +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index bf2b762..38fe22d 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -65,6 +65,10 @@ struct BlockJobDriver { + * If the callback is not NULL, it will be invoked when the job has to be + * synchronously cancelled or completed; it should drain BlockDriverStates + * as required to ensure progress. ++ * ++ * Block jobs must use the default implementation for job_driver.drain, ++ * which will in turn call this callback after doing generic block job ++ * stuff. + */ + void (*drain)(BlockJob *job); + }; +@@ -112,6 +116,14 @@ void block_job_free(Job *job); + void block_job_user_resume(Job *job); + + /** ++ * block_job_drain: ++ * Callback to be used for JobDriver.drain in all block jobs. Drains the main ++ * block node associated with the block jobs and calls BlockJobDriver.drain for ++ * job-specific actions. ++ */ ++void block_job_drain(Job *job); ++ ++/** + * block_job_yield: + * @job: The job that calls the function. + * +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 2648c74..aebc195 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -167,6 +167,13 @@ struct JobDriver { + */ + void (*user_resume)(Job *job); + ++ /* ++ * If the callback is not NULL, it will be invoked when the job has to be ++ * synchronously cancelled or completed; it should drain any activities ++ * as required to ensure progress. ++ */ ++ void (*drain)(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 +@@ -325,6 +332,12 @@ bool job_user_paused(Job *job); + */ + void job_user_resume(Job *job, Error **errp); + ++/* ++ * Drain any activities as required to ensure progress. This can be called in a ++ * loop to synchronously complete a job. ++ */ ++void job_drain(Job *job); ++ + /** + * Get the next element from the list of block jobs after @job, or the + * first one if @job is %NULL. +diff --git a/job.c b/job.c +index 64b64da..3772a35 100644 +--- a/job.c ++++ b/job.c +@@ -367,6 +367,17 @@ void coroutine_fn job_sleep_ns(Job *job, int64_t ns) + job_pause_point(job); + } + ++void job_drain(Job *job) ++{ ++ /* If job is !busy this kicks it into the next pause point. */ ++ job_enter(job); ++ ++ if (job->driver->drain) { ++ job->driver->drain(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. +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index c993512..58ea566 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -525,6 +525,7 @@ BlockJobDriver test_job_driver = { + .instance_size = sizeof(TestBlockJob), + .free = block_job_free, + .user_resume = block_job_user_resume, ++ .drain = block_job_drain, + .start = test_job_start, + }, + .complete = test_job_complete, +diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c +index 60e9fa2..1572f8d 100644 +--- a/tests/test-blockjob-txn.c ++++ b/tests/test-blockjob-txn.c +@@ -79,6 +79,7 @@ static const BlockJobDriver test_block_job_driver = { + .instance_size = sizeof(TestBlockJob), + .free = block_job_free, + .user_resume = block_job_user_resume, ++ .drain = block_job_drain, + .start = test_block_job_run, + }, + }; +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index 1fe6803..592a136 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -21,6 +21,7 @@ static const BlockJobDriver test_block_job_driver = { + .instance_size = sizeof(BlockJob), + .free = block_job_free, + .user_resume = block_job_user_resume, ++ .drain = block_job_drain, + }, + }; + +@@ -201,6 +202,7 @@ static const BlockJobDriver test_cancel_driver = { + .instance_size = sizeof(CancelJob), + .free = block_job_free, + .user_resume = block_job_user_resume, ++ .drain = block_job_drain, + .start = cancel_job_start, + }, + .complete = cancel_job_complete, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Add-job_event_.patch b/SOURCES/kvm-job-Add-job_event_.patch new file mode 100644 index 0000000..2d84e82 --- /dev/null +++ b/SOURCES/kvm-job-Add-job_event_.patch @@ -0,0 +1,232 @@ +From 68b982f2790c7a8caad15f9c088402bbd9d0c611 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:15 +0200 +Subject: [PATCH 46/89] job: Add job_event_*() + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-33-kwolf@redhat.com> +Patchwork-id: 81092 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 32/73] job: Add job_event_*() +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Go through the Job layer in order to send QMP events. For the moment, +these functions only call a notifier in the BlockJob layer that sends +the existing commands. + +This uses notifiers rather than JobDriver callbacks because internal +users of jobs won't receive QMP events, but might still be interested +in getting notified for the events. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit 139a9f020d49e9f863e0d46fd3d0b440dfb3b9d7) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + blockjob.c | 41 +++++++++++++++++++++++++++-------------- + include/block/blockjob.h | 9 +++++++++ + include/qemu/job.h | 18 ++++++++++++++++++ + job.c | 19 +++++++++++++++++++ + 4 files changed, 73 insertions(+), 14 deletions(-) + +diff --git a/blockjob.c b/blockjob.c +index b4334fb..05d7921 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -36,10 +36,6 @@ + #include "qemu/coroutine.h" + #include "qemu/timer.h" + +-static void block_job_event_cancelled(BlockJob *job); +-static void block_job_event_completed(BlockJob *job, const char *msg); +-static void block_job_event_pending(BlockJob *job); +- + /* Transactional group of block jobs */ + struct BlockJobTxn { + +@@ -352,13 +348,9 @@ static int block_job_finalize_single(BlockJob *job) + /* Emit events only if we actually started */ + if (job_started(&job->job)) { + if (job_is_cancelled(&job->job)) { +- block_job_event_cancelled(job); ++ job_event_cancelled(&job->job); + } else { +- const char *msg = NULL; +- if (job->ret < 0) { +- msg = strerror(-job->ret); +- } +- block_job_event_completed(job, msg); ++ job_event_completed(&job->job); + } + } + +@@ -504,7 +496,7 @@ static int block_job_transition_to_pending(BlockJob *job) + { + job_state_transition(&job->job, JOB_STATUS_PENDING); + if (!job->job.auto_finalize) { +- block_job_event_pending(job); ++ job_event_pending(&job->job); + } + return 0; + } +@@ -712,8 +704,10 @@ static void block_job_iostatus_set_err(BlockJob *job, int error) + } + } + +-static void block_job_event_cancelled(BlockJob *job) ++static void block_job_event_cancelled(Notifier *n, void *opaque) + { ++ BlockJob *job = opaque; ++ + if (block_job_is_internal(job)) { + return; + } +@@ -726,12 +720,19 @@ static void block_job_event_cancelled(BlockJob *job) + &error_abort); + } + +-static void block_job_event_completed(BlockJob *job, const char *msg) ++static void block_job_event_completed(Notifier *n, void *opaque) + { ++ BlockJob *job = opaque; ++ const char *msg = NULL; ++ + if (block_job_is_internal(job)) { + return; + } + ++ if (job->ret < 0) { ++ msg = strerror(-job->ret); ++ } ++ + qapi_event_send_block_job_completed(job_type(&job->job), + job->job.id, + job->len, +@@ -742,8 +743,10 @@ static void block_job_event_completed(BlockJob *job, const char *msg) + &error_abort); + } + +-static void block_job_event_pending(BlockJob *job) ++static void block_job_event_pending(Notifier *n, void *opaque) + { ++ BlockJob *job = opaque; ++ + if (block_job_is_internal(job)) { + return; + } +@@ -799,6 +802,16 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + job->cb = cb; + job->opaque = opaque; + ++ job->finalize_cancelled_notifier.notify = block_job_event_cancelled; ++ job->finalize_completed_notifier.notify = block_job_event_completed; ++ job->pending_notifier.notify = block_job_event_pending; ++ ++ 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); ++ + error_setg(&job->blocker, "block device is in use by block job: %s", + job_type_str(&job->job)); + block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort); +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index f9aaaaa..aef0629 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -82,6 +82,15 @@ typedef struct BlockJob { + /** Block other operations when block job is running */ + Error *blocker; + ++ /** 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; ++ + /** BlockDriverStates that are involved in this block job */ + GSList *nodes; + +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 9783e40..14d9377 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -105,6 +105,15 @@ typedef struct Job { + /** True if this job should automatically dismiss itself */ + bool auto_dismiss; + ++ /** 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; ++ + /** Element of the list of jobs */ + QLIST_ENTRY(Job) job_list; + } Job; +@@ -182,6 +191,15 @@ void job_ref(Job *job); + */ + void job_unref(Job *job); + ++/** To be called when a cancelled job is finalised. */ ++void job_event_cancelled(Job *job); ++ ++/** To be called when a successfully completed job is finalised. */ ++void job_event_completed(Job *job); ++ ++/** To be called when the job transitions to PENDING */ ++void job_event_pending(Job *job); ++ + /** + * 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 +diff --git a/job.c b/job.c +index dd46170..817c3b4 100644 +--- a/job.c ++++ b/job.c +@@ -215,6 +215,10 @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, + job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE); + job->auto_dismiss = !(flags & JOB_MANUAL_DISMISS); + ++ notifier_list_init(&job->on_finalize_cancelled); ++ notifier_list_init(&job->on_finalize_completed); ++ notifier_list_init(&job->on_pending); ++ + job_state_transition(job, JOB_STATUS_CREATED); + aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, + QEMU_CLOCK_REALTIME, SCALE_NS, +@@ -247,6 +251,21 @@ void job_unref(Job *job) + } + } + ++void job_event_cancelled(Job *job) ++{ ++ notifier_list_notify(&job->on_finalize_cancelled, job); ++} ++ ++void job_event_completed(Job *job) ++{ ++ notifier_list_notify(&job->on_finalize_completed, job); ++} ++ ++void job_event_pending(Job *job) ++{ ++ notifier_list_notify(&job->on_pending, job); ++} ++ + void job_enter_cond(Job *job, bool(*fn)(Job *job)) + { + if (!job_started(job)) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Add-job_is_ready.patch b/SOURCES/kvm-job-Add-job_is_ready.patch new file mode 100644 index 0000000..3316fdb --- /dev/null +++ b/SOURCES/kvm-job-Add-job_is_ready.patch @@ -0,0 +1,155 @@ +From bba237b4cbbd22a61ef4cc961a1b0937b806480b Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:27 +0200 +Subject: [PATCH 58/89] job: Add job_is_ready() + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-45-kwolf@redhat.com> +Patchwork-id: 81128 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 44/73] job: Add job_is_ready() +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Instead of having a 'bool ready' in BlockJob, add a function that +derives its value from the job status. + +At the same time, this fixes the behaviour to match what the QAPI +documentation promises for query-block-job: 'true if the job may be +completed'. When the ready flag was introduced in commit ef6dbf1e46e, +the flag never had to be reset to match the description because after +being ready, the jobs would immediately complete and disappear. + +Job transactions and manual job finalisation were introduced only later. +With these changes, jobs may stay around even after having completed +(and they are not ready to be completed a second time), however their +patches forgot to reset the ready flag. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit df956ae2014340bf7de0190edb1d09be55d9eadf) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + blockjob.c | 3 +-- + include/block/blockjob.h | 5 ----- + include/qemu/job.h | 3 +++ + job.c | 22 ++++++++++++++++++++++ + qemu-img.c | 2 +- + tests/test-blockjob.c | 2 +- + 6 files changed, 28 insertions(+), 9 deletions(-) + +diff --git a/blockjob.c b/blockjob.c +index 3ca009b..38f18e9 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -269,7 +269,7 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) + info->offset = job->offset; + info->speed = job->speed; + info->io_status = job->iostatus; +- info->ready = job->ready; ++ info->ready = job_is_ready(&job->job), + info->status = job->job.status; + info->auto_finalize = job->job.auto_finalize; + info->auto_dismiss = job->job.auto_dismiss; +@@ -436,7 +436,6 @@ void block_job_user_resume(Job *job) + void block_job_event_ready(BlockJob *job) + { + job_state_transition(&job->job, JOB_STATUS_READY); +- job->ready = true; + + if (block_job_is_internal(job)) { + return; +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 5a81afc..8e1e1ee 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -49,11 +49,6 @@ typedef struct BlockJob { + /** The block device on which the job is operating. */ + BlockBackend *blk; + +- /** +- * Set to true when the job is ready to be completed. +- */ +- bool ready; +- + /** Status that is published by the query-block-jobs QMP API */ + BlockDeviceIoStatus iostatus; + +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 1e8050c..487f9d9 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -367,6 +367,9 @@ bool job_is_cancelled(Job *job); + /** Returns whether the job is in a completed state. */ + bool job_is_completed(Job *job); + ++/** Returns whether the job is ready to be completed. */ ++bool job_is_ready(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 +diff --git a/job.c b/job.c +index 7cd3602..aa4c746 100644 +--- a/job.c ++++ b/job.c +@@ -199,6 +199,28 @@ bool job_is_cancelled(Job *job) + return job->cancelled; + } + ++bool job_is_ready(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_completed(Job *job) + { + switch (job->status) { +diff --git a/qemu-img.c b/qemu-img.c +index 734ea94..3c449a2 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -878,7 +878,7 @@ static void run_block_job(BlockJob *job, Error **errp) + aio_poll(aio_context, true); + qemu_progress_print(job->len ? + ((float)job->offset / job->len * 100.f) : 0.0f, 0); +- } while (!job->ready && !job_is_completed(&job->job)); ++ } while (!job_is_ready(&job->job) && !job_is_completed(&job->job)); + + if (!job_is_completed(&job->job)) { + ret = job_complete_sync(&job->job, errp); +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index 7131cab..8180d03 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -185,7 +185,7 @@ static void coroutine_fn cancel_job_start(void *opaque) + goto defer; + } + +- if (!s->common.ready && s->should_converge) { ++ if (!job_is_ready(&s->common.job) && s->should_converge) { + block_job_event_ready(&s->common); + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Add-job_sleep_ns.patch b/SOURCES/kvm-job-Add-job_sleep_ns.patch new file mode 100644 index 0000000..acea36a --- /dev/null +++ b/SOURCES/kvm-job-Add-job_sleep_ns.patch @@ -0,0 +1,363 @@ +From 4d81873c780a63cbad24b6ca611760a464c77999 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:10 +0200 +Subject: [PATCH 41/89] job: Add job_sleep_ns() + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-28-kwolf@redhat.com> +Patchwork-id: 81117 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 27/73] job: Add job_sleep_ns() +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +There is nothing block layer specific about block_job_sleep_ns(), so +move the function to Job. + +Signed-off-by: Kevin Wolf +Reviewed-by: John Snow +Reviewed-by: Max Reitz +(cherry picked from commit 5d43e86e11f488fda7956b13160e0c0105a84845) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 2 +- + block/commit.c | 2 +- + block/mirror.c | 4 ++-- + block/stream.c | 2 +- + blockjob.c | 27 --------------------------- + include/block/blockjob_int.h | 11 ----------- + include/qemu/job.h | 19 ++++++++++++++++++- + job.c | 32 ++++++++++++++++++++++++++++++++ + tests/test-bdrv-drain.c | 8 ++++---- + tests/test-blockjob-txn.c | 2 +- + tests/test-blockjob.c | 2 +- + 11 files changed, 61 insertions(+), 50 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index 7d9aad9..f3a4f7c 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -338,7 +338,7 @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job) + * return. Without a yield, the VM would not reboot. */ + delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read); + job->bytes_read = 0; +- block_job_sleep_ns(&job->common, delay_ns); ++ job_sleep_ns(&job->common.job, delay_ns); + + if (job_is_cancelled(&job->common.job)) { + return true; +diff --git a/block/commit.c b/block/commit.c +index 2fbc310..1c6cb6c 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -172,7 +172,7 @@ static void coroutine_fn commit_run(void *opaque) + /* 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_sleep_ns(&s->common, delay_ns); ++ job_sleep_ns(&s->common.job, delay_ns); + if (job_is_cancelled(&s->common.job)) { + break; + } +diff --git a/block/mirror.c b/block/mirror.c +index 95fc807..5d8f75c 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -595,7 +595,7 @@ static void mirror_throttle(MirrorBlockJob *s) + + if (now - s->last_pause_ns > BLOCK_JOB_SLICE_TIME) { + s->last_pause_ns = now; +- block_job_sleep_ns(&s->common, 0); ++ job_sleep_ns(&s->common.job, 0); + } else { + job_pause_point(&s->common.job); + } +@@ -869,7 +869,7 @@ static void coroutine_fn mirror_run(void *opaque) + cnt == 0 ? BLOCK_JOB_SLICE_TIME : 0); + } + trace_mirror_before_sleep(s, cnt, s->synced, delay_ns); +- block_job_sleep_ns(&s->common, delay_ns); ++ job_sleep_ns(&s->common.job, delay_ns); + if (job_is_cancelled(&s->common.job) && + (!s->synced || s->common.force)) + { +diff --git a/block/stream.c b/block/stream.c +index 6d8b7b6..1faab02 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -140,7 +140,7 @@ static void coroutine_fn stream_run(void *opaque) + /* 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_sleep_ns(&s->common, delay_ns); ++ job_sleep_ns(&s->common.job, delay_ns); + if (job_is_cancelled(&s->common.job)) { + break; + } +diff --git a/blockjob.c b/blockjob.c +index 313b1ff..4dc360c 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -181,7 +181,6 @@ void block_job_free(Job *job) + block_job_detach_aio_context, bjob); + blk_unref(bjob->blk); + error_free(bjob->blocker); +- assert(!timer_pending(&bjob->job.sleep_timer)); + } + + static void block_job_attached_aio_context(AioContext *new_context, +@@ -290,13 +289,6 @@ const BlockJobDriver *block_job_driver(BlockJob *job) + return job->driver; + } + +-static void block_job_sleep_timer_cb(void *opaque) +-{ +- BlockJob *job = opaque; +- +- block_job_enter(job); +-} +- + static void block_job_decommission(BlockJob *job) + { + assert(job); +@@ -866,9 +858,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + job->opaque = opaque; + job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE); + job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS); +- aio_timer_init(qemu_get_aio_context(), &job->job.sleep_timer, +- QEMU_CLOCK_REALTIME, SCALE_NS, +- block_job_sleep_timer_cb, job); + + error_setg(&job->blocker, "block device is in use by block job: %s", + job_type_str(&job->job)); +@@ -931,22 +920,6 @@ void block_job_enter(BlockJob *job) + job_enter_cond(&job->job, NULL); + } + +-void block_job_sleep_ns(BlockJob *job, int64_t ns) +-{ +- assert(job->job.busy); +- +- /* Check cancellation *before* setting busy = false, too! */ +- if (job_is_cancelled(&job->job)) { +- return; +- } +- +- if (!job_should_pause(&job->job)) { +- job_do_yield(&job->job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns); +- } +- +- job_pause_point(&job->job); +-} +- + void block_job_yield(BlockJob *job) + { + assert(job->job.busy); +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index 0a614a8..8937f5b 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -134,17 +134,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + void block_job_free(Job *job); + + /** +- * block_job_sleep_ns: +- * @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. +- */ +-void block_job_sleep_ns(BlockJob *job, int64_t ns); +- +-/** + * block_job_yield: + * @job: The job that calls the function. + * +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 9dcff12..509408f 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -58,7 +58,7 @@ typedef struct Job { + Coroutine *co; + + /** +- * Timer that is used by @block_job_sleep_ns. Accessed under job_mutex (in ++ * Timer that is used by @job_sleep_ns. Accessed under job_mutex (in + * job.c). + */ + QEMUTimer sleep_timer; +@@ -168,6 +168,13 @@ void job_enter_cond(Job *job, bool(*fn)(Job *job)); + void job_start(Job *job); + + /** ++ * @job: The job to enter. ++ * ++ * Continue the specified job by entering the coroutine. ++ */ ++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 +@@ -175,6 +182,16 @@ void job_start(Job *job); + */ + void coroutine_fn job_pause_point(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. ++ */ ++void coroutine_fn job_sleep_ns(Job *job, int64_t ns); ++ + + /** Returns the JobType of a given Job. */ + JobType job_type(const Job *job); +diff --git a/job.c b/job.c +index 78497fd..1b8cba1 100644 +--- a/job.c ++++ b/job.c +@@ -152,6 +152,13 @@ Job *job_get(const char *id) + return NULL; + } + ++static void job_sleep_timer_cb(void *opaque) ++{ ++ Job *job = opaque; ++ ++ job_enter(job); ++} ++ + void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, + Error **errp) + { +@@ -178,6 +185,9 @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, + job->pause_count = 1; + + job_state_transition(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); + +@@ -193,6 +203,7 @@ void job_unref(Job *job) + { + if (--job->refcnt == 0) { + assert(job->status == JOB_STATUS_NULL); ++ assert(!timer_pending(&job->sleep_timer)); + + if (job->driver->free) { + job->driver->free(job); +@@ -232,6 +243,11 @@ void job_enter_cond(Job *job, bool(*fn)(Job *job)) + aio_co_wake(job->co); + } + ++void job_enter(Job *job) ++{ ++ job_enter_cond(job, NULL); ++} ++ + /* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds. + * Reentering the job coroutine with block_job_enter() before the timer has + * expired is allowed and cancels the timer. +@@ -283,6 +299,22 @@ void coroutine_fn job_pause_point(Job *job) + } + } + ++void coroutine_fn job_sleep_ns(Job *job, int64_t ns) ++{ ++ assert(job->busy); ++ ++ /* Check cancellation *before* setting busy = false, too! */ ++ if (job_is_cancelled(job)) { ++ return; ++ } ++ ++ if (!job_should_pause(job)) { ++ job_do_yield(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns); ++ } ++ ++ job_pause_point(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. +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index c9f2f9b..50232f5 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -508,7 +508,7 @@ static void coroutine_fn test_job_start(void *opaque) + + block_job_event_ready(&s->common); + while (!s->should_complete) { +- block_job_sleep_ns(&s->common, 100000); ++ job_sleep_ns(&s->common.job, 100000); + } + + job_defer_to_main_loop(&s->common.job, test_job_completed, NULL); +@@ -553,7 +553,7 @@ static void test_blockjob_common(enum drain_type drain_type) + + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); +- g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */ ++ g_assert_false(job->job.busy); /* We're in job_sleep_ns() */ + + do_drain_begin(drain_type, src); + +@@ -571,7 +571,7 @@ static void test_blockjob_common(enum drain_type drain_type) + + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); +- g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */ ++ g_assert_false(job->job.busy); /* We're in job_sleep_ns() */ + + do_drain_begin(drain_type, target); + +@@ -589,7 +589,7 @@ static void test_blockjob_common(enum drain_type drain_type) + + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); +- g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */ ++ g_assert_false(job->job.busy); /* We're in job_sleep_ns() */ + + ret = block_job_complete_sync(job, &error_abort); + g_assert_cmpint(ret, ==, 0); +diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c +index 323e154..0e6162b 100644 +--- a/tests/test-blockjob-txn.c ++++ b/tests/test-blockjob-txn.c +@@ -45,7 +45,7 @@ static void coroutine_fn test_block_job_run(void *opaque) + + while (s->iterations--) { + if (s->use_timer) { +- block_job_sleep_ns(job, 0); ++ job_sleep_ns(&job->job, 0); + } else { + block_job_yield(job); + } +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index 1d18325..b329bd5 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -188,7 +188,7 @@ static void coroutine_fn cancel_job_start(void *opaque) + block_job_event_ready(&s->common); + } + +- block_job_sleep_ns(&s->common, 100000); ++ job_sleep_ns(&s->common.job, 100000); + } + + defer: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Add-job_transition_to_ready.patch b/SOURCES/kvm-job-Add-job_transition_to_ready.patch new file mode 100644 index 0000000..53ddba5 --- /dev/null +++ b/SOURCES/kvm-job-Add-job_transition_to_ready.patch @@ -0,0 +1,267 @@ +From 0bf028e317ae12d5559465d15a5780de8abfe464 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:28 +0200 +Subject: [PATCH 59/89] job: Add job_transition_to_ready() + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-46-kwolf@redhat.com> +Patchwork-id: 81114 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 45/73] job: Add job_transition_to_ready() +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +The transition to the READY state was still performed in the BlockJob +layer, in the same function that sent the BLOCK_JOB_READY QMP event. + +This patch brings the state transition to the Job layer and implements +the QMP event using a notifier called from the Job layer, like we +already do for other events related to state transitions. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit 2e1795b58131427719c7cd11f8b9b6984b3f24f8) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/mirror.c | 6 +++--- + blockjob.c | 33 ++++++++++++++++++--------------- + include/block/blockjob.h | 3 +++ + include/block/blockjob_int.h | 8 -------- + include/qemu/job.h | 9 ++++++--- + job.c | 16 +++++++++++++--- + tests/test-bdrv-drain.c | 2 +- + tests/test-blockjob.c | 2 +- + 8 files changed, 45 insertions(+), 34 deletions(-) + +diff --git a/block/mirror.c b/block/mirror.c +index 687f955..bdc1b5b 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -727,8 +727,8 @@ static void coroutine_fn mirror_run(void *opaque) + } + + if (s->bdev_length == 0) { +- /* Report BLOCK_JOB_READY and wait for complete. */ +- block_job_event_ready(&s->common); ++ /* Transition to the READY state and wait for complete. */ ++ job_transition_to_ready(&s->common.job); + s->synced = true; + while (!job_is_cancelled(&s->common.job) && !s->should_complete) { + job_yield(&s->common.job); +@@ -824,7 +824,7 @@ static void coroutine_fn mirror_run(void *opaque) + * report completion. This way, block-job-cancel will leave + * the target in a consistent state. + */ +- block_job_event_ready(&s->common); ++ job_transition_to_ready(&s->common.job); + s->synced = true; + } + +diff --git a/blockjob.c b/blockjob.c +index 38f18e9..da11b3b 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -338,6 +338,22 @@ static void block_job_event_pending(Notifier *n, void *opaque) + &error_abort); + } + ++static void block_job_event_ready(Notifier *n, void *opaque) ++{ ++ BlockJob *job = opaque; ++ ++ if (block_job_is_internal(job)) { ++ return; ++ } ++ ++ qapi_event_send_block_job_ready(job_type(&job->job), ++ job->job.id, ++ job->len, ++ job->offset, ++ job->speed, &error_abort); ++} ++ ++ + /* + * API for block job drivers and the block layer. These functions are + * declared in blockjob_int.h. +@@ -386,12 +402,14 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + job->finalize_cancelled_notifier.notify = block_job_event_cancelled; + job->finalize_completed_notifier.notify = block_job_event_completed; + job->pending_notifier.notify = block_job_event_pending; ++ job->ready_notifier.notify = block_job_event_ready; + + 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); + + error_setg(&job->blocker, "block device is in use by block job: %s", + job_type_str(&job->job)); +@@ -433,21 +451,6 @@ void block_job_user_resume(Job *job) + block_job_iostatus_reset(bjob); + } + +-void block_job_event_ready(BlockJob *job) +-{ +- job_state_transition(&job->job, JOB_STATUS_READY); +- +- if (block_job_is_internal(job)) { +- return; +- } +- +- qapi_event_send_block_job_ready(job_type(&job->job), +- job->job.id, +- job->len, +- job->offset, +- job->speed, &error_abort); +-} +- + BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, + int is_read, int error) + { +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 8e1e1ee..4fca45f 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -76,6 +76,9 @@ typedef struct BlockJob { + /** Called when the job transitions to PENDING */ + Notifier pending_notifier; + ++ /** Called when the job transitions to READY */ ++ Notifier ready_notifier; ++ + /** BlockDriverStates that are involved in this block job */ + GSList *nodes; + } BlockJob; +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index 806ac64..5cd50c6 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -116,14 +116,6 @@ void block_job_drain(Job *job); + int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n); + + /** +- * block_job_event_ready: +- * @job: The job which is now ready to be completed. +- * +- * Send a BLOCK_JOB_READY event for the specified job. +- */ +-void block_job_event_ready(BlockJob *job); +- +-/** + * block_job_error_action: + * @job: The job to signal an error for. + * @on_err: The error action setting. +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 487f9d9..bfc2bc5 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -132,6 +132,9 @@ typedef struct Job { + /** Notifiers called when the job transitions to PENDING */ + NotifierList on_pending; + ++ /** Notifiers called when the job transitions to READY */ ++ NotifierList on_ready; ++ + /** Element of the list of jobs */ + QLIST_ENTRY(Job) job_list; + +@@ -426,6 +429,9 @@ int job_apply_verb(Job *job, JobVerb verb, Error **errp); + /** The @job could not be started, free it. */ + void job_early_fail(Job *job); + ++/** Moves the @job from RUNNING to READY */ ++void job_transition_to_ready(Job *job); ++ + /** + * @job: The job being completed. + * @ret: The status code. +@@ -522,7 +528,4 @@ void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque); + */ + int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp); + +-/* TODO To be removed from the public interface */ +-void job_state_transition(Job *job, JobStatus s1); +- + #endif +diff --git a/job.c b/job.c +index aa4c746..b5bd51b 100644 +--- a/job.c ++++ b/job.c +@@ -157,9 +157,7 @@ static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock) + return rc; + } + +- +-/* TODO Make static once the whole state machine is in job.c */ +-void job_state_transition(Job *job, JobStatus s1) ++static void job_state_transition(Job *job, JobStatus s1) + { + JobStatus s0 = job->status; + assert(s1 >= 0 && s1 <= JOB_STATUS__MAX); +@@ -321,6 +319,7 @@ void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, + 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); + + job_state_transition(job, JOB_STATUS_CREATED); + aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, +@@ -380,6 +379,11 @@ static void job_event_pending(Job *job) + notifier_list_notify(&job->on_pending, job); + } + ++static void job_event_ready(Job *job) ++{ ++ notifier_list_notify(&job->on_ready, job); ++} ++ + void job_enter_cond(Job *job, bool(*fn)(Job *job)) + { + if (!job_started(job)) { +@@ -799,6 +803,12 @@ static int job_transition_to_pending(Job *job) + return 0; + } + ++void job_transition_to_ready(Job *job) ++{ ++ job_state_transition(job, JOB_STATUS_READY); ++ job_event_ready(job); ++} ++ + static void job_completed_txn_success(Job *job) + { + JobTxn *txn = job->txn; +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index 3600ffd..2cba63b 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -505,7 +505,7 @@ static void coroutine_fn test_job_start(void *opaque) + { + TestBlockJob *s = opaque; + +- block_job_event_ready(&s->common); ++ job_transition_to_ready(&s->common.job); + while (!s->should_complete) { + job_sleep_ns(&s->common.job, 100000); + } +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index 8180d03..e408d52 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -186,7 +186,7 @@ static void coroutine_fn cancel_job_start(void *opaque) + } + + if (!job_is_ready(&s->common.job) && s->should_converge) { +- block_job_event_ready(&s->common); ++ job_transition_to_ready(&s->common.job); + } + + job_sleep_ns(&s->common.job, 100000); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Add-job_yield.patch b/SOURCES/kvm-job-Add-job_yield.patch new file mode 100644 index 0000000..c26d74c --- /dev/null +++ b/SOURCES/kvm-job-Add-job_yield.patch @@ -0,0 +1,191 @@ +From 053dd2951086a745e456f083d67e055f27029c3d Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:25 +0200 +Subject: [PATCH 56/89] job: Add job_yield() + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-43-kwolf@redhat.com> +Patchwork-id: 81120 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 42/73] job: Add job_yield() +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This moves block_job_yield() to the Job layer. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit 198c49cc8d81e8eb0df3749d395599895c3a3a76) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 2 +- + block/mirror.c | 2 +- + blockjob.c | 16 ---------------- + include/block/blockjob_int.h | 8 -------- + include/qemu/job.h | 9 +++++++-- + job.c | 20 ++++++++++++++++++-- + tests/test-blockjob-txn.c | 2 +- + 7 files changed, 28 insertions(+), 31 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index b13f91d..6f4f3df 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -444,7 +444,7 @@ static void coroutine_fn backup_run(void *opaque) + while (!job_is_cancelled(&job->common.job)) { + /* Yield until the job is cancelled. We just let our before_write + * notify callback service CoW requests. */ +- block_job_yield(&job->common); ++ job_yield(&job->common.job); + } + } else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { + ret = backup_run_incremental(job); +diff --git a/block/mirror.c b/block/mirror.c +index c63cf7c..687f955 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -731,7 +731,7 @@ static void coroutine_fn mirror_run(void *opaque) + block_job_event_ready(&s->common); + s->synced = true; + while (!job_is_cancelled(&s->common.job) && !s->should_complete) { +- block_job_yield(&s->common); ++ job_yield(&s->common.job); + } + s->common.job.cancelled = false; + goto immediate_exit; +diff --git a/blockjob.c b/blockjob.c +index 438baa1..f146fe0 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -431,22 +431,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + return job; + } + +-void block_job_yield(BlockJob *job) +-{ +- assert(job->job.busy); +- +- /* Check cancellation *before* setting busy = false, too! */ +- if (job_is_cancelled(&job->job)) { +- return; +- } +- +- if (!job_should_pause(&job->job)) { +- job_do_yield(&job->job, -1); +- } +- +- job_pause_point(&job->job); +-} +- + void block_job_iostatus_reset(BlockJob *job) + { + if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index 7df07b2..806ac64 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -108,14 +108,6 @@ void block_job_user_resume(Job *job); + void block_job_drain(Job *job); + + /** +- * block_job_yield: +- * @job: The job that calls the function. +- * +- * Yield the block job coroutine. +- */ +-void block_job_yield(BlockJob *job); +- +-/** + * block_job_ratelimit_get_delay: + * + * Calculate and return delay for the next request in ns. See the documentation +diff --git a/include/qemu/job.h b/include/qemu/job.h +index bbe1b0c..94900ec 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -339,6 +339,13 @@ void coroutine_fn job_pause_point(Job *job); + + /** + * @job: The job that calls the function. ++ * ++ * Yield the job coroutine. ++ */ ++void 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 +@@ -508,8 +515,6 @@ int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) + + /* TODO To be removed from the public interface */ + void job_state_transition(Job *job, JobStatus s1); +-void coroutine_fn job_do_yield(Job *job, uint64_t ns); +-bool job_should_pause(Job *job); + void job_do_dismiss(Job *job); + + #endif +diff --git a/job.c b/job.c +index 2e453f6..eede680 100644 +--- a/job.c ++++ b/job.c +@@ -226,7 +226,7 @@ static bool job_started(Job *job) + return job->co; + } + +-bool job_should_pause(Job *job) ++static bool job_should_pause(Job *job) + { + return job->pause_count > 0; + } +@@ -396,7 +396,7 @@ void job_enter(Job *job) + * + * If @ns is (uint64_t) -1, no timer is scheduled and job_enter() must be + * called explicitly. */ +-void coroutine_fn job_do_yield(Job *job, uint64_t ns) ++static void coroutine_fn job_do_yield(Job *job, uint64_t ns) + { + job_lock(); + if (ns != -1) { +@@ -441,6 +441,22 @@ void coroutine_fn job_pause_point(Job *job) + } + } + ++void job_yield(Job *job) ++{ ++ assert(job->busy); ++ ++ /* Check cancellation *before* setting busy = false, too! */ ++ if (job_is_cancelled(job)) { ++ return; ++ } ++ ++ if (!job_should_pause(job)) { ++ job_do_yield(job, -1); ++ } ++ ++ job_pause_point(job); ++} ++ + void coroutine_fn job_sleep_ns(Job *job, int64_t ns) + { + assert(job->busy); +diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c +index 34ee179..fce8366 100644 +--- a/tests/test-blockjob-txn.c ++++ b/tests/test-blockjob-txn.c +@@ -47,7 +47,7 @@ static void coroutine_fn test_block_job_run(void *opaque) + if (s->use_timer) { + job_sleep_ns(&job->job, 0); + } else { +- block_job_yield(job); ++ job_yield(&job->job); + } + + if (job_is_cancelled(&job->job)) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Add-lifecycle-QMP-commands.patch b/SOURCES/kvm-job-Add-lifecycle-QMP-commands.patch new file mode 100644 index 0000000..103e179 --- /dev/null +++ b/SOURCES/kvm-job-Add-lifecycle-QMP-commands.patch @@ -0,0 +1,324 @@ +From 50909c400b504bc133165f26971f621f915d5fdf Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:32 +0200 +Subject: [PATCH 63/89] job: Add lifecycle QMP commands + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-50-kwolf@redhat.com> +Patchwork-id: 81129 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 49/73] job: Add lifecycle QMP commands +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This adds QMP commands that control the transition between states of the +job lifecycle. + +Signed-off-by: Kevin Wolf +(cherry picked from commit 1a90bc8128ee7d16ce4abb131961e37084d75b16) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + MAINTAINERS | 1 + + Makefile.objs | 1 + + job-qmp.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + qapi/job.json | 99 +++++++++++++++++++++++++++++++++++++++++++ + trace-events | 9 ++++ + 5 files changed, 244 insertions(+) + create mode 100644 job-qmp.c + +diff --git a/MAINTAINERS b/MAINTAINERS +index 5aaf264..a783c92 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -1372,6 +1372,7 @@ S: Supported + F: blockjob.c + F: include/block/blockjob.h + F: job.c ++F: job-qmp.c + F: include/block/job.h + F: block/backup.c + F: block/commit.c +diff --git a/Makefile.objs b/Makefile.objs +index 3df8d58..c6c3554 100644 +--- a/Makefile.objs ++++ b/Makefile.objs +@@ -97,6 +97,7 @@ io-obj-y = io/ + ifeq ($(CONFIG_SOFTMMU),y) + common-obj-y = blockdev.o blockdev-nbd.o block/ + common-obj-y += bootdevice.o iothread.o ++common-obj-y += job-qmp.o + common-obj-y += net/ + common-obj-y += qdev-monitor.o device-hotplug.o + common-obj-$(CONFIG_WIN32) += os-win32.o +diff --git a/job-qmp.c b/job-qmp.c +new file mode 100644 +index 0000000..b2e18cf +--- /dev/null ++++ b/job-qmp.c +@@ -0,0 +1,134 @@ ++/* ++ * 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-common.h" ++#include "qemu/job.h" ++#include "qapi/qapi-commands-job.h" ++#include "qapi/error.h" ++#include "trace-root.h" ++ ++/* Get a job using its ID and acquire its AioContext */ ++static Job *find_job(const char *id, AioContext **aio_context, Error **errp) ++{ ++ Job *job; ++ ++ *aio_context = NULL; ++ ++ job = job_get(id); ++ if (!job) { ++ error_setg(errp, "Job not found"); ++ return NULL; ++ } ++ ++ *aio_context = job->aio_context; ++ aio_context_acquire(*aio_context); ++ ++ return job; ++} ++ ++void qmp_job_cancel(const char *id, Error **errp) ++{ ++ AioContext *aio_context; ++ Job *job = find_job(id, &aio_context, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_job_cancel(job); ++ job_user_cancel(job, true, errp); ++ aio_context_release(aio_context); ++} ++ ++void qmp_job_pause(const char *id, Error **errp) ++{ ++ AioContext *aio_context; ++ Job *job = find_job(id, &aio_context, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_job_pause(job); ++ job_user_pause(job, errp); ++ aio_context_release(aio_context); ++} ++ ++void qmp_job_resume(const char *id, Error **errp) ++{ ++ AioContext *aio_context; ++ Job *job = find_job(id, &aio_context, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_job_resume(job); ++ job_user_resume(job, errp); ++ aio_context_release(aio_context); ++} ++ ++void qmp_job_complete(const char *id, Error **errp) ++{ ++ AioContext *aio_context; ++ Job *job = find_job(id, &aio_context, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_job_complete(job); ++ job_complete(job, errp); ++ aio_context_release(aio_context); ++} ++ ++void qmp_job_finalize(const char *id, Error **errp) ++{ ++ AioContext *aio_context; ++ Job *job = find_job(id, &aio_context, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_job_finalize(job); ++ job_finalize(job, errp); ++ aio_context_release(aio_context); ++} ++ ++void qmp_job_dismiss(const char *id, Error **errp) ++{ ++ AioContext *aio_context; ++ Job *job = find_job(id, &aio_context, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_job_dismiss(job); ++ job_dismiss(&job, errp); ++ aio_context_release(aio_context); ++} +diff --git a/qapi/job.json b/qapi/job.json +index 9fbdd0c..b84dc6c 100644 +--- a/qapi/job.json ++++ b/qapi/job.json +@@ -106,3 +106,102 @@ + { 'event': 'JOB_STATUS_CHANGE', + 'data': { 'id': 'str', + 'status': 'JobStatus' } } ++ ++## ++# @job-pause: ++# ++# Pause an active job. ++# ++# This command returns immediately after marking the active job for pausing. ++# Pausing an already paused job is an error. ++# ++# The job will pause as soon as possible, which means transitioning into the ++# PAUSED state if it was RUNNING, or into STANDBY if it was READY. The ++# corresponding JOB_STATUS_CHANGE event will be emitted. ++# ++# Cancelling a paused job automatically resumes it. ++# ++# @id: The job identifier. ++# ++# Since: 2.13 ++## ++{ 'command': 'job-pause', 'data': { 'id': 'str' } } ++ ++## ++# @job-resume: ++# ++# Resume a paused job. ++# ++# This command returns immediately after resuming a paused job. Resuming an ++# already running job is an error. ++# ++# @id : The job identifier. ++# ++# Since: 2.13 ++## ++{ 'command': 'job-resume', 'data': { 'id': 'str' } } ++ ++## ++# @job-cancel: ++# ++# Instruct an active background job to cancel at the next opportunity. ++# This command returns immediately after marking the active job for ++# cancellation. ++# ++# The job will cancel as soon as possible and then emit a JOB_STATUS_CHANGE ++# event. Usually, the status will change to ABORTING, but it is possible that ++# a job successfully completes (e.g. because it was almost done and there was ++# no opportunity to cancel earlier than completing the job) and transitions to ++# PENDING instead. ++# ++# @id: The job identifier. ++# ++# Since: 2.13 ++## ++{ 'command': 'job-cancel', 'data': { 'id': 'str' } } ++ ++ ++## ++# @job-complete: ++# ++# Manually trigger completion of an active job in the READY state. ++# ++# @id: The job identifier. ++# ++# Since: 2.13 ++## ++{ 'command': 'job-complete', 'data': { 'id': 'str' } } ++ ++## ++# @job-dismiss: ++# ++# Deletes a job that is in the CONCLUDED state. This command only needs to be ++# run explicitly for jobs that don't have automatic dismiss enabled. ++# ++# This command will refuse to operate on any job that has not yet reached its ++# terminal state, JOB_STATUS_CONCLUDED. For jobs that make use of JOB_READY ++# event, job-cancel or job-complete will still need to be used as appropriate. ++# ++# @id: The job identifier. ++# ++# Since: 2.13 ++## ++{ 'command': 'job-dismiss', 'data': { 'id': 'str' } } ++ ++## ++# @job-finalize: ++# ++# Instructs all jobs in a transaction (or a single job if it is not part of any ++# transaction) to finalize any graph changes and do any necessary cleanup. This ++# command requires that all involved jobs are in the PENDING state. ++# ++# For jobs in a transaction, instructing one job to finalize will force ++# ALL jobs in the transaction to finalize, so it is only necessary to instruct ++# a single member job to finalize. ++# ++# @id: The identifier of any job in the transaction, or of a job that is not ++# part of any transaction. ++# ++# Since: 2.13 ++## ++{ 'command': 'job-finalize', 'data': { 'id': 'str' } } +diff --git a/trace-events b/trace-events +index ef7579a..c445f54 100644 +--- a/trace-events ++++ b/trace-events +@@ -109,6 +109,15 @@ job_state_transition(void *job, int ret, const char *legal, const char *s0, con + job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)" + job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d" + ++# job-qmp.c ++qmp_job_cancel(void *job) "job %p" ++qmp_job_pause(void *job) "job %p" ++qmp_job_resume(void *job) "job %p" ++qmp_job_complete(void *job) "job %p" ++qmp_job_finalize(void *job) "job %p" ++qmp_job_dismiss(void *job) "job %p" ++ ++ + ### Guest events, keep at bottom + + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Add-query-jobs-QMP-command.patch b/SOURCES/kvm-job-Add-query-jobs-QMP-command.patch new file mode 100644 index 0000000..4f9edb3 --- /dev/null +++ b/SOURCES/kvm-job-Add-query-jobs-QMP-command.patch @@ -0,0 +1,175 @@ +From d32f4952587625fab92b42687273fbb9f89ae78c Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:33 +0200 +Subject: [PATCH 64/89] job: Add query-jobs QMP command + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-51-kwolf@redhat.com> +Patchwork-id: 81095 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 50/73] job: Add query-jobs QMP command +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This adds a minimal query-jobs implementation that shouldn't pose many +design questions. It can later be extended to expose more information, +and especially job-specific information. + +Signed-off-by: Kevin Wolf +(cherry picked from commit 456273b02474780537e2bb52a72213f63bb5227a) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + include/qemu/job.h | 3 +++ + job-qmp.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + job.c | 2 +- + qapi/job.json | 46 ++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 104 insertions(+), 1 deletion(-) + +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 92d1d24..8c8badf 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -392,6 +392,9 @@ 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 scheduled for cancellation. */ + bool job_is_cancelled(Job *job); + +diff --git a/job-qmp.c b/job-qmp.c +index b2e18cf..7f38f63 100644 +--- a/job-qmp.c ++++ b/job-qmp.c +@@ -132,3 +132,57 @@ void qmp_job_dismiss(const char *id, Error **errp) + job_dismiss(&job, errp); + aio_context_release(aio_context); + } ++ ++static JobInfo *job_query_single(Job *job, Error **errp) ++{ ++ JobInfo *info; ++ const char *errmsg = NULL; ++ ++ assert(!job_is_internal(job)); ++ ++ if (job->ret < 0) { ++ errmsg = strerror(-job->ret); ++ } ++ ++ info = g_new(JobInfo, 1); ++ *info = (JobInfo) { ++ .id = g_strdup(job->id), ++ .type = job_type(job), ++ .status = job->status, ++ .current_progress = job->progress_current, ++ .total_progress = job->progress_total, ++ .has_error = !!errmsg, ++ .error = g_strdup(errmsg), ++ }; ++ ++ return info; ++} ++ ++JobInfoList *qmp_query_jobs(Error **errp) ++{ ++ JobInfoList *head = NULL, **p_next = &head; ++ Job *job; ++ ++ for (job = job_next(NULL); job; job = job_next(job)) { ++ JobInfoList *elem; ++ AioContext *aio_context; ++ ++ if (job_is_internal(job)) { ++ continue; ++ } ++ elem = g_new0(JobInfoList, 1); ++ aio_context = job->aio_context; ++ aio_context_acquire(aio_context); ++ elem->value = job_query_single(job, errp); ++ aio_context_release(aio_context); ++ if (!elem->value) { ++ g_free(elem); ++ qapi_free_JobInfoList(head); ++ return NULL; ++ } ++ *p_next = elem; ++ p_next = &elem->next; ++ } ++ ++ return head; ++} +diff --git a/job.c b/job.c +index 599a104..f026661 100644 +--- a/job.c ++++ b/job.c +@@ -158,7 +158,7 @@ static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock) + return rc; + } + +-static bool job_is_internal(Job *job) ++bool job_is_internal(Job *job) + { + return (job->id == NULL); + } +diff --git a/qapi/job.json b/qapi/job.json +index b84dc6c..970124d 100644 +--- a/qapi/job.json ++++ b/qapi/job.json +@@ -205,3 +205,49 @@ + # Since: 2.13 + ## + { 'command': 'job-finalize', 'data': { 'id': 'str' } } ++ ++## ++# @JobInfo: ++# ++# Information about a job. ++# ++# @id: The job identifier ++# ++# @type: The kind of job that is being performed ++# ++# @status: Current job state/status ++# ++# @current-progress: Progress made until now. The unit is arbitrary and the ++# value can only meaningfully be used for the ratio of ++# @current-progress to @total-progress. The value is ++# monotonically increasing. ++# ++# @total-progress: Estimated @current-progress value at the completion of ++# the job. This value can arbitrarily change while the ++# job is running, in both directions. ++# ++# @error: If this field is present, the job failed; if it is ++# still missing in the CONCLUDED state, this indicates ++# successful completion. ++# ++# The value is a human-readable error message to describe ++# the reason for the job failure. It should not be parsed ++# by applications. ++# ++# Since: 2.13 ++## ++{ 'struct': 'JobInfo', ++ 'data': { 'id': 'str', 'type': 'JobType', 'status': 'JobStatus', ++ 'current-progress': 'int', 'total-progress': 'int', ++ '*error': 'str' } } ++ ++## ++# @query-jobs: ++# ++# Return information about jobs. ++# ++# Returns: a list with a @JobInfo for each active job ++# ++# Since: 2.13 ++## ++{ 'command': 'query-jobs', 'returns': ['JobInfo'] } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Add-reference-counting.patch b/SOURCES/kvm-job-Add-reference-counting.patch new file mode 100644 index 0000000..7f8dfc1 --- /dev/null +++ b/SOURCES/kvm-job-Add-reference-counting.patch @@ -0,0 +1,446 @@ +From 3f7eb910b9ce698ebc33a039e6bbef1df1437072 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:05 +0200 +Subject: [PATCH 36/89] job: Add reference counting + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-23-kwolf@redhat.com> +Patchwork-id: 81073 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 22/73] job: Add reference counting +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This moves reference counting from BlockJob to Job. + +In order to keep calling the BlockJob cleanup code when the job is +deleted via job_unref(), introduce a new JobDriver.free callback. Every +block job must use block_job_free() for this callback, this is asserted +in block_job_create(). + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: John Snow +(cherry picked from commit 80fa2c756b3241f24015a7503a01f7999d4a942d) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 1 + + block/commit.c | 1 + + block/mirror.c | 2 ++ + block/stream.c | 1 + + blockjob.c | 48 +++++++++++++++++++------------------------- + include/block/blockjob.h | 21 ------------------- + include/block/blockjob_int.h | 7 +++++++ + include/qemu/job.h | 19 ++++++++++++++++-- + job.c | 22 ++++++++++++++++---- + qemu-img.c | 4 ++-- + tests/test-bdrv-drain.c | 1 + + tests/test-blockjob-txn.c | 1 + + tests/test-blockjob.c | 6 ++++-- + 13 files changed, 76 insertions(+), 58 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index baf8d43..cfdb89d 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -526,6 +526,7 @@ static const BlockJobDriver backup_job_driver = { + .job_driver = { + .instance_size = sizeof(BackupBlockJob), + .job_type = JOB_TYPE_BACKUP, ++ .free = block_job_free, + }, + .start = backup_run, + .commit = backup_commit, +diff --git a/block/commit.c b/block/commit.c +index 32d29c8..925c96a 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -218,6 +218,7 @@ static const BlockJobDriver commit_job_driver = { + .job_driver = { + .instance_size = sizeof(CommitBlockJob), + .job_type = JOB_TYPE_COMMIT, ++ .free = block_job_free, + }, + .start = commit_run, + }; +diff --git a/block/mirror.c b/block/mirror.c +index 35fcc1f..0df4f70 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -989,6 +989,7 @@ static const BlockJobDriver mirror_job_driver = { + .job_driver = { + .instance_size = sizeof(MirrorBlockJob), + .job_type = JOB_TYPE_MIRROR, ++ .free = block_job_free, + }, + .start = mirror_run, + .complete = mirror_complete, +@@ -1001,6 +1002,7 @@ static const BlockJobDriver commit_active_job_driver = { + .job_driver = { + .instance_size = sizeof(MirrorBlockJob), + .job_type = JOB_TYPE_COMMIT, ++ .free = block_job_free, + }, + .start = mirror_run, + .complete = mirror_complete, +diff --git a/block/stream.c b/block/stream.c +index cb723f1..7273d22 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -212,6 +212,7 @@ static const BlockJobDriver stream_job_driver = { + .job_driver = { + .instance_size = sizeof(StreamBlockJob), + .job_type = JOB_TYPE_STREAM, ++ .free = block_job_free, + }, + .start = stream_run, + }; +diff --git a/blockjob.c b/blockjob.c +index 0fba01e..0bf0a26 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -190,31 +190,25 @@ static void block_job_resume(BlockJob *job) + block_job_enter_cond(job, block_job_timer_not_pending); + } + +-void block_job_ref(BlockJob *job) +-{ +- ++job->refcnt; +-} +- + static void block_job_attached_aio_context(AioContext *new_context, + void *opaque); + static void block_job_detach_aio_context(void *opaque); + +-void block_job_unref(BlockJob *job) ++void block_job_free(Job *job) + { +- if (--job->refcnt == 0) { +- assert(job->job.status == JOB_STATUS_NULL); +- assert(!job->txn); +- BlockDriverState *bs = blk_bs(job->blk); +- bs->job = NULL; +- block_job_remove_all_bdrv(job); +- blk_remove_aio_context_notifier(job->blk, +- block_job_attached_aio_context, +- block_job_detach_aio_context, job); +- blk_unref(job->blk); +- error_free(job->blocker); +- assert(!timer_pending(&job->sleep_timer)); +- job_delete(&job->job); +- } ++ BlockJob *bjob = container_of(job, BlockJob, job); ++ BlockDriverState *bs = blk_bs(bjob->blk); ++ ++ assert(!bjob->txn); ++ ++ bs->job = NULL; ++ block_job_remove_all_bdrv(bjob); ++ blk_remove_aio_context_notifier(bjob->blk, ++ block_job_attached_aio_context, ++ block_job_detach_aio_context, bjob); ++ blk_unref(bjob->blk); ++ error_free(bjob->blocker); ++ assert(!timer_pending(&bjob->sleep_timer)); + } + + static void block_job_attached_aio_context(AioContext *new_context, +@@ -245,7 +239,7 @@ static void block_job_detach_aio_context(void *opaque) + BlockJob *job = opaque; + + /* In case the job terminates during aio_poll()... */ +- block_job_ref(job); ++ job_ref(&job->job); + + block_job_pause(job); + +@@ -253,7 +247,7 @@ static void block_job_detach_aio_context(void *opaque) + block_job_drain(job); + } + +- block_job_unref(job); ++ job_unref(&job->job); + } + + static char *child_job_get_parent_desc(BdrvChild *c) +@@ -367,7 +361,7 @@ static void block_job_decommission(BlockJob *job) + job->deferred_to_main_loop = true; + block_job_txn_del_job(job); + job_state_transition(&job->job, JOB_STATUS_NULL); +- block_job_unref(job); ++ job_unref(&job->job); + } + + static void block_job_do_dismiss(BlockJob *job) +@@ -506,14 +500,14 @@ static int block_job_finish_sync(BlockJob *job, + + assert(blk_bs(job->blk)->job == job); + +- block_job_ref(job); ++ job_ref(&job->job); + + if (finish) { + finish(job, &local_err); + } + if (local_err) { + error_propagate(errp, local_err); +- block_job_unref(job); ++ job_unref(&job->job); + return -EBUSY; + } + /* block_job_drain calls block_job_enter, and it should be enough to +@@ -526,7 +520,7 @@ static int block_job_finish_sync(BlockJob *job, + aio_poll(qemu_get_aio_context(), true); + } + ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret; +- block_job_unref(job); ++ job_unref(&job->job); + return ret; + } + +@@ -909,6 +903,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + } + + assert(is_block_job(&job->job)); ++ assert(job->job.driver->free == &block_job_free); + + job->driver = driver; + job->blk = blk; +@@ -917,7 +912,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + job->busy = false; + job->paused = true; + job->pause_count = 1; +- job->refcnt = 1; + job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE); + job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS); + aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 01cdee6..087e782 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -132,9 +132,6 @@ typedef struct BlockJob { + /** The opaque value that is passed to the completion function. */ + void *opaque; + +- /** Reference count of the block job */ +- int refcnt; +- + /** True when job has reported completion by calling block_job_completed. */ + bool completed; + +@@ -400,24 +397,6 @@ void block_job_iostatus_reset(BlockJob *job); + BlockJobTxn *block_job_txn_new(void); + + /** +- * block_job_ref: +- * +- * Add a reference to BlockJob refcnt, it will be decreased with +- * block_job_unref, and then be freed if it comes to be the last +- * reference. +- */ +-void block_job_ref(BlockJob *job); +- +-/** +- * block_job_unref: +- * +- * Release a reference that was previously acquired with block_job_ref +- * or block_job_create. If it's the last reference to the object, it will be +- * freed. +- */ +-void block_job_unref(BlockJob *job); +- +-/** + * block_job_txn_unref: + * + * Release a reference that was previously acquired with block_job_txn_add_job +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index 1e62d6d..6f0fe3c 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -144,6 +144,13 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + 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_sleep_ns: + * @job: The job that calls the function. + * @ns: How many nanoseconds to stop for. +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 0b78778..0751e2a 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -41,6 +41,9 @@ typedef struct Job { + /** The type of this job. */ + const JobDriver *driver; + ++ /** Reference count of the block job */ ++ int refcnt; ++ + /** Current state; See @JobStatus for details. */ + JobStatus status; + +@@ -57,6 +60,9 @@ struct JobDriver { + + /** Enum describing the operation */ + JobType job_type; ++ ++ /** Called when the job is freed */ ++ void (*free)(Job *job); + }; + + +@@ -69,8 +75,17 @@ struct JobDriver { + */ + void *job_create(const char *job_id, const JobDriver *driver, Error **errp); + +-/** Frees the @job object. */ +-void job_delete(Job *job); ++/** ++ * 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. ++ */ ++void job_ref(Job *job); ++ ++/** ++ * Release a reference that was previously acquired with job_ref() or ++ * job_create(). If it's the last reference to the object, it will be freed. ++ */ ++void job_unref(Job *job); + + /** Returns the JobType of a given Job. */ + JobType job_type(const Job *job); +diff --git a/job.c b/job.c +index b049a32..926f1de 100644 +--- a/job.c ++++ b/job.c +@@ -134,6 +134,7 @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp) + job = g_malloc0(driver->instance_size); + job->driver = driver; + job->id = g_strdup(job_id); ++ job->refcnt = 1; + + job_state_transition(job, JOB_STATUS_CREATED); + +@@ -142,10 +143,23 @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp) + return job; + } + +-void job_delete(Job *job) ++void job_ref(Job *job) + { +- QLIST_REMOVE(job, job_list); ++ ++job->refcnt; ++} ++ ++void job_unref(Job *job) ++{ ++ if (--job->refcnt == 0) { ++ assert(job->status == JOB_STATUS_NULL); + +- g_free(job->id); +- g_free(job); ++ if (job->driver->free) { ++ job->driver->free(job); ++ } ++ ++ QLIST_REMOVE(job, job_list); ++ ++ g_free(job->id); ++ g_free(job); ++ } + } +diff --git a/qemu-img.c b/qemu-img.c +index e2395b9..f745919 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -873,7 +873,7 @@ static void run_block_job(BlockJob *job, Error **errp) + int ret = 0; + + aio_context_acquire(aio_context); +- block_job_ref(job); ++ job_ref(&job->job); + do { + aio_poll(aio_context, true); + qemu_progress_print(job->len ? +@@ -885,7 +885,7 @@ static void run_block_job(BlockJob *job, Error **errp) + } else { + ret = job->ret; + } +- block_job_unref(job); ++ job_unref(&job->job); + aio_context_release(aio_context); + + /* publish completion progress only when success */ +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index fe9f412..f9e37d4 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -522,6 +522,7 @@ static void test_job_complete(BlockJob *job, Error **errp) + BlockJobDriver test_job_driver = { + .job_driver = { + .instance_size = sizeof(TestBlockJob), ++ .free = block_job_free, + }, + .start = test_job_start, + .complete = test_job_complete, +diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c +index 48b12d1..b49b28c 100644 +--- a/tests/test-blockjob-txn.c ++++ b/tests/test-blockjob-txn.c +@@ -76,6 +76,7 @@ static void test_block_job_cb(void *opaque, int ret) + static const BlockJobDriver test_block_job_driver = { + .job_driver = { + .instance_size = sizeof(TestBlockJob), ++ .free = block_job_free, + }, + .start = test_block_job_run, + }; +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index 6ccd585..e24fc3f 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -19,6 +19,7 @@ + static const BlockJobDriver test_block_job_driver = { + .job_driver = { + .instance_size = sizeof(BlockJob), ++ .free = block_job_free, + }, + }; + +@@ -196,6 +197,7 @@ static void coroutine_fn cancel_job_start(void *opaque) + static const BlockJobDriver test_cancel_driver = { + .job_driver = { + .instance_size = sizeof(CancelJob), ++ .free = block_job_free, + }, + .start = cancel_job_start, + .complete = cancel_job_complete, +@@ -210,7 +212,7 @@ static CancelJob *create_common(BlockJob **pjob) + blk = create_blk(NULL); + job = mk_job(blk, "Steve", &test_cancel_driver, true, + BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS); +- block_job_ref(job); ++ job_ref(&job->job); + assert(job->job.status == JOB_STATUS_CREATED); + s = container_of(job, CancelJob, common); + s->blk = blk; +@@ -231,7 +233,7 @@ static void cancel_common(CancelJob *s) + block_job_dismiss(&dummy, &error_abort); + } + assert(job->job.status == JOB_STATUS_NULL); +- block_job_unref(job); ++ job_unref(&job->job); + destroy_blk(blk); + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Avoid-deadlocks-in-job_completed_txn_abort.patch b/SOURCES/kvm-job-Avoid-deadlocks-in-job_completed_txn_abort.patch new file mode 100644 index 0000000..35b5c8c --- /dev/null +++ b/SOURCES/kvm-job-Avoid-deadlocks-in-job_completed_txn_abort.patch @@ -0,0 +1,83 @@ +From c933e15569c9d9b3db7031d8c54712f819816f39 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:39 +0200 +Subject: [PATCH 48/49] job: Avoid deadlocks in job_completed_txn_abort() + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-42-kwolf@redhat.com> +Patchwork-id: 82194 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 41/42] job: Avoid deadlocks in job_completed_txn_abort() +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Amongst others, job_finalize_single() calls the .prepare/.commit/.abort +callbacks of the individual job driver. Recently, their use was adapted +for all block jobs so that they involve code calling AIO_WAIT_WHILE() +now. Such code must be called under the AioContext lock for the +respective job, but without holding any other AioContext lock. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + job.c | 16 +++++++++++----- + 1 file changed, 11 insertions(+), 5 deletions(-) + +diff --git a/job.c b/job.c +index 42af9e2..5b53e43 100644 +--- a/job.c ++++ b/job.c +@@ -713,6 +713,7 @@ static void job_cancel_async(Job *job, bool force) + + static void job_completed_txn_abort(Job *job) + { ++ AioContext *outer_ctx = job->aio_context; + AioContext *ctx; + JobTxn *txn = job->txn; + Job *other_job; +@@ -726,23 +727,26 @@ static void job_completed_txn_abort(Job *job) + txn->aborting = true; + job_txn_ref(txn); + +- /* We are the first failed job. Cancel other jobs. */ +- QLIST_FOREACH(other_job, &txn->jobs, txn_list) { +- ctx = other_job->aio_context; +- aio_context_acquire(ctx); +- } ++ /* We can only hold the single job's AioContext lock while calling ++ * job_finalize_single() because the finalization callbacks can involve ++ * calls of AIO_WAIT_WHILE(), which could deadlock otherwise. */ ++ aio_context_release(outer_ctx); + + /* 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) { ++ ctx = other_job->aio_context; ++ aio_context_acquire(ctx); + job_cancel_async(other_job, false); ++ aio_context_release(ctx); + } + } + while (!QLIST_EMPTY(&txn->jobs)) { + other_job = QLIST_FIRST(&txn->jobs); + ctx = other_job->aio_context; ++ aio_context_acquire(ctx); + if (!job_is_completed(other_job)) { + assert(job_is_cancelled(other_job)); + job_finish_sync(other_job, NULL, NULL); +@@ -751,6 +755,8 @@ static void job_completed_txn_abort(Job *job) + aio_context_release(ctx); + } + ++ aio_context_acquire(outer_ctx); ++ + job_txn_unref(txn); + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Convert-block_job_cancel_async-to-Job.patch b/SOURCES/kvm-job-Convert-block_job_cancel_async-to-Job.patch new file mode 100644 index 0000000..5629cd6 --- /dev/null +++ b/SOURCES/kvm-job-Convert-block_job_cancel_async-to-Job.patch @@ -0,0 +1,159 @@ +From c1d6c990e959598f6a1269756b481e4e4230ded8 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:17 +0200 +Subject: [PATCH 48/89] job: Convert block_job_cancel_async() to Job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-35-kwolf@redhat.com> +Patchwork-id: 81072 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 34/73] job: Convert block_job_cancel_async() to Job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +block_job_cancel_async() did two things that were still block job +specific: + +* Setting job->force. This field makes sense on the Job level, so we can + just move it. While at it, rename it to job->force_cancel to make its + purpose more obvious. + +* Resetting the I/O status. This can't be moved because generic Jobs + don't have an I/O status. What the function really implements is a + user resume, except without entering the coroutine. Consequently, it + makes sense to call the .user_resume driver callback here which + already resets the I/O status. + + The old block_job_cancel_async() has two separate if statements that + check job->iostatus != BLOCK_DEVICE_IO_STATUS_OK and job->user_paused. + However, the former condition always implies the latter (as is + asserted in block_job_iostatus_reset()), so changing the explicit call + of block_job_iostatus_reset() on the former condition with the + .user_resume callback on the latter condition is equivalent and + doesn't need to access any BlockJob specific state. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit 004e95df98266da33e08c9f1731aca71b6d6d7c4) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/mirror.c | 4 ++-- + blockjob.c | 25 +++++++++++++------------ + include/block/blockjob.h | 6 ------ + include/qemu/job.h | 6 ++++++ + 4 files changed, 21 insertions(+), 20 deletions(-) + +diff --git a/block/mirror.c b/block/mirror.c +index e9a90ea..c3951d1 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -871,7 +871,7 @@ static void coroutine_fn mirror_run(void *opaque) + trace_mirror_before_sleep(s, cnt, s->synced, delay_ns); + job_sleep_ns(&s->common.job, delay_ns); + if (job_is_cancelled(&s->common.job) && +- (!s->synced || s->common.force)) ++ (!s->synced || s->common.job.force_cancel)) + { + break; + } +@@ -884,7 +884,7 @@ immediate_exit: + * or it was cancelled prematurely so that we do not guarantee that + * the target is a copy of the source. + */ +- assert(ret < 0 || ((s->common.force || !s->synced) && ++ assert(ret < 0 || ((s->common.job.force_cancel || !s->synced) && + job_is_cancelled(&s->common.job))); + assert(need_drain); + mirror_wait_for_all_io(s); +diff --git a/blockjob.c b/blockjob.c +index 34c57da..4cac367 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -270,19 +270,20 @@ static int block_job_prepare(BlockJob *job) + return job->job.ret; + } + +-static void block_job_cancel_async(BlockJob *job, bool force) ++static void job_cancel_async(Job *job, bool force) + { +- if (job->iostatus != BLOCK_DEVICE_IO_STATUS_OK) { +- block_job_iostatus_reset(job); +- } +- if (job->job.user_paused) { +- /* Do not call block_job_enter here, the caller will handle it. */ +- job->job.user_paused = false; +- job->job.pause_count--; ++ if (job->user_paused) { ++ /* Do not call job_enter here, the caller will handle it. */ ++ job->user_paused = false; ++ if (job->driver->user_resume) { ++ job->driver->user_resume(job); ++ } ++ assert(job->pause_count > 0); ++ job->pause_count--; + } +- job->job.cancelled = true; ++ job->cancelled = true; + /* To prevent 'force == false' overriding a previous 'force == true' */ +- job->force |= force; ++ job->force_cancel |= force; + } + + static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock) +@@ -367,7 +368,7 @@ static void block_job_completed_txn_abort(BlockJob *job) + * on the caller, so leave it. */ + QLIST_FOREACH(other_job, &txn->jobs, txn_list) { + if (other_job != job) { +- block_job_cancel_async(other_job, false); ++ job_cancel_async(&other_job->job, false); + } + } + while (!QLIST_EMPTY(&txn->jobs)) { +@@ -527,7 +528,7 @@ void block_job_cancel(BlockJob *job, bool force) + job_do_dismiss(&job->job); + return; + } +- block_job_cancel_async(job, force); ++ job_cancel_async(&job->job, force); + if (!job_started(&job->job)) { + block_job_completed(job, -ECANCELED); + } else if (job->job.deferred_to_main_loop) { +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 3f405d1..d975efe 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -51,12 +51,6 @@ typedef struct BlockJob { + BlockBackend *blk; + + /** +- * Set to true if the job should abort immediately without waiting +- * for data to be in sync. +- */ +- bool force; +- +- /** + * Set to true when the job is ready to be completed. + */ + bool ready; +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 3e817be..2648c74 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -97,6 +97,12 @@ typedef struct Job { + */ + 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; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Create-Job-JobDriver-and-job_create.patch b/SOURCES/kvm-job-Create-Job-JobDriver-and-job_create.patch new file mode 100644 index 0000000..e162901 --- /dev/null +++ b/SOURCES/kvm-job-Create-Job-JobDriver-and-job_create.patch @@ -0,0 +1,571 @@ +From 0223091bd8c515f4eab85de49c039f1275dd8a66 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:47:59 +0200 +Subject: [PATCH 30/89] job: Create Job, JobDriver and job_create() + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-17-kwolf@redhat.com> +Patchwork-id: 81124 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 16/73] job: Create Job, JobDriver and job_create() +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This is the first step towards creating an infrastructure for generic +background jobs that aren't tied to a block device. For now, Job only +stores its ID and JobDriver, the rest stays in BlockJob. + +The following patches will move over more parts of BlockJob to Job if +they are meaningful outside the context of a block job. + +BlockJob.driver is now redundant, but this patch leaves it around to +avoid unnecessary churn. The next patches will get rid of almost all of +its uses anyway so that it can be removed later with much less churn. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: John Snow +(cherry picked from commit 33e9e9bd62d9ae00880d9e12ad8a5b7d2c00e8a5) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + MAINTAINERS | 2 ++ + Makefile.objs | 2 +- + block/backup.c | 4 ++- + block/commit.c | 4 ++- + block/mirror.c | 10 +++++--- + block/stream.c | 4 ++- + blockjob.c | 46 ++++++++++++++++----------------- + include/block/blockjob.h | 9 +++---- + include/block/blockjob_int.h | 4 +-- + include/qemu/job.h | 60 ++++++++++++++++++++++++++++++++++++++++++++ + job.c | 48 +++++++++++++++++++++++++++++++++++ + tests/test-bdrv-drain.c | 4 ++- + tests/test-blockjob-txn.c | 4 ++- + tests/test-blockjob.c | 12 ++++++--- + 14 files changed, 169 insertions(+), 44 deletions(-) + create mode 100644 include/qemu/job.h + create mode 100644 job.c + +diff --git a/MAINTAINERS b/MAINTAINERS +index ad5edc8..c5e3dfb 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -1371,6 +1371,8 @@ L: qemu-block@nongnu.org + S: Supported + F: blockjob.c + F: include/block/blockjob.h ++F: job.c ++F: include/block/job.h + F: block/backup.c + F: block/commit.c + F: block/stream.c +diff --git a/Makefile.objs b/Makefile.objs +index c6c9b8f..92b73fc 100644 +--- a/Makefile.objs ++++ b/Makefile.objs +@@ -63,7 +63,7 @@ chardev-obj-y = chardev/ + # block-obj-y is code used by both qemu system emulation and qemu-img + + block-obj-y += nbd/ +-block-obj-y += block.o blockjob.o ++block-obj-y += block.o blockjob.o job.o + block-obj-y += block/ scsi/ + block-obj-y += qemu-io-cmds.o + block-obj-$(CONFIG_REPLICATION) += replication.o +diff --git a/block/backup.c b/block/backup.c +index e14d995..9e672bb 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -523,7 +523,9 @@ static void coroutine_fn backup_run(void *opaque) + } + + static const BlockJobDriver backup_job_driver = { +- .instance_size = sizeof(BackupBlockJob), ++ .job_driver = { ++ .instance_size = sizeof(BackupBlockJob), ++ }, + .job_type = BLOCK_JOB_TYPE_BACKUP, + .start = backup_run, + .commit = backup_commit, +diff --git a/block/commit.c b/block/commit.c +index ba5df6a..18cbb2f 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -215,7 +215,9 @@ out: + } + + static const BlockJobDriver commit_job_driver = { +- .instance_size = sizeof(CommitBlockJob), ++ .job_driver = { ++ .instance_size = sizeof(CommitBlockJob), ++ }, + .job_type = BLOCK_JOB_TYPE_COMMIT, + .start = commit_run, + }; +diff --git a/block/mirror.c b/block/mirror.c +index a4197bb..aa1d6b7 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -913,7 +913,7 @@ static void mirror_complete(BlockJob *job, Error **errp) + + if (!s->synced) { + error_setg(errp, "The active block job '%s' cannot be completed", +- job->id); ++ job->job.id); + return; + } + +@@ -986,7 +986,9 @@ static void mirror_drain(BlockJob *job) + } + + static const BlockJobDriver mirror_job_driver = { +- .instance_size = sizeof(MirrorBlockJob), ++ .job_driver = { ++ .instance_size = sizeof(MirrorBlockJob), ++ }, + .job_type = BLOCK_JOB_TYPE_MIRROR, + .start = mirror_run, + .complete = mirror_complete, +@@ -996,7 +998,9 @@ static const BlockJobDriver mirror_job_driver = { + }; + + static const BlockJobDriver commit_active_job_driver = { +- .instance_size = sizeof(MirrorBlockJob), ++ .job_driver = { ++ .instance_size = sizeof(MirrorBlockJob), ++ }, + .job_type = BLOCK_JOB_TYPE_COMMIT, + .start = mirror_run, + .complete = mirror_complete, +diff --git a/block/stream.c b/block/stream.c +index df9660d..f88fc75 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -209,7 +209,9 @@ out: + } + + static const BlockJobDriver stream_job_driver = { +- .instance_size = sizeof(StreamBlockJob), ++ .job_driver = { ++ .instance_size = sizeof(StreamBlockJob), ++ }, + .job_type = BLOCK_JOB_TYPE_STREAM, + .start = stream_run, + }; +diff --git a/blockjob.c b/blockjob.c +index 112672a..1464856 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -34,7 +34,6 @@ + #include "qapi/qapi-events-block-core.h" + #include "qapi/qmp/qerror.h" + #include "qemu/coroutine.h" +-#include "qemu/id.h" + #include "qemu/timer.h" + + /* Right now, this mutex is only needed to synchronize accesses to job->busy +@@ -92,7 +91,8 @@ static int block_job_apply_verb(BlockJob *job, BlockJobVerb bv, Error **errp) + return 0; + } + error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'", +- job->id, BlockJobStatus_str(job->status), BlockJobVerb_str(bv)); ++ job->job.id, BlockJobStatus_str(job->status), ++ BlockJobVerb_str(bv)); + return -EPERM; + } + +@@ -159,7 +159,7 @@ BlockJob *block_job_get(const char *id) + BlockJob *job; + + QLIST_FOREACH(job, &block_jobs, job_list) { +- if (job->id && !strcmp(id, job->id)) { ++ if (job->job.id && !strcmp(id, job->job.id)) { + return job; + } + } +@@ -261,7 +261,7 @@ void block_job_unref(BlockJob *job) + block_job_detach_aio_context, job); + blk_unref(job->blk); + error_free(job->blocker); +- g_free(job->id); ++ g_free(job->job.id); + assert(!timer_pending(&job->sleep_timer)); + g_free(job); + } +@@ -311,7 +311,7 @@ static char *child_job_get_parent_desc(BdrvChild *c) + BlockJob *job = c->opaque; + return g_strdup_printf("%s job '%s'", + BlockJobType_str(job->driver->job_type), +- job->id); ++ job->job.id); + } + + static void child_job_drained_begin(BdrvChild *c) +@@ -365,7 +365,7 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, + + bool block_job_is_internal(BlockJob *job) + { +- return (job->id == NULL); ++ return (job->job.id == NULL); + } + + static bool block_job_started(BlockJob *job) +@@ -705,13 +705,13 @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) + void block_job_complete(BlockJob *job, Error **errp) + { + /* Should not be reachable via external interface for internal jobs */ +- assert(job->id); ++ assert(job->job.id); + if (block_job_apply_verb(job, BLOCK_JOB_VERB_COMPLETE, errp)) { + return; + } + if (job->pause_count || job->cancelled || !job->driver->complete) { + error_setg(errp, "The active block job '%s' cannot be completed", +- job->id); ++ job->job.id); + return; + } + +@@ -720,7 +720,7 @@ void block_job_complete(BlockJob *job, Error **errp) + + void block_job_finalize(BlockJob *job, Error **errp) + { +- assert(job && job->id); ++ assert(job && job->job.id); + if (block_job_apply_verb(job, BLOCK_JOB_VERB_FINALIZE, errp)) { + return; + } +@@ -731,7 +731,7 @@ void block_job_dismiss(BlockJob **jobptr, Error **errp) + { + BlockJob *job = *jobptr; + /* similarly to _complete, this is QMP-interface only. */ +- assert(job->id); ++ assert(job->job.id); + if (block_job_apply_verb(job, BLOCK_JOB_VERB_DISMISS, errp)) { + return; + } +@@ -848,7 +848,7 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) + } + info = g_new0(BlockJobInfo, 1); + info->type = g_strdup(BlockJobType_str(job->driver->job_type)); +- info->device = g_strdup(job->id); ++ info->device = g_strdup(job->job.id); + info->len = job->len; + info->busy = atomic_read(&job->busy); + info->paused = job->pause_count > 0; +@@ -879,7 +879,7 @@ static void block_job_event_cancelled(BlockJob *job) + } + + qapi_event_send_block_job_cancelled(job->driver->job_type, +- job->id, ++ job->job.id, + job->len, + job->offset, + job->speed, +@@ -893,7 +893,7 @@ static void block_job_event_completed(BlockJob *job, const char *msg) + } + + qapi_event_send_block_job_completed(job->driver->job_type, +- job->id, ++ job->job.id, + job->len, + job->offset, + job->speed, +@@ -907,7 +907,7 @@ static int block_job_event_pending(BlockJob *job) + block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING); + if (!job->auto_finalize && !block_job_is_internal(job)) { + qapi_event_send_block_job_pending(job->driver->job_type, +- job->id, ++ job->job.id, + &error_abort); + } + return 0; +@@ -945,12 +945,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + error_setg(errp, "Cannot specify job ID for internal block job"); + return NULL; + } +- +- if (!id_wellformed(job_id)) { +- error_setg(errp, "Invalid job ID '%s'", job_id); +- return NULL; +- } +- + if (block_job_get(job_id)) { + error_setg(errp, "Job ID '%s' already in use", job_id); + return NULL; +@@ -964,9 +958,13 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + return NULL; + } + +- job = g_malloc0(driver->instance_size); ++ job = job_create(job_id, &driver->job_driver, errp); ++ if (job == NULL) { ++ blk_unref(blk); ++ return NULL; ++ } ++ + job->driver = driver; +- job->id = g_strdup(job_id); + job->blk = blk; + job->cb = cb; + job->opaque = opaque; +@@ -1187,7 +1185,7 @@ void block_job_event_ready(BlockJob *job) + } + + qapi_event_send_block_job_ready(job->driver->job_type, +- job->id, ++ job->job.id, + job->len, + job->offset, + job->speed, &error_abort); +@@ -1217,7 +1215,7 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, + abort(); + } + if (!block_job_is_internal(job)) { +- qapi_event_send_block_job_error(job->id, ++ qapi_event_send_block_job_error(job->job.id, + is_read ? IO_OPERATION_TYPE_READ : + IO_OPERATION_TYPE_WRITE, + action, &error_abort); +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 0f56f72..640e649 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -26,6 +26,7 @@ + #ifndef BLOCKJOB_H + #define BLOCKJOB_H + ++#include "qemu/job.h" + #include "block/block.h" + #include "qemu/ratelimit.h" + +@@ -40,6 +41,9 @@ typedef struct BlockJobTxn BlockJobTxn; + * Long-running operation on a BlockDriverState. + */ + typedef struct BlockJob { ++ /** Data belonging to the generic Job infrastructure */ ++ Job job; ++ + /** The job type, including the job vtable. */ + const BlockJobDriver *driver; + +@@ -47,11 +51,6 @@ typedef struct BlockJob { + BlockBackend *blk; + + /** +- * The ID of the block job. May be NULL for internal jobs. +- */ +- char *id; +- +- /** + * The coroutine that executes the job. If not NULL, it is + * reentered when busy is false and the job is cancelled. + */ +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index 62ec964..e8eca44 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -35,8 +35,8 @@ + * A class type for block job driver. + */ + struct BlockJobDriver { +- /** Derived BlockJob struct size */ +- size_t instance_size; ++ /** Generic JobDriver callbacks and settings */ ++ JobDriver job_driver; + + /** String describing the operation, part of query-block-jobs QMP API */ + BlockJobType job_type; +diff --git a/include/qemu/job.h b/include/qemu/job.h +new file mode 100644 +index 0000000..b4b49f1 +--- /dev/null ++++ b/include/qemu/job.h +@@ -0,0 +1,60 @@ ++/* ++ * 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 ++ ++typedef struct JobDriver JobDriver; ++ ++/** ++ * Long-running operation. ++ */ ++typedef struct Job { ++ /** The ID of the job. May be NULL for internal jobs. */ ++ char *id; ++ ++ /** The type of this job. */ ++ const JobDriver *driver; ++} Job; ++ ++/** ++ * Callbacks and other information about a Job driver. ++ */ ++struct JobDriver { ++ /** Derived Job struct size */ ++ size_t instance_size; ++}; ++ ++ ++/** ++ * Create a new long-running job and return it. ++ * ++ * @job_id: The id of the newly-created job, or %NULL for internal jobs ++ * @driver: The class object for the newly-created job. ++ * @errp: Error object. ++ */ ++void *job_create(const char *job_id, const JobDriver *driver, Error **errp); ++ ++#endif +diff --git a/job.c b/job.c +new file mode 100644 +index 0000000..87fd484 +--- /dev/null ++++ b/job.c +@@ -0,0 +1,48 @@ ++/* ++ * 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 "qemu-common.h" ++#include "qapi/error.h" ++#include "qemu/job.h" ++#include "qemu/id.h" ++ ++void *job_create(const char *job_id, const JobDriver *driver, Error **errp) ++{ ++ Job *job; ++ ++ if (job_id) { ++ if (!id_wellformed(job_id)) { ++ error_setg(errp, "Invalid job ID '%s'", job_id); ++ return NULL; ++ } ++ } ++ ++ job = g_malloc0(driver->instance_size); ++ job->driver = driver; ++ job->id = g_strdup(job_id); ++ ++ return job; ++} +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index 7673de1..fe9f412 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -520,7 +520,9 @@ static void test_job_complete(BlockJob *job, Error **errp) + } + + BlockJobDriver test_job_driver = { +- .instance_size = sizeof(TestBlockJob), ++ .job_driver = { ++ .instance_size = sizeof(TestBlockJob), ++ }, + .start = test_job_start, + .complete = test_job_complete, + }; +diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c +index 5789893..48b12d1 100644 +--- a/tests/test-blockjob-txn.c ++++ b/tests/test-blockjob-txn.c +@@ -74,7 +74,9 @@ static void test_block_job_cb(void *opaque, int ret) + } + + static const BlockJobDriver test_block_job_driver = { +- .instance_size = sizeof(TestBlockJob), ++ .job_driver = { ++ .instance_size = sizeof(TestBlockJob), ++ }, + .start = test_block_job_run, + }; + +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index 8946bfd..b820261 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -17,7 +17,9 @@ + #include "sysemu/block-backend.h" + + static const BlockJobDriver test_block_job_driver = { +- .instance_size = sizeof(BlockJob), ++ .job_driver = { ++ .instance_size = sizeof(BlockJob), ++ }, + }; + + static void block_job_cb(void *opaque, int ret) +@@ -38,9 +40,9 @@ static BlockJob *mk_job(BlockBackend *blk, const char *id, + g_assert_null(errp); + g_assert_nonnull(job); + if (id) { +- g_assert_cmpstr(job->id, ==, id); ++ g_assert_cmpstr(job->job.id, ==, id); + } else { +- g_assert_cmpstr(job->id, ==, blk_name(blk)); ++ g_assert_cmpstr(job->job.id, ==, blk_name(blk)); + } + } else { + g_assert_nonnull(errp); +@@ -192,7 +194,9 @@ static void coroutine_fn cancel_job_start(void *opaque) + } + + static const BlockJobDriver test_cancel_driver = { +- .instance_size = sizeof(CancelJob), ++ .job_driver = { ++ .instance_size = sizeof(CancelJob), ++ }, + .start = cancel_job_start, + .complete = cancel_job_complete, + }; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Fix-missing-locking-due-to-mismerge.patch b/SOURCES/kvm-job-Fix-missing-locking-due-to-mismerge.patch new file mode 100644 index 0000000..18197df --- /dev/null +++ b/SOURCES/kvm-job-Fix-missing-locking-due-to-mismerge.patch @@ -0,0 +1,53 @@ +From a3a6c860441cfaa684fe819791628f9d43af98dc Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:24 +0200 +Subject: [PATCH 33/49] job: Fix missing locking due to mismerge + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-27-kwolf@redhat.com> +Patchwork-id: 82178 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 26/42] job: Fix missing locking due to mismerge +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +job_completed() had a problem with double locking that was recently +fixed independently by two different commits: + +"job: Fix nested aio_poll() hanging in job_txn_apply" +"jobs: add exit shim" + +One fix removed the first aio_context_acquire(), the other fix removed +the other one. Now we have a bug again and the code is run without any +locking. + +Add it back in one of the places. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + job.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/job.c b/job.c +index 5d117fb..db53163 100644 +--- a/job.c ++++ b/job.c +@@ -842,7 +842,11 @@ static void job_completed(Job *job) + static void job_exit(void *opaque) + { + Job *job = (Job *)opaque; ++ AioContext *ctx = job->aio_context; ++ ++ aio_context_acquire(ctx); + job_completed(job); ++ aio_context_release(ctx); + } + + /** +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Fix-nested-aio_poll-hanging-in-job_txn_apply.patch b/SOURCES/kvm-job-Fix-nested-aio_poll-hanging-in-job_txn_apply.patch new file mode 100644 index 0000000..b1e6220 --- /dev/null +++ b/SOURCES/kvm-job-Fix-nested-aio_poll-hanging-in-job_txn_apply.patch @@ -0,0 +1,103 @@ +From 2a9333d5c086c587c527afe9dc9936e3a1658e84 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:23 +0200 +Subject: [PATCH 32/49] job: Fix nested aio_poll() hanging in job_txn_apply + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-26-kwolf@redhat.com> +Patchwork-id: 82177 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 25/42] job: Fix nested aio_poll() hanging in job_txn_apply +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +From: Fam Zheng + +All callers have acquired ctx already. Doing that again results in +aio_poll() hang. This fixes the problem that a BDRV_POLL_WHILE() in the +callback cannot make progress because ctx is recursively locked, for +example, when drive-backup finishes. + +There are two callers of job_finalize(): + + fam@lemon:~/work/qemu [master]$ git grep -w -A1 '^\s*job_finalize' + blockdev.c: job_finalize(&job->job, errp); + blockdev.c- aio_context_release(aio_context); + -- + job-qmp.c: job_finalize(job, errp); + job-qmp.c- aio_context_release(aio_context); + -- + tests/test-blockjob.c: job_finalize(&job->job, &error_abort); + tests/test-blockjob.c- assert(job->job.status == JOB_STATUS_CONCLUDED); + +Ignoring the test, it's easy to see both callers to job_finalize (and +job_do_finalize) have acquired the context. + +Cc: qemu-stable@nongnu.org +Reported-by: Gu Nini +Reviewed-by: Eric Blake +Signed-off-by: Fam Zheng +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + job.c | 18 +++++------------- + 1 file changed, 5 insertions(+), 13 deletions(-) + +diff --git a/job.c b/job.c +index dfba4bc..5d117fb 100644 +--- a/job.c ++++ b/job.c +@@ -136,21 +136,13 @@ static void job_txn_del_job(Job *job) + } + } + +-static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock) ++static int job_txn_apply(JobTxn *txn, int fn(Job *)) + { +- AioContext *ctx; + Job *job, *next; + int rc = 0; + + QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) { +- if (lock) { +- ctx = job->aio_context; +- aio_context_acquire(ctx); +- } + rc = fn(job); +- if (lock) { +- aio_context_release(ctx); +- } + if (rc) { + break; + } +@@ -775,11 +767,11 @@ static void job_do_finalize(Job *job) + assert(job && job->txn); + + /* prepare the transaction to complete */ +- rc = job_txn_apply(job->txn, job_prepare, true); ++ rc = job_txn_apply(job->txn, job_prepare); + if (rc) { + job_completed_txn_abort(job); + } else { +- job_txn_apply(job->txn, job_finalize_single, true); ++ job_txn_apply(job->txn, job_finalize_single); + } + } + +@@ -825,10 +817,10 @@ static void job_completed_txn_success(Job *job) + assert(other_job->ret == 0); + } + +- job_txn_apply(txn, job_transition_to_pending, false); ++ job_txn_apply(txn, job_transition_to_pending); + + /* If no jobs need manual finalization, automatically do so */ +- if (job_txn_apply(txn, job_needs_finalize, false) == 0) { ++ if (job_txn_apply(txn, job_needs_finalize) == 0) { + job_do_finalize(job); + } + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Introduce-qapi-job.json.patch b/SOURCES/kvm-job-Introduce-qapi-job.json.patch new file mode 100644 index 0000000..d1f44ed --- /dev/null +++ b/SOURCES/kvm-job-Introduce-qapi-job.json.patch @@ -0,0 +1,381 @@ +From 4cbc960084f4dc3e4e760c0d79989edbed1157e8 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:30 +0200 +Subject: [PATCH 61/89] job: Introduce qapi/job.json + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-48-kwolf@redhat.com> +Patchwork-id: 81099 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 47/73] job: Introduce qapi/job.json +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This adds a separate schema file for all job-related definitions that +aren't tied to the block layer. + +For a start, move the enums JobType, JobStatus and JobVerb. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +(cherry picked from commit bf42508f24ee1368267b421e145fa90315b77936) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + MAINTAINERS | 1 + + Makefile | 9 +++++ + Makefile.objs | 4 +++ + qapi/block-core.json | 90 +----------------------------------------------- + qapi/job.json | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++ + qapi/qapi-schema.json | 1 + + 6 files changed, 110 insertions(+), 89 deletions(-) + create mode 100644 qapi/job.json + +diff --git a/MAINTAINERS b/MAINTAINERS +index c5e3dfb..5aaf264 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -1377,6 +1377,7 @@ F: block/backup.c + F: block/commit.c + F: block/stream.c + F: block/mirror.c ++F: qapi/job.json + T: git git://github.com/codyprime/qemu-kvm-jtc.git block + + Block QAPI, monitor, command line +diff --git a/Makefile b/Makefile +index bdd8223..c95553f 100644 +--- a/Makefile ++++ b/Makefile +@@ -98,6 +98,7 @@ GENERATED_FILES += qapi/qapi-types-char.h qapi/qapi-types-char.c + GENERATED_FILES += qapi/qapi-types-common.h qapi/qapi-types-common.c + GENERATED_FILES += qapi/qapi-types-crypto.h qapi/qapi-types-crypto.c + GENERATED_FILES += qapi/qapi-types-introspect.h qapi/qapi-types-introspect.c ++GENERATED_FILES += qapi/qapi-types-job.h qapi/qapi-types-job.c + GENERATED_FILES += qapi/qapi-types-migration.h qapi/qapi-types-migration.c + GENERATED_FILES += qapi/qapi-types-misc.h qapi/qapi-types-misc.c + GENERATED_FILES += qapi/qapi-types-net.h qapi/qapi-types-net.c +@@ -116,6 +117,7 @@ GENERATED_FILES += qapi/qapi-visit-char.h qapi/qapi-visit-char.c + GENERATED_FILES += qapi/qapi-visit-common.h qapi/qapi-visit-common.c + GENERATED_FILES += qapi/qapi-visit-crypto.h qapi/qapi-visit-crypto.c + GENERATED_FILES += qapi/qapi-visit-introspect.h qapi/qapi-visit-introspect.c ++GENERATED_FILES += qapi/qapi-visit-job.h qapi/qapi-visit-job.c + GENERATED_FILES += qapi/qapi-visit-migration.h qapi/qapi-visit-migration.c + GENERATED_FILES += qapi/qapi-visit-misc.h qapi/qapi-visit-misc.c + GENERATED_FILES += qapi/qapi-visit-net.h qapi/qapi-visit-net.c +@@ -133,6 +135,7 @@ GENERATED_FILES += qapi/qapi-commands-char.h qapi/qapi-commands-char.c + GENERATED_FILES += qapi/qapi-commands-common.h qapi/qapi-commands-common.c + GENERATED_FILES += qapi/qapi-commands-crypto.h qapi/qapi-commands-crypto.c + GENERATED_FILES += qapi/qapi-commands-introspect.h qapi/qapi-commands-introspect.c ++GENERATED_FILES += qapi/qapi-commands-job.h qapi/qapi-commands-job.c + GENERATED_FILES += qapi/qapi-commands-migration.h qapi/qapi-commands-migration.c + GENERATED_FILES += qapi/qapi-commands-misc.h qapi/qapi-commands-misc.c + GENERATED_FILES += qapi/qapi-commands-net.h qapi/qapi-commands-net.c +@@ -150,6 +153,7 @@ GENERATED_FILES += qapi/qapi-events-char.h qapi/qapi-events-char.c + GENERATED_FILES += qapi/qapi-events-common.h qapi/qapi-events-common.c + GENERATED_FILES += qapi/qapi-events-crypto.h qapi/qapi-events-crypto.c + GENERATED_FILES += qapi/qapi-events-introspect.h qapi/qapi-events-introspect.c ++GENERATED_FILES += qapi/qapi-events-job.h qapi/qapi-events-job.c + GENERATED_FILES += qapi/qapi-events-migration.h qapi/qapi-events-migration.c + GENERATED_FILES += qapi/qapi-events-misc.h qapi/qapi-events-misc.c + GENERATED_FILES += qapi/qapi-events-net.h qapi/qapi-events-net.c +@@ -585,6 +589,7 @@ qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json $(SRC_PATH)/qapi/common.json \ + $(SRC_PATH)/qapi/char.json \ + $(SRC_PATH)/qapi/crypto.json \ + $(SRC_PATH)/qapi/introspect.json \ ++ $(SRC_PATH)/qapi/job.json \ + $(SRC_PATH)/qapi/migration.json \ + $(SRC_PATH)/qapi/misc.json \ + $(SRC_PATH)/qapi/net.json \ +@@ -604,6 +609,7 @@ qapi/qapi-types-char.c qapi/qapi-types-char.h \ + qapi/qapi-types-common.c qapi/qapi-types-common.h \ + qapi/qapi-types-crypto.c qapi/qapi-types-crypto.h \ + qapi/qapi-types-introspect.c qapi/qapi-types-introspect.h \ ++qapi/qapi-types-job.c qapi/qapi-types-job.h \ + qapi/qapi-types-migration.c qapi/qapi-types-migration.h \ + qapi/qapi-types-misc.c qapi/qapi-types-misc.h \ + qapi/qapi-types-net.c qapi/qapi-types-net.h \ +@@ -622,6 +628,7 @@ qapi/qapi-visit-char.c qapi/qapi-visit-char.h \ + qapi/qapi-visit-common.c qapi/qapi-visit-common.h \ + qapi/qapi-visit-crypto.c qapi/qapi-visit-crypto.h \ + qapi/qapi-visit-introspect.c qapi/qapi-visit-introspect.h \ ++qapi/qapi-visit-job.c qapi/qapi-visit-job.h \ + qapi/qapi-visit-migration.c qapi/qapi-visit-migration.h \ + qapi/qapi-visit-misc.c qapi/qapi-visit-misc.h \ + qapi/qapi-visit-net.c qapi/qapi-visit-net.h \ +@@ -639,6 +646,7 @@ qapi/qapi-commands-char.c qapi/qapi-commands-char.h \ + qapi/qapi-commands-common.c qapi/qapi-commands-common.h \ + qapi/qapi-commands-crypto.c qapi/qapi-commands-crypto.h \ + qapi/qapi-commands-introspect.c qapi/qapi-commands-introspect.h \ ++qapi/qapi-commands-job.c qapi/qapi-commands-job.h \ + qapi/qapi-commands-migration.c qapi/qapi-commands-migration.h \ + qapi/qapi-commands-misc.c qapi/qapi-commands-misc.h \ + qapi/qapi-commands-net.c qapi/qapi-commands-net.h \ +@@ -656,6 +664,7 @@ qapi/qapi-events-char.c qapi/qapi-events-char.h \ + qapi/qapi-events-common.c qapi/qapi-events-common.h \ + qapi/qapi-events-crypto.c qapi/qapi-events-crypto.h \ + qapi/qapi-events-introspect.c qapi/qapi-events-introspect.h \ ++qapi/qapi-events-job.c qapi/qapi-events-job.h \ + qapi/qapi-events-migration.c qapi/qapi-events-migration.h \ + qapi/qapi-events-misc.c qapi/qapi-events-misc.h \ + qapi/qapi-events-net.c qapi/qapi-events-net.h \ +diff --git a/Makefile.objs b/Makefile.objs +index 92b73fc..3df8d58 100644 +--- a/Makefile.objs ++++ b/Makefile.objs +@@ -10,6 +10,7 @@ util-obj-y += qapi/qapi-types-char.o + util-obj-y += qapi/qapi-types-common.o + util-obj-y += qapi/qapi-types-crypto.o + util-obj-y += qapi/qapi-types-introspect.o ++util-obj-y += qapi/qapi-types-job.o + util-obj-y += qapi/qapi-types-migration.o + util-obj-y += qapi/qapi-types-misc.o + util-obj-y += qapi/qapi-types-net.o +@@ -28,6 +29,7 @@ util-obj-y += qapi/qapi-visit-char.o + util-obj-y += qapi/qapi-visit-common.o + util-obj-y += qapi/qapi-visit-crypto.o + util-obj-y += qapi/qapi-visit-introspect.o ++util-obj-y += qapi/qapi-visit-job.o + util-obj-y += qapi/qapi-visit-migration.o + util-obj-y += qapi/qapi-visit-misc.o + util-obj-y += qapi/qapi-visit-net.o +@@ -45,6 +47,7 @@ util-obj-y += qapi/qapi-events-char.o + util-obj-y += qapi/qapi-events-common.o + util-obj-y += qapi/qapi-events-crypto.o + util-obj-y += qapi/qapi-events-introspect.o ++util-obj-y += qapi/qapi-events-job.o + util-obj-y += qapi/qapi-events-migration.o + util-obj-y += qapi/qapi-events-misc.o + util-obj-y += qapi/qapi-events-net.o +@@ -140,6 +143,7 @@ common-obj-y += qapi/qapi-commands-char.o + common-obj-y += qapi/qapi-commands-common.o + common-obj-y += qapi/qapi-commands-crypto.o + common-obj-y += qapi/qapi-commands-introspect.o ++common-obj-y += qapi/qapi-commands-job.o + common-obj-y += qapi/qapi-commands-migration.o + common-obj-y += qapi/qapi-commands-misc.o + common-obj-y += qapi/qapi-commands-net.o +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 85bf353..67e613a 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -6,6 +6,7 @@ + + { 'include': 'common.json' } + { 'include': 'crypto.json' } ++{ 'include': 'job.json' } + { 'include': 'sockets.json' } + + ## +@@ -1050,95 +1051,6 @@ + 'data': ['top', 'full', 'none', 'incremental'] } + + ## +-# @JobType: +-# +-# Type of a background job. +-# +-# @commit: block commit job type, see "block-commit" +-# +-# @stream: block stream job type, see "block-stream" +-# +-# @mirror: drive mirror job type, see "drive-mirror" +-# +-# @backup: drive backup job type, see "drive-backup" +-# +-# Since: 1.7 +-## +-{ 'enum': 'JobType', +- 'data': ['commit', 'stream', 'mirror', 'backup'] } +- +-## +-# @JobVerb: +-# +-# Represents command verbs that can be applied to a job. +-# +-# @cancel: see @block-job-cancel +-# +-# @pause: see @block-job-pause +-# +-# @resume: see @block-job-resume +-# +-# @set-speed: see @block-job-set-speed +-# +-# @complete: see @block-job-complete +-# +-# @dismiss: see @block-job-dismiss +-# +-# @finalize: see @block-job-finalize +-# +-# Since: 2.12 +-## +-{ 'enum': 'JobVerb', +- 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss', +- 'finalize' ] } +- +-## +-# @JobStatus: +-# +-# Indicates the present state of a given job in its lifetime. +-# +-# @undefined: Erroneous, default state. Should not ever be visible. +-# +-# @created: The job has been created, but not yet started. +-# +-# @running: The job is currently running. +-# +-# @paused: The job is running, but paused. The pause may be requested by +-# either the QMP user or by internal processes. +-# +-# @ready: The job is running, but is ready for the user to signal completion. +-# This is used for long-running jobs like mirror that are designed to +-# run indefinitely. +-# +-# @standby: The job is ready, but paused. This is nearly identical to @paused. +-# The job may return to @ready or otherwise be canceled. +-# +-# @waiting: The job is waiting for other jobs in the transaction to converge +-# to the waiting state. This status will likely not be visible for +-# the last job in a transaction. +-# +-# @pending: The job has finished its work, but has finalization steps that it +-# needs to make prior to completing. These changes may require +-# manual intervention by the management process if manual was set +-# to true. These changes may still fail. +-# +-# @aborting: The job is in the process of being aborted, and will finish with +-# an error. The job will afterwards report that it is @concluded. +-# This status may not be visible to the management process. +-# +-# @concluded: The job has finished all work. If manual was set to true, the job +-# will remain in the query list until it is dismissed. +-# +-# @null: The job is in the process of being dismantled. This state should not +-# ever be visible externally. +-# +-# Since: 2.12 +-## +-{ 'enum': 'JobStatus', +- 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby', +- 'waiting', 'pending', 'aborting', 'concluded', 'null' ] } +- +-## + # @BlockJobInfo: + # + # Information about a long-running block device operation. +diff --git a/qapi/job.json b/qapi/job.json +new file mode 100644 +index 0000000..a472c0c +--- /dev/null ++++ b/qapi/job.json +@@ -0,0 +1,94 @@ ++# -*- Mode: Python -*- ++ ++## ++# == Background jobs ++## ++ ++## ++# @JobType: ++# ++# Type of a background job. ++# ++# @commit: block commit job type, see "block-commit" ++# ++# @stream: block stream job type, see "block-stream" ++# ++# @mirror: drive mirror job type, see "drive-mirror" ++# ++# @backup: drive backup job type, see "drive-backup" ++# ++# Since: 1.7 ++## ++{ 'enum': 'JobType', ++ 'data': ['commit', 'stream', 'mirror', 'backup'] } ++ ++## ++# @JobStatus: ++# ++# Indicates the present state of a given job in its lifetime. ++# ++# @undefined: Erroneous, default state. Should not ever be visible. ++# ++# @created: The job has been created, but not yet started. ++# ++# @running: The job is currently running. ++# ++# @paused: The job is running, but paused. The pause may be requested by ++# either the QMP user or by internal processes. ++# ++# @ready: The job is running, but is ready for the user to signal completion. ++# This is used for long-running jobs like mirror that are designed to ++# run indefinitely. ++# ++# @standby: The job is ready, but paused. This is nearly identical to @paused. ++# The job may return to @ready or otherwise be canceled. ++# ++# @waiting: The job is waiting for other jobs in the transaction to converge ++# to the waiting state. This status will likely not be visible for ++# the last job in a transaction. ++# ++# @pending: The job has finished its work, but has finalization steps that it ++# needs to make prior to completing. These changes may require ++# manual intervention by the management process if manual was set ++# to true. These changes may still fail. ++# ++# @aborting: The job is in the process of being aborted, and will finish with ++# an error. The job will afterwards report that it is @concluded. ++# This status may not be visible to the management process. ++# ++# @concluded: The job has finished all work. If manual was set to true, the job ++# will remain in the query list until it is dismissed. ++# ++# @null: The job is in the process of being dismantled. This state should not ++# ever be visible externally. ++# ++# Since: 2.12 ++## ++{ 'enum': 'JobStatus', ++ 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby', ++ 'waiting', 'pending', 'aborting', 'concluded', 'null' ] } ++ ++## ++# @JobVerb: ++# ++# Represents command verbs that can be applied to a job. ++# ++# @cancel: see @block-job-cancel ++# ++# @pause: see @block-job-pause ++# ++# @resume: see @block-job-resume ++# ++# @set-speed: see @block-job-set-speed ++# ++# @complete: see @block-job-complete ++# ++# @dismiss: see @block-job-dismiss ++# ++# @finalize: see @block-job-finalize ++# ++# Since: 2.12 ++## ++{ 'enum': 'JobVerb', ++ 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss', ++ 'finalize' ] } +diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json +index 25bce78..65b6dc2 100644 +--- a/qapi/qapi-schema.json ++++ b/qapi/qapi-schema.json +@@ -84,6 +84,7 @@ + { 'include': 'crypto.json' } + { 'include': 'block.json' } + { 'include': 'char.json' } ++{ 'include': 'job.json' } + { 'include': 'net.json' } + { 'include': 'rocker.json' } + { 'include': 'tpm.json' } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Maintain-a-list-of-all-jobs.patch b/SOURCES/kvm-job-Maintain-a-list-of-all-jobs.patch new file mode 100644 index 0000000..27f6bfc --- /dev/null +++ b/SOURCES/kvm-job-Maintain-a-list-of-all-jobs.patch @@ -0,0 +1,262 @@ +From 40315feeaa8186057e85d18aa653a0916dc55bfd Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:03 +0200 +Subject: [PATCH 34/89] job: Maintain a list of all jobs + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-21-kwolf@redhat.com> +Patchwork-id: 81105 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 20/73] job: Maintain a list of all jobs +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This moves the job list from BlockJob to Job. Now we can check for +duplicate IDs in job_create(). + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: John Snow +(cherry picked from commit e7c1d78bbd5867804debeb7159b137fd9a6c44d3) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + blockjob.c | 46 ++++++++++++++++++++++++---------------------- + include/block/blockjob.h | 3 --- + include/qemu/job.h | 19 +++++++++++++++++++ + job.c | 31 +++++++++++++++++++++++++++++++ + 4 files changed, 74 insertions(+), 25 deletions(-) + +diff --git a/blockjob.c b/blockjob.c +index 430a67b..c69b2e7 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -129,8 +129,6 @@ struct BlockJobTxn { + int refcnt; + }; + +-static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs); +- + /* + * The block job API is composed of two categories of functions. + * +@@ -146,25 +144,34 @@ static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs); + * blockjob_int.h. + */ + +-BlockJob *block_job_next(BlockJob *job) ++static bool is_block_job(Job *job) + { +- if (!job) { +- return QLIST_FIRST(&block_jobs); +- } +- return QLIST_NEXT(job, job_list); ++ 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(BlockJob *bjob) ++{ ++ Job *job = bjob ? &bjob->job : NULL; ++ ++ do { ++ job = job_next(job); ++ } while (job && !is_block_job(job)); ++ ++ return job ? container_of(job, BlockJob, job) : NULL; + } + + BlockJob *block_job_get(const char *id) + { +- BlockJob *job; ++ Job *job = job_get(id); + +- QLIST_FOREACH(job, &block_jobs, job_list) { +- if (job->job.id && !strcmp(id, job->job.id)) { +- return job; +- } ++ if (job && is_block_job(job)) { ++ return container_of(job, BlockJob, job); ++ } else { ++ return NULL; + } +- +- return NULL; + } + + BlockJobTxn *block_job_txn_new(void) +@@ -253,7 +260,6 @@ void block_job_unref(BlockJob *job) + assert(job->status == BLOCK_JOB_STATUS_NULL); + assert(!job->txn); + BlockDriverState *bs = blk_bs(job->blk); +- QLIST_REMOVE(job, job_list); + bs->job = NULL; + block_job_remove_all_bdrv(job); + blk_remove_aio_context_notifier(job->blk, +@@ -812,7 +818,7 @@ void block_job_cancel_sync_all(void) + BlockJob *job; + AioContext *aio_context; + +- while ((job = QLIST_FIRST(&block_jobs))) { ++ while ((job = block_job_next(NULL))) { + aio_context = blk_get_aio_context(job->blk); + aio_context_acquire(aio_context); + block_job_cancel_sync(job); +@@ -942,10 +948,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + error_setg(errp, "Cannot specify job ID for internal block job"); + return NULL; + } +- if (block_job_get(job_id)) { +- error_setg(errp, "Job ID '%s' already in use", job_id); +- return NULL; +- } + } + + blk = blk_new(perm, shared_perm); +@@ -961,6 +963,8 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + return NULL; + } + ++ assert(is_block_job(&job->job)); ++ + job->driver = driver; + job->blk = blk; + job->cb = cb; +@@ -983,8 +987,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + + bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker); + +- QLIST_INSERT_HEAD(&block_jobs, job, job_list); +- + blk_add_aio_context_notifier(blk, block_job_attached_aio_context, + block_job_detach_aio_context, job); + +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 640e649..10bd9f7 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -105,9 +105,6 @@ typedef struct BlockJob { + */ + bool deferred_to_main_loop; + +- /** Element of the list of block jobs */ +- QLIST_ENTRY(BlockJob) job_list; +- + /** Status that is published by the query-block-jobs QMP API */ + BlockDeviceIoStatus iostatus; + +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 43dc2e4..bae2b09 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -27,6 +27,7 @@ + #define JOB_H + + #include "qapi/qapi-types-block-core.h" ++#include "qemu/queue.h" + + typedef struct JobDriver JobDriver; + +@@ -39,6 +40,9 @@ typedef struct Job { + + /** The type of this job. */ + const JobDriver *driver; ++ ++ /** Element of the list of jobs */ ++ QLIST_ENTRY(Job) job_list; + } Job; + + /** +@@ -71,4 +75,19 @@ 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); + ++/** ++ * 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. ++ */ ++Job *job_next(Job *job); ++ ++/** ++ * Get the job identified by @id (which must not be %NULL). ++ * ++ * Returns the requested job, or %NULL if it doesn't exist. ++ */ ++Job *job_get(const char *id); ++ + #endif +diff --git a/job.c b/job.c +index cfdd008..e57303c 100644 +--- a/job.c ++++ b/job.c +@@ -29,6 +29,8 @@ + #include "qemu/job.h" + #include "qemu/id.h" + ++static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs); ++ + JobType job_type(const Job *job) + { + return job->driver->job_type; +@@ -39,6 +41,27 @@ const char *job_type_str(const Job *job) + return JobType_str(job_type(job)); + } + ++Job *job_next(Job *job) ++{ ++ if (!job) { ++ return QLIST_FIRST(&jobs); ++ } ++ return QLIST_NEXT(job, job_list); ++} ++ ++Job *job_get(const char *id) ++{ ++ Job *job; ++ ++ QLIST_FOREACH(job, &jobs, job_list) { ++ if (job->id && !strcmp(id, job->id)) { ++ return job; ++ } ++ } ++ ++ return NULL; ++} ++ + void *job_create(const char *job_id, const JobDriver *driver, Error **errp) + { + Job *job; +@@ -48,17 +71,25 @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp) + error_setg(errp, "Invalid job ID '%s'", job_id); + return NULL; + } ++ if (job_get(job_id)) { ++ error_setg(errp, "Job ID '%s' already in use", job_id); ++ return NULL; ++ } + } + + job = g_malloc0(driver->instance_size); + job->driver = driver; + job->id = g_strdup(job_id); + ++ QLIST_INSERT_HEAD(&jobs, job, job_list); ++ + return job; + } + + void job_delete(Job *job) + { ++ QLIST_REMOVE(job, job_list); ++ + g_free(job->id); + g_free(job); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Move-.complete-callback-to-Job.patch b/SOURCES/kvm-job-Move-.complete-callback-to-Job.patch new file mode 100644 index 0000000..efb600a --- /dev/null +++ b/SOURCES/kvm-job-Move-.complete-callback-to-Job.patch @@ -0,0 +1,298 @@ +From dcfc2f6514b672e68b62effbbc04c88ca16ac89a Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:19 +0200 +Subject: [PATCH 50/89] job: Move .complete callback to Job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-37-kwolf@redhat.com> +Patchwork-id: 81084 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 36/73] job: Move .complete callback to Job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This moves the .complete callback that tells a READY job to complete +from BlockJobDriver to JobDriver. The wrapper function job_complete() +doesn't require anything block job specific any more and can be moved +to Job. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit 3453d97243c72988c89a0105fa9546890eae7bd4) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/mirror.c | 10 +++++----- + blockdev.c | 2 +- + blockjob.c | 23 +++++------------------ + include/block/blockjob.h | 10 ---------- + include/block/blockjob_int.h | 6 ------ + include/qemu/job.h | 8 ++++++++ + job.c | 16 ++++++++++++++++ + tests/test-bdrv-drain.c | 6 +++--- + tests/test-blockjob.c | 10 +++++----- + 9 files changed, 43 insertions(+), 48 deletions(-) + +diff --git a/block/mirror.c b/block/mirror.c +index a579bd8..656237a 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -905,16 +905,16 @@ immediate_exit: + job_defer_to_main_loop(&s->common.job, mirror_exit, data); + } + +-static void mirror_complete(BlockJob *job, Error **errp) ++static void mirror_complete(Job *job, Error **errp) + { +- MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); + BlockDriverState *target; + + target = blk_bs(s->target); + + if (!s->synced) { + error_setg(errp, "The active block job '%s' cannot be completed", +- job->job.id); ++ job->id); + return; + } + +@@ -995,8 +995,8 @@ static const BlockJobDriver mirror_job_driver = { + .drain = block_job_drain, + .start = mirror_run, + .pause = mirror_pause, ++ .complete = mirror_complete, + }, +- .complete = mirror_complete, + .attached_aio_context = mirror_attached_aio_context, + .drain = mirror_drain, + }; +@@ -1010,8 +1010,8 @@ static const BlockJobDriver commit_active_job_driver = { + .drain = block_job_drain, + .start = mirror_run, + .pause = mirror_pause, ++ .complete = mirror_complete, + }, +- .complete = mirror_complete, + .attached_aio_context = mirror_attached_aio_context, + .drain = mirror_drain, + }; +diff --git a/blockdev.c b/blockdev.c +index 89df7d9..c2b2be1 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3941,7 +3941,7 @@ void qmp_block_job_complete(const char *device, Error **errp) + } + + trace_qmp_block_job_complete(job); +- block_job_complete(job, errp); ++ job_complete(&job->job, errp); + aio_context_release(aio_context); + } + +diff --git a/blockjob.c b/blockjob.c +index 63e1669..0ca7672 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -481,24 +481,6 @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) + return ratelimit_calculate_delay(&job->limit, n); + } + +-void block_job_complete(BlockJob *job, Error **errp) +-{ +- /* Should not be reachable via external interface for internal jobs */ +- assert(job->job.id); +- if (job_apply_verb(&job->job, JOB_VERB_COMPLETE, errp)) { +- return; +- } +- if (job->job.pause_count || job_is_cancelled(&job->job) || +- !job->driver->complete) +- { +- error_setg(errp, "The active block job '%s' cannot be completed", +- job->job.id); +- return; +- } +- +- job->driver->complete(job, errp); +-} +- + void block_job_finalize(BlockJob *job, Error **errp) + { + assert(job && job->job.id); +@@ -571,6 +553,11 @@ void block_job_cancel_sync_all(void) + } + } + ++static void block_job_complete(BlockJob *job, Error **errp) ++{ ++ job_complete(&job->job, errp); ++} ++ + int block_job_complete_sync(BlockJob *job, Error **errp) + { + return block_job_finish_sync(job, &block_job_complete, errp); +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index d975efe..85ce18a 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -154,16 +154,6 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp); + void block_job_cancel(BlockJob *job, bool force); + + /** +- * block_job_complete: +- * @job: The job to be completed. +- * @errp: Error object. +- * +- * Asynchronously complete the specified job. +- */ +-void block_job_complete(BlockJob *job, Error **errp); +- +- +-/** + * block_job_finalize: + * @job: The job to fully commit and finish. + * @errp: Error object. +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index 38fe22d..b8ca7bb 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -39,12 +39,6 @@ struct BlockJobDriver { + JobDriver job_driver; + + /** +- * Optional callback for job types whose completion must be triggered +- * manually. +- */ +- void (*complete)(BlockJob *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. +diff --git a/include/qemu/job.h b/include/qemu/job.h +index aebc195..8f7f71a 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -167,6 +167,12 @@ struct JobDriver { + */ + 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, it will be invoked when the job has to be + * synchronously cancelled or completed; it should drain any activities +@@ -363,6 +369,8 @@ int job_apply_verb(Job *job, JobVerb verb, Error **errp); + /** The @job could not be started, free it. */ + void job_early_fail(Job *job); + ++/** Asynchronously complete the specified @job. */ ++void job_complete(Job *job, Error **errp);; + + typedef void JobDeferToMainLoopFn(Job *job, void *opaque); + +diff --git a/job.c b/job.c +index 3772a35..8ceac0b 100644 +--- a/job.c ++++ b/job.c +@@ -556,6 +556,22 @@ int job_finalize_single(Job *job) + return 0; + } + ++void job_complete(Job *job, Error **errp) ++{ ++ /* Should not be reachable via external interface for internal jobs */ ++ assert(job->id); ++ if (job_apply_verb(job, JOB_VERB_COMPLETE, errp)) { ++ return; ++ } ++ if (job->pause_count || job_is_cancelled(job) || !job->driver->complete) { ++ error_setg(errp, "The active block job '%s' cannot be completed", ++ job->id); ++ return; ++ } ++ ++ job->driver->complete(job, errp); ++} ++ + + typedef struct { + Job *job; +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index 58ea566..b428aac 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -514,9 +514,9 @@ static void coroutine_fn test_job_start(void *opaque) + job_defer_to_main_loop(&s->common.job, test_job_completed, NULL); + } + +-static void test_job_complete(BlockJob *job, Error **errp) ++static void test_job_complete(Job *job, Error **errp) + { +- TestBlockJob *s = container_of(job, TestBlockJob, common); ++ TestBlockJob *s = container_of(job, TestBlockJob, common.job); + s->should_complete = true; + } + +@@ -527,8 +527,8 @@ BlockJobDriver test_job_driver = { + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .start = test_job_start, ++ .complete = test_job_complete, + }, +- .complete = test_job_complete, + }; + + static void test_blockjob_common(enum drain_type drain_type) +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index 592a136..e44c608 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -171,9 +171,9 @@ static void cancel_job_completed(Job *job, void *opaque) + block_job_completed(bjob, 0); + } + +-static void cancel_job_complete(BlockJob *job, Error **errp) ++static void cancel_job_complete(Job *job, Error **errp) + { +- CancelJob *s = container_of(job, CancelJob, common); ++ CancelJob *s = container_of(job, CancelJob, common.job); + s->should_complete = true; + } + +@@ -204,8 +204,8 @@ static const BlockJobDriver test_cancel_driver = { + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .start = cancel_job_start, ++ .complete = cancel_job_complete, + }, +- .complete = cancel_job_complete, + }; + + static CancelJob *create_common(BlockJob **pjob) +@@ -333,7 +333,7 @@ static void test_cancel_pending(void) + block_job_enter(job); + assert(job->job.status == JOB_STATUS_READY); + +- block_job_complete(job, &error_abort); ++ job_complete(&job->job, &error_abort); + block_job_enter(job); + while (!s->completed) { + aio_poll(qemu_get_aio_context(), true); +@@ -357,7 +357,7 @@ static void test_cancel_concluded(void) + block_job_enter(job); + assert(job->job.status == JOB_STATUS_READY); + +- block_job_complete(job, &error_abort); ++ job_complete(&job->job, &error_abort); + block_job_enter(job); + while (!s->completed) { + aio_poll(qemu_get_aio_context(), true); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Move-BlockJobCreateFlags-to-Job.patch b/SOURCES/kvm-job-Move-BlockJobCreateFlags-to-Job.patch new file mode 100644 index 0000000..40b0663 --- /dev/null +++ b/SOURCES/kvm-job-Move-BlockJobCreateFlags-to-Job.patch @@ -0,0 +1,423 @@ +From 4d3f866a590d0c5005bd864cc2ee7bb8187f6dbb Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:13 +0200 +Subject: [PATCH 44/89] job: Move BlockJobCreateFlags to Job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-31-kwolf@redhat.com> +Patchwork-id: 81106 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 30/73] job: Move BlockJobCreateFlags to Job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This renames the BlockJobCreateFlags constants, moves a few JOB_INTERNAL +checks to job_create() and the auto_{finalize,dismiss} fields from +BlockJob to Job. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit bb02b65c7d57e4f2136f39bfba95cc68d89eb216) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/commit.c | 2 +- + block/mirror.c | 2 +- + block/replication.c | 4 ++-- + block/stream.c | 2 +- + blockdev.c | 14 +++++++------- + blockjob.c | 27 +++++++-------------------- + include/block/blockjob.h | 17 ----------------- + include/block/blockjob_int.h | 3 +-- + include/qemu/job.h | 20 +++++++++++++++++++- + job.c | 11 ++++++++++- + qemu-img.c | 2 +- + tests/test-blockjob-txn.c | 2 +- + tests/test-blockjob.c | 4 ++-- + 13 files changed, 53 insertions(+), 57 deletions(-) + +diff --git a/block/commit.c b/block/commit.c +index c4a98e5..7a6ae59 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -282,7 +282,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, + } + + s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL, +- speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp); ++ speed, JOB_DEFAULT, NULL, NULL, errp); + if (!s) { + return; + } +diff --git a/block/mirror.c b/block/mirror.c +index 9a7226f..5091e72 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -1284,7 +1284,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, + } + is_none_mode = mode == MIRROR_SYNC_MODE_NONE; + base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL; +- mirror_start_job(job_id, bs, BLOCK_JOB_DEFAULT, target, replaces, ++ mirror_start_job(job_id, bs, JOB_DEFAULT, target, replaces, + speed, granularity, buf_size, backing_mode, + on_source_error, on_target_error, unmap, NULL, NULL, + &mirror_job_driver, is_none_mode, base, false, +diff --git a/block/replication.c b/block/replication.c +index 3f7500e..8241400 100644 +--- a/block/replication.c ++++ b/block/replication.c +@@ -566,7 +566,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, + job = backup_job_create(NULL, s->secondary_disk->bs, s->hidden_disk->bs, + 0, MIRROR_SYNC_MODE_NONE, NULL, false, + BLOCKDEV_ON_ERROR_REPORT, +- BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL, ++ BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL, + backup_job_completed, bs, NULL, &local_err); + if (local_err) { + error_propagate(errp, local_err); +@@ -691,7 +691,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) + + s->stage = BLOCK_REPLICATION_FAILOVER; + commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs, +- BLOCK_JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT, ++ JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT, + NULL, replication_done, bs, true, errp); + break; + default: +diff --git a/block/stream.c b/block/stream.c +index e81b488..eee0253 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -242,7 +242,7 @@ void stream_start(const char *job_id, BlockDriverState *bs, + BLK_PERM_GRAPH_MOD, + BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED | + BLK_PERM_WRITE, +- speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp); ++ speed, JOB_DEFAULT, NULL, NULL, errp); + if (!s) { + goto fail; + } +diff --git a/blockdev.c b/blockdev.c +index 0fa1990..89df7d9 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3291,7 +3291,7 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, + goto out; + } + commit_active_start(has_job_id ? job_id : NULL, bs, base_bs, +- BLOCK_JOB_DEFAULT, speed, on_error, ++ JOB_DEFAULT, speed, on_error, + filter_node_name, NULL, NULL, false, &local_err); + } else { + BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs); +@@ -3322,7 +3322,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, + AioContext *aio_context; + QDict *options = NULL; + Error *local_err = NULL; +- int flags, job_flags = BLOCK_JOB_DEFAULT; ++ int flags, job_flags = JOB_DEFAULT; + int64_t size; + bool set_backing_hd = false; + +@@ -3445,10 +3445,10 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, + } + } + if (!backup->auto_finalize) { +- job_flags |= BLOCK_JOB_MANUAL_FINALIZE; ++ job_flags |= JOB_MANUAL_FINALIZE; + } + if (!backup->auto_dismiss) { +- job_flags |= BLOCK_JOB_MANUAL_DISMISS; ++ job_flags |= JOB_MANUAL_DISMISS; + } + + job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, +@@ -3489,7 +3489,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, + Error *local_err = NULL; + AioContext *aio_context; + BlockJob *job = NULL; +- int job_flags = BLOCK_JOB_DEFAULT; ++ int job_flags = JOB_DEFAULT; + + if (!backup->has_speed) { + backup->speed = 0; +@@ -3538,10 +3538,10 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, + } + } + if (!backup->auto_finalize) { +- job_flags |= BLOCK_JOB_MANUAL_FINALIZE; ++ job_flags |= JOB_MANUAL_FINALIZE; + } + if (!backup->auto_dismiss) { +- job_flags |= BLOCK_JOB_MANUAL_DISMISS; ++ job_flags |= JOB_MANUAL_DISMISS; + } + job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, + backup->sync, NULL, backup->compress, +diff --git a/blockjob.c b/blockjob.c +index a1d1f48..d9d8ff7 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -285,7 +285,7 @@ static void block_job_do_dismiss(BlockJob *job) + static void block_job_conclude(BlockJob *job) + { + job_state_transition(&job->job, JOB_STATUS_CONCLUDED); +- if (job->auto_dismiss || !job_started(&job->job)) { ++ if (job->job.auto_dismiss || !job_started(&job->job)) { + block_job_do_dismiss(job); + } + } +@@ -483,7 +483,7 @@ static void block_job_completed_txn_abort(BlockJob *job) + + static int block_job_needs_finalize(BlockJob *job) + { +- return !job->auto_finalize; ++ return !job->job.auto_finalize; + } + + static void block_job_do_finalize(BlockJob *job) +@@ -688,8 +688,8 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) + info->io_status = job->iostatus; + info->ready = job->ready; + info->status = job->job.status; +- info->auto_finalize = job->auto_finalize; +- info->auto_dismiss = job->auto_dismiss; ++ info->auto_finalize = job->job.auto_finalize; ++ info->auto_dismiss = job->job.auto_dismiss; + info->has_error = job->ret != 0; + info->error = job->ret ? g_strdup(strerror(-job->ret)) : NULL; + return info; +@@ -736,7 +736,7 @@ static void block_job_event_completed(BlockJob *job, const char *msg) + static int block_job_event_pending(BlockJob *job) + { + job_state_transition(&job->job, JOB_STATUS_PENDING); +- if (!job->auto_finalize && !block_job_is_internal(job)) { ++ if (!job->job.auto_finalize && !block_job_is_internal(job)) { + qapi_event_send_block_job_pending(job_type(&job->job), + job->job.id, + &error_abort); +@@ -763,19 +763,8 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + return NULL; + } + +- if (job_id == NULL && !(flags & BLOCK_JOB_INTERNAL)) { ++ if (job_id == NULL && !(flags & JOB_INTERNAL)) { + job_id = bdrv_get_device_name(bs); +- if (!*job_id) { +- error_setg(errp, "An explicit job ID is required for this node"); +- return NULL; +- } +- } +- +- if (job_id) { +- if (flags & BLOCK_JOB_INTERNAL) { +- error_setg(errp, "Cannot specify job ID for internal block job"); +- return NULL; +- } + } + + blk = blk_new(perm, shared_perm); +@@ -786,7 +775,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + } + + job = job_create(job_id, &driver->job_driver, blk_get_aio_context(blk), +- errp); ++ flags, errp); + if (job == NULL) { + blk_unref(blk); + return NULL; +@@ -800,8 +789,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + job->blk = blk; + job->cb = cb; + job->opaque = opaque; +- job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE); +- job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS); + + error_setg(&job->blocker, "block device is in use by block job: %s", + job_type_str(&job->job)); +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 3e94e18..f9aaaaa 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -91,27 +91,10 @@ typedef struct BlockJob { + /** ret code passed to block_job_completed. */ + int ret; + +- /** True if this job should automatically finalize itself */ +- bool auto_finalize; +- +- /** True if this job should automatically dismiss itself */ +- bool auto_dismiss; +- + BlockJobTxn *txn; + QLIST_ENTRY(BlockJob) txn_list; + } BlockJob; + +-typedef enum BlockJobCreateFlags { +- /* Default behavior */ +- BLOCK_JOB_DEFAULT = 0x00, +- /* BlockJob is not QMP-created and should not send QMP events */ +- BLOCK_JOB_INTERNAL = 0x01, +- /* BlockJob requires manual finalize step */ +- BLOCK_JOB_MANUAL_FINALIZE = 0x02, +- /* BlockJob requires manual dismiss step */ +- BLOCK_JOB_MANUAL_DISMISS = 0x04, +-} BlockJobCreateFlags; +- + /** + * block_job_next: + * @job: A block job, or %NULL. +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index 7e705ae..88639f7 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -106,8 +106,7 @@ struct BlockJobDriver { + * @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 @BlockJobCreateFlags ++ * @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. +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 858f3be..9783e40 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -99,6 +99,12 @@ typedef struct Job { + /** Set to true when the job has deferred work to the main loop. */ + bool deferred_to_main_loop; + ++ /** True if this job should automatically finalize itself */ ++ bool auto_finalize; ++ ++ /** True if this job should automatically dismiss itself */ ++ bool auto_dismiss; ++ + /** Element of the list of jobs */ + QLIST_ENTRY(Job) job_list; + } Job; +@@ -140,6 +146,17 @@ struct JobDriver { + 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; ++ + + /** + * Create a new long-running job and return it. +@@ -147,10 +164,11 @@ struct JobDriver { + * @job_id: The id of the newly-created job, or %NULL for internal jobs + * @driver: The class object for the newly-created job. + * @ctx: The AioContext to run the job coroutine in. ++ * @flags: Creation flags for the job. See @JobCreateFlags. + * @errp: Error object. + */ + void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, +- Error **errp); ++ int flags, Error **errp); + + /** + * Add a reference to Job refcnt, it will be decreased with job_unref, and then +diff --git a/job.c b/job.c +index aaacfcc..dd46170 100644 +--- a/job.c ++++ b/job.c +@@ -182,11 +182,15 @@ static void job_sleep_timer_cb(void *opaque) + } + + void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, +- Error **errp) ++ int flags, Error **errp) + { + Job *job; + + 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; +@@ -195,6 +199,9 @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, + 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); +@@ -205,6 +212,8 @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *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_state_transition(job, JOB_STATUS_CREATED); + aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, +diff --git a/qemu-img.c b/qemu-img.c +index f5bb0ef..843dc6a 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -1026,7 +1026,7 @@ static int img_commit(int argc, char **argv) + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); +- commit_active_start("commit", bs, base_bs, BLOCK_JOB_DEFAULT, 0, ++ commit_active_start("commit", bs, base_bs, JOB_DEFAULT, 0, + BLOCKDEV_ON_ERROR_REPORT, NULL, common_block_job_cb, + &cbi, false, &local_err); + aio_context_release(aio_context); +diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c +index 93d1ff0..60e9fa2 100644 +--- a/tests/test-blockjob-txn.c ++++ b/tests/test-blockjob-txn.c +@@ -107,7 +107,7 @@ static BlockJob *test_block_job_start(unsigned int iterations, + + snprintf(job_id, sizeof(job_id), "job%u", counter++); + s = block_job_create(job_id, &test_block_job_driver, txn, bs, +- 0, BLK_PERM_ALL, 0, BLOCK_JOB_DEFAULT, ++ 0, BLK_PERM_ALL, 0, JOB_DEFAULT, + test_block_job_cb, data, &error_abort); + s->iterations = iterations; + s->use_timer = use_timer; +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index ceb5960..8bb0aa8 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -59,7 +59,7 @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id, + bool should_succeed) + { + return mk_job(blk, id, &test_block_job_driver, +- should_succeed, BLOCK_JOB_DEFAULT); ++ should_succeed, JOB_DEFAULT); + } + + /* This creates a BlockBackend (optionally with a name) with a +@@ -214,7 +214,7 @@ static CancelJob *create_common(BlockJob **pjob) + + blk = create_blk(NULL); + job = mk_job(blk, "Steve", &test_cancel_driver, true, +- BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS); ++ JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS); + job_ref(&job->job); + assert(job->job.status == JOB_STATUS_CREATED); + s = container_of(job, CancelJob, common); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Move-cancelled-to-Job.patch b/SOURCES/kvm-job-Move-cancelled-to-Job.patch new file mode 100644 index 0000000..6c75ba7 --- /dev/null +++ b/SOURCES/kvm-job-Move-cancelled-to-Job.patch @@ -0,0 +1,439 @@ +From e799889203d2f06adac2c1636b15be360e50f5a2 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:06 +0200 +Subject: [PATCH 37/89] job: Move cancelled to Job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-24-kwolf@redhat.com> +Patchwork-id: 81103 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 23/73] job: Move cancelled to Job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +We cannot yet move the whole logic around job cancelling to Job because +it depends on quite a few other things that are still only in BlockJob, +but we can move the cancelled field at least. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: John Snow +(cherry picked from commit daa7f2f9467bc5624f04f28d4b01b88f08c6589c) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 6 +++--- + block/commit.c | 4 ++-- + block/mirror.c | 20 ++++++++++---------- + block/stream.c | 4 ++-- + blockjob.c | 28 +++++++++++++--------------- + include/block/blockjob.h | 8 -------- + include/block/blockjob_int.h | 8 -------- + include/qemu/job.h | 11 +++++++++++ + job.c | 5 +++++ + tests/test-blockjob-txn.c | 6 +++--- + tests/test-blockjob.c | 2 +- + 11 files changed, 50 insertions(+), 52 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index cfdb89d..ef0aa0e 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -329,7 +329,7 @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job) + { + uint64_t delay_ns; + +- if (block_job_is_cancelled(&job->common)) { ++ if (job_is_cancelled(&job->common.job)) { + return true; + } + +@@ -339,7 +339,7 @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job) + job->bytes_read = 0; + block_job_sleep_ns(&job->common, delay_ns); + +- if (block_job_is_cancelled(&job->common)) { ++ if (job_is_cancelled(&job->common.job)) { + return true; + } + +@@ -441,7 +441,7 @@ static void coroutine_fn backup_run(void *opaque) + if (job->sync_mode == MIRROR_SYNC_MODE_NONE) { + /* All bits are set in copy_bitmap to allow any cluster to be copied. + * This does not actually require them to be copied. */ +- while (!block_job_is_cancelled(&job->common)) { ++ while (!job_is_cancelled(&job->common.job)) { + /* Yield until the job is cancelled. We just let our before_write + * notify callback service CoW requests. */ + block_job_yield(&job->common); +diff --git a/block/commit.c b/block/commit.c +index 925c96a..85baea8 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -90,7 +90,7 @@ static void commit_complete(BlockJob *job, void *opaque) + * the normal backing chain can be restored. */ + blk_unref(s->base); + +- if (!block_job_is_cancelled(&s->common) && ret == 0) { ++ if (!job_is_cancelled(&s->common.job) && ret == 0) { + /* success */ + ret = bdrv_drop_intermediate(s->commit_top_bs, base, + s->backing_file_str); +@@ -172,7 +172,7 @@ static void coroutine_fn commit_run(void *opaque) + * with no pending I/O here so that bdrv_drain_all() returns. + */ + block_job_sleep_ns(&s->common, delay_ns); +- if (block_job_is_cancelled(&s->common)) { ++ if (job_is_cancelled(&s->common.job)) { + break; + } + /* Copy if allocated above the base */ +diff --git a/block/mirror.c b/block/mirror.c +index 0df4f70..424072e 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -622,7 +622,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) + + mirror_throttle(s); + +- if (block_job_is_cancelled(&s->common)) { ++ if (job_is_cancelled(&s->common.job)) { + s->initial_zeroing_ongoing = false; + return 0; + } +@@ -650,7 +650,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) + + mirror_throttle(s); + +- if (block_job_is_cancelled(&s->common)) { ++ if (job_is_cancelled(&s->common.job)) { + return 0; + } + +@@ -695,7 +695,7 @@ static void coroutine_fn mirror_run(void *opaque) + checking for a NULL string */ + int ret = 0; + +- if (block_job_is_cancelled(&s->common)) { ++ if (job_is_cancelled(&s->common.job)) { + goto immediate_exit; + } + +@@ -729,10 +729,10 @@ static void coroutine_fn mirror_run(void *opaque) + /* Report BLOCK_JOB_READY and wait for complete. */ + block_job_event_ready(&s->common); + s->synced = true; +- while (!block_job_is_cancelled(&s->common) && !s->should_complete) { ++ while (!job_is_cancelled(&s->common.job) && !s->should_complete) { + block_job_yield(&s->common); + } +- s->common.cancelled = false; ++ s->common.job.cancelled = false; + goto immediate_exit; + } + +@@ -768,7 +768,7 @@ static void coroutine_fn mirror_run(void *opaque) + s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + if (!s->is_none_mode) { + ret = mirror_dirty_init(s); +- if (ret < 0 || block_job_is_cancelled(&s->common)) { ++ if (ret < 0 || job_is_cancelled(&s->common.job)) { + goto immediate_exit; + } + } +@@ -828,7 +828,7 @@ static void coroutine_fn mirror_run(void *opaque) + } + + should_complete = s->should_complete || +- block_job_is_cancelled(&s->common); ++ job_is_cancelled(&s->common.job); + cnt = bdrv_get_dirty_count(s->dirty_bitmap); + } + +@@ -856,7 +856,7 @@ static void coroutine_fn mirror_run(void *opaque) + * completion. + */ + assert(QLIST_EMPTY(&bs->tracked_requests)); +- s->common.cancelled = false; ++ s->common.job.cancelled = false; + need_drain = false; + break; + } +@@ -869,7 +869,7 @@ static void coroutine_fn mirror_run(void *opaque) + } + trace_mirror_before_sleep(s, cnt, s->synced, delay_ns); + block_job_sleep_ns(&s->common, delay_ns); +- if (block_job_is_cancelled(&s->common) && ++ if (job_is_cancelled(&s->common.job) && + (!s->synced || s->common.force)) + { + break; +@@ -884,7 +884,7 @@ immediate_exit: + * the target is a copy of the source. + */ + assert(ret < 0 || ((s->common.force || !s->synced) && +- block_job_is_cancelled(&s->common))); ++ job_is_cancelled(&s->common.job))); + assert(need_drain); + mirror_wait_for_all_io(s); + } +diff --git a/block/stream.c b/block/stream.c +index 7273d22..22c71ae 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -66,7 +66,7 @@ static void stream_complete(BlockJob *job, void *opaque) + BlockDriverState *base = s->base; + Error *local_err = NULL; + +- if (!block_job_is_cancelled(&s->common) && bs->backing && ++ if (!job_is_cancelled(&s->common.job) && bs->backing && + data->ret == 0) { + const char *base_id = NULL, *base_fmt = NULL; + if (base) { +@@ -141,7 +141,7 @@ static void coroutine_fn stream_run(void *opaque) + * with no pending I/O here so that bdrv_drain_all() returns. + */ + block_job_sleep_ns(&s->common, delay_ns); +- if (block_job_is_cancelled(&s->common)) { ++ if (job_is_cancelled(&s->common.job)) { + break; + } + +diff --git a/blockjob.c b/blockjob.c +index 0bf0a26..f4f9956 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -379,7 +379,7 @@ static void block_job_conclude(BlockJob *job) + + static void block_job_update_rc(BlockJob *job) + { +- if (!job->ret && block_job_is_cancelled(job)) { ++ if (!job->ret && job_is_cancelled(&job->job)) { + job->ret = -ECANCELED; + } + if (job->ret) { +@@ -438,7 +438,7 @@ static int block_job_finalize_single(BlockJob *job) + + /* Emit events only if we actually started */ + if (block_job_started(job)) { +- if (block_job_is_cancelled(job)) { ++ if (job_is_cancelled(&job->job)) { + block_job_event_cancelled(job); + } else { + const char *msg = NULL; +@@ -464,7 +464,7 @@ static void block_job_cancel_async(BlockJob *job, bool force) + job->user_paused = false; + job->pause_count--; + } +- job->cancelled = true; ++ job->job.cancelled = true; + /* To prevent 'force == false' overriding a previous 'force == true' */ + job->force |= force; + } +@@ -519,7 +519,8 @@ static int block_job_finish_sync(BlockJob *job, + while (!job->completed) { + aio_poll(qemu_get_aio_context(), true); + } +- ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret; ++ ret = (job_is_cancelled(&job->job) && job->ret == 0) ++ ? -ECANCELED : job->ret; + job_unref(&job->job); + return ret; + } +@@ -557,7 +558,7 @@ static void block_job_completed_txn_abort(BlockJob *job) + other_job = QLIST_FIRST(&txn->jobs); + ctx = blk_get_aio_context(other_job->blk); + if (!other_job->completed) { +- assert(other_job->cancelled); ++ assert(job_is_cancelled(&other_job->job)); + block_job_finish_sync(other_job, NULL, NULL); + } + block_job_finalize_single(other_job); +@@ -651,7 +652,9 @@ void block_job_complete(BlockJob *job, Error **errp) + if (job_apply_verb(&job->job, JOB_VERB_COMPLETE, errp)) { + return; + } +- if (job->pause_count || job->cancelled || !job->driver->complete) { ++ if (job->pause_count || job_is_cancelled(&job->job) || ++ !job->driver->complete) ++ { + error_setg(errp, "The active block job '%s' cannot be completed", + job->job.id); + return; +@@ -1006,7 +1009,7 @@ void coroutine_fn block_job_pause_point(BlockJob *job) + if (!block_job_should_pause(job)) { + return; + } +- if (block_job_is_cancelled(job)) { ++ if (job_is_cancelled(&job->job)) { + return; + } + +@@ -1014,7 +1017,7 @@ void coroutine_fn block_job_pause_point(BlockJob *job) + job->driver->pause(job); + } + +- if (block_job_should_pause(job) && !block_job_is_cancelled(job)) { ++ if (block_job_should_pause(job) && !job_is_cancelled(&job->job)) { + JobStatus status = job->job.status; + job_state_transition(&job->job, status == JOB_STATUS_READY + ? JOB_STATUS_STANDBY +@@ -1066,17 +1069,12 @@ void block_job_enter(BlockJob *job) + block_job_enter_cond(job, NULL); + } + +-bool block_job_is_cancelled(BlockJob *job) +-{ +- return job->cancelled; +-} +- + void block_job_sleep_ns(BlockJob *job, int64_t ns) + { + assert(job->busy); + + /* Check cancellation *before* setting busy = false, too! */ +- if (block_job_is_cancelled(job)) { ++ if (job_is_cancelled(&job->job)) { + return; + } + +@@ -1092,7 +1090,7 @@ void block_job_yield(BlockJob *job) + assert(job->busy); + + /* Check cancellation *before* setting busy = false, too! */ +- if (block_job_is_cancelled(job)) { ++ if (job_is_cancelled(&job->job)) { + return; + } + +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 087e782..1e708f4 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -57,14 +57,6 @@ typedef struct BlockJob { + Coroutine *co; + + /** +- * 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. + */ +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index 6f0fe3c..d64f30e 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -196,14 +196,6 @@ void block_job_early_fail(BlockJob *job); + void block_job_completed(BlockJob *job, int ret); + + /** +- * block_job_is_cancelled: +- * @job: The job being queried. +- * +- * Returns whether the job is scheduled for cancellation. +- */ +-bool block_job_is_cancelled(BlockJob *job); +- +-/** + * block_job_pause_point: + * @job: The job that is ready to pause. + * +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 0751e2a..5dfbec5 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -47,6 +47,14 @@ typedef struct Job { + /** Current state; See @JobStatus for details. */ + JobStatus status; + ++ /** ++ * 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; ++ + /** Element of the list of jobs */ + QLIST_ENTRY(Job) job_list; + } Job; +@@ -93,6 +101,9 @@ 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 whether the job is scheduled for cancellation. */ ++bool job_is_cancelled(Job *job); ++ + /** + * Get the next element from the list of block jobs after @job, or the + * first one if @job is %NULL. +diff --git a/job.c b/job.c +index 926f1de..1abca6a 100644 +--- a/job.c ++++ b/job.c +@@ -95,6 +95,11 @@ const char *job_type_str(const Job *job) + return JobType_str(job_type(job)); + } + ++bool job_is_cancelled(Job *job) ++{ ++ return job->cancelled; ++} ++ + Job *job_next(Job *job) + { + if (!job) { +diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c +index b49b28c..26b4bbb 100644 +--- a/tests/test-blockjob-txn.c ++++ b/tests/test-blockjob-txn.c +@@ -29,7 +29,7 @@ static void test_block_job_complete(BlockJob *job, void *opaque) + BlockDriverState *bs = blk_bs(job->blk); + int rc = (intptr_t)opaque; + +- if (block_job_is_cancelled(job)) { ++ if (job_is_cancelled(&job->job)) { + rc = -ECANCELED; + } + +@@ -49,7 +49,7 @@ static void coroutine_fn test_block_job_run(void *opaque) + block_job_yield(job); + } + +- if (block_job_is_cancelled(job)) { ++ if (job_is_cancelled(&job->job)) { + break; + } + } +@@ -66,7 +66,7 @@ typedef struct { + static void test_block_job_cb(void *opaque, int ret) + { + TestBlockJobCBData *data = opaque; +- if (!ret && block_job_is_cancelled(&data->job->common)) { ++ if (!ret && job_is_cancelled(&data->job->common.job)) { + ret = -ECANCELED; + } + *data->result = ret; +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index e24fc3f..fa31481 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -179,7 +179,7 @@ static void coroutine_fn cancel_job_start(void *opaque) + CancelJob *s = opaque; + + while (!s->should_complete) { +- if (block_job_is_cancelled(&s->common)) { ++ if (job_is_cancelled(&s->common.job)) { + goto defer; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Move-completion-and-cancellation-to-Job.patch b/SOURCES/kvm-job-Move-completion-and-cancellation-to-Job.patch new file mode 100644 index 0000000..daa2617 --- /dev/null +++ b/SOURCES/kvm-job-Move-completion-and-cancellation-to-Job.patch @@ -0,0 +1,843 @@ +From 9b04fa7921806440d3eec89b90667dd77761f149 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:23 +0200 +Subject: [PATCH 54/89] job: Move completion and cancellation to Job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-41-kwolf@redhat.com> +Patchwork-id: 81116 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 40/73] job: Move completion and cancellation to Job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This moves the top-level job completion and cancellation functions from +BlockJob to Job. + +Signed-off-by: Kevin Wolf +(cherry picked from commit 3d70ff53b6bf90d9eec6f97024ec9895f6799d9e) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block.c | 4 ++- + block/backup.c | 3 +- + block/commit.c | 6 ++-- + block/mirror.c | 6 ++-- + block/replication.c | 4 +-- + block/stream.c | 2 +- + block/trace-events | 3 -- + blockdev.c | 8 ++--- + blockjob.c | 76 --------------------------------------- + include/block/blockjob.h | 55 ----------------------------- + include/block/blockjob_int.h | 18 ---------- + include/qemu/job.h | 68 +++++++++++++++++++++++++++++------ + job.c | 84 +++++++++++++++++++++++++++++++++++++++----- + qemu-img.c | 2 +- + tests/test-bdrv-drain.c | 5 ++- + tests/test-blockjob-txn.c | 14 ++++---- + tests/test-blockjob.c | 21 ++++++----- + trace-events | 1 + + 18 files changed, 171 insertions(+), 209 deletions(-) + +diff --git a/block.c b/block.c +index d991a09..a83787f 100644 +--- a/block.c ++++ b/block.c +@@ -3374,7 +3374,9 @@ static void bdrv_close(BlockDriverState *bs) + + void bdrv_close_all(void) + { +- block_job_cancel_sync_all(); ++ /* TODO We do want to cancel all jobs instead of just block jobs on ++ * shutdown, but bdrv_close_all() isn't the right place any more. */ ++ job_cancel_sync_all(); + nbd_export_close_all(); + + /* Drop references from requests still in flight, such as canceled block +diff --git a/block/backup.c b/block/backup.c +index 6172f90..b13f91d 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -319,10 +319,9 @@ typedef struct { + + static void backup_complete(Job *job, void *opaque) + { +- BlockJob *bjob = container_of(job, BlockJob, job); + BackupCompleteData *data = opaque; + +- block_job_completed(bjob, data->ret); ++ job_completed(job, data->ret); + g_free(data); + } + +diff --git a/block/commit.c b/block/commit.c +index 40d97a3..b0a847e 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -112,12 +112,12 @@ static void commit_complete(Job *job, void *opaque) + blk_unref(s->top); + + /* If there is more than one reference to the job (e.g. if called from +- * job_finish_sync()), block_job_completed() won't free it and therefore +- * the blockers on the intermediate nodes remain. This would cause ++ * job_finish_sync()), job_completed() won't free it and therefore the ++ * blockers on the intermediate nodes remain. This would cause + * bdrv_set_backing_hd() to fail. */ + block_job_remove_all_bdrv(bjob); + +- block_job_completed(&s->common, ret); ++ job_completed(job, ret); + g_free(data); + + /* If bdrv_drop_intermediate() didn't already do that, remove the commit +diff --git a/block/mirror.c b/block/mirror.c +index 656237a..c63cf7c 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -498,7 +498,7 @@ static void mirror_exit(Job *job, void *opaque) + bdrv_release_dirty_bitmap(src, s->dirty_bitmap); + + /* Make sure that the source BDS doesn't go away before we called +- * block_job_completed(). */ ++ * job_completed(). */ + bdrv_ref(src); + bdrv_ref(mirror_top_bs); + bdrv_ref(target_bs); +@@ -581,7 +581,7 @@ static void mirror_exit(Job *job, void *opaque) + blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort); + blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort); + +- block_job_completed(&s->common, data->ret); ++ job_completed(job, data->ret); + + g_free(data); + bdrv_drained_end(src); +@@ -954,7 +954,7 @@ static void mirror_complete(Job *job, Error **errp) + } + + s->should_complete = true; +- block_job_enter(&s->common); ++ job_enter(job); + } + + static void mirror_pause(Job *job) +diff --git a/block/replication.c b/block/replication.c +index 8241400..f3b2839 100644 +--- a/block/replication.c ++++ b/block/replication.c +@@ -145,7 +145,7 @@ static void replication_close(BlockDriverState *bs) + replication_stop(s->rs, false, NULL); + } + if (s->stage == BLOCK_REPLICATION_FAILOVER) { +- block_job_cancel_sync(s->active_disk->bs->job); ++ job_cancel_sync(&s->active_disk->bs->job->job); + } + + if (s->mode == REPLICATION_MODE_SECONDARY) { +@@ -679,7 +679,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) + * disk, secondary disk in backup_job_completed(). + */ + if (s->secondary_disk->bs->job) { +- block_job_cancel_sync(s->secondary_disk->bs->job); ++ job_cancel_sync(&s->secondary_disk->bs->job->job); + } + + if (!failover) { +diff --git a/block/stream.c b/block/stream.c +index b996278..8546c41 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -93,7 +93,7 @@ out: + } + + g_free(s->backing_file_str); +- block_job_completed(&s->common, data->ret); ++ job_completed(job, data->ret); + g_free(data); + } + +diff --git a/block/trace-events b/block/trace-events +index 93b9279..2d59b53 100644 +--- a/block/trace-events ++++ b/block/trace-events +@@ -4,9 +4,6 @@ + bdrv_open_common(void *bs, const char *filename, int flags, const char *format_name) "bs %p filename \"%s\" flags 0x%x format_name \"%s\"" + bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d" + +-# blockjob.c +-block_job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d" +- + # block/block-backend.c + blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" + blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" +diff --git a/blockdev.c b/blockdev.c +index 422b5ac..d6cd8ed 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -151,7 +151,7 @@ void blockdev_mark_auto_del(BlockBackend *blk) + aio_context_acquire(aio_context); + + if (bs->job) { +- block_job_cancel(bs->job, false); ++ job_cancel(&bs->job->job, false); + } + + aio_context_release(aio_context); +@@ -1967,7 +1967,7 @@ static void drive_backup_abort(BlkActionState *common) + aio_context = bdrv_get_aio_context(state->bs); + aio_context_acquire(aio_context); + +- block_job_cancel_sync(state->job); ++ job_cancel_sync(&state->job->job); + + aio_context_release(aio_context); + } +@@ -2065,7 +2065,7 @@ static void blockdev_backup_abort(BlkActionState *common) + aio_context = bdrv_get_aio_context(state->bs); + aio_context_acquire(aio_context); + +- block_job_cancel_sync(state->job); ++ job_cancel_sync(&state->job->job); + + aio_context_release(aio_context); + } +@@ -3898,7 +3898,7 @@ void qmp_block_job_cancel(const char *device, + } + + trace_qmp_block_job_cancel(job); +- block_job_user_cancel(job, force, errp); ++ job_user_cancel(&job->job, force, errp); + out: + aio_context_release(aio_context); + } +diff --git a/blockjob.c b/blockjob.c +index 14b21c8..438baa1 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -255,63 +255,6 @@ void block_job_dismiss(BlockJob **jobptr, Error **errp) + *jobptr = NULL; + } + +-void block_job_cancel(BlockJob *job, bool force) +-{ +- if (job->job.status == JOB_STATUS_CONCLUDED) { +- job_do_dismiss(&job->job); +- return; +- } +- job_cancel_async(&job->job, force); +- if (!job_started(&job->job)) { +- block_job_completed(job, -ECANCELED); +- } else if (job->job.deferred_to_main_loop) { +- job_completed_txn_abort(&job->job); +- } else { +- block_job_enter(job); +- } +-} +- +-void block_job_user_cancel(BlockJob *job, bool force, Error **errp) +-{ +- if (job_apply_verb(&job->job, JOB_VERB_CANCEL, errp)) { +- return; +- } +- block_job_cancel(job, force); +-} +- +-/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be +- * used with job_finish_sync() without the need for (rather nasty) function +- * pointer casts there. */ +-static void block_job_cancel_err(Job *job, Error **errp) +-{ +- BlockJob *bjob = container_of(job, BlockJob, job); +- assert(is_block_job(job)); +- block_job_cancel(bjob, false); +-} +- +-int block_job_cancel_sync(BlockJob *job) +-{ +- return job_finish_sync(&job->job, &block_job_cancel_err, NULL); +-} +- +-void block_job_cancel_sync_all(void) +-{ +- BlockJob *job; +- AioContext *aio_context; +- +- while ((job = block_job_next(NULL))) { +- aio_context = blk_get_aio_context(job->blk); +- aio_context_acquire(aio_context); +- block_job_cancel_sync(job); +- aio_context_release(aio_context); +- } +-} +- +-int block_job_complete_sync(BlockJob *job, Error **errp) +-{ +- return job_finish_sync(&job->job, job_complete, errp); +-} +- + void block_job_progress_update(BlockJob *job, uint64_t done) + { + job->offset += done; +@@ -488,25 +431,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + return job; + } + +-void block_job_completed(BlockJob *job, int ret) +-{ +- assert(job && job->job.txn && !job_is_completed(&job->job)); +- assert(blk_bs(job->blk)->job == job); +- job->job.ret = ret; +- job_update_rc(&job->job); +- trace_block_job_completed(job, ret, job->job.ret); +- if (job->job.ret) { +- job_completed_txn_abort(&job->job); +- } else { +- job_completed_txn_success(&job->job); +- } +-} +- +-void block_job_enter(BlockJob *job) +-{ +- job_enter_cond(&job->job, NULL); +-} +- + void block_job_yield(BlockJob *job) + { + assert(job->job.busy); +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 09e6bb4..e9ed7b8 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -141,15 +141,6 @@ void block_job_remove_all_bdrv(BlockJob *job); + void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp); + + /** +- * block_job_cancel: +- * @job: The job to be canceled. +- * @force: Quit a job without waiting for data to be in sync. +- * +- * Asynchronously cancel the specified job. +- */ +-void block_job_cancel(BlockJob *job, bool force); +- +-/** + * block_job_dismiss: + * @job: The job to be dismissed. + * @errp: Error object. +@@ -186,52 +177,6 @@ void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining); + BlockJobInfo *block_job_query(BlockJob *job, Error **errp); + + /** +- * block_job_user_cancel: +- * @job: The job to be cancelled. +- * @force: Quit a job without waiting for data to be in sync. +- * +- * Cancels the specified job, but may refuse to do so if the +- * operation isn't currently meaningful. +- */ +-void block_job_user_cancel(BlockJob *job, bool force, Error **errp); +- +-/** +- * block_job_cancel_sync: +- * @job: The job to be canceled. +- * +- * Synchronously cancel the job. The completion callback is called +- * before the function returns. 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. +- */ +-int block_job_cancel_sync(BlockJob *job); +- +-/** +- * block_job_cancel_sync_all: +- * +- * Synchronously cancels all jobs using block_job_cancel_sync(). +- */ +-void block_job_cancel_sync_all(void); +- +-/** +- * block_job_complete_sync: +- * @job: The job to be completed. +- * @errp: Error object which may be set by block_job_complete(); 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. +- */ +-int block_job_complete_sync(BlockJob *job, Error **errp); +- +-/** + * block_job_iostatus_reset: + * @job: The job whose I/O status should be reset. + * +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index 29a2802..7df07b2 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -124,24 +124,6 @@ void block_job_yield(BlockJob *job); + int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n); + + /** +- * block_job_completed: +- * @job: The job being completed. +- * @ret: The status code. +- * +- * Call the completion function that was registered at creation time, and +- * free @job. +- */ +-void block_job_completed(BlockJob *job, int ret); +- +-/** +- * block_job_enter: +- * @job: The job to enter. +- * +- * Continue the specified job by entering the coroutine. +- */ +-void block_job_enter(BlockJob *job); +- +-/** + * block_job_event_ready: + * @job: The job which is now ready to be completed. + * +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 39d74ab..bbe1b0c 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -74,8 +74,8 @@ typedef struct Job { + + /** + * Set to false by the job while the coroutine has yielded and may be +- * re-entered by block_job_enter(). There may still be I/O or event loop +- * activity pending. Accessed under block_job_mutex (in blockjob.c). ++ * re-entered by job_enter(). There may still be I/O or event loop activity ++ * pending. Accessed under block_job_mutex (in blockjob.c). + */ + bool busy; + +@@ -114,7 +114,7 @@ typedef struct Job { + /** True if this job should automatically dismiss itself */ + bool auto_dismiss; + +- /** ret code passed to block_job_completed. */ ++ /** ret code passed to job_completed. */ + int ret; + + /** The completion function that will be called when the job completes. */ +@@ -266,8 +266,8 @@ void job_txn_unref(JobTxn *txn); + * @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 block_job_completed() to +- * release the reference that is automatically grabbed here. ++ * 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. + */ +@@ -416,8 +416,59 @@ int job_apply_verb(Job *job, JobVerb verb, Error **errp); + /** The @job could not be started, free it. */ + void job_early_fail(Job *job); + ++/** ++ * @job: The job being completed. ++ * @ret: The status code. ++ * ++ * Marks @job as completed. If @ret is non-zero, the job transaction it is part ++ * of is aborted. If @ret is 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. ++ */ ++void job_completed(Job *job, int ret); ++ + /** Asynchronously complete the specified @job. */ +-void job_complete(Job *job, Error **errp);; ++void job_complete(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. ++ */ ++void job_cancel(Job *job, bool force); ++ ++/** ++ * Cancels the specified job like job_cancel(), but may refuse to do so if the ++ * operation isn't meaningful in the current state of the job. ++ */ ++void job_user_cancel(Job *job, bool force, Error **errp); ++ ++/** ++ * Synchronously cancel the @job. The completion callback is called ++ * before the function returns. 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. ++ */ ++int job_cancel_sync(Job *job); ++ ++/** Synchronously cancels all jobs using job_cancel_sync(). */ ++void job_cancel_sync_all(void); ++ ++/** ++ * @job: The job to be completed. ++ * @errp: Error object which may be set by job_complete(); 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. ++ */ ++int job_complete_sync(Job *job, Error **errp); + + /** + * For a @job that has finished its work and is pending awaiting explicit +@@ -459,11 +510,6 @@ int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) + void job_state_transition(Job *job, JobStatus s1); + void coroutine_fn job_do_yield(Job *job, uint64_t ns); + bool job_should_pause(Job *job); +-bool job_started(Job *job); + void job_do_dismiss(Job *job); +-void job_update_rc(Job *job); +-void job_cancel_async(Job *job, bool force); +-void job_completed_txn_abort(Job *job); +-void job_completed_txn_success(Job *job); + + #endif +diff --git a/job.c b/job.c +index 4f6fd73..2e453f6 100644 +--- a/job.c ++++ b/job.c +@@ -221,7 +221,7 @@ bool job_is_completed(Job *job) + return false; + } + +-bool job_started(Job *job) ++static bool job_started(Job *job) + { + return job->co; + } +@@ -391,10 +391,10 @@ void job_enter(Job *job) + } + + /* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds. +- * Reentering the job coroutine with block_job_enter() before the timer has +- * expired is allowed and cancels the timer. ++ * 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 block_job_enter() must be ++ * If @ns is (uint64_t) -1, no timer is scheduled and job_enter() must be + * called explicitly. */ + void coroutine_fn job_do_yield(Job *job, uint64_t ns) + { +@@ -579,7 +579,7 @@ static void job_conclude(Job *job) + } + } + +-void job_update_rc(Job *job) ++static void job_update_rc(Job *job) + { + if (!job->ret && job_is_cancelled(job)) { + job->ret = -ECANCELED; +@@ -644,7 +644,7 @@ static int job_finalize_single(Job *job) + return 0; + } + +-void job_cancel_async(Job *job, bool force) ++static void job_cancel_async(Job *job, bool force) + { + if (job->user_paused) { + /* Do not call job_enter here, the caller will handle it. */ +@@ -660,7 +660,7 @@ void job_cancel_async(Job *job, bool force) + job->force_cancel |= force; + } + +-void job_completed_txn_abort(Job *job) ++static void job_completed_txn_abort(Job *job) + { + AioContext *ctx; + JobTxn *txn = job->txn; +@@ -748,7 +748,7 @@ static int job_transition_to_pending(Job *job) + return 0; + } + +-void job_completed_txn_success(Job *job) ++static void job_completed_txn_success(Job *job) + { + JobTxn *txn = job->txn; + Job *other_job; +@@ -774,6 +774,74 @@ void job_completed_txn_success(Job *job) + } + } + ++void job_completed(Job *job, int ret) ++{ ++ assert(job && job->txn && !job_is_completed(job)); ++ job->ret = ret; ++ job_update_rc(job); ++ trace_job_completed(job, ret, job->ret); ++ if (job->ret) { ++ job_completed_txn_abort(job); ++ } else { ++ job_completed_txn_success(job); ++ } ++} ++ ++void job_cancel(Job *job, bool force) ++{ ++ if (job->status == JOB_STATUS_CONCLUDED) { ++ job_do_dismiss(job); ++ return; ++ } ++ job_cancel_async(job, force); ++ if (!job_started(job)) { ++ job_completed(job, -ECANCELED); ++ } else if (job->deferred_to_main_loop) { ++ job_completed_txn_abort(job); ++ } else { ++ job_enter(job); ++ } ++} ++ ++void job_user_cancel(Job *job, bool force, Error **errp) ++{ ++ if (job_apply_verb(job, JOB_VERB_CANCEL, errp)) { ++ return; ++ } ++ job_cancel(job, force); ++} ++ ++/* A wrapper around job_cancel() taking an Error ** parameter so it may be ++ * used with job_finish_sync() without the need for (rather nasty) function ++ * pointer casts there. */ ++static void job_cancel_err(Job *job, Error **errp) ++{ ++ job_cancel(job, false); ++} ++ ++int job_cancel_sync(Job *job) ++{ ++ return job_finish_sync(job, &job_cancel_err, NULL); ++} ++ ++void job_cancel_sync_all(void) ++{ ++ Job *job; ++ AioContext *aio_context; ++ ++ while ((job = job_next(NULL))) { ++ aio_context = job->aio_context; ++ aio_context_acquire(aio_context); ++ job_cancel_sync(job); ++ aio_context_release(aio_context); ++ } ++} ++ ++int job_complete_sync(Job *job, Error **errp) ++{ ++ return job_finish_sync(job, job_complete, errp); ++} ++ + void job_complete(Job *job, Error **errp) + { + /* Should not be reachable via external interface for internal jobs */ +diff --git a/qemu-img.c b/qemu-img.c +index 91b3151..734ea94 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -881,7 +881,7 @@ static void run_block_job(BlockJob *job, Error **errp) + } while (!job->ready && !job_is_completed(&job->job)); + + if (!job_is_completed(&job->job)) { +- ret = block_job_complete_sync(job, errp); ++ ret = job_complete_sync(&job->job, errp); + } else { + ret = job->job.ret; + } +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index b428aac..3600ffd 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -498,8 +498,7 @@ typedef struct TestBlockJob { + + static void test_job_completed(Job *job, void *opaque) + { +- BlockJob *bjob = container_of(job, BlockJob, job); +- block_job_completed(bjob, 0); ++ job_completed(job, 0); + } + + static void coroutine_fn test_job_start(void *opaque) +@@ -593,7 +592,7 @@ static void test_blockjob_common(enum drain_type drain_type) + g_assert_false(job->job.paused); + g_assert_false(job->job.busy); /* We're in job_sleep_ns() */ + +- ret = block_job_complete_sync(job, &error_abort); ++ ret = job_complete_sync(&job->job, &error_abort); + g_assert_cmpint(ret, ==, 0); + + blk_unref(blk_src); +diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c +index 6ee31d5..34ee179 100644 +--- a/tests/test-blockjob-txn.c ++++ b/tests/test-blockjob-txn.c +@@ -34,7 +34,7 @@ static void test_block_job_complete(Job *job, void *opaque) + rc = -ECANCELED; + } + +- block_job_completed(bjob, rc); ++ job_completed(job, rc); + bdrv_unref(bs); + } + +@@ -130,7 +130,7 @@ static void test_single_job(int expected) + job_start(&job->job); + + if (expected == -ECANCELED) { +- block_job_cancel(job, false); ++ job_cancel(&job->job, false); + } + + while (result == -EINPROGRESS) { +@@ -176,10 +176,10 @@ static void test_pair_jobs(int expected1, int expected2) + job_txn_unref(txn); + + if (expected1 == -ECANCELED) { +- block_job_cancel(job1, false); ++ job_cancel(&job1->job, false); + } + if (expected2 == -ECANCELED) { +- block_job_cancel(job2, false); ++ job_cancel(&job2->job, false); + } + + while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { +@@ -232,13 +232,13 @@ static void test_pair_jobs_fail_cancel_race(void) + job_start(&job1->job); + job_start(&job2->job); + +- block_job_cancel(job1, false); ++ job_cancel(&job1->job, false); + + /* Now make job2 finish before the main loop kicks jobs. This simulates + * the race between a pending kick and another job completing. + */ +- block_job_enter(job2); +- block_job_enter(job2); ++ job_enter(&job2->job); ++ job_enter(&job2->job); + + while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { + aio_poll(qemu_get_aio_context(), true); +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index 1e052c2..46a7873 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -165,10 +165,9 @@ typedef struct CancelJob { + + static void cancel_job_completed(Job *job, void *opaque) + { +- BlockJob *bjob = container_of(job, BlockJob, job); + CancelJob *s = opaque; + s->completed = true; +- block_job_completed(bjob, 0); ++ job_completed(job, 0); + } + + static void cancel_job_complete(Job *job, Error **errp) +@@ -232,7 +231,7 @@ static void cancel_common(CancelJob *s) + BlockBackend *blk = s->blk; + JobStatus sts = job->job.status; + +- block_job_cancel_sync(job); ++ job_cancel_sync(&job->job); + if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) { + BlockJob *dummy = job; + block_job_dismiss(&dummy, &error_abort); +@@ -275,7 +274,7 @@ static void test_cancel_paused(void) + assert(job->job.status == JOB_STATUS_RUNNING); + + job_user_pause(&job->job, &error_abort); +- block_job_enter(job); ++ job_enter(&job->job); + assert(job->job.status == JOB_STATUS_PAUSED); + + cancel_common(s); +@@ -292,7 +291,7 @@ static void test_cancel_ready(void) + assert(job->job.status == JOB_STATUS_RUNNING); + + s->should_converge = true; +- block_job_enter(job); ++ job_enter(&job->job); + assert(job->job.status == JOB_STATUS_READY); + + cancel_common(s); +@@ -309,11 +308,11 @@ static void test_cancel_standby(void) + assert(job->job.status == JOB_STATUS_RUNNING); + + s->should_converge = true; +- block_job_enter(job); ++ job_enter(&job->job); + assert(job->job.status == JOB_STATUS_READY); + + job_user_pause(&job->job, &error_abort); +- block_job_enter(job); ++ job_enter(&job->job); + assert(job->job.status == JOB_STATUS_STANDBY); + + cancel_common(s); +@@ -330,11 +329,11 @@ static void test_cancel_pending(void) + assert(job->job.status == JOB_STATUS_RUNNING); + + s->should_converge = true; +- block_job_enter(job); ++ job_enter(&job->job); + assert(job->job.status == JOB_STATUS_READY); + + job_complete(&job->job, &error_abort); +- block_job_enter(job); ++ job_enter(&job->job); + while (!s->completed) { + aio_poll(qemu_get_aio_context(), true); + } +@@ -354,11 +353,11 @@ static void test_cancel_concluded(void) + assert(job->job.status == JOB_STATUS_RUNNING); + + s->should_converge = true; +- block_job_enter(job); ++ job_enter(&job->job); + assert(job->job.status == JOB_STATUS_READY); + + job_complete(&job->job, &error_abort); +- block_job_enter(job); ++ job_enter(&job->job); + while (!s->completed) { + aio_poll(qemu_get_aio_context(), true); + } +diff --git a/trace-events b/trace-events +index 2507e13..ef7579a 100644 +--- a/trace-events ++++ b/trace-events +@@ -107,6 +107,7 @@ gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packe + # job.c + job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)" + job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)" ++job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d" + + ### Guest events, keep at bottom + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Move-coroutine-and-related-code-to-Job.patch b/SOURCES/kvm-job-Move-coroutine-and-related-code-to-Job.patch new file mode 100644 index 0000000..0b9cb08 --- /dev/null +++ b/SOURCES/kvm-job-Move-coroutine-and-related-code-to-Job.patch @@ -0,0 +1,1257 @@ +From 0a25884271e9d6a99f0775c3b0c631d006ba2fc2 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:09 +0200 +Subject: [PATCH 40/89] job: Move coroutine and related code to Job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-27-kwolf@redhat.com> +Patchwork-id: 81113 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 26/73] job: Move coroutine and related code to Job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This commit moves some core functions for dealing with the job coroutine +from BlockJob to Job. This includes primarily entering the coroutine +(both for the first and reentering) and yielding explicitly and at pause +points. + +Signed-off-by: Kevin Wolf +Reviewed-by: John Snow +(cherry picked from commit da01ff7f38f52791f93fc3ca59afcfbb220f15af) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 2 +- + block/commit.c | 4 +- + block/mirror.c | 22 ++--- + block/replication.c | 2 +- + block/stream.c | 4 +- + blockdev.c | 8 +- + blockjob.c | 219 ++++++++----------------------------------- + include/block/blockjob.h | 40 -------- + include/block/blockjob_int.h | 26 ----- + include/qemu/job.h | 76 +++++++++++++++ + job.c | 137 +++++++++++++++++++++++++++ + tests/test-bdrv-drain.c | 38 ++++---- + tests/test-blockjob-txn.c | 12 +-- + tests/test-blockjob.c | 14 +-- + 14 files changed, 305 insertions(+), 299 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index 22dd368..7d9aad9 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -528,8 +528,8 @@ static const BlockJobDriver backup_job_driver = { + .instance_size = sizeof(BackupBlockJob), + .job_type = JOB_TYPE_BACKUP, + .free = block_job_free, ++ .start = backup_run, + }, +- .start = backup_run, + .commit = backup_commit, + .abort = backup_abort, + .clean = backup_clean, +diff --git a/block/commit.c b/block/commit.c +index d326766..2fbc310 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -220,8 +220,8 @@ static const BlockJobDriver commit_job_driver = { + .instance_size = sizeof(CommitBlockJob), + .job_type = JOB_TYPE_COMMIT, + .free = block_job_free, ++ .start = commit_run, + }, +- .start = commit_run, + }; + + static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs, +@@ -371,7 +371,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, + s->on_error = on_error; + + trace_commit_start(bs, base, top, s); +- block_job_start(&s->common); ++ job_start(&s->common.job); + return; + + fail: +diff --git a/block/mirror.c b/block/mirror.c +index 90d4ac9..95fc807 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -126,7 +126,7 @@ static void mirror_iteration_done(MirrorOp *op, int ret) + g_free(op); + + if (s->waiting_for_io) { +- qemu_coroutine_enter(s->common.co); ++ qemu_coroutine_enter(s->common.job.co); + } + } + +@@ -345,7 +345,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) + mirror_wait_for_io(s); + } + +- block_job_pause_point(&s->common); ++ job_pause_point(&s->common.job); + + /* Find the number of consective dirty chunks following the first dirty + * one, and wait for in flight requests in them. */ +@@ -597,7 +597,7 @@ static void mirror_throttle(MirrorBlockJob *s) + s->last_pause_ns = now; + block_job_sleep_ns(&s->common, 0); + } else { +- block_job_pause_point(&s->common); ++ job_pause_point(&s->common.job); + } + } + +@@ -786,7 +786,7 @@ static void coroutine_fn mirror_run(void *opaque) + goto immediate_exit; + } + +- block_job_pause_point(&s->common); ++ job_pause_point(&s->common.job); + + cnt = bdrv_get_dirty_count(s->dirty_bitmap); + /* cnt is the number of dirty bytes remaining and s->bytes_in_flight is +@@ -957,9 +957,9 @@ static void mirror_complete(BlockJob *job, Error **errp) + block_job_enter(&s->common); + } + +-static void mirror_pause(BlockJob *job) ++static void mirror_pause(Job *job) + { +- MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); + + mirror_wait_for_all_io(s); + } +@@ -991,10 +991,10 @@ static const BlockJobDriver mirror_job_driver = { + .instance_size = sizeof(MirrorBlockJob), + .job_type = JOB_TYPE_MIRROR, + .free = block_job_free, ++ .start = mirror_run, ++ .pause = mirror_pause, + }, +- .start = mirror_run, + .complete = mirror_complete, +- .pause = mirror_pause, + .attached_aio_context = mirror_attached_aio_context, + .drain = mirror_drain, + }; +@@ -1004,10 +1004,10 @@ static const BlockJobDriver commit_active_job_driver = { + .instance_size = sizeof(MirrorBlockJob), + .job_type = JOB_TYPE_COMMIT, + .free = block_job_free, ++ .start = mirror_run, ++ .pause = mirror_pause, + }, +- .start = mirror_run, + .complete = mirror_complete, +- .pause = mirror_pause, + .attached_aio_context = mirror_attached_aio_context, + .drain = mirror_drain, + }; +@@ -1244,7 +1244,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, + } + + trace_mirror_start(bs, s, opaque); +- block_job_start(&s->common); ++ job_start(&s->common.job); + return; + + fail: +diff --git a/block/replication.c b/block/replication.c +index 6c0c718..3f7500e 100644 +--- a/block/replication.c ++++ b/block/replication.c +@@ -574,7 +574,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, + aio_context_release(aio_context); + return; + } +- block_job_start(job); ++ job_start(&job->job); + break; + default: + aio_context_release(aio_context); +diff --git a/block/stream.c b/block/stream.c +index 0bba816..6d8b7b6 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -213,8 +213,8 @@ static const BlockJobDriver stream_job_driver = { + .instance_size = sizeof(StreamBlockJob), + .job_type = JOB_TYPE_STREAM, + .free = block_job_free, ++ .start = stream_run, + }, +- .start = stream_run, + }; + + void stream_start(const char *job_id, BlockDriverState *bs, +@@ -262,7 +262,7 @@ void stream_start(const char *job_id, BlockDriverState *bs, + + s->on_error = on_error; + trace_stream_start(bs, base, s); +- block_job_start(&s->common); ++ job_start(&s->common.job); + return; + + fail: +diff --git a/blockdev.c b/blockdev.c +index 96a89cc..efb83c4 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -1952,7 +1952,7 @@ static void drive_backup_commit(BlkActionState *common) + aio_context_acquire(aio_context); + + assert(state->job); +- block_job_start(state->job); ++ job_start(&state->job->job); + + aio_context_release(aio_context); + } +@@ -2050,7 +2050,7 @@ static void blockdev_backup_commit(BlkActionState *common) + aio_context_acquire(aio_context); + + assert(state->job); +- block_job_start(state->job); ++ job_start(&state->job->job); + + aio_context_release(aio_context); + } +@@ -3472,7 +3472,7 @@ void qmp_drive_backup(DriveBackup *arg, Error **errp) + BlockJob *job; + job = do_drive_backup(arg, NULL, errp); + if (job) { +- block_job_start(job); ++ job_start(&job->job); + } + } + +@@ -3560,7 +3560,7 @@ void qmp_blockdev_backup(BlockdevBackup *arg, Error **errp) + BlockJob *job; + job = do_blockdev_backup(arg, NULL, errp); + if (job) { +- block_job_start(job); ++ job_start(&job->job); + } + } + +diff --git a/blockjob.c b/blockjob.c +index 3ede511..313b1ff 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -36,30 +36,9 @@ + #include "qemu/coroutine.h" + #include "qemu/timer.h" + +-/* Right now, this mutex is only needed to synchronize accesses to job->busy +- * and job->sleep_timer, such as concurrent calls to block_job_do_yield and +- * block_job_enter. */ +-static QemuMutex block_job_mutex; +- +-static void block_job_lock(void) +-{ +- qemu_mutex_lock(&block_job_mutex); +-} +- +-static void block_job_unlock(void) +-{ +- qemu_mutex_unlock(&block_job_mutex); +-} +- +-static void __attribute__((__constructor__)) block_job_init(void) +-{ +- qemu_mutex_init(&block_job_mutex); +-} +- + static void block_job_event_cancelled(BlockJob *job); + static void block_job_event_completed(BlockJob *job, const char *msg); + static int block_job_event_pending(BlockJob *job); +-static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job)); + + /* Transactional group of block jobs */ + struct BlockJobTxn { +@@ -161,33 +140,27 @@ static void block_job_txn_del_job(BlockJob *job) + } + } + +-/* Assumes the block_job_mutex is held */ +-static bool block_job_timer_pending(BlockJob *job) +-{ +- return timer_pending(&job->sleep_timer); +-} +- +-/* Assumes the block_job_mutex is held */ +-static bool block_job_timer_not_pending(BlockJob *job) ++/* Assumes the job_mutex is held */ ++static bool job_timer_not_pending(Job *job) + { +- return !block_job_timer_pending(job); ++ return !timer_pending(&job->sleep_timer); + } + + static void block_job_pause(BlockJob *job) + { +- job->pause_count++; ++ job->job.pause_count++; + } + + static void block_job_resume(BlockJob *job) + { +- assert(job->pause_count > 0); +- job->pause_count--; +- if (job->pause_count) { ++ assert(job->job.pause_count > 0); ++ job->job.pause_count--; ++ if (job->job.pause_count) { + return; + } + + /* kick only if no timer is pending */ +- block_job_enter_cond(job, block_job_timer_not_pending); ++ job_enter_cond(&job->job, job_timer_not_pending); + } + + static void block_job_attached_aio_context(AioContext *new_context, +@@ -208,7 +181,7 @@ void block_job_free(Job *job) + block_job_detach_aio_context, bjob); + blk_unref(bjob->blk); + error_free(bjob->blocker); +- assert(!timer_pending(&bjob->sleep_timer)); ++ assert(!timer_pending(&bjob->job.sleep_timer)); + } + + static void block_job_attached_aio_context(AioContext *new_context, +@@ -226,7 +199,7 @@ static void block_job_attached_aio_context(AioContext *new_context, + + static void block_job_drain(BlockJob *job) + { +- /* If job is !job->busy this kicks it into the next pause point. */ ++ /* If job is !job->job.busy this kicks it into the next pause point. */ + block_job_enter(job); + + blk_drain(job->blk); +@@ -244,7 +217,7 @@ static void block_job_detach_aio_context(void *opaque) + + block_job_pause(job); + +- while (!job->paused && !job->completed) { ++ while (!job->job.paused && !job->completed) { + block_job_drain(job); + } + +@@ -312,29 +285,11 @@ bool block_job_is_internal(BlockJob *job) + return (job->job.id == NULL); + } + +-static bool block_job_started(BlockJob *job) +-{ +- return job->co; +-} +- + const BlockJobDriver *block_job_driver(BlockJob *job) + { + return job->driver; + } + +-/** +- * 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 block_job_co_entry(void *opaque) +-{ +- BlockJob *job = opaque; +- +- assert(job && job->driver && job->driver->start); +- block_job_pause_point(job); +- job->driver->start(job); +-} +- + static void block_job_sleep_timer_cb(void *opaque) + { + BlockJob *job = opaque; +@@ -342,24 +297,12 @@ static void block_job_sleep_timer_cb(void *opaque) + block_job_enter(job); + } + +-void block_job_start(BlockJob *job) +-{ +- assert(job && !block_job_started(job) && job->paused && +- job->driver && job->driver->start); +- job->co = qemu_coroutine_create(block_job_co_entry, job); +- job->pause_count--; +- job->busy = true; +- job->paused = false; +- job_state_transition(&job->job, JOB_STATUS_RUNNING); +- bdrv_coroutine_enter(blk_bs(job->blk), job->co); +-} +- + static void block_job_decommission(BlockJob *job) + { + assert(job); + job->completed = true; +- job->busy = false; +- job->paused = false; ++ job->job.busy = false; ++ job->job.paused = false; + job->job.deferred_to_main_loop = true; + block_job_txn_del_job(job); + job_state_transition(&job->job, JOB_STATUS_NULL); +@@ -374,7 +317,7 @@ static void block_job_do_dismiss(BlockJob *job) + static void block_job_conclude(BlockJob *job) + { + job_state_transition(&job->job, JOB_STATUS_CONCLUDED); +- if (job->auto_dismiss || !block_job_started(job)) { ++ if (job->auto_dismiss || !job_started(&job->job)) { + block_job_do_dismiss(job); + } + } +@@ -439,7 +382,7 @@ static int block_job_finalize_single(BlockJob *job) + } + + /* Emit events only if we actually started */ +- if (block_job_started(job)) { ++ if (job_started(&job->job)) { + if (job_is_cancelled(&job->job)) { + block_job_event_cancelled(job); + } else { +@@ -464,7 +407,7 @@ static void block_job_cancel_async(BlockJob *job, bool force) + if (job->user_paused) { + /* Do not call block_job_enter here, the caller will handle it. */ + job->user_paused = false; +- job->pause_count--; ++ job->job.pause_count--; + } + job->job.cancelled = true; + /* To prevent 'force == false' overriding a previous 'force == true' */ +@@ -615,6 +558,12 @@ static void block_job_completed_txn_success(BlockJob *job) + } + } + ++/* Assumes the job_mutex is held */ ++static bool job_timer_pending(Job *job) ++{ ++ return timer_pending(&job->sleep_timer); ++} ++ + void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) + { + int64_t old_speed = job->speed; +@@ -635,7 +584,7 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) + } + + /* kick only if a timer is pending */ +- block_job_enter_cond(job, block_job_timer_pending); ++ job_enter_cond(&job->job, job_timer_pending); + } + + int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) +@@ -654,7 +603,7 @@ void block_job_complete(BlockJob *job, Error **errp) + if (job_apply_verb(&job->job, JOB_VERB_COMPLETE, errp)) { + return; + } +- if (job->pause_count || job_is_cancelled(&job->job) || ++ if (job->job.pause_count || job_is_cancelled(&job->job) || + !job->driver->complete) + { + error_setg(errp, "The active block job '%s' cannot be completed", +@@ -708,7 +657,7 @@ bool block_job_user_paused(BlockJob *job) + void block_job_user_resume(BlockJob *job, Error **errp) + { + assert(job); +- if (!job->user_paused || job->pause_count <= 0) { ++ if (!job->user_paused || job->job.pause_count <= 0) { + error_setg(errp, "Can't resume a job that was not paused"); + return; + } +@@ -727,7 +676,7 @@ void block_job_cancel(BlockJob *job, bool force) + return; + } + block_job_cancel_async(job, force); +- if (!block_job_started(job)) { ++ if (!job_started(&job->job)) { + block_job_completed(job, -ECANCELED); + } else if (job->job.deferred_to_main_loop) { + block_job_completed_txn_abort(job); +@@ -797,8 +746,8 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) + info->type = g_strdup(job_type_str(&job->job)); + info->device = g_strdup(job->job.id); + info->len = job->len; +- info->busy = atomic_read(&job->busy); +- info->paused = job->pause_count > 0; ++ info->busy = atomic_read(&job->job.busy); ++ info->paused = job->job.pause_count > 0; + info->offset = job->offset; + info->speed = job->speed; + info->io_status = job->iostatus; +@@ -915,12 +864,9 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + job->blk = blk; + job->cb = cb; + job->opaque = opaque; +- job->busy = false; +- job->paused = true; +- job->pause_count = 1; + job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE); + job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS); +- aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, ++ aio_timer_init(qemu_get_aio_context(), &job->job.sleep_timer, + QEMU_CLOCK_REALTIME, SCALE_NS, + block_job_sleep_timer_cb, job); + +@@ -980,128 +926,41 @@ void block_job_completed(BlockJob *job, int ret) + } + } + +-static bool block_job_should_pause(BlockJob *job) +-{ +- return job->pause_count > 0; +-} +- +-/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds. +- * Reentering the job coroutine with block_job_enter() before the timer has +- * expired is allowed and cancels the timer. +- * +- * If @ns is (uint64_t) -1, no timer is scheduled and block_job_enter() must be +- * called explicitly. */ +-static void block_job_do_yield(BlockJob *job, uint64_t ns) +-{ +- block_job_lock(); +- if (ns != -1) { +- timer_mod(&job->sleep_timer, ns); +- } +- job->busy = false; +- block_job_unlock(); +- qemu_coroutine_yield(); +- +- /* Set by block_job_enter before re-entering the coroutine. */ +- assert(job->busy); +-} +- +-void coroutine_fn block_job_pause_point(BlockJob *job) +-{ +- assert(job && block_job_started(job)); +- +- if (!block_job_should_pause(job)) { +- return; +- } +- if (job_is_cancelled(&job->job)) { +- return; +- } +- +- if (job->driver->pause) { +- job->driver->pause(job); +- } +- +- if (block_job_should_pause(job) && !job_is_cancelled(&job->job)) { +- JobStatus status = job->job.status; +- job_state_transition(&job->job, status == JOB_STATUS_READY +- ? JOB_STATUS_STANDBY +- : JOB_STATUS_PAUSED); +- job->paused = true; +- block_job_do_yield(job, -1); +- job->paused = false; +- job_state_transition(&job->job, status); +- } +- +- if (job->driver->resume) { +- job->driver->resume(job); +- } +-} +- +-/* +- * Conditionally enter a block_job pending a call to fn() while +- * under the block_job_lock critical section. +- */ +-static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job)) +-{ +- if (!block_job_started(job)) { +- return; +- } +- if (job->job.deferred_to_main_loop) { +- return; +- } +- +- block_job_lock(); +- if (job->busy) { +- block_job_unlock(); +- return; +- } +- +- if (fn && !fn(job)) { +- block_job_unlock(); +- return; +- } +- +- assert(!job->job.deferred_to_main_loop); +- timer_del(&job->sleep_timer); +- job->busy = true; +- block_job_unlock(); +- aio_co_wake(job->co); +-} +- + void block_job_enter(BlockJob *job) + { +- block_job_enter_cond(job, NULL); ++ job_enter_cond(&job->job, NULL); + } + + void block_job_sleep_ns(BlockJob *job, int64_t ns) + { +- assert(job->busy); ++ assert(job->job.busy); + + /* Check cancellation *before* setting busy = false, too! */ + if (job_is_cancelled(&job->job)) { + return; + } + +- if (!block_job_should_pause(job)) { +- block_job_do_yield(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns); ++ if (!job_should_pause(&job->job)) { ++ job_do_yield(&job->job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns); + } + +- block_job_pause_point(job); ++ job_pause_point(&job->job); + } + + void block_job_yield(BlockJob *job) + { +- assert(job->busy); ++ assert(job->job.busy); + + /* Check cancellation *before* setting busy = false, too! */ + if (job_is_cancelled(&job->job)) { + return; + } + +- if (!block_job_should_pause(job)) { +- block_job_do_yield(job, -1); ++ if (!job_should_pause(&job->job)) { ++ job_do_yield(&job->job, -1); + } + +- block_job_pause_point(job); ++ job_pause_point(&job->job); + } + + void block_job_iostatus_reset(BlockJob *job) +@@ -1109,7 +968,7 @@ void block_job_iostatus_reset(BlockJob *job) + if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { + return; + } +- assert(job->user_paused && job->pause_count > 0); ++ assert(job->user_paused && job->job.pause_count > 0); + job->iostatus = BLOCK_DEVICE_IO_STATUS_OK; + } + +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 2a9e865..b60d919 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -51,43 +51,18 @@ typedef struct BlockJob { + BlockBackend *blk; + + /** +- * The coroutine that executes the job. If not NULL, it is +- * reentered when busy is false and the job is cancelled. +- */ +- Coroutine *co; +- +- /** + * Set to true if the job should abort immediately without waiting + * for data to be in sync. + */ + bool force; + + /** +- * 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 true if the job is paused by user. Can be unpaused with the + * block-job-resume QMP command. + */ + bool user_paused; + + /** +- * Set to false by the job while the coroutine has yielded and may be +- * re-entered by block_job_enter(). There may still be I/O or event loop +- * activity pending. Accessed under block_job_mutex (in blockjob.c). +- */ +- 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 when the job is ready to be completed. + */ + bool ready; +@@ -125,12 +100,6 @@ typedef struct BlockJob { + /** ret code passed to block_job_completed. */ + int ret; + +- /** +- * Timer that is used by @block_job_sleep_ns. Accessed under +- * block_job_mutex (in blockjob.c). +- */ +- QEMUTimer sleep_timer; +- + /** True if this job should automatically finalize itself */ + bool auto_finalize; + +@@ -208,15 +177,6 @@ void block_job_remove_all_bdrv(BlockJob *job); + void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp); + + /** +- * block_job_start: +- * @job: A job that has not yet been started. +- * +- * Begins execution of a block job. +- * Takes ownership of one reference to the job object. +- */ +-void block_job_start(BlockJob *job); +- +-/** + * block_job_cancel: + * @job: The job to be canceled. + * @force: Quit a job without waiting for data to be in sync. +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index 0c2f8de..0a614a8 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -38,9 +38,6 @@ struct BlockJobDriver { + /** Generic JobDriver callbacks and settings */ + JobDriver job_driver; + +- /** Mandatory: Entrypoint for the Coroutine. */ +- CoroutineEntry *start; +- + /** + * Optional callback for job types whose completion must be triggered + * manually. +@@ -85,20 +82,6 @@ struct BlockJobDriver { + */ + void (*clean)(BlockJob *job); + +- /** +- * 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)(BlockJob *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)(BlockJob *job); +- + /* + * 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 +@@ -196,15 +179,6 @@ void block_job_early_fail(BlockJob *job); + void block_job_completed(BlockJob *job, int ret); + + /** +- * block_job_pause_point: +- * @job: The job that is ready to pause. +- * +- * Pause now if block_job_pause() has been called. Block jobs that perform +- * lots of I/O must call this between requests so that the job can be paused. +- */ +-void coroutine_fn block_job_pause_point(BlockJob *job); +- +-/** + * block_job_enter: + * @job: The job to enter. + * +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 933e0ab..9dcff12 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -28,6 +28,7 @@ + + #include "qapi/qapi-types-block-core.h" + #include "qemu/queue.h" ++#include "qemu/coroutine.h" + + typedef struct JobDriver JobDriver; + +@@ -51,6 +52,37 @@ typedef struct Job { + AioContext *aio_context; + + /** ++ * The coroutine that executes the job. If not NULL, it is reentered when ++ * busy is false and the job is cancelled. ++ */ ++ Coroutine *co; ++ ++ /** ++ * Timer that is used by @block_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 block_job_enter(). There may still be I/O or event loop ++ * activity pending. Accessed under block_job_mutex (in blockjob.c). ++ */ ++ 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 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 +@@ -75,6 +107,23 @@ struct JobDriver { + /** Enum describing the operation */ + JobType job_type; + ++ /** Mandatory: Entrypoint for the Coroutine. */ ++ CoroutineEntry *start; ++ ++ /** ++ * 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); ++ + /** Called when the job is freed */ + void (*free)(Job *job); + }; +@@ -103,6 +152,30 @@ void job_ref(Job *job); + */ + void job_unref(Job *job); + ++/** ++ * 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. ++ */ ++void job_enter_cond(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. ++ */ ++void job_start(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. ++ */ ++void coroutine_fn job_pause_point(Job *job); ++ ++ + /** Returns the JobType of a given Job. */ + JobType job_type(const Job *job); + +@@ -153,5 +226,8 @@ void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque); + + /* TODO To be removed from the public interface */ + void job_state_transition(Job *job, JobStatus s1); ++void coroutine_fn job_do_yield(Job *job, uint64_t ns); ++bool job_should_pause(Job *job); ++bool job_started(Job *job); + + #endif +diff --git a/job.c b/job.c +index c5a37fb..78497fd 100644 +--- a/job.c ++++ b/job.c +@@ -60,6 +60,26 @@ bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = { + [JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, + }; + ++/* Right now, this mutex is only needed to synchronize accesses to job->busy ++ * and job->sleep_timer, such as concurrent calls to job_do_yield and ++ * job_enter. */ ++static QemuMutex job_mutex; ++ ++static void job_lock(void) ++{ ++ qemu_mutex_lock(&job_mutex); ++} ++ ++static void job_unlock(void) ++{ ++ qemu_mutex_unlock(&job_mutex); ++} ++ ++static void __attribute__((__constructor__)) job_init(void) ++{ ++ qemu_mutex_init(&job_mutex); ++} ++ + /* TODO Make static once the whole state machine is in job.c */ + void job_state_transition(Job *job, JobStatus s1) + { +@@ -101,6 +121,16 @@ bool job_is_cancelled(Job *job) + return job->cancelled; + } + ++bool job_started(Job *job) ++{ ++ return job->co; ++} ++ ++bool job_should_pause(Job *job) ++{ ++ return job->pause_count > 0; ++} ++ + Job *job_next(Job *job) + { + if (!job) { +@@ -143,6 +173,9 @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, + job->id = g_strdup(job_id); + job->refcnt = 1; + job->aio_context = ctx; ++ job->busy = false; ++ job->paused = true; ++ job->pause_count = 1; + + job_state_transition(job, JOB_STATUS_CREATED); + +@@ -172,6 +205,110 @@ void job_unref(Job *job) + } + } + ++void job_enter_cond(Job *job, bool(*fn)(Job *job)) ++{ ++ if (!job_started(job)) { ++ return; ++ } ++ if (job->deferred_to_main_loop) { ++ return; ++ } ++ ++ job_lock(); ++ if (job->busy) { ++ job_unlock(); ++ return; ++ } ++ ++ if (fn && !fn(job)) { ++ job_unlock(); ++ return; ++ } ++ ++ assert(!job->deferred_to_main_loop); ++ timer_del(&job->sleep_timer); ++ job->busy = true; ++ job_unlock(); ++ aio_co_wake(job->co); ++} ++ ++/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds. ++ * Reentering the job coroutine with block_job_enter() before the timer has ++ * expired is allowed and cancels the timer. ++ * ++ * If @ns is (uint64_t) -1, no timer is scheduled and block_job_enter() must be ++ * called explicitly. */ ++void coroutine_fn job_do_yield(Job *job, uint64_t ns) ++{ ++ job_lock(); ++ if (ns != -1) { ++ timer_mod(&job->sleep_timer, ns); ++ } ++ job->busy = false; ++ job_unlock(); ++ qemu_coroutine_yield(); ++ ++ /* Set by job_enter_cond() before re-entering the coroutine. */ ++ assert(job->busy); ++} ++ ++void coroutine_fn job_pause_point(Job *job) ++{ ++ assert(job && job_started(job)); ++ ++ if (!job_should_pause(job)) { ++ return; ++ } ++ if (job_is_cancelled(job)) { ++ return; ++ } ++ ++ if (job->driver->pause) { ++ job->driver->pause(job); ++ } ++ ++ if (job_should_pause(job) && !job_is_cancelled(job)) { ++ JobStatus status = job->status; ++ job_state_transition(job, status == JOB_STATUS_READY ++ ? JOB_STATUS_STANDBY ++ : JOB_STATUS_PAUSED); ++ job->paused = true; ++ job_do_yield(job, -1); ++ job->paused = false; ++ job_state_transition(job, status); ++ } ++ ++ if (job->driver->resume) { ++ job->driver->resume(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; ++ ++ assert(job && job->driver && job->driver->start); ++ job_pause_point(job); ++ job->driver->start(job); ++} ++ ++ ++void job_start(Job *job) ++{ ++ assert(job && !job_started(job) && job->paused && ++ job->driver && job->driver->start); ++ job->co = qemu_coroutine_create(job_co_entry, job); ++ job->pause_count--; ++ job->busy = true; ++ job->paused = false; ++ job_state_transition(job, JOB_STATUS_RUNNING); ++ aio_co_enter(job->aio_context, job->co); ++} ++ + typedef struct { + Job *job; + JobDeferToMainLoopFn *fn; +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index 4f8cba8..c9f2f9b 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -524,8 +524,8 @@ BlockJobDriver test_job_driver = { + .job_driver = { + .instance_size = sizeof(TestBlockJob), + .free = block_job_free, ++ .start = test_job_start, + }, +- .start = test_job_start, + .complete = test_job_complete, + }; + +@@ -549,47 +549,47 @@ static void test_blockjob_common(enum drain_type drain_type) + job = block_job_create("job0", &test_job_driver, NULL, src, 0, BLK_PERM_ALL, + 0, 0, NULL, NULL, &error_abort); + block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort); +- block_job_start(job); ++ job_start(&job->job); + +- g_assert_cmpint(job->pause_count, ==, 0); +- g_assert_false(job->paused); +- g_assert_false(job->busy); /* We're in block_job_sleep_ns() */ ++ g_assert_cmpint(job->job.pause_count, ==, 0); ++ g_assert_false(job->job.paused); ++ g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */ + + do_drain_begin(drain_type, src); + + if (drain_type == BDRV_DRAIN_ALL) { + /* bdrv_drain_all() drains both src and target */ +- g_assert_cmpint(job->pause_count, ==, 2); ++ g_assert_cmpint(job->job.pause_count, ==, 2); + } else { +- g_assert_cmpint(job->pause_count, ==, 1); ++ g_assert_cmpint(job->job.pause_count, ==, 1); + } + /* XXX We don't wait until the job is actually paused. Is this okay? */ +- /* g_assert_true(job->paused); */ +- g_assert_false(job->busy); /* The job is paused */ ++ /* g_assert_true(job->job.paused); */ ++ g_assert_false(job->job.busy); /* The job is paused */ + + do_drain_end(drain_type, src); + +- g_assert_cmpint(job->pause_count, ==, 0); +- g_assert_false(job->paused); +- g_assert_false(job->busy); /* We're in block_job_sleep_ns() */ ++ g_assert_cmpint(job->job.pause_count, ==, 0); ++ g_assert_false(job->job.paused); ++ g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */ + + do_drain_begin(drain_type, target); + + if (drain_type == BDRV_DRAIN_ALL) { + /* bdrv_drain_all() drains both src and target */ +- g_assert_cmpint(job->pause_count, ==, 2); ++ g_assert_cmpint(job->job.pause_count, ==, 2); + } else { +- g_assert_cmpint(job->pause_count, ==, 1); ++ g_assert_cmpint(job->job.pause_count, ==, 1); + } + /* XXX We don't wait until the job is actually paused. Is this okay? */ +- /* g_assert_true(job->paused); */ +- g_assert_false(job->busy); /* The job is paused */ ++ /* g_assert_true(job->job.paused); */ ++ g_assert_false(job->job.busy); /* The job is paused */ + + do_drain_end(drain_type, target); + +- g_assert_cmpint(job->pause_count, ==, 0); +- g_assert_false(job->paused); +- g_assert_false(job->busy); /* We're in block_job_sleep_ns() */ ++ g_assert_cmpint(job->job.pause_count, ==, 0); ++ g_assert_false(job->job.paused); ++ g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */ + + ret = block_job_complete_sync(job, &error_abort); + g_assert_cmpint(ret, ==, 0); +diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c +index c03f966..323e154 100644 +--- a/tests/test-blockjob-txn.c ++++ b/tests/test-blockjob-txn.c +@@ -78,8 +78,8 @@ static const BlockJobDriver test_block_job_driver = { + .job_driver = { + .instance_size = sizeof(TestBlockJob), + .free = block_job_free, ++ .start = test_block_job_run, + }, +- .start = test_block_job_run, + }; + + /* Create a block job that completes with a given return code after a given +@@ -125,7 +125,7 @@ static void test_single_job(int expected) + + txn = block_job_txn_new(); + job = test_block_job_start(1, true, expected, &result, txn); +- block_job_start(job); ++ job_start(&job->job); + + if (expected == -ECANCELED) { + block_job_cancel(job, false); +@@ -165,8 +165,8 @@ static void test_pair_jobs(int expected1, int expected2) + txn = block_job_txn_new(); + job1 = test_block_job_start(1, true, expected1, &result1, txn); + job2 = test_block_job_start(2, true, expected2, &result2, txn); +- block_job_start(job1); +- block_job_start(job2); ++ job_start(&job1->job); ++ job_start(&job2->job); + + /* Release our reference now to trigger as many nice + * use-after-free bugs as possible. +@@ -227,8 +227,8 @@ static void test_pair_jobs_fail_cancel_race(void) + txn = block_job_txn_new(); + job1 = test_block_job_start(1, true, -ECANCELED, &result1, txn); + job2 = test_block_job_start(2, false, 0, &result2, txn); +- block_job_start(job1); +- block_job_start(job2); ++ job_start(&job1->job); ++ job_start(&job2->job); + + block_job_cancel(job1, false); + +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index 5f43bd7..1d18325 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -199,8 +199,8 @@ static const BlockJobDriver test_cancel_driver = { + .job_driver = { + .instance_size = sizeof(CancelJob), + .free = block_job_free, ++ .start = cancel_job_start, + }, +- .start = cancel_job_start, + .complete = cancel_job_complete, + }; + +@@ -254,7 +254,7 @@ static void test_cancel_running(void) + + s = create_common(&job); + +- block_job_start(job); ++ job_start(&job->job); + assert(job->job.status == JOB_STATUS_RUNNING); + + cancel_common(s); +@@ -267,7 +267,7 @@ static void test_cancel_paused(void) + + s = create_common(&job); + +- block_job_start(job); ++ job_start(&job->job); + assert(job->job.status == JOB_STATUS_RUNNING); + + block_job_user_pause(job, &error_abort); +@@ -284,7 +284,7 @@ static void test_cancel_ready(void) + + s = create_common(&job); + +- block_job_start(job); ++ job_start(&job->job); + assert(job->job.status == JOB_STATUS_RUNNING); + + s->should_converge = true; +@@ -301,7 +301,7 @@ static void test_cancel_standby(void) + + s = create_common(&job); + +- block_job_start(job); ++ job_start(&job->job); + assert(job->job.status == JOB_STATUS_RUNNING); + + s->should_converge = true; +@@ -322,7 +322,7 @@ static void test_cancel_pending(void) + + s = create_common(&job); + +- block_job_start(job); ++ job_start(&job->job); + assert(job->job.status == JOB_STATUS_RUNNING); + + s->should_converge = true; +@@ -346,7 +346,7 @@ static void test_cancel_concluded(void) + + s = create_common(&job); + +- block_job_start(job); ++ job_start(&job->job); + assert(job->job.status == JOB_STATUS_RUNNING); + + s->should_converge = true; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Move-defer_to_main_loop-to-Job.patch b/SOURCES/kvm-job-Move-defer_to_main_loop-to-Job.patch new file mode 100644 index 0000000..0bbaf27 --- /dev/null +++ b/SOURCES/kvm-job-Move-defer_to_main_loop-to-Job.patch @@ -0,0 +1,535 @@ +From 1adf648f5842dd52698c338b0a4916606325ccb6 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:08 +0200 +Subject: [PATCH 39/89] job: Move defer_to_main_loop to Job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-26-kwolf@redhat.com> +Patchwork-id: 81091 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 25/73] job: Move defer_to_main_loop to Job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Move the defer_to_main_loop functionality from BlockJob to Job. + +The code can be simplified because we can use job->aio_context in +job_defer_to_main_loop_bh() now, instead of having to access the +BlockDriverState. + +Probably taking the data->aio_context lock in addition was already +unnecessary in the old code because we didn't actually make use of +anything protected by the old AioContext except getting the new +AioContext, in case it changed between scheduling the BH and running it. +But it's certainly unnecessary now that the BDS isn't accessed at all +any more. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: John Snow +(cherry picked from commit 1908a5590c7d214b1b6886bc19b81076fb65cec9) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 7 +++--- + block/commit.c | 11 +++++---- + block/mirror.c | 15 ++++++------ + block/stream.c | 14 +++++------ + blockjob.c | 57 ++++---------------------------------------- + include/block/blockjob.h | 5 ---- + include/block/blockjob_int.h | 19 --------------- + include/qemu/job.h | 20 ++++++++++++++++ + job.c | 32 +++++++++++++++++++++++++ + tests/test-bdrv-drain.c | 7 +++--- + tests/test-blockjob-txn.c | 13 +++++----- + tests/test-blockjob.c | 7 +++--- + 12 files changed, 97 insertions(+), 110 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index ef0aa0e..22dd368 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -317,11 +317,12 @@ typedef struct { + int ret; + } BackupCompleteData; + +-static void backup_complete(BlockJob *job, void *opaque) ++static void backup_complete(Job *job, void *opaque) + { ++ BlockJob *bjob = container_of(job, BlockJob, job); + BackupCompleteData *data = opaque; + +- block_job_completed(job, data->ret); ++ block_job_completed(bjob, data->ret); + g_free(data); + } + +@@ -519,7 +520,7 @@ static void coroutine_fn backup_run(void *opaque) + + data = g_malloc(sizeof(*data)); + data->ret = ret; +- block_job_defer_to_main_loop(&job->common, backup_complete, data); ++ job_defer_to_main_loop(&job->common.job, backup_complete, data); + } + + static const BlockJobDriver backup_job_driver = { +diff --git a/block/commit.c b/block/commit.c +index 85baea8..d326766 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -72,9 +72,10 @@ typedef struct { + int ret; + } CommitCompleteData; + +-static void commit_complete(BlockJob *job, void *opaque) ++static void commit_complete(Job *job, void *opaque) + { +- CommitBlockJob *s = container_of(job, CommitBlockJob, common); ++ CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); ++ BlockJob *bjob = &s->common; + CommitCompleteData *data = opaque; + BlockDriverState *top = blk_bs(s->top); + BlockDriverState *base = blk_bs(s->base); +@@ -90,7 +91,7 @@ static void commit_complete(BlockJob *job, void *opaque) + * the normal backing chain can be restored. */ + blk_unref(s->base); + +- if (!job_is_cancelled(&s->common.job) && ret == 0) { ++ if (!job_is_cancelled(job) && ret == 0) { + /* success */ + ret = bdrv_drop_intermediate(s->commit_top_bs, base, + s->backing_file_str); +@@ -114,7 +115,7 @@ static void commit_complete(BlockJob *job, void *opaque) + * block_job_finish_sync()), block_job_completed() won't free it and + * therefore the blockers on the intermediate nodes remain. This would + * cause bdrv_set_backing_hd() to fail. */ +- block_job_remove_all_bdrv(job); ++ block_job_remove_all_bdrv(bjob); + + block_job_completed(&s->common, ret); + g_free(data); +@@ -211,7 +212,7 @@ out: + + data = g_malloc(sizeof(*data)); + data->ret = ret; +- block_job_defer_to_main_loop(&s->common, commit_complete, data); ++ job_defer_to_main_loop(&s->common.job, commit_complete, data); + } + + static const BlockJobDriver commit_job_driver = { +diff --git a/block/mirror.c b/block/mirror.c +index 424072e..90d4ac9 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -484,9 +484,10 @@ typedef struct { + int ret; + } MirrorExitData; + +-static void mirror_exit(BlockJob *job, void *opaque) ++static void mirror_exit(Job *job, void *opaque) + { +- MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); ++ BlockJob *bjob = &s->common; + MirrorExitData *data = opaque; + AioContext *replace_aio_context = NULL; + BlockDriverState *src = s->source; +@@ -568,7 +569,7 @@ static void mirror_exit(BlockJob *job, void *opaque) + * the blockers on the intermediate nodes so that the resulting state is + * valid. Also give up permissions on mirror_top_bs->backing, which might + * block the removal. */ +- block_job_remove_all_bdrv(job); ++ block_job_remove_all_bdrv(bjob); + bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL, + &error_abort); + bdrv_replace_node(mirror_top_bs, backing_bs(mirror_top_bs), &error_abort); +@@ -576,9 +577,9 @@ static void mirror_exit(BlockJob *job, void *opaque) + /* We just changed the BDS the job BB refers to (with either or both of the + * bdrv_replace_node() calls), so switch the BB back so the cleanup does + * the right thing. We don't need any permissions any more now. */ +- blk_remove_bs(job->blk); +- blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort); +- blk_insert_bs(job->blk, mirror_top_bs, &error_abort); ++ blk_remove_bs(bjob->blk); ++ blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort); ++ blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort); + + block_job_completed(&s->common, data->ret); + +@@ -901,7 +902,7 @@ immediate_exit: + if (need_drain) { + bdrv_drained_begin(bs); + } +- block_job_defer_to_main_loop(&s->common, mirror_exit, data); ++ job_defer_to_main_loop(&s->common.job, mirror_exit, data); + } + + static void mirror_complete(BlockJob *job, Error **errp) +diff --git a/block/stream.c b/block/stream.c +index 22c71ae..0bba816 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -58,16 +58,16 @@ typedef struct { + int ret; + } StreamCompleteData; + +-static void stream_complete(BlockJob *job, void *opaque) ++static void stream_complete(Job *job, void *opaque) + { +- StreamBlockJob *s = container_of(job, StreamBlockJob, common); ++ StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); ++ BlockJob *bjob = &s->common; + StreamCompleteData *data = opaque; +- BlockDriverState *bs = blk_bs(job->blk); ++ BlockDriverState *bs = blk_bs(bjob->blk); + BlockDriverState *base = s->base; + Error *local_err = NULL; + +- if (!job_is_cancelled(&s->common.job) && bs->backing && +- data->ret == 0) { ++ if (!job_is_cancelled(job) && bs->backing && data->ret == 0) { + const char *base_id = NULL, *base_fmt = NULL; + if (base) { + base_id = s->backing_file_str; +@@ -88,7 +88,7 @@ out: + /* Reopen the image back in read-only mode if necessary */ + if (s->bs_flags != bdrv_get_flags(bs)) { + /* Give up write permissions before making it read-only */ +- blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort); ++ blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort); + bdrv_reopen(bs, s->bs_flags, NULL); + } + +@@ -205,7 +205,7 @@ out: + /* Modify backing chain and close BDSes in main loop */ + data = g_malloc(sizeof(*data)); + data->ret = ret; +- block_job_defer_to_main_loop(&s->common, stream_complete, data); ++ job_defer_to_main_loop(&s->common.job, stream_complete, data); + } + + static const BlockJobDriver stream_job_driver = { +diff --git a/blockjob.c b/blockjob.c +index 0a0b1c4..3ede511 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -360,7 +360,7 @@ static void block_job_decommission(BlockJob *job) + job->completed = true; + job->busy = false; + job->paused = false; +- job->deferred_to_main_loop = true; ++ job->job.deferred_to_main_loop = true; + block_job_txn_del_job(job); + job_state_transition(&job->job, JOB_STATUS_NULL); + job_unref(&job->job); +@@ -515,7 +515,7 @@ static int block_job_finish_sync(BlockJob *job, + /* block_job_drain calls block_job_enter, and it should be enough to + * induce progress until the job completes or moves to the main thread. + */ +- while (!job->deferred_to_main_loop && !job->completed) { ++ while (!job->job.deferred_to_main_loop && !job->completed) { + block_job_drain(job); + } + while (!job->completed) { +@@ -729,7 +729,7 @@ void block_job_cancel(BlockJob *job, bool force) + block_job_cancel_async(job, force); + if (!block_job_started(job)) { + block_job_completed(job, -ECANCELED); +- } else if (job->deferred_to_main_loop) { ++ } else if (job->job.deferred_to_main_loop) { + block_job_completed_txn_abort(job); + } else { + block_job_enter(job); +@@ -1045,7 +1045,7 @@ static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job)) + if (!block_job_started(job)) { + return; + } +- if (job->deferred_to_main_loop) { ++ if (job->job.deferred_to_main_loop) { + return; + } + +@@ -1060,7 +1060,7 @@ static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job)) + return; + } + +- assert(!job->deferred_to_main_loop); ++ assert(!job->job.deferred_to_main_loop); + timer_del(&job->sleep_timer); + job->busy = true; + block_job_unlock(); +@@ -1166,50 +1166,3 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, + } + return action; + } +- +-typedef struct { +- BlockJob *job; +- AioContext *aio_context; +- BlockJobDeferToMainLoopFn *fn; +- void *opaque; +-} BlockJobDeferToMainLoopData; +- +-static void block_job_defer_to_main_loop_bh(void *opaque) +-{ +- BlockJobDeferToMainLoopData *data = opaque; +- AioContext *aio_context; +- +- /* Prevent race with block_job_defer_to_main_loop() */ +- aio_context_acquire(data->aio_context); +- +- /* Fetch BDS AioContext again, in case it has changed */ +- aio_context = blk_get_aio_context(data->job->blk); +- if (aio_context != data->aio_context) { +- aio_context_acquire(aio_context); +- } +- +- data->fn(data->job, data->opaque); +- +- if (aio_context != data->aio_context) { +- aio_context_release(aio_context); +- } +- +- aio_context_release(data->aio_context); +- +- g_free(data); +-} +- +-void block_job_defer_to_main_loop(BlockJob *job, +- BlockJobDeferToMainLoopFn *fn, +- void *opaque) +-{ +- BlockJobDeferToMainLoopData *data = g_malloc(sizeof(*data)); +- data->job = job; +- data->aio_context = blk_get_aio_context(job->blk); +- data->fn = fn; +- data->opaque = opaque; +- job->deferred_to_main_loop = true; +- +- aio_bh_schedule_oneshot(qemu_get_aio_context(), +- block_job_defer_to_main_loop_bh, data); +-} +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 1e708f4..2a9e865 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -92,11 +92,6 @@ typedef struct BlockJob { + */ + bool ready; + +- /** +- * Set to true when the job has deferred work to the main loop. +- */ +- bool deferred_to_main_loop; +- + /** Status that is published by the query-block-jobs QMP API */ + BlockDeviceIoStatus iostatus; + +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index d64f30e..0c2f8de 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -233,23 +233,4 @@ void block_job_event_ready(BlockJob *job); + BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, + int is_read, int error); + +-typedef void BlockJobDeferToMainLoopFn(BlockJob *job, void *opaque); +- +-/** +- * block_job_defer_to_main_loop: +- * @job: The job +- * @fn: The function to run in the main loop +- * @opaque: The opaque value that is passed to @fn +- * +- * This function must be called by the main job coroutine just before it +- * returns. @fn is executed in the main loop with the BlockDriverState +- * AioContext acquired. Block jobs must call bdrv_unref(), bdrv_close(), and +- * anything that uses bdrv_drain_all() in the main loop. +- * +- * The @job AioContext is held while @fn executes. +- */ +-void block_job_defer_to_main_loop(BlockJob *job, +- BlockJobDeferToMainLoopFn *fn, +- void *opaque); +- + #endif +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 01e083f..933e0ab 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -58,6 +58,9 @@ typedef struct Job { + */ + bool cancelled; + ++ /** Set to true when the job has deferred work to the main loop. */ ++ bool deferred_to_main_loop; ++ + /** Element of the list of jobs */ + QLIST_ENTRY(Job) job_list; + } Job; +@@ -131,6 +134,23 @@ Job *job_get(const char *id); + */ + int job_apply_verb(Job *job, JobVerb verb, Error **errp); + ++typedef void JobDeferToMainLoopFn(Job *job, void *opaque); ++ ++/** ++ * @job: The job ++ * @fn: The function to run in the main loop ++ * @opaque: The opaque value that is passed to @fn ++ * ++ * This function must be called by the main job coroutine just before it ++ * returns. @fn is executed in the main loop with the job AioContext acquired. ++ * ++ * Block jobs must call bdrv_unref(), bdrv_close(), and anything that uses ++ * bdrv_drain_all() in the main loop. ++ * ++ * The @job AioContext is held while @fn executes. ++ */ ++void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque); ++ + /* TODO To be removed from the public interface */ + void job_state_transition(Job *job, JobStatus s1); + +diff --git a/job.c b/job.c +index 01074d0..c5a37fb 100644 +--- a/job.c ++++ b/job.c +@@ -28,6 +28,7 @@ + #include "qapi/error.h" + #include "qemu/job.h" + #include "qemu/id.h" ++#include "qemu/main-loop.h" + #include "trace-root.h" + + static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs); +@@ -170,3 +171,34 @@ void job_unref(Job *job) + g_free(job); + } + } ++ ++typedef struct { ++ Job *job; ++ JobDeferToMainLoopFn *fn; ++ void *opaque; ++} JobDeferToMainLoopData; ++ ++static void job_defer_to_main_loop_bh(void *opaque) ++{ ++ JobDeferToMainLoopData *data = opaque; ++ Job *job = data->job; ++ AioContext *aio_context = job->aio_context; ++ ++ aio_context_acquire(aio_context); ++ data->fn(data->job, data->opaque); ++ aio_context_release(aio_context); ++ ++ g_free(data); ++} ++ ++void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque) ++{ ++ JobDeferToMainLoopData *data = g_malloc(sizeof(*data)); ++ data->job = job; ++ data->fn = fn; ++ data->opaque = opaque; ++ job->deferred_to_main_loop = true; ++ ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), ++ job_defer_to_main_loop_bh, data); ++} +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index f9e37d4..4f8cba8 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -496,9 +496,10 @@ typedef struct TestBlockJob { + bool should_complete; + } TestBlockJob; + +-static void test_job_completed(BlockJob *job, void *opaque) ++static void test_job_completed(Job *job, void *opaque) + { +- block_job_completed(job, 0); ++ BlockJob *bjob = container_of(job, BlockJob, job); ++ block_job_completed(bjob, 0); + } + + static void coroutine_fn test_job_start(void *opaque) +@@ -510,7 +511,7 @@ static void coroutine_fn test_job_start(void *opaque) + block_job_sleep_ns(&s->common, 100000); + } + +- block_job_defer_to_main_loop(&s->common, test_job_completed, NULL); ++ job_defer_to_main_loop(&s->common.job, test_job_completed, NULL); + } + + static void test_job_complete(BlockJob *job, Error **errp) +diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c +index 26b4bbb..c03f966 100644 +--- a/tests/test-blockjob-txn.c ++++ b/tests/test-blockjob-txn.c +@@ -24,16 +24,17 @@ typedef struct { + int *result; + } TestBlockJob; + +-static void test_block_job_complete(BlockJob *job, void *opaque) ++static void test_block_job_complete(Job *job, void *opaque) + { +- BlockDriverState *bs = blk_bs(job->blk); ++ BlockJob *bjob = container_of(job, BlockJob, job); ++ BlockDriverState *bs = blk_bs(bjob->blk); + int rc = (intptr_t)opaque; + +- if (job_is_cancelled(&job->job)) { ++ if (job_is_cancelled(job)) { + rc = -ECANCELED; + } + +- block_job_completed(job, rc); ++ block_job_completed(bjob, rc); + bdrv_unref(bs); + } + +@@ -54,8 +55,8 @@ static void coroutine_fn test_block_job_run(void *opaque) + } + } + +- block_job_defer_to_main_loop(job, test_block_job_complete, +- (void *)(intptr_t)s->rc); ++ job_defer_to_main_loop(&job->job, test_block_job_complete, ++ (void *)(intptr_t)s->rc); + } + + typedef struct { +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index fa31481..5f43bd7 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -161,11 +161,12 @@ typedef struct CancelJob { + bool completed; + } CancelJob; + +-static void cancel_job_completed(BlockJob *job, void *opaque) ++static void cancel_job_completed(Job *job, void *opaque) + { ++ BlockJob *bjob = container_of(job, BlockJob, job); + CancelJob *s = opaque; + s->completed = true; +- block_job_completed(job, 0); ++ block_job_completed(bjob, 0); + } + + static void cancel_job_complete(BlockJob *job, Error **errp) +@@ -191,7 +192,7 @@ static void coroutine_fn cancel_job_start(void *opaque) + } + + defer: +- block_job_defer_to_main_loop(&s->common, cancel_job_completed, s); ++ job_defer_to_main_loop(&s->common.job, cancel_job_completed, s); + } + + static const BlockJobDriver test_cancel_driver = { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Move-job_finish_sync-to-Job.patch b/SOURCES/kvm-job-Move-job_finish_sync-to-Job.patch new file mode 100644 index 0000000..5646557 --- /dev/null +++ b/SOURCES/kvm-job-Move-job_finish_sync-to-Job.patch @@ -0,0 +1,199 @@ +From 29dceceeaac867e78dcf4b8ef121f361b14e06b4 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:20 +0200 +Subject: [PATCH 51/89] job: Move job_finish_sync() to Job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-38-kwolf@redhat.com> +Patchwork-id: 81076 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 37/73] job: Move job_finish_sync() to Job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +block_job_finish_sync() doesn't contain anything block job specific any +more, so it can be moved to Job. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit 6a74c075aca731e7e945201a4ae2336b8e328433) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/commit.c | 6 +++--- + blockjob.c | 55 +++++++++--------------------------------------------- + include/qemu/job.h | 9 +++++++++ + job.c | 28 +++++++++++++++++++++++++++ + 4 files changed, 49 insertions(+), 49 deletions(-) + +diff --git a/block/commit.c b/block/commit.c +index 02a8af9..40d97a3 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -112,9 +112,9 @@ static void commit_complete(Job *job, void *opaque) + blk_unref(s->top); + + /* If there is more than one reference to the job (e.g. if called from +- * block_job_finish_sync()), block_job_completed() won't free it and +- * therefore the blockers on the intermediate nodes remain. This would +- * cause bdrv_set_backing_hd() to fail. */ ++ * job_finish_sync()), block_job_completed() won't free it and therefore ++ * the blockers on the intermediate nodes remain. This would cause ++ * bdrv_set_backing_hd() to fail. */ + block_job_remove_all_bdrv(bjob); + + block_job_completed(&s->common, ret); +diff --git a/blockjob.c b/blockjob.c +index 0ca7672..1ed3e9c 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -307,40 +307,6 @@ static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock) + return rc; + } + +-static int block_job_finish_sync(BlockJob *job, +- void (*finish)(BlockJob *, Error **errp), +- Error **errp) +-{ +- Error *local_err = NULL; +- int ret; +- +- assert(blk_bs(job->blk)->job == job); +- +- job_ref(&job->job); +- +- if (finish) { +- finish(job, &local_err); +- } +- if (local_err) { +- error_propagate(errp, local_err); +- job_unref(&job->job); +- return -EBUSY; +- } +- /* job_drain calls job_enter, and it should be enough to induce progress +- * until the job completes or moves to the main thread. +- */ +- while (!job->job.deferred_to_main_loop && !job_is_completed(&job->job)) { +- job_drain(&job->job); +- } +- while (!job_is_completed(&job->job)) { +- aio_poll(qemu_get_aio_context(), true); +- } +- ret = (job_is_cancelled(&job->job) && job->job.ret == 0) +- ? -ECANCELED : job->job.ret; +- job_unref(&job->job); +- return ret; +-} +- + static void block_job_completed_txn_abort(BlockJob *job) + { + AioContext *ctx; +@@ -375,7 +341,7 @@ static void block_job_completed_txn_abort(BlockJob *job) + ctx = blk_get_aio_context(other_job->blk); + if (!job_is_completed(&other_job->job)) { + assert(job_is_cancelled(&other_job->job)); +- block_job_finish_sync(other_job, NULL, NULL); ++ job_finish_sync(&other_job->job, NULL, NULL); + } + job_finalize_single(&other_job->job); + aio_context_release(ctx); +@@ -528,16 +494,18 @@ void block_job_user_cancel(BlockJob *job, bool force, Error **errp) + } + + /* A wrapper around block_job_cancel() taking an Error ** parameter so it may be +- * used with block_job_finish_sync() without the need for (rather nasty) +- * function pointer casts there. */ +-static void block_job_cancel_err(BlockJob *job, Error **errp) ++ * used with job_finish_sync() without the need for (rather nasty) function ++ * pointer casts there. */ ++static void block_job_cancel_err(Job *job, Error **errp) + { +- block_job_cancel(job, false); ++ BlockJob *bjob = container_of(job, BlockJob, job); ++ assert(is_block_job(job)); ++ block_job_cancel(bjob, false); + } + + int block_job_cancel_sync(BlockJob *job) + { +- return block_job_finish_sync(job, &block_job_cancel_err, NULL); ++ return job_finish_sync(&job->job, &block_job_cancel_err, NULL); + } + + void block_job_cancel_sync_all(void) +@@ -553,14 +521,9 @@ void block_job_cancel_sync_all(void) + } + } + +-static void block_job_complete(BlockJob *job, Error **errp) +-{ +- job_complete(&job->job, errp); +-} +- + int block_job_complete_sync(BlockJob *job, Error **errp) + { +- return block_job_finish_sync(job, &block_job_complete, errp); ++ return job_finish_sync(&job->job, job_complete, errp); + } + + void block_job_progress_update(BlockJob *job, uint64_t done) +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 8f7f71a..17e2cec 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -389,6 +389,15 @@ typedef void JobDeferToMainLoopFn(Job *job, void *opaque); + */ + void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque); + ++/** ++ * 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. ++ */ ++int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp); ++ + /* TODO To be removed from the public interface */ + void job_state_transition(Job *job, JobStatus s1); + void coroutine_fn job_do_yield(Job *job, uint64_t ns); +diff --git a/job.c b/job.c +index 8ceac0b..aa74b4c 100644 +--- a/job.c ++++ b/job.c +@@ -603,3 +603,31 @@ void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque) + aio_bh_schedule_oneshot(qemu_get_aio_context(), + job_defer_to_main_loop_bh, data); + } ++ ++int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) ++{ ++ Error *local_err = NULL; ++ int ret; ++ ++ job_ref(job); ++ ++ if (finish) { ++ finish(job, &local_err); ++ } ++ if (local_err) { ++ error_propagate(errp, local_err); ++ job_unref(job); ++ return -EBUSY; ++ } ++ /* job_drain calls job_enter, and it should be enough to induce progress ++ * until the job completes or moves to the main thread. */ ++ while (!job->deferred_to_main_loop && !job_is_completed(job)) { ++ job_drain(job); ++ } ++ while (!job_is_completed(job)) { ++ aio_poll(qemu_get_aio_context(), true); ++ } ++ ret = (job_is_cancelled(job) && job->ret == 0) ? -ECANCELED : job->ret; ++ job_unref(job); ++ return ret; ++} +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Move-pause-resume-functions-to-Job.patch b/SOURCES/kvm-job-Move-pause-resume-functions-to-Job.patch new file mode 100644 index 0000000..ff159ba --- /dev/null +++ b/SOURCES/kvm-job-Move-pause-resume-functions-to-Job.patch @@ -0,0 +1,550 @@ +From 6e9a5d0ef7ada1b153e0808c0fe73eeab940cfbd Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:11 +0200 +Subject: [PATCH 42/89] job: Move pause/resume functions to Job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-29-kwolf@redhat.com> +Patchwork-id: 81062 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 28/73] job: Move pause/resume functions to Job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +While we already moved the state related to job pausing to Job, the +functions to do were still BlockJob only. This commit moves them over to +Job. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: John Snow +(cherry picked from commit b15de82867975e0b4acf644b5ee36d84904b6612) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 1 + + block/commit.c | 1 + + block/mirror.c | 2 ++ + block/stream.c | 1 + + blockdev.c | 6 ++-- + blockjob.c | 81 +++++++++----------------------------------- + include/block/blockjob.h | 32 ----------------- + include/block/blockjob_int.h | 7 ++++ + include/qemu/job.h | 37 ++++++++++++++++++++ + job.c | 59 ++++++++++++++++++++++++++++++++ + tests/test-bdrv-drain.c | 1 + + tests/test-blockjob-txn.c | 1 + + tests/test-blockjob.c | 6 ++-- + 13 files changed, 133 insertions(+), 102 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index f3a4f7c..4d011d5 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -528,6 +528,7 @@ static const BlockJobDriver backup_job_driver = { + .instance_size = sizeof(BackupBlockJob), + .job_type = JOB_TYPE_BACKUP, + .free = block_job_free, ++ .user_resume = block_job_user_resume, + .start = backup_run, + }, + .commit = backup_commit, +diff --git a/block/commit.c b/block/commit.c +index 1c6cb6c..c4a98e5 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -220,6 +220,7 @@ static const BlockJobDriver commit_job_driver = { + .instance_size = sizeof(CommitBlockJob), + .job_type = JOB_TYPE_COMMIT, + .free = block_job_free, ++ .user_resume = block_job_user_resume, + .start = commit_run, + }, + }; +diff --git a/block/mirror.c b/block/mirror.c +index 5d8f75c..9a7226f 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -991,6 +991,7 @@ static const BlockJobDriver mirror_job_driver = { + .instance_size = sizeof(MirrorBlockJob), + .job_type = JOB_TYPE_MIRROR, + .free = block_job_free, ++ .user_resume = block_job_user_resume, + .start = mirror_run, + .pause = mirror_pause, + }, +@@ -1004,6 +1005,7 @@ static const BlockJobDriver commit_active_job_driver = { + .instance_size = sizeof(MirrorBlockJob), + .job_type = JOB_TYPE_COMMIT, + .free = block_job_free, ++ .user_resume = block_job_user_resume, + .start = mirror_run, + .pause = mirror_pause, + }, +diff --git a/block/stream.c b/block/stream.c +index 1faab02..e81b488 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -214,6 +214,7 @@ static const BlockJobDriver stream_job_driver = { + .job_type = JOB_TYPE_STREAM, + .free = block_job_free, + .start = stream_run, ++ .user_resume = block_job_user_resume, + }, + }; + +diff --git a/blockdev.c b/blockdev.c +index efb83c4..0fa1990 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3891,7 +3891,7 @@ void qmp_block_job_cancel(const char *device, + force = false; + } + +- if (block_job_user_paused(job) && !force) { ++ if (job_user_paused(&job->job) && !force) { + error_setg(errp, "The block job for device '%s' is currently paused", + device); + goto out; +@@ -3913,7 +3913,7 @@ void qmp_block_job_pause(const char *device, Error **errp) + } + + trace_qmp_block_job_pause(job); +- block_job_user_pause(job, errp); ++ job_user_pause(&job->job, errp); + aio_context_release(aio_context); + } + +@@ -3927,7 +3927,7 @@ void qmp_block_job_resume(const char *device, Error **errp) + } + + trace_qmp_block_job_resume(job); +- block_job_user_resume(job, errp); ++ job_user_resume(&job->job, errp); + aio_context_release(aio_context); + } + +diff --git a/blockjob.c b/blockjob.c +index 4dc360c..6334a54 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -140,29 +140,6 @@ static void block_job_txn_del_job(BlockJob *job) + } + } + +-/* Assumes the job_mutex is held */ +-static bool job_timer_not_pending(Job *job) +-{ +- return !timer_pending(&job->sleep_timer); +-} +- +-static void block_job_pause(BlockJob *job) +-{ +- job->job.pause_count++; +-} +- +-static void block_job_resume(BlockJob *job) +-{ +- assert(job->job.pause_count > 0); +- job->job.pause_count--; +- if (job->job.pause_count) { +- return; +- } +- +- /* kick only if no timer is pending */ +- job_enter_cond(&job->job, job_timer_not_pending); +-} +- + static void block_job_attached_aio_context(AioContext *new_context, + void *opaque); + static void block_job_detach_aio_context(void *opaque); +@@ -193,7 +170,7 @@ static void block_job_attached_aio_context(AioContext *new_context, + job->driver->attached_aio_context(job, new_context); + } + +- block_job_resume(job); ++ job_resume(&job->job); + } + + static void block_job_drain(BlockJob *job) +@@ -214,7 +191,7 @@ static void block_job_detach_aio_context(void *opaque) + /* In case the job terminates during aio_poll()... */ + job_ref(&job->job); + +- block_job_pause(job); ++ job_pause(&job->job); + + while (!job->job.paused && !job->completed) { + block_job_drain(job); +@@ -233,13 +210,13 @@ static char *child_job_get_parent_desc(BdrvChild *c) + static void child_job_drained_begin(BdrvChild *c) + { + BlockJob *job = c->opaque; +- block_job_pause(job); ++ job_pause(&job->job); + } + + static void child_job_drained_end(BdrvChild *c) + { + BlockJob *job = c->opaque; +- block_job_resume(job); ++ job_resume(&job->job); + } + + static const BdrvChildRole child_job = { +@@ -396,9 +373,9 @@ static void block_job_cancel_async(BlockJob *job, bool force) + if (job->iostatus != BLOCK_DEVICE_IO_STATUS_OK) { + block_job_iostatus_reset(job); + } +- if (job->user_paused) { ++ if (job->job.user_paused) { + /* Do not call block_job_enter here, the caller will handle it. */ +- job->user_paused = false; ++ job->job.user_paused = false; + job->job.pause_count--; + } + job->job.cancelled = true; +@@ -628,39 +605,6 @@ void block_job_dismiss(BlockJob **jobptr, Error **errp) + *jobptr = NULL; + } + +-void block_job_user_pause(BlockJob *job, Error **errp) +-{ +- if (job_apply_verb(&job->job, JOB_VERB_PAUSE, errp)) { +- return; +- } +- if (job->user_paused) { +- error_setg(errp, "Job is already paused"); +- return; +- } +- job->user_paused = true; +- block_job_pause(job); +-} +- +-bool block_job_user_paused(BlockJob *job) +-{ +- return job->user_paused; +-} +- +-void block_job_user_resume(BlockJob *job, Error **errp) +-{ +- assert(job); +- if (!job->user_paused || job->job.pause_count <= 0) { +- error_setg(errp, "Can't resume a job that was not paused"); +- return; +- } +- if (job_apply_verb(&job->job, JOB_VERB_RESUME, errp)) { +- return; +- } +- block_job_iostatus_reset(job); +- job->user_paused = false; +- block_job_resume(job); +-} +- + void block_job_cancel(BlockJob *job, bool force) + { + if (job->job.status == JOB_STATUS_CONCLUDED) { +@@ -851,6 +795,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + + assert(is_block_job(&job->job)); + assert(job->job.driver->free == &block_job_free); ++ assert(job->job.driver->user_resume == &block_job_user_resume); + + job->driver = driver; + job->blk = blk; +@@ -941,10 +886,16 @@ void block_job_iostatus_reset(BlockJob *job) + if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { + return; + } +- assert(job->user_paused && job->job.pause_count > 0); ++ assert(job->job.user_paused && job->job.pause_count > 0); + job->iostatus = BLOCK_DEVICE_IO_STATUS_OK; + } + ++void block_job_user_resume(Job *job) ++{ ++ BlockJob *bjob = container_of(job, BlockJob, job); ++ block_job_iostatus_reset(bjob); ++} ++ + void block_job_event_ready(BlockJob *job) + { + job_state_transition(&job->job, JOB_STATUS_READY); +@@ -991,9 +942,9 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, + action, &error_abort); + } + if (action == BLOCK_ERROR_ACTION_STOP) { +- block_job_pause(job); ++ job_pause(&job->job); + /* make the pause user visible, which will be resumed from QMP. */ +- job->user_paused = true; ++ job->job.user_paused = true; + block_job_iostatus_set_err(job, error); + } + return action; +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index b60d919..556a8f6 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -57,12 +57,6 @@ typedef struct BlockJob { + bool force; + + /** +- * 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 when the job is ready to be completed. + */ + bool ready; +@@ -248,32 +242,6 @@ void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining); + BlockJobInfo *block_job_query(BlockJob *job, Error **errp); + + /** +- * block_job_user_pause: +- * @job: The job to be paused. +- * +- * Asynchronously pause the specified job. +- * Do not allow a resume until a matching call to block_job_user_resume. +- */ +-void block_job_user_pause(BlockJob *job, Error **errp); +- +-/** +- * block_job_paused: +- * @job: The job to query. +- * +- * Returns true if the job is user-paused. +- */ +-bool block_job_user_paused(BlockJob *job); +- +-/** +- * block_job_user_resume: +- * @job: The job to be resumed. +- * +- * Resume the specified job. +- * Must be paired with a preceding block_job_user_pause. +- */ +-void block_job_user_resume(BlockJob *job, Error **errp); +- +-/** + * block_job_user_cancel: + * @job: The job to be cancelled. + * @force: Quit a job without waiting for data to be in sync. +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index 8937f5b..7e705ae 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -134,6 +134,13 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + 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); ++ ++/** + * block_job_yield: + * @job: The job that calls the function. + * +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 509408f..bc63985 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -83,6 +83,12 @@ typedef struct Job { + 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 +@@ -124,6 +130,12 @@ struct JobDriver { + */ + void coroutine_fn (*resume)(Job *job); + ++ /** ++ * 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); ++ + /** Called when the job is freed */ + void (*free)(Job *job); + }; +@@ -203,6 +215,31 @@ const char *job_type_str(const Job *job); + bool job_is_cancelled(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() instead. ++ */ ++void job_pause(Job *job); ++ ++/** Resumes a @job paused with job_pause. */ ++void job_resume(Job *job); ++ ++/** ++ * Asynchronously pause the specified @job. ++ * Do not allow a resume until a matching call to job_user_resume. ++ */ ++void job_user_pause(Job *job, Error **errp); ++ ++/** Returns true if the job is user-paused. */ ++bool job_user_paused(Job *job); ++ ++/** ++ * Resume the specified @job. ++ * Must be paired with a preceding job_user_pause. ++ */ ++void job_user_resume(Job *job, Error **errp); ++ ++/** + * Get the next element from the list of block jobs after @job, or the + * first one if @job is %NULL. + * +diff --git a/job.c b/job.c +index 1b8cba1..fd10b1d 100644 +--- a/job.c ++++ b/job.c +@@ -341,6 +341,65 @@ void job_start(Job *job) + aio_co_enter(job->aio_context, job->co); + } + ++/* Assumes the block_job_mutex is held */ ++static bool job_timer_not_pending(Job *job) ++{ ++ return !timer_pending(&job->sleep_timer); ++} ++ ++void job_pause(Job *job) ++{ ++ job->pause_count++; ++} ++ ++void job_resume(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(job, job_timer_not_pending); ++} ++ ++void job_user_pause(Job *job, Error **errp) ++{ ++ if (job_apply_verb(job, JOB_VERB_PAUSE, errp)) { ++ return; ++ } ++ if (job->user_paused) { ++ error_setg(errp, "Job is already paused"); ++ return; ++ } ++ job->user_paused = true; ++ job_pause(job); ++} ++ ++bool job_user_paused(Job *job) ++{ ++ return job->user_paused; ++} ++ ++void job_user_resume(Job *job, Error **errp) ++{ ++ assert(job); ++ 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(job, JOB_VERB_RESUME, errp)) { ++ return; ++ } ++ if (job->driver->user_resume) { ++ job->driver->user_resume(job); ++ } ++ job->user_paused = false; ++ job_resume(job); ++} ++ ++ + typedef struct { + Job *job; + JobDeferToMainLoopFn *fn; +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index 50232f5..c993512 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -524,6 +524,7 @@ BlockJobDriver test_job_driver = { + .job_driver = { + .instance_size = sizeof(TestBlockJob), + .free = block_job_free, ++ .user_resume = block_job_user_resume, + .start = test_job_start, + }, + .complete = test_job_complete, +diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c +index 0e6162b..93d1ff0 100644 +--- a/tests/test-blockjob-txn.c ++++ b/tests/test-blockjob-txn.c +@@ -78,6 +78,7 @@ static const BlockJobDriver test_block_job_driver = { + .job_driver = { + .instance_size = sizeof(TestBlockJob), + .free = block_job_free, ++ .user_resume = block_job_user_resume, + .start = test_block_job_run, + }, + }; +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index b329bd5..ceb5960 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -20,6 +20,7 @@ static const BlockJobDriver test_block_job_driver = { + .job_driver = { + .instance_size = sizeof(BlockJob), + .free = block_job_free, ++ .user_resume = block_job_user_resume, + }, + }; + +@@ -199,6 +200,7 @@ static const BlockJobDriver test_cancel_driver = { + .job_driver = { + .instance_size = sizeof(CancelJob), + .free = block_job_free, ++ .user_resume = block_job_user_resume, + .start = cancel_job_start, + }, + .complete = cancel_job_complete, +@@ -270,7 +272,7 @@ static void test_cancel_paused(void) + job_start(&job->job); + assert(job->job.status == JOB_STATUS_RUNNING); + +- block_job_user_pause(job, &error_abort); ++ job_user_pause(&job->job, &error_abort); + block_job_enter(job); + assert(job->job.status == JOB_STATUS_PAUSED); + +@@ -308,7 +310,7 @@ static void test_cancel_standby(void) + block_job_enter(job); + assert(job->job.status == JOB_STATUS_READY); + +- block_job_user_pause(job, &error_abort); ++ job_user_pause(&job->job, &error_abort); + block_job_enter(job); + assert(job->job.status == JOB_STATUS_STANDBY); + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Move-progress-fields-to-Job.patch b/SOURCES/kvm-job-Move-progress-fields-to-Job.patch new file mode 100644 index 0000000..fdf2694 --- /dev/null +++ b/SOURCES/kvm-job-Move-progress-fields-to-Job.patch @@ -0,0 +1,337 @@ +From 1da3dfa844e67f9948eddd18188307acb7f9fe95 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:29 +0200 +Subject: [PATCH 60/89] job: Move progress fields to Job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-47-kwolf@redhat.com> +Patchwork-id: 81115 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 46/73] job: Move progress fields to Job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +BlockJob has fields .offset and .len, which are actually misnomers today +because they are no longer tied to block device sizes, but just progress +counters. As such they make a lot of sense in generic Jobs. + +This patch moves the fields to Job and renames them to .progress_current +and .progress_total to describe their function better. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit 30a5c887bf4a7e00d0e0ecbb08509e8ba2902620) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 8 ++++---- + block/commit.c | 4 ++-- + block/mirror.c | 4 ++-- + block/stream.c | 4 ++-- + blockjob.c | 26 ++++++++------------------ + include/block/blockjob.h | 25 ------------------------- + include/qemu/job.h | 28 ++++++++++++++++++++++++++++ + job.c | 10 ++++++++++ + qemu-img.c | 8 ++++++-- + 9 files changed, 62 insertions(+), 55 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index 6f4f3df..4e228e9 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -160,7 +160,7 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job, + * offset field is an opaque progress value, it is not a disk offset. + */ + job->bytes_read += n; +- block_job_progress_update(&job->common, n); ++ job_progress_update(&job->common.job, n); + } + + out: +@@ -406,8 +406,8 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) + bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size); + } + +- /* TODO block_job_progress_set_remaining() would make more sense */ +- block_job_progress_update(&job->common, ++ /* TODO job_progress_set_remaining() would make more sense */ ++ job_progress_update(&job->common.job, + job->len - hbitmap_count(job->copy_bitmap) * job->cluster_size); + + bdrv_dirty_iter_free(dbi); +@@ -425,7 +425,7 @@ static void coroutine_fn backup_run(void *opaque) + qemu_co_rwlock_init(&job->flush_rwlock); + + nb_clusters = DIV_ROUND_UP(job->len, job->cluster_size); +- block_job_progress_set_remaining(&job->common, job->len); ++ job_progress_set_remaining(&job->common.job, job->len); + + job->copy_bitmap = hbitmap_alloc(nb_clusters, 0); + if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { +diff --git a/block/commit.c b/block/commit.c +index b0a847e..6206661 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -150,7 +150,7 @@ static void coroutine_fn commit_run(void *opaque) + if (len < 0) { + goto out; + } +- block_job_progress_set_remaining(&s->common, len); ++ job_progress_set_remaining(&s->common.job, len); + + ret = base_len = blk_getlength(s->base); + if (base_len < 0) { +@@ -196,7 +196,7 @@ static void coroutine_fn commit_run(void *opaque) + } + } + /* Publish progress */ +- block_job_progress_update(&s->common, n); ++ job_progress_update(&s->common.job, n); + + if (copy) { + delay_ns = block_job_ratelimit_get_delay(&s->common, n); +diff --git a/block/mirror.c b/block/mirror.c +index bdc1b5b..dcb66ec 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -119,7 +119,7 @@ static void mirror_iteration_done(MirrorOp *op, int ret) + bitmap_set(s->cow_bitmap, chunk_num, nb_chunks); + } + if (!s->initial_zeroing_ongoing) { +- block_job_progress_update(&s->common, op->bytes); ++ job_progress_update(&s->common.job, op->bytes); + } + } + qemu_iovec_destroy(&op->qiov); +@@ -792,7 +792,7 @@ static void coroutine_fn mirror_run(void *opaque) + /* 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 */ +- block_job_progress_set_remaining(&s->common, s->bytes_in_flight + cnt); ++ job_progress_set_remaining(&s->common.job, s->bytes_in_flight + cnt); + + /* 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. +diff --git a/block/stream.c b/block/stream.c +index 8546c41..a5d6e0c 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -121,7 +121,7 @@ static void coroutine_fn stream_run(void *opaque) + ret = len; + goto out; + } +- block_job_progress_set_remaining(&s->common, len); ++ job_progress_set_remaining(&s->common.job, len); + + buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE); + +@@ -184,7 +184,7 @@ static void coroutine_fn stream_run(void *opaque) + ret = 0; + + /* Publish progress */ +- block_job_progress_update(&s->common, n); ++ job_progress_update(&s->common.job, n); + if (copy) { + delay_ns = block_job_ratelimit_get_delay(&s->common, n); + } else { +diff --git a/blockjob.c b/blockjob.c +index da11b3b..5c8ff6f 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -242,16 +242,6 @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) + return ratelimit_calculate_delay(&job->limit, n); + } + +-void block_job_progress_update(BlockJob *job, uint64_t done) +-{ +- job->offset += done; +-} +- +-void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining) +-{ +- job->len = job->offset + remaining; +-} +- + BlockJobInfo *block_job_query(BlockJob *job, Error **errp) + { + BlockJobInfo *info; +@@ -263,10 +253,10 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) + info = g_new0(BlockJobInfo, 1); + info->type = g_strdup(job_type_str(&job->job)); + info->device = g_strdup(job->job.id); +- info->len = job->len; + info->busy = atomic_read(&job->job.busy); + info->paused = job->job.pause_count > 0; +- info->offset = job->offset; ++ info->offset = job->job.progress_current; ++ info->len = job->job.progress_total; + info->speed = job->speed; + info->io_status = job->iostatus; + info->ready = job_is_ready(&job->job), +@@ -296,8 +286,8 @@ static void block_job_event_cancelled(Notifier *n, void *opaque) + + qapi_event_send_block_job_cancelled(job_type(&job->job), + job->job.id, +- job->len, +- job->offset, ++ job->job.progress_total, ++ job->job.progress_current, + job->speed, + &error_abort); + } +@@ -317,8 +307,8 @@ static void block_job_event_completed(Notifier *n, void *opaque) + + qapi_event_send_block_job_completed(job_type(&job->job), + job->job.id, +- job->len, +- job->offset, ++ job->job.progress_total, ++ job->job.progress_current, + job->speed, + !!msg, + msg, +@@ -348,8 +338,8 @@ static void block_job_event_ready(Notifier *n, void *opaque) + + qapi_event_send_block_job_ready(job_type(&job->job), + job->job.id, +- job->len, +- job->offset, ++ job->job.progress_total, ++ job->job.progress_current, + job->speed, &error_abort); + } + +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 4fca45f..3021d11 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -52,12 +52,6 @@ typedef struct BlockJob { + /** Status that is published by the query-block-jobs QMP API */ + BlockDeviceIoStatus iostatus; + +- /** Offset that is published by the query-block-jobs QMP API */ +- int64_t offset; +- +- /** Length that is published by the query-block-jobs QMP API */ +- int64_t len; +- + /** Speed that was set with @block_job_set_speed. */ + int64_t speed; + +@@ -139,25 +133,6 @@ void block_job_remove_all_bdrv(BlockJob *job); + void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp); + + /** +- * block_job_progress_update: +- * @job: The job that has made progress +- * @done: How much progress the job made +- * +- * Updates the progress counter of the job. +- */ +-void block_job_progress_update(BlockJob *job, uint64_t done); +- +-/** +- * block_job_progress_set_remaining: +- * @job: The job whose expected progress end value is set +- * @remaining: Expected end value of the progress counter of the job +- * +- * 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. +- */ +-void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining); +- +-/** + * block_job_query: + * @job: The job to get information about. + * +diff --git a/include/qemu/job.h b/include/qemu/job.h +index bfc2bc5..92d1d24 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -114,6 +114,16 @@ typedef struct Job { + /** True if this job should automatically dismiss itself */ + bool auto_dismiss; + ++ /** ++ * Current progress. The unit is arbitrary as long as the ratio between ++ * progress_current and progress_total represents the estimated percentage ++ * of work already done. ++ */ ++ int64_t progress_current; ++ ++ /** Estimated progress_current value at the completion of the job */ ++ int64_t progress_total; ++ + /** ret code passed to job_completed. */ + int ret; + +@@ -304,6 +314,24 @@ void job_ref(Job *job); + */ + void job_unref(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. ++ */ ++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. ++ */ ++void job_progress_set_remaining(Job *job, uint64_t remaining); ++ + /** To be called when a cancelled job is finalised. */ + void job_event_cancelled(Job *job); + +diff --git a/job.c b/job.c +index b5bd51b..2046d2f 100644 +--- a/job.c ++++ b/job.c +@@ -364,6 +364,16 @@ void job_unref(Job *job) + } + } + ++void job_progress_update(Job *job, uint64_t done) ++{ ++ job->progress_current += done; ++} ++ ++void job_progress_set_remaining(Job *job, uint64_t remaining) ++{ ++ job->progress_total = job->progress_current + remaining; ++} ++ + void job_event_cancelled(Job *job) + { + notifier_list_notify(&job->on_finalize_cancelled, job); +diff --git a/qemu-img.c b/qemu-img.c +index 3c449a2..9fc8e66 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -875,9 +875,13 @@ static void run_block_job(BlockJob *job, Error **errp) + aio_context_acquire(aio_context); + job_ref(&job->job); + do { ++ float progress = 0.0f; + aio_poll(aio_context, true); +- qemu_progress_print(job->len ? +- ((float)job->offset / job->len * 100.f) : 0.0f, 0); ++ if (job->job.progress_total) { ++ progress = (float)job->job.progress_current / ++ job->job.progress_total * 100.f; ++ } ++ qemu_progress_print(progress, 0); + } while (!job_is_ready(&job->job) && !job_is_completed(&job->job)); + + if (!job_is_completed(&job->job)) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Move-single-job-finalisation-to-Job.patch b/SOURCES/kvm-job-Move-single-job-finalisation-to-Job.patch new file mode 100644 index 0000000..473b584 --- /dev/null +++ b/SOURCES/kvm-job-Move-single-job-finalisation-to-Job.patch @@ -0,0 +1,736 @@ +From adcfa2a0ddbf68a34b24afcbfaaf03a19d1406ca Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:16 +0200 +Subject: [PATCH 47/89] job: Move single job finalisation to Job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-34-kwolf@redhat.com> +Patchwork-id: 81070 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 33/73] job: Move single job finalisation to Job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This moves the finalisation of a single job from BlockJob to Job. + +Some part of this code depends on job transactions, and job transactions +call this code, we introduce some temporary calls from Job functions to +BlockJob ones. This will be fixed once transactions move to Job, too. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit 4ad351819b974d724e926fd23cdd66bec3c9768e) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 22 +++---- + block/commit.c | 2 +- + block/mirror.c | 2 +- + blockjob.c | 142 ++++++++----------------------------------- + include/block/blockjob.h | 9 --- + include/block/blockjob_int.h | 36 ----------- + include/qemu/job.h | 53 +++++++++++++++- + job.c | 100 +++++++++++++++++++++++++++++- + qemu-img.c | 2 +- + tests/test-blockjob.c | 10 +-- + 10 files changed, 194 insertions(+), 184 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index 4d011d5..bd31282 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -207,25 +207,25 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) + } + } + +-static void backup_commit(BlockJob *job) ++static void backup_commit(Job *job) + { +- BackupBlockJob *s = container_of(job, BackupBlockJob, common); ++ BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); + if (s->sync_bitmap) { + backup_cleanup_sync_bitmap(s, 0); + } + } + +-static void backup_abort(BlockJob *job) ++static void backup_abort(Job *job) + { +- BackupBlockJob *s = container_of(job, BackupBlockJob, common); ++ BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); + if (s->sync_bitmap) { + backup_cleanup_sync_bitmap(s, -1); + } + } + +-static void backup_clean(BlockJob *job) ++static void backup_clean(Job *job) + { +- BackupBlockJob *s = container_of(job, BackupBlockJob, common); ++ BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); + assert(s->target); + blk_unref(s->target); + s->target = NULL; +@@ -530,10 +530,10 @@ static const BlockJobDriver backup_job_driver = { + .free = block_job_free, + .user_resume = block_job_user_resume, + .start = backup_run, ++ .commit = backup_commit, ++ .abort = backup_abort, ++ .clean = backup_clean, + }, +- .commit = backup_commit, +- .abort = backup_abort, +- .clean = backup_clean, + .attached_aio_context = backup_attached_aio_context, + .drain = backup_drain, + }; +@@ -678,8 +678,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, + bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL); + } + if (job) { +- backup_clean(&job->common); +- block_job_early_fail(&job->common); ++ backup_clean(&job->common.job); ++ job_early_fail(&job->common.job); + } + + return NULL; +diff --git a/block/commit.c b/block/commit.c +index 7a6ae59..e53b2d7 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -385,7 +385,7 @@ fail: + if (commit_top_bs) { + bdrv_replace_node(commit_top_bs, top, &error_abort); + } +- block_job_early_fail(&s->common); ++ job_early_fail(&s->common.job); + } + + +diff --git a/block/mirror.c b/block/mirror.c +index 5091e72..e9a90ea 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -1257,7 +1257,7 @@ fail: + + g_free(s->replaces); + blk_unref(s->target); +- block_job_early_fail(&s->common); ++ job_early_fail(&s->common.job); + } + + bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL, +diff --git a/blockjob.c b/blockjob.c +index 05d7921..34c57da 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -127,7 +127,7 @@ void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job) + block_job_txn_ref(txn); + } + +-static void block_job_txn_del_job(BlockJob *job) ++void block_job_txn_del_job(BlockJob *job) + { + if (job->txn) { + QLIST_REMOVE(job, txn_list); +@@ -262,101 +262,12 @@ const BlockJobDriver *block_job_driver(BlockJob *job) + return job->driver; + } + +-static void block_job_decommission(BlockJob *job) +-{ +- assert(job); +- job->job.busy = false; +- job->job.paused = false; +- job->job.deferred_to_main_loop = true; +- block_job_txn_del_job(job); +- job_state_transition(&job->job, JOB_STATUS_NULL); +- job_unref(&job->job); +-} +- +-static void block_job_do_dismiss(BlockJob *job) +-{ +- block_job_decommission(job); +-} +- +-static void block_job_conclude(BlockJob *job) +-{ +- job_state_transition(&job->job, JOB_STATUS_CONCLUDED); +- if (job->job.auto_dismiss || !job_started(&job->job)) { +- block_job_do_dismiss(job); +- } +-} +- +-static void block_job_update_rc(BlockJob *job) +-{ +- if (!job->ret && job_is_cancelled(&job->job)) { +- job->ret = -ECANCELED; +- } +- if (job->ret) { +- job_state_transition(&job->job, JOB_STATUS_ABORTING); +- } +-} +- + static int block_job_prepare(BlockJob *job) + { +- if (job->ret == 0 && job->driver->prepare) { +- job->ret = job->driver->prepare(job); +- } +- return job->ret; +-} +- +-static void block_job_commit(BlockJob *job) +-{ +- assert(!job->ret); +- if (job->driver->commit) { +- job->driver->commit(job); +- } +-} +- +-static void block_job_abort(BlockJob *job) +-{ +- assert(job->ret); +- if (job->driver->abort) { +- job->driver->abort(job); +- } +-} +- +-static void block_job_clean(BlockJob *job) +-{ +- if (job->driver->clean) { +- job->driver->clean(job); ++ if (job->job.ret == 0 && job->driver->prepare) { ++ job->job.ret = job->driver->prepare(job); + } +-} +- +-static int block_job_finalize_single(BlockJob *job) +-{ +- assert(job_is_completed(&job->job)); +- +- /* Ensure abort is called for late-transactional failures */ +- block_job_update_rc(job); +- +- if (!job->ret) { +- block_job_commit(job); +- } else { +- block_job_abort(job); +- } +- block_job_clean(job); +- +- if (job->cb) { +- job->cb(job->opaque, job->ret); +- } +- +- /* Emit events only if we actually started */ +- if (job_started(&job->job)) { +- if (job_is_cancelled(&job->job)) { +- job_event_cancelled(&job->job); +- } else { +- job_event_completed(&job->job); +- } +- } +- +- block_job_txn_del_job(job); +- block_job_conclude(job); +- return 0; ++ return job->job.ret; + } + + static void block_job_cancel_async(BlockJob *job, bool force) +@@ -424,8 +335,8 @@ static int block_job_finish_sync(BlockJob *job, + while (!job_is_completed(&job->job)) { + aio_poll(qemu_get_aio_context(), true); + } +- ret = (job_is_cancelled(&job->job) && job->ret == 0) +- ? -ECANCELED : job->ret; ++ ret = (job_is_cancelled(&job->job) && job->job.ret == 0) ++ ? -ECANCELED : job->job.ret; + job_unref(&job->job); + return ret; + } +@@ -466,7 +377,7 @@ static void block_job_completed_txn_abort(BlockJob *job) + assert(job_is_cancelled(&other_job->job)); + block_job_finish_sync(other_job, NULL, NULL); + } +- block_job_finalize_single(other_job); ++ job_finalize_single(&other_job->job); + aio_context_release(ctx); + } + +@@ -478,6 +389,11 @@ static int block_job_needs_finalize(BlockJob *job) + return !job->job.auto_finalize; + } + ++static int block_job_finalize_single(BlockJob *job) ++{ ++ return job_finalize_single(&job->job); ++} ++ + static void block_job_do_finalize(BlockJob *job) + { + int rc; +@@ -516,7 +432,7 @@ static void block_job_completed_txn_success(BlockJob *job) + if (!job_is_completed(&other_job->job)) { + return; + } +- assert(other_job->ret == 0); ++ assert(other_job->job.ret == 0); + } + + block_job_txn_apply(txn, block_job_transition_to_pending, false); +@@ -601,14 +517,14 @@ void block_job_dismiss(BlockJob **jobptr, Error **errp) + return; + } + +- block_job_do_dismiss(job); ++ job_do_dismiss(&job->job); + *jobptr = NULL; + } + + void block_job_cancel(BlockJob *job, bool force) + { + if (job->job.status == JOB_STATUS_CONCLUDED) { +- block_job_do_dismiss(job); ++ job_do_dismiss(&job->job); + return; + } + block_job_cancel_async(job, force); +@@ -691,8 +607,8 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) + info->status = job->job.status; + info->auto_finalize = job->job.auto_finalize; + info->auto_dismiss = job->job.auto_dismiss; +- info->has_error = job->ret != 0; +- info->error = job->ret ? g_strdup(strerror(-job->ret)) : NULL; ++ info->has_error = job->job.ret != 0; ++ info->error = job->job.ret ? g_strdup(strerror(-job->job.ret)) : NULL; + return info; + } + +@@ -729,8 +645,8 @@ static void block_job_event_completed(Notifier *n, void *opaque) + return; + } + +- if (job->ret < 0) { +- msg = strerror(-job->ret); ++ if (job->job.ret < 0) { ++ msg = strerror(-job->job.ret); + } + + qapi_event_send_block_job_completed(job_type(&job->job), +@@ -787,7 +703,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + } + + job = job_create(job_id, &driver->job_driver, blk_get_aio_context(blk), +- flags, errp); ++ flags, cb, opaque, errp); + if (job == NULL) { + blk_unref(blk); + return NULL; +@@ -799,8 +715,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + + job->driver = driver; + job->blk = blk; +- job->cb = cb; +- job->opaque = opaque; + + job->finalize_cancelled_notifier.notify = block_job_event_cancelled; + job->finalize_completed_notifier.notify = block_job_event_completed; +@@ -828,7 +742,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + + block_job_set_speed(job, speed, &local_err); + if (local_err) { +- block_job_early_fail(job); ++ job_early_fail(&job->job); + error_propagate(errp, local_err); + return NULL; + } +@@ -847,20 +761,14 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + return job; + } + +-void block_job_early_fail(BlockJob *job) +-{ +- assert(job->job.status == JOB_STATUS_CREATED); +- block_job_decommission(job); +-} +- + void block_job_completed(BlockJob *job, int ret) + { + assert(job && job->txn && !job_is_completed(&job->job)); + assert(blk_bs(job->blk)->job == job); +- job->ret = ret; +- block_job_update_rc(job); +- trace_block_job_completed(job, ret, job->ret); +- if (job->ret) { ++ job->job.ret = ret; ++ job_update_rc(&job->job); ++ trace_block_job_completed(job, ret, job->job.ret); ++ if (job->job.ret) { + block_job_completed_txn_abort(job); + } else { + block_job_completed_txn_success(job); +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index aef0629..3f405d1 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -76,9 +76,6 @@ typedef struct BlockJob { + /** Rate limiting data structure for implementing @speed. */ + RateLimit limit; + +- /** The completion function that will be called when the job completes. */ +- BlockCompletionFunc *cb; +- + /** Block other operations when block job is running */ + Error *blocker; + +@@ -94,12 +91,6 @@ typedef struct BlockJob { + /** BlockDriverStates that are involved in this block job */ + GSList *nodes; + +- /** The opaque value that is passed to the completion function. */ +- void *opaque; +- +- /** ret code passed to block_job_completed. */ +- int ret; +- + BlockJobTxn *txn; + QLIST_ENTRY(BlockJob) txn_list; + } BlockJob; +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index 88639f7..bf2b762 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -54,34 +54,6 @@ struct BlockJobDriver { + */ + int (*prepare)(BlockJob *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)(BlockJob *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)(BlockJob *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)(BlockJob *job); +- + /* + * 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 +@@ -156,14 +128,6 @@ void block_job_yield(BlockJob *job); + int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n); + + /** +- * block_job_early_fail: +- * @bs: The block device. +- * +- * The block job could not be started, free it. +- */ +-void block_job_early_fail(BlockJob *job); +- +-/** + * block_job_completed: + * @job: The job being completed. + * @ret: The status code. +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 14d9377..3e817be 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -29,6 +29,7 @@ + #include "qapi/qapi-types-block-core.h" + #include "qemu/queue.h" + #include "qemu/coroutine.h" ++#include "block/aio.h" + + typedef struct JobDriver JobDriver; + +@@ -105,6 +106,15 @@ typedef struct Job { + /** True if this job should automatically dismiss itself */ + bool auto_dismiss; + ++ /** ret code passed to block_job_completed. */ ++ int ret; ++ ++ /** 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; ++ + /** Notifiers called when a cancelled job is finalised */ + NotifierList on_finalize_cancelled; + +@@ -151,6 +161,35 @@ struct JobDriver { + */ + void (*user_resume)(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); ++ ++ + /** Called when the job is freed */ + void (*free)(Job *job); + }; +@@ -174,10 +213,12 @@ typedef enum JobCreateFlags { + * @driver: The class object for the newly-created job. + * @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, AioContext *ctx, +- int flags, Error **errp); ++ int flags, BlockCompletionFunc *cb, void *opaque, Error **errp); + + /** + * Add a reference to Job refcnt, it will be decreased with job_unref, and then +@@ -300,6 +341,10 @@ Job *job_get(const char *id); + */ + int job_apply_verb(Job *job, JobVerb verb, Error **errp); + ++/** The @job could not be started, free it. */ ++void job_early_fail(Job *job); ++ ++ + typedef void JobDeferToMainLoopFn(Job *job, void *opaque); + + /** +@@ -322,5 +367,11 @@ void job_state_transition(Job *job, JobStatus s1); + void coroutine_fn job_do_yield(Job *job, uint64_t ns); + bool job_should_pause(Job *job); + bool job_started(Job *job); ++void job_do_dismiss(Job *job); ++int job_finalize_single(Job *job); ++void job_update_rc(Job *job); ++ ++typedef struct BlockJob BlockJob; ++void block_job_txn_del_job(BlockJob *job); + + #endif +diff --git a/job.c b/job.c +index 817c3b4..64b64da 100644 +--- a/job.c ++++ b/job.c +@@ -85,7 +85,7 @@ void job_state_transition(Job *job, JobStatus s1) + { + JobStatus s0 = job->status; + assert(s1 >= 0 && s1 <= JOB_STATUS__MAX); +- trace_job_state_transition(job, /* TODO re-enable: job->ret */ 0, ++ trace_job_state_transition(job, job->ret, + JobSTT[s0][s1] ? "allowed" : "disallowed", + JobStatus_str(s0), JobStatus_str(s1)); + assert(JobSTT[s0][s1]); +@@ -182,7 +182,7 @@ static void job_sleep_timer_cb(void *opaque) + } + + void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, +- int flags, Error **errp) ++ int flags, BlockCompletionFunc *cb, void *opaque, Error **errp) + { + Job *job; + +@@ -214,6 +214,8 @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, + job->pause_count = 1; + job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE); + job->auto_dismiss = !(flags & JOB_MANUAL_DISMISS); ++ job->cb = cb; ++ job->opaque = opaque; + + notifier_list_init(&job->on_finalize_cancelled); + notifier_list_init(&job->on_finalize_completed); +@@ -449,6 +451,100 @@ void job_user_resume(Job *job, Error **errp) + job_resume(job); + } + ++void job_do_dismiss(Job *job) ++{ ++ assert(job); ++ job->busy = false; ++ job->paused = false; ++ job->deferred_to_main_loop = true; ++ ++ /* TODO Don't assume it's a BlockJob */ ++ block_job_txn_del_job((BlockJob*) job); ++ ++ job_state_transition(job, JOB_STATUS_NULL); ++ job_unref(job); ++} ++ ++void job_early_fail(Job *job) ++{ ++ assert(job->status == JOB_STATUS_CREATED); ++ job_do_dismiss(job); ++} ++ ++static void job_conclude(Job *job) ++{ ++ job_state_transition(job, JOB_STATUS_CONCLUDED); ++ if (job->auto_dismiss || !job_started(job)) { ++ job_do_dismiss(job); ++ } ++} ++ ++void job_update_rc(Job *job) ++{ ++ if (!job->ret && job_is_cancelled(job)) { ++ job->ret = -ECANCELED; ++ } ++ if (job->ret) { ++ job_state_transition(job, JOB_STATUS_ABORTING); ++ } ++} ++ ++static void job_commit(Job *job) ++{ ++ assert(!job->ret); ++ if (job->driver->commit) { ++ job->driver->commit(job); ++ } ++} ++ ++static void job_abort(Job *job) ++{ ++ assert(job->ret); ++ if (job->driver->abort) { ++ job->driver->abort(job); ++ } ++} ++ ++static void job_clean(Job *job) ++{ ++ if (job->driver->clean) { ++ job->driver->clean(job); ++ } ++} ++ ++int job_finalize_single(Job *job) ++{ ++ assert(job_is_completed(job)); ++ ++ /* Ensure abort is called for late-transactional failures */ ++ job_update_rc(job); ++ ++ if (!job->ret) { ++ job_commit(job); ++ } else { ++ job_abort(job); ++ } ++ job_clean(job); ++ ++ if (job->cb) { ++ job->cb(job->opaque, job->ret); ++ } ++ ++ /* Emit events only if we actually started */ ++ if (job_started(job)) { ++ if (job_is_cancelled(job)) { ++ job_event_cancelled(job); ++ } else { ++ job_event_completed(job); ++ } ++ } ++ ++ /* TODO Don't assume it's a BlockJob */ ++ block_job_txn_del_job((BlockJob*) job); ++ job_conclude(job); ++ return 0; ++} ++ + + typedef struct { + Job *job; +diff --git a/qemu-img.c b/qemu-img.c +index 843dc6a..91b3151 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -883,7 +883,7 @@ static void run_block_job(BlockJob *job, Error **errp) + if (!job_is_completed(&job->job)) { + ret = block_job_complete_sync(job, errp); + } else { +- ret = job->ret; ++ ret = job->job.ret; + } + job_unref(&job->job); + aio_context_release(aio_context); +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index 8bb0aa8..1fe6803 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -128,11 +128,11 @@ static void test_job_ids(void) + job[1] = do_test_id(blk[1], "id0", false); + + /* But once job[0] finishes we can reuse its ID */ +- block_job_early_fail(job[0]); ++ job_early_fail(&job[0]->job); + job[1] = do_test_id(blk[1], "id0", true); + + /* No job ID specified, defaults to the backend name ('drive1') */ +- block_job_early_fail(job[1]); ++ job_early_fail(&job[1]->job); + job[1] = do_test_id(blk[1], NULL, true); + + /* Duplicate job ID */ +@@ -145,9 +145,9 @@ static void test_job_ids(void) + /* This one is valid */ + job[2] = do_test_id(blk[2], "id_2", true); + +- block_job_early_fail(job[0]); +- block_job_early_fail(job[1]); +- block_job_early_fail(job[2]); ++ job_early_fail(&job[0]->job); ++ job_early_fail(&job[1]->job); ++ job_early_fail(&job[2]->job); + + destroy_blk(blk[0]); + destroy_blk(blk[1]); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Move-state-transitions-to-Job.patch b/SOURCES/kvm-job-Move-state-transitions-to-Job.patch new file mode 100644 index 0000000..880a1f7 --- /dev/null +++ b/SOURCES/kvm-job-Move-state-transitions-to-Job.patch @@ -0,0 +1,640 @@ +From bb250647bab72a20db342030756d1837b91bc91a Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:04 +0200 +Subject: [PATCH 35/89] job: Move state transitions to Job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-22-kwolf@redhat.com> +Patchwork-id: 81093 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 21/73] job: Move state transitions to Job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This moves BlockJob.status and the closely related functions +(block_)job_state_transition() and (block_)job_apply_verb to Job. The +two QAPI enums are renamed to JobStatus and JobVerb. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: John Snow +Reviewed-by: Eric Blake +(cherry picked from commit a50c2ab858fe613fb805e53b4f6b970ab936706d) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/trace-events | 2 - + blockjob.c | 102 +++++++++++------------------------------------ + include/block/blockjob.h | 3 -- + include/qemu/job.h | 13 ++++++ + job.c | 56 ++++++++++++++++++++++++++ + qapi/block-core.json | 16 ++++---- + tests/test-blockjob.c | 39 +++++++++--------- + trace-events | 4 ++ + 8 files changed, 123 insertions(+), 112 deletions(-) + +diff --git a/block/trace-events b/block/trace-events +index f8c50b4..93b9279 100644 +--- a/block/trace-events ++++ b/block/trace-events +@@ -6,8 +6,6 @@ bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d" + + # blockjob.c + block_job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d" +-block_job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)" +-block_job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)" + + # block/block-backend.c + blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" +diff --git a/blockjob.c b/blockjob.c +index c69b2e7..0fba01e 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -41,61 +41,6 @@ + * block_job_enter. */ + static QemuMutex block_job_mutex; + +-/* BlockJob State Transition Table */ +-bool BlockJobSTT[BLOCK_JOB_STATUS__MAX][BLOCK_JOB_STATUS__MAX] = { +- /* U, C, R, P, Y, S, W, D, X, E, N */ +- /* U: */ [BLOCK_JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- /* C: */ [BLOCK_JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1}, +- /* R: */ [BLOCK_JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0}, +- /* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, +- /* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0}, +- /* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, +- /* W: */ [BLOCK_JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, +- /* D: */ [BLOCK_JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, +- /* X: */ [BLOCK_JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, +- /* E: */ [BLOCK_JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, +- /* N: */ [BLOCK_JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +-}; +- +-bool BlockJobVerbTable[BLOCK_JOB_VERB__MAX][BLOCK_JOB_STATUS__MAX] = { +- /* U, C, R, P, Y, S, W, D, X, E, N */ +- [BLOCK_JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, +- [BLOCK_JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, +- [BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, +- [BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, +- [BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, +- [BLOCK_JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, +- [BLOCK_JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, +-}; +- +-static void block_job_state_transition(BlockJob *job, BlockJobStatus s1) +-{ +- BlockJobStatus s0 = job->status; +- assert(s1 >= 0 && s1 <= BLOCK_JOB_STATUS__MAX); +- trace_block_job_state_transition(job, job->ret, BlockJobSTT[s0][s1] ? +- "allowed" : "disallowed", +- BlockJobStatus_str(s0), +- BlockJobStatus_str(s1)); +- assert(BlockJobSTT[s0][s1]); +- job->status = s1; +-} +- +-static int block_job_apply_verb(BlockJob *job, BlockJobVerb bv, Error **errp) +-{ +- assert(bv >= 0 && bv <= BLOCK_JOB_VERB__MAX); +- trace_block_job_apply_verb(job, BlockJobStatus_str(job->status), +- BlockJobVerb_str(bv), +- BlockJobVerbTable[bv][job->status] ? +- "allowed" : "prohibited"); +- if (BlockJobVerbTable[bv][job->status]) { +- return 0; +- } +- error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'", +- job->job.id, BlockJobStatus_str(job->status), +- BlockJobVerb_str(bv)); +- return -EPERM; +-} +- + static void block_job_lock(void) + { + qemu_mutex_lock(&block_job_mutex); +@@ -257,7 +202,7 @@ static void block_job_detach_aio_context(void *opaque); + void block_job_unref(BlockJob *job) + { + if (--job->refcnt == 0) { +- assert(job->status == BLOCK_JOB_STATUS_NULL); ++ assert(job->job.status == JOB_STATUS_NULL); + assert(!job->txn); + BlockDriverState *bs = blk_bs(job->blk); + bs->job = NULL; +@@ -409,7 +354,7 @@ void block_job_start(BlockJob *job) + job->pause_count--; + job->busy = true; + job->paused = false; +- block_job_state_transition(job, BLOCK_JOB_STATUS_RUNNING); ++ job_state_transition(&job->job, JOB_STATUS_RUNNING); + bdrv_coroutine_enter(blk_bs(job->blk), job->co); + } + +@@ -421,7 +366,7 @@ static void block_job_decommission(BlockJob *job) + job->paused = false; + job->deferred_to_main_loop = true; + block_job_txn_del_job(job); +- block_job_state_transition(job, BLOCK_JOB_STATUS_NULL); ++ job_state_transition(&job->job, JOB_STATUS_NULL); + block_job_unref(job); + } + +@@ -432,7 +377,7 @@ static void block_job_do_dismiss(BlockJob *job) + + static void block_job_conclude(BlockJob *job) + { +- block_job_state_transition(job, BLOCK_JOB_STATUS_CONCLUDED); ++ job_state_transition(&job->job, JOB_STATUS_CONCLUDED); + if (job->auto_dismiss || !block_job_started(job)) { + block_job_do_dismiss(job); + } +@@ -444,7 +389,7 @@ static void block_job_update_rc(BlockJob *job) + job->ret = -ECANCELED; + } + if (job->ret) { +- block_job_state_transition(job, BLOCK_JOB_STATUS_ABORTING); ++ job_state_transition(&job->job, JOB_STATUS_ABORTING); + } + } + +@@ -652,7 +597,7 @@ static void block_job_completed_txn_success(BlockJob *job) + BlockJobTxn *txn = job->txn; + BlockJob *other_job; + +- block_job_state_transition(job, BLOCK_JOB_STATUS_WAITING); ++ job_state_transition(&job->job, JOB_STATUS_WAITING); + + /* + * Successful completion, see if there are other running jobs in this +@@ -677,7 +622,7 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) + { + int64_t old_speed = job->speed; + +- if (block_job_apply_verb(job, BLOCK_JOB_VERB_SET_SPEED, errp)) { ++ if (job_apply_verb(&job->job, JOB_VERB_SET_SPEED, errp)) { + return; + } + if (speed < 0) { +@@ -709,7 +654,7 @@ void block_job_complete(BlockJob *job, Error **errp) + { + /* Should not be reachable via external interface for internal jobs */ + assert(job->job.id); +- if (block_job_apply_verb(job, BLOCK_JOB_VERB_COMPLETE, errp)) { ++ if (job_apply_verb(&job->job, JOB_VERB_COMPLETE, errp)) { + return; + } + if (job->pause_count || job->cancelled || !job->driver->complete) { +@@ -724,7 +669,7 @@ void block_job_complete(BlockJob *job, Error **errp) + void block_job_finalize(BlockJob *job, Error **errp) + { + assert(job && job->job.id); +- if (block_job_apply_verb(job, BLOCK_JOB_VERB_FINALIZE, errp)) { ++ if (job_apply_verb(&job->job, JOB_VERB_FINALIZE, errp)) { + return; + } + block_job_do_finalize(job); +@@ -735,7 +680,7 @@ void block_job_dismiss(BlockJob **jobptr, Error **errp) + BlockJob *job = *jobptr; + /* similarly to _complete, this is QMP-interface only. */ + assert(job->job.id); +- if (block_job_apply_verb(job, BLOCK_JOB_VERB_DISMISS, errp)) { ++ if (job_apply_verb(&job->job, JOB_VERB_DISMISS, errp)) { + return; + } + +@@ -745,7 +690,7 @@ void block_job_dismiss(BlockJob **jobptr, Error **errp) + + void block_job_user_pause(BlockJob *job, Error **errp) + { +- if (block_job_apply_verb(job, BLOCK_JOB_VERB_PAUSE, errp)) { ++ if (job_apply_verb(&job->job, JOB_VERB_PAUSE, errp)) { + return; + } + if (job->user_paused) { +@@ -768,7 +713,7 @@ void block_job_user_resume(BlockJob *job, Error **errp) + error_setg(errp, "Can't resume a job that was not paused"); + return; + } +- if (block_job_apply_verb(job, BLOCK_JOB_VERB_RESUME, errp)) { ++ if (job_apply_verb(&job->job, JOB_VERB_RESUME, errp)) { + return; + } + block_job_iostatus_reset(job); +@@ -778,7 +723,7 @@ void block_job_user_resume(BlockJob *job, Error **errp) + + void block_job_cancel(BlockJob *job, bool force) + { +- if (job->status == BLOCK_JOB_STATUS_CONCLUDED) { ++ if (job->job.status == JOB_STATUS_CONCLUDED) { + block_job_do_dismiss(job); + return; + } +@@ -794,7 +739,7 @@ void block_job_cancel(BlockJob *job, bool force) + + void block_job_user_cancel(BlockJob *job, bool force, Error **errp) + { +- if (block_job_apply_verb(job, BLOCK_JOB_VERB_CANCEL, errp)) { ++ if (job_apply_verb(&job->job, JOB_VERB_CANCEL, errp)) { + return; + } + block_job_cancel(job, force); +@@ -859,7 +804,7 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) + info->speed = job->speed; + info->io_status = job->iostatus; + info->ready = job->ready; +- info->status = job->status; ++ info->status = job->job.status; + info->auto_finalize = job->auto_finalize; + info->auto_dismiss = job->auto_dismiss; + info->has_error = job->ret != 0; +@@ -907,7 +852,7 @@ static void block_job_event_completed(BlockJob *job, const char *msg) + + static int block_job_event_pending(BlockJob *job) + { +- block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING); ++ job_state_transition(&job->job, JOB_STATUS_PENDING); + if (!job->auto_finalize && !block_job_is_internal(job)) { + qapi_event_send_block_job_pending(job_type(&job->job), + job->job.id, +@@ -975,7 +920,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + job->refcnt = 1; + job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE); + job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS); +- block_job_state_transition(job, BLOCK_JOB_STATUS_CREATED); + aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, + QEMU_CLOCK_REALTIME, SCALE_NS, + block_job_sleep_timer_cb, job); +@@ -1017,7 +961,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + + void block_job_early_fail(BlockJob *job) + { +- assert(job->status == BLOCK_JOB_STATUS_CREATED); ++ assert(job->job.status == JOB_STATUS_CREATED); + block_job_decommission(job); + } + +@@ -1077,14 +1021,14 @@ void coroutine_fn block_job_pause_point(BlockJob *job) + } + + if (block_job_should_pause(job) && !block_job_is_cancelled(job)) { +- BlockJobStatus status = job->status; +- block_job_state_transition(job, status == BLOCK_JOB_STATUS_READY ? \ +- BLOCK_JOB_STATUS_STANDBY : \ +- BLOCK_JOB_STATUS_PAUSED); ++ JobStatus status = job->job.status; ++ job_state_transition(&job->job, status == JOB_STATUS_READY ++ ? JOB_STATUS_STANDBY ++ : JOB_STATUS_PAUSED); + job->paused = true; + block_job_do_yield(job, -1); + job->paused = false; +- block_job_state_transition(job, status); ++ job_state_transition(&job->job, status); + } + + if (job->driver->resume) { +@@ -1176,7 +1120,7 @@ void block_job_iostatus_reset(BlockJob *job) + + void block_job_event_ready(BlockJob *job) + { +- block_job_state_transition(job, BLOCK_JOB_STATUS_READY); ++ job_state_transition(&job->job, JOB_STATUS_READY); + job->ready = true; + + if (block_job_is_internal(job)) { +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 10bd9f7..01cdee6 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -147,9 +147,6 @@ typedef struct BlockJob { + */ + QEMUTimer sleep_timer; + +- /** Current state; See @BlockJobStatus for details. */ +- BlockJobStatus status; +- + /** True if this job should automatically finalize itself */ + bool auto_finalize; + +diff --git a/include/qemu/job.h b/include/qemu/job.h +index bae2b09..0b78778 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -41,6 +41,9 @@ typedef struct Job { + /** The type of this job. */ + const JobDriver *driver; + ++ /** Current state; See @JobStatus for details. */ ++ JobStatus status; ++ + /** Element of the list of jobs */ + QLIST_ENTRY(Job) job_list; + } Job; +@@ -90,4 +93,14 @@ Job *job_next(Job *job); + */ + Job *job_get(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. ++ */ ++int job_apply_verb(Job *job, JobVerb verb, Error **errp); ++ ++/* TODO To be removed from the public interface */ ++void job_state_transition(Job *job, JobStatus s1); ++ + #endif +diff --git a/job.c b/job.c +index e57303c..b049a32 100644 +--- a/job.c ++++ b/job.c +@@ -28,9 +28,63 @@ + #include "qapi/error.h" + #include "qemu/job.h" + #include "qemu/id.h" ++#include "trace-root.h" + + 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, 0, 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}, ++}; ++ ++/* TODO Make static once the whole state machine is in job.c */ ++void job_state_transition(Job *job, JobStatus s1) ++{ ++ JobStatus s0 = job->status; ++ assert(s1 >= 0 && s1 <= JOB_STATUS__MAX); ++ trace_job_state_transition(job, /* TODO re-enable: job->ret */ 0, ++ JobSTT[s0][s1] ? "allowed" : "disallowed", ++ JobStatus_str(s0), JobStatus_str(s1)); ++ assert(JobSTT[s0][s1]); ++ job->status = s1; ++} ++ ++int job_apply_verb(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; +@@ -81,6 +135,8 @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp) + job->driver = driver; + job->id = g_strdup(job_id); + ++ job_state_transition(job, JOB_STATUS_CREATED); ++ + QLIST_INSERT_HEAD(&jobs, job, job_list); + + return job; +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 607e5c5..85bf353 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -1068,9 +1068,9 @@ + 'data': ['commit', 'stream', 'mirror', 'backup'] } + + ## +-# @BlockJobVerb: ++# @JobVerb: + # +-# Represents command verbs that can be applied to a blockjob. ++# Represents command verbs that can be applied to a job. + # + # @cancel: see @block-job-cancel + # +@@ -1088,14 +1088,14 @@ + # + # Since: 2.12 + ## +-{ 'enum': 'BlockJobVerb', ++{ 'enum': 'JobVerb', + 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss', + 'finalize' ] } + + ## +-# @BlockJobStatus: ++# @JobStatus: + # +-# Indicates the present state of a given blockjob in its lifetime. ++# Indicates the present state of a given job in its lifetime. + # + # @undefined: Erroneous, default state. Should not ever be visible. + # +@@ -1134,7 +1134,7 @@ + # + # Since: 2.12 + ## +-{ 'enum': 'BlockJobStatus', ++{ 'enum': 'JobStatus', + 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby', + 'waiting', 'pending', 'aborting', 'concluded', 'null' ] } + +@@ -1184,7 +1184,7 @@ + 'data': {'type': 'str', 'device': 'str', 'len': 'int', + 'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int', + 'io-status': 'BlockDeviceIoStatus', 'ready': 'bool', +- 'status': 'BlockJobStatus', ++ 'status': 'JobStatus', + 'auto-finalize': 'bool', 'auto-dismiss': 'bool', + '*error': 'str' } } + +@@ -2416,7 +2416,7 @@ + # QEMU 2.12+ job lifetime management semantics. + # + # This command will refuse to operate on any job that has not yet reached +-# its terminal state, BLOCK_JOB_STATUS_CONCLUDED. For jobs that make use of ++# its terminal state, JOB_STATUS_CONCLUDED. For jobs that make use of the + # BLOCK_JOB_READY event, block-job-cancel or block-job-complete will still need + # to be used as appropriate. + # +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index b820261..6ccd585 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -211,7 +211,7 @@ static CancelJob *create_common(BlockJob **pjob) + job = mk_job(blk, "Steve", &test_cancel_driver, true, + BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS); + block_job_ref(job); +- assert(job->status == BLOCK_JOB_STATUS_CREATED); ++ assert(job->job.status == JOB_STATUS_CREATED); + s = container_of(job, CancelJob, common); + s->blk = blk; + +@@ -223,15 +223,14 @@ static void cancel_common(CancelJob *s) + { + BlockJob *job = &s->common; + BlockBackend *blk = s->blk; +- BlockJobStatus sts = job->status; ++ JobStatus sts = job->job.status; + + block_job_cancel_sync(job); +- if ((sts != BLOCK_JOB_STATUS_CREATED) && +- (sts != BLOCK_JOB_STATUS_CONCLUDED)) { ++ if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) { + BlockJob *dummy = job; + block_job_dismiss(&dummy, &error_abort); + } +- assert(job->status == BLOCK_JOB_STATUS_NULL); ++ assert(job->job.status == JOB_STATUS_NULL); + block_job_unref(job); + destroy_blk(blk); + } +@@ -253,7 +252,7 @@ static void test_cancel_running(void) + s = create_common(&job); + + block_job_start(job); +- assert(job->status == BLOCK_JOB_STATUS_RUNNING); ++ assert(job->job.status == JOB_STATUS_RUNNING); + + cancel_common(s); + } +@@ -266,11 +265,11 @@ static void test_cancel_paused(void) + s = create_common(&job); + + block_job_start(job); +- assert(job->status == BLOCK_JOB_STATUS_RUNNING); ++ assert(job->job.status == JOB_STATUS_RUNNING); + + block_job_user_pause(job, &error_abort); + block_job_enter(job); +- assert(job->status == BLOCK_JOB_STATUS_PAUSED); ++ assert(job->job.status == JOB_STATUS_PAUSED); + + cancel_common(s); + } +@@ -283,11 +282,11 @@ static void test_cancel_ready(void) + s = create_common(&job); + + block_job_start(job); +- assert(job->status == BLOCK_JOB_STATUS_RUNNING); ++ assert(job->job.status == JOB_STATUS_RUNNING); + + s->should_converge = true; + block_job_enter(job); +- assert(job->status == BLOCK_JOB_STATUS_READY); ++ assert(job->job.status == JOB_STATUS_READY); + + cancel_common(s); + } +@@ -300,15 +299,15 @@ static void test_cancel_standby(void) + s = create_common(&job); + + block_job_start(job); +- assert(job->status == BLOCK_JOB_STATUS_RUNNING); ++ assert(job->job.status == JOB_STATUS_RUNNING); + + s->should_converge = true; + block_job_enter(job); +- assert(job->status == BLOCK_JOB_STATUS_READY); ++ assert(job->job.status == JOB_STATUS_READY); + + block_job_user_pause(job, &error_abort); + block_job_enter(job); +- assert(job->status == BLOCK_JOB_STATUS_STANDBY); ++ assert(job->job.status == JOB_STATUS_STANDBY); + + cancel_common(s); + } +@@ -321,18 +320,18 @@ static void test_cancel_pending(void) + s = create_common(&job); + + block_job_start(job); +- assert(job->status == BLOCK_JOB_STATUS_RUNNING); ++ assert(job->job.status == JOB_STATUS_RUNNING); + + s->should_converge = true; + block_job_enter(job); +- assert(job->status == BLOCK_JOB_STATUS_READY); ++ assert(job->job.status == JOB_STATUS_READY); + + block_job_complete(job, &error_abort); + block_job_enter(job); + while (!s->completed) { + aio_poll(qemu_get_aio_context(), true); + } +- assert(job->status == BLOCK_JOB_STATUS_PENDING); ++ assert(job->job.status == JOB_STATUS_PENDING); + + cancel_common(s); + } +@@ -345,21 +344,21 @@ static void test_cancel_concluded(void) + s = create_common(&job); + + block_job_start(job); +- assert(job->status == BLOCK_JOB_STATUS_RUNNING); ++ assert(job->job.status == JOB_STATUS_RUNNING); + + s->should_converge = true; + block_job_enter(job); +- assert(job->status == BLOCK_JOB_STATUS_READY); ++ assert(job->job.status == JOB_STATUS_READY); + + block_job_complete(job, &error_abort); + block_job_enter(job); + while (!s->completed) { + aio_poll(qemu_get_aio_context(), true); + } +- assert(job->status == BLOCK_JOB_STATUS_PENDING); ++ assert(job->job.status == JOB_STATUS_PENDING); + + block_job_finalize(job, &error_abort); +- assert(job->status == BLOCK_JOB_STATUS_CONCLUDED); ++ assert(job->job.status == JOB_STATUS_CONCLUDED); + + cancel_common(s); + } +diff --git a/trace-events b/trace-events +index ed71f44..2507e13 100644 +--- a/trace-events ++++ b/trace-events +@@ -104,6 +104,10 @@ gdbstub_err_invalid_rle(void) "got invalid RLE sequence" + gdbstub_err_checksum_invalid(uint8_t ch) "got invalid command checksum digit: 0x%02x" + gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packet with incorrect checksum, expected=0x%02x, received=0x%02x" + ++# job.c ++job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)" ++job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)" ++ + ### Guest events, keep at bottom + + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Move-transactions-to-Job.patch b/SOURCES/kvm-job-Move-transactions-to-Job.patch new file mode 100644 index 0000000..0745c01 --- /dev/null +++ b/SOURCES/kvm-job-Move-transactions-to-Job.patch @@ -0,0 +1,995 @@ +From 870c205da77467ee664de7f74ba9c47b6b8e528b Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:22 +0200 +Subject: [PATCH 53/89] job: Move transactions to Job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-40-kwolf@redhat.com> +Patchwork-id: 81097 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 39/73] job: Move transactions to Job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This moves the logic that implements job transactions from BlockJob to +Job. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit 7eaa8fb57da96301f4a8ce176ad503f80efc7cc0) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 6 +- + blockjob.c | 238 +------------------------------------------ + include/block/blockjob.h | 54 ---------- + include/block/blockjob_int.h | 10 -- + include/qemu/job.h | 71 +++++++++++-- + job.c | 234 ++++++++++++++++++++++++++++++++++++++++-- + tests/test-blockjob-txn.c | 12 +-- + tests/test-blockjob.c | 2 +- + 8 files changed, 303 insertions(+), 324 deletions(-) + +diff --git a/blockdev.c b/blockdev.c +index b6f3e92..422b5ac 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -2297,7 +2297,7 @@ void qmp_transaction(TransactionActionList *dev_list, + */ + props = get_transaction_properties(props); + if (props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) { +- block_job_txn = block_job_txn_new(); ++ block_job_txn = job_txn_new(); + } + + /* drain all i/o before any operations */ +@@ -2356,7 +2356,7 @@ exit: + if (!has_props) { + qapi_free_TransactionProperties(props); + } +- block_job_txn_unref(block_job_txn); ++ job_txn_unref(block_job_txn); + } + + void qmp_eject(bool has_device, const char *device, +@@ -3955,7 +3955,7 @@ void qmp_block_job_finalize(const char *id, Error **errp) + } + + trace_qmp_block_job_finalize(job); +- block_job_finalize(job, errp); ++ job_finalize(&job->job, errp); + aio_context_release(aio_context); + } + +diff --git a/blockjob.c b/blockjob.c +index bd35c4f..14b21c8 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -36,19 +36,6 @@ + #include "qemu/coroutine.h" + #include "qemu/timer.h" + +-/* Transactional group of block jobs */ +-struct JobTxn { +- +- /* Is this txn being cancelled? */ +- bool aborting; +- +- /* List of jobs */ +- QLIST_HEAD(, Job) jobs; +- +- /* Reference count */ +- int refcnt; +-}; +- + /* + * The block job API is composed of two categories of functions. + * +@@ -94,48 +81,6 @@ BlockJob *block_job_get(const char *id) + } + } + +-JobTxn *block_job_txn_new(void) +-{ +- JobTxn *txn = g_new0(JobTxn, 1); +- QLIST_INIT(&txn->jobs); +- txn->refcnt = 1; +- return txn; +-} +- +-static void block_job_txn_ref(JobTxn *txn) +-{ +- txn->refcnt++; +-} +- +-void block_job_txn_unref(JobTxn *txn) +-{ +- if (txn && --txn->refcnt == 0) { +- g_free(txn); +- } +-} +- +-void block_job_txn_add_job(JobTxn *txn, BlockJob *job) +-{ +- if (!txn) { +- return; +- } +- +- assert(!job->txn); +- job->txn = txn; +- +- QLIST_INSERT_HEAD(&txn->jobs, &job->job, txn_list); +- block_job_txn_ref(txn); +-} +- +-void block_job_txn_del_job(BlockJob *job) +-{ +- if (job->txn) { +- QLIST_REMOVE(&job->job, txn_list); +- block_job_txn_unref(job->txn); +- job->txn = NULL; +- } +-} +- + static void block_job_attached_aio_context(AioContext *new_context, + void *opaque); + static void block_job_detach_aio_context(void *opaque); +@@ -145,8 +90,6 @@ void block_job_free(Job *job) + BlockJob *bjob = container_of(job, BlockJob, job); + BlockDriverState *bs = blk_bs(bjob->blk); + +- assert(!bjob->txn); +- + bs->job = NULL; + block_job_remove_all_bdrv(bjob); + blk_remove_aio_context_notifier(bjob->blk, +@@ -261,158 +204,6 @@ const BlockJobDriver *block_job_driver(BlockJob *job) + return job->driver; + } + +-static int block_job_prepare(BlockJob *job) +-{ +- if (job->job.ret == 0 && job->driver->prepare) { +- job->job.ret = job->driver->prepare(job); +- } +- return job->job.ret; +-} +- +-static void job_cancel_async(Job *job, bool force) +-{ +- if (job->user_paused) { +- /* Do not call job_enter here, the caller will handle it. */ +- job->user_paused = false; +- if (job->driver->user_resume) { +- job->driver->user_resume(job); +- } +- assert(job->pause_count > 0); +- job->pause_count--; +- } +- job->cancelled = true; +- /* To prevent 'force == false' overriding a previous 'force == true' */ +- job->force_cancel |= force; +-} +- +-static int block_job_txn_apply(JobTxn *txn, int fn(BlockJob *), bool lock) +-{ +- AioContext *ctx; +- Job *job, *next; +- BlockJob *bjob; +- int rc = 0; +- +- QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) { +- assert(is_block_job(job)); +- bjob = container_of(job, BlockJob, job); +- +- if (lock) { +- ctx = job->aio_context; +- aio_context_acquire(ctx); +- } +- rc = fn(bjob); +- if (lock) { +- aio_context_release(ctx); +- } +- if (rc) { +- break; +- } +- } +- return rc; +-} +- +-static void block_job_completed_txn_abort(BlockJob *job) +-{ +- AioContext *ctx; +- JobTxn *txn = job->txn; +- Job *other_job; +- +- if (txn->aborting) { +- /* +- * We are cancelled by another job, which will handle everything. +- */ +- return; +- } +- txn->aborting = true; +- block_job_txn_ref(txn); +- +- /* We are the first failed job. Cancel other jobs. */ +- QLIST_FOREACH(other_job, &txn->jobs, txn_list) { +- ctx = other_job->aio_context; +- aio_context_acquire(ctx); +- } +- +- /* 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->job) { +- job_cancel_async(other_job, false); +- } +- } +- while (!QLIST_EMPTY(&txn->jobs)) { +- other_job = QLIST_FIRST(&txn->jobs); +- ctx = other_job->aio_context; +- if (!job_is_completed(other_job)) { +- assert(job_is_cancelled(other_job)); +- job_finish_sync(other_job, NULL, NULL); +- } +- job_finalize_single(other_job); +- aio_context_release(ctx); +- } +- +- block_job_txn_unref(txn); +-} +- +-static int block_job_needs_finalize(BlockJob *job) +-{ +- return !job->job.auto_finalize; +-} +- +-static int block_job_finalize_single(BlockJob *job) +-{ +- return job_finalize_single(&job->job); +-} +- +-static void block_job_do_finalize(BlockJob *job) +-{ +- int rc; +- assert(job && job->txn); +- +- /* prepare the transaction to complete */ +- rc = block_job_txn_apply(job->txn, block_job_prepare, true); +- if (rc) { +- block_job_completed_txn_abort(job); +- } else { +- block_job_txn_apply(job->txn, block_job_finalize_single, true); +- } +-} +- +-static int block_job_transition_to_pending(BlockJob *job) +-{ +- job_state_transition(&job->job, JOB_STATUS_PENDING); +- if (!job->job.auto_finalize) { +- job_event_pending(&job->job); +- } +- return 0; +-} +- +-static void block_job_completed_txn_success(BlockJob *job) +-{ +- JobTxn *txn = job->txn; +- Job *other_job; +- +- job_state_transition(&job->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(other_job)) { +- return; +- } +- assert(other_job->ret == 0); +- } +- +- block_job_txn_apply(txn, block_job_transition_to_pending, false); +- +- /* If no jobs need manual finalization, automatically do so */ +- if (block_job_txn_apply(txn, block_job_needs_finalize, false) == 0) { +- block_job_do_finalize(job); +- } +-} +- + /* Assumes the job_mutex is held */ + static bool job_timer_pending(Job *job) + { +@@ -451,15 +242,6 @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) + return ratelimit_calculate_delay(&job->limit, n); + } + +-void block_job_finalize(BlockJob *job, Error **errp) +-{ +- assert(job && job->job.id); +- if (job_apply_verb(&job->job, JOB_VERB_FINALIZE, errp)) { +- return; +- } +- block_job_do_finalize(job); +-} +- + void block_job_dismiss(BlockJob **jobptr, Error **errp) + { + BlockJob *job = *jobptr; +@@ -483,7 +265,7 @@ void block_job_cancel(BlockJob *job, bool force) + if (!job_started(&job->job)) { + block_job_completed(job, -ECANCELED); + } else if (job->job.deferred_to_main_loop) { +- block_job_completed_txn_abort(job); ++ job_completed_txn_abort(&job->job); + } else { + block_job_enter(job); + } +@@ -656,7 +438,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + return NULL; + } + +- job = job_create(job_id, &driver->job_driver, blk_get_aio_context(blk), ++ job = job_create(job_id, &driver->job_driver, txn, blk_get_aio_context(blk), + flags, cb, opaque, errp); + if (job == NULL) { + blk_unref(blk); +@@ -703,30 +485,20 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + } + } + +- /* Single jobs are modeled as single-job transactions for sake of +- * consolidating the job management logic */ +- if (!txn) { +- txn = block_job_txn_new(); +- block_job_txn_add_job(txn, job); +- block_job_txn_unref(txn); +- } else { +- block_job_txn_add_job(txn, job); +- } +- + return job; + } + + void block_job_completed(BlockJob *job, int ret) + { +- assert(job && job->txn && !job_is_completed(&job->job)); ++ assert(job && job->job.txn && !job_is_completed(&job->job)); + assert(blk_bs(job->blk)->job == job); + job->job.ret = ret; + job_update_rc(&job->job); + trace_block_job_completed(job, ret, job->job.ret); + if (job->job.ret) { +- block_job_completed_txn_abort(job); ++ job_completed_txn_abort(&job->job); + } else { +- block_job_completed_txn_success(job); ++ job_completed_txn_success(&job->job); + } + } + +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 44df025..09e6bb4 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -33,7 +33,6 @@ + #define BLOCK_JOB_SLICE_TIME 100000000ULL /* ns */ + + typedef struct BlockJobDriver BlockJobDriver; +-typedef struct JobTxn JobTxn; + + /** + * BlockJob: +@@ -84,8 +83,6 @@ typedef struct BlockJob { + + /** BlockDriverStates that are involved in this block job */ + GSList *nodes; +- +- JobTxn *txn; + } BlockJob; + + /** +@@ -153,22 +150,6 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp); + void block_job_cancel(BlockJob *job, bool force); + + /** +- * block_job_finalize: +- * @job: The job to fully commit and finish. +- * @errp: Error object. +- * +- * For jobs that have finished their work and are pending +- * awaiting explicit acknowledgement to commit their 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. +- */ +-void block_job_finalize(BlockJob *job, Error **errp); +- +-/** + * block_job_dismiss: + * @job: The job to be dismissed. + * @errp: Error object. +@@ -260,41 +241,6 @@ int block_job_complete_sync(BlockJob *job, Error **errp); + void block_job_iostatus_reset(BlockJob *job); + + /** +- * block_job_txn_new: +- * +- * Allocate and return a new block job transaction. Jobs can be added to the +- * transaction using block_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 *block_job_txn_new(void); +- +-/** +- * block_job_txn_unref: +- * +- * Release a reference that was previously acquired with block_job_txn_add_job +- * or block_job_txn_new. If it's the last reference to the object, it will be +- * freed. +- */ +-void block_job_txn_unref(JobTxn *txn); +- +-/** +- * block_job_txn_add_job: +- * @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 block_job_txn_unref() or block_job_completed() +- * to release the reference that is automatically grabbed here. +- */ +-void block_job_txn_add_job(JobTxn *txn, BlockJob *job); +- +-/** + * block_job_is_internal: + * @job: The job to determine if it is user-visible or not. + * +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index ce66a9b..29a2802 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -38,16 +38,6 @@ struct BlockJobDriver { + /** Generic JobDriver callbacks and settings */ + JobDriver job_driver; + +- /** +- * 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)(BlockJob *job); +- + /* + * 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 +diff --git a/include/qemu/job.h b/include/qemu/job.h +index d4aa7fa..39d74ab 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -32,6 +32,8 @@ + #include "block/aio.h" + + typedef struct JobDriver JobDriver; ++typedef struct JobTxn JobTxn; ++ + + /** + * Long-running operation. +@@ -133,6 +135,9 @@ typedef struct Job { + /** 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; +@@ -184,6 +189,16 @@ struct JobDriver { + void (*drain)(Job *job); + + /** ++ * 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. +@@ -227,20 +242,52 @@ typedef enum JobCreateFlags { + JOB_MANUAL_DISMISS = 0x04, + } JobCreateFlags; + ++/** ++ * 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. ++ */ ++void job_txn_unref(JobTxn *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 block_job_completed() to ++ * release the reference that is automatically grabbed here. ++ * ++ * If @txn is NULL, the function does nothing. ++ */ ++void job_txn_add_job(JobTxn *txn, Job *job); + + /** + * Create a new long-running job and return it. + * + * @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, AioContext *ctx, +- int flags, BlockCompletionFunc *cb, void *opaque, Error **errp); ++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 +@@ -260,9 +307,6 @@ void job_event_cancelled(Job *job); + /** To be called when a successfully completed job is finalised. */ + void job_event_completed(Job *job); + +-/** To be called when the job transitions to PENDING */ +-void job_event_pending(Job *job); +- + /** + * 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 +@@ -375,6 +419,16 @@ void job_early_fail(Job *job); + /** Asynchronously complete the specified @job. */ + void job_complete(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. ++ */ ++void job_finalize(Job *job, Error **errp); ++ + typedef void JobDeferToMainLoopFn(Job *job, void *opaque); + + /** +@@ -407,10 +461,9 @@ void coroutine_fn job_do_yield(Job *job, uint64_t ns); + bool job_should_pause(Job *job); + bool job_started(Job *job); + void job_do_dismiss(Job *job); +-int job_finalize_single(Job *job); + void job_update_rc(Job *job); +- +-typedef struct BlockJob BlockJob; +-void block_job_txn_del_job(BlockJob *job); ++void job_cancel_async(Job *job, bool force); ++void job_completed_txn_abort(Job *job); ++void job_completed_txn_success(Job *job); + + #endif +diff --git a/job.c b/job.c +index aa74b4c..4f6fd73 100644 +--- a/job.c ++++ b/job.c +@@ -60,6 +60,19 @@ bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = { + [JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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; ++}; ++ + /* Right now, this mutex is only needed to synchronize accesses to job->busy + * and job->sleep_timer, such as concurrent calls to job_do_yield and + * job_enter. */ +@@ -80,6 +93,71 @@ 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; ++} ++ ++static void job_txn_ref(JobTxn *txn) ++{ ++ txn->refcnt++; ++} ++ ++void job_txn_unref(JobTxn *txn) ++{ ++ if (txn && --txn->refcnt == 0) { ++ g_free(txn); ++ } ++} ++ ++void job_txn_add_job(JobTxn *txn, Job *job) ++{ ++ if (!txn) { ++ return; ++ } ++ ++ assert(!job->txn); ++ job->txn = txn; ++ ++ QLIST_INSERT_HEAD(&txn->jobs, job, txn_list); ++ job_txn_ref(txn); ++} ++ ++static void job_txn_del_job(Job *job) ++{ ++ if (job->txn) { ++ QLIST_REMOVE(job, txn_list); ++ job_txn_unref(job->txn); ++ job->txn = NULL; ++ } ++} ++ ++static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock) ++{ ++ AioContext *ctx; ++ Job *job, *next; ++ int rc = 0; ++ ++ QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) { ++ if (lock) { ++ ctx = job->aio_context; ++ aio_context_acquire(ctx); ++ } ++ rc = fn(job); ++ if (lock) { ++ aio_context_release(ctx); ++ } ++ if (rc) { ++ break; ++ } ++ } ++ return rc; ++} ++ ++ + /* TODO Make static once the whole state machine is in job.c */ + void job_state_transition(Job *job, JobStatus s1) + { +@@ -181,8 +259,9 @@ static void job_sleep_timer_cb(void *opaque) + job_enter(job); + } + +-void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, +- int flags, BlockCompletionFunc *cb, void *opaque, Error **errp) ++void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, ++ AioContext *ctx, int flags, BlockCompletionFunc *cb, ++ void *opaque, Error **errp) + { + Job *job; + +@@ -228,6 +307,16 @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, + + 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(txn, job); ++ job_txn_unref(txn); ++ } else { ++ job_txn_add_job(txn, job); ++ } ++ + return job; + } + +@@ -241,6 +330,7 @@ void job_unref(Job *job) + if (--job->refcnt == 0) { + assert(job->status == JOB_STATUS_NULL); + assert(!timer_pending(&job->sleep_timer)); ++ assert(!job->txn); + + if (job->driver->free) { + job->driver->free(job); +@@ -263,7 +353,7 @@ void job_event_completed(Job *job) + notifier_list_notify(&job->on_finalize_completed, job); + } + +-void job_event_pending(Job *job) ++static void job_event_pending(Job *job) + { + notifier_list_notify(&job->on_pending, job); + } +@@ -469,8 +559,7 @@ void job_do_dismiss(Job *job) + job->paused = false; + job->deferred_to_main_loop = true; + +- /* TODO Don't assume it's a BlockJob */ +- block_job_txn_del_job((BlockJob*) job); ++ job_txn_del_job(job); + + job_state_transition(job, JOB_STATUS_NULL); + job_unref(job); +@@ -523,7 +612,7 @@ static void job_clean(Job *job) + } + } + +-int job_finalize_single(Job *job) ++static int job_finalize_single(Job *job) + { + assert(job_is_completed(job)); + +@@ -550,12 +639,141 @@ int job_finalize_single(Job *job) + } + } + +- /* TODO Don't assume it's a BlockJob */ +- block_job_txn_del_job((BlockJob*) job); ++ job_txn_del_job(job); + job_conclude(job); + return 0; + } + ++void job_cancel_async(Job *job, bool force) ++{ ++ if (job->user_paused) { ++ /* Do not call job_enter here, the caller will handle it. */ ++ job->user_paused = false; ++ if (job->driver->user_resume) { ++ job->driver->user_resume(job); ++ } ++ assert(job->pause_count > 0); ++ job->pause_count--; ++ } ++ job->cancelled = true; ++ /* To prevent 'force == false' overriding a previous 'force == true' */ ++ job->force_cancel |= force; ++} ++ ++void job_completed_txn_abort(Job *job) ++{ ++ AioContext *ctx; ++ 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(txn); ++ ++ /* We are the first failed job. Cancel other jobs. */ ++ QLIST_FOREACH(other_job, &txn->jobs, txn_list) { ++ ctx = other_job->aio_context; ++ aio_context_acquire(ctx); ++ } ++ ++ /* 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) { ++ job_cancel_async(other_job, false); ++ } ++ } ++ while (!QLIST_EMPTY(&txn->jobs)) { ++ other_job = QLIST_FIRST(&txn->jobs); ++ ctx = other_job->aio_context; ++ if (!job_is_completed(other_job)) { ++ assert(job_is_cancelled(other_job)); ++ job_finish_sync(other_job, NULL, NULL); ++ } ++ job_finalize_single(other_job); ++ aio_context_release(ctx); ++ } ++ ++ job_txn_unref(txn); ++} ++ ++static int job_prepare(Job *job) ++{ ++ if (job->ret == 0 && job->driver->prepare) { ++ job->ret = job->driver->prepare(job); ++ } ++ return job->ret; ++} ++ ++static int job_needs_finalize(Job *job) ++{ ++ return !job->auto_finalize; ++} ++ ++static void job_do_finalize(Job *job) ++{ ++ int rc; ++ assert(job && job->txn); ++ ++ /* prepare the transaction to complete */ ++ rc = job_txn_apply(job->txn, job_prepare, true); ++ if (rc) { ++ job_completed_txn_abort(job); ++ } else { ++ job_txn_apply(job->txn, job_finalize_single, true); ++ } ++} ++ ++void job_finalize(Job *job, Error **errp) ++{ ++ assert(job && job->id); ++ if (job_apply_verb(job, JOB_VERB_FINALIZE, errp)) { ++ return; ++ } ++ job_do_finalize(job); ++} ++ ++static int job_transition_to_pending(Job *job) ++{ ++ job_state_transition(job, JOB_STATUS_PENDING); ++ if (!job->auto_finalize) { ++ job_event_pending(job); ++ } ++ return 0; ++} ++ ++void job_completed_txn_success(Job *job) ++{ ++ JobTxn *txn = job->txn; ++ Job *other_job; ++ ++ job_state_transition(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(other_job)) { ++ return; ++ } ++ assert(other_job->ret == 0); ++ } ++ ++ job_txn_apply(txn, job_transition_to_pending, false); ++ ++ /* If no jobs need manual finalization, automatically do so */ ++ if (job_txn_apply(txn, job_needs_finalize, false) == 0) { ++ job_do_finalize(job); ++ } ++} ++ + void job_complete(Job *job, Error **errp) + { + /* Should not be reachable via external interface for internal jobs */ +diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c +index ec5d592..6ee31d5 100644 +--- a/tests/test-blockjob-txn.c ++++ b/tests/test-blockjob-txn.c +@@ -125,7 +125,7 @@ static void test_single_job(int expected) + JobTxn *txn; + int result = -EINPROGRESS; + +- txn = block_job_txn_new(); ++ txn = job_txn_new(); + job = test_block_job_start(1, true, expected, &result, txn); + job_start(&job->job); + +@@ -138,7 +138,7 @@ static void test_single_job(int expected) + } + g_assert_cmpint(result, ==, expected); + +- block_job_txn_unref(txn); ++ job_txn_unref(txn); + } + + static void test_single_job_success(void) +@@ -164,7 +164,7 @@ static void test_pair_jobs(int expected1, int expected2) + int result1 = -EINPROGRESS; + int result2 = -EINPROGRESS; + +- txn = block_job_txn_new(); ++ txn = job_txn_new(); + job1 = test_block_job_start(1, true, expected1, &result1, txn); + job2 = test_block_job_start(2, true, expected2, &result2, txn); + job_start(&job1->job); +@@ -173,7 +173,7 @@ static void test_pair_jobs(int expected1, int expected2) + /* Release our reference now to trigger as many nice + * use-after-free bugs as possible. + */ +- block_job_txn_unref(txn); ++ job_txn_unref(txn); + + if (expected1 == -ECANCELED) { + block_job_cancel(job1, false); +@@ -226,7 +226,7 @@ static void test_pair_jobs_fail_cancel_race(void) + int result1 = -EINPROGRESS; + int result2 = -EINPROGRESS; + +- txn = block_job_txn_new(); ++ txn = job_txn_new(); + job1 = test_block_job_start(1, true, -ECANCELED, &result1, txn); + job2 = test_block_job_start(2, false, 0, &result2, txn); + job_start(&job1->job); +@@ -247,7 +247,7 @@ static void test_pair_jobs_fail_cancel_race(void) + g_assert_cmpint(result1, ==, -ECANCELED); + g_assert_cmpint(result2, ==, -ECANCELED); + +- block_job_txn_unref(txn); ++ job_txn_unref(txn); + } + + int main(int argc, char **argv) +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index e44c608..1e052c2 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -364,7 +364,7 @@ static void test_cancel_concluded(void) + } + assert(job->job.status == JOB_STATUS_PENDING); + +- block_job_finalize(job, &error_abort); ++ job_finalize(&job->job, &error_abort); + assert(job->job.status == JOB_STATUS_CONCLUDED); + + cancel_common(s); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Rename-BlockJobType-into-JobType.patch b/SOURCES/kvm-job-Rename-BlockJobType-into-JobType.patch new file mode 100644 index 0000000..5085146 --- /dev/null +++ b/SOURCES/kvm-job-Rename-BlockJobType-into-JobType.patch @@ -0,0 +1,204 @@ +From 5ca59831fc7c51ec7f0c002bced4a41d088a0fb2 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:00 +0200 +Subject: [PATCH 31/89] job: Rename BlockJobType into JobType + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-18-kwolf@redhat.com> +Patchwork-id: 81112 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 17/73] job: Rename BlockJobType into JobType +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +QAPI types aren't externally visible, so we can rename them without +causing problems. Before we add a job type to Job, rename the enum +so it can be used for more than just block jobs. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +Reviewed-by: Max Reitz +Reviewed-by: John Snow +(cherry picked from commit 8e4c87000fc515f8f65f7c8f18afb1e9270b11d6) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 2 +- + block/commit.c | 2 +- + block/mirror.c | 4 ++-- + block/stream.c | 2 +- + blockjob.c | 6 +++--- + include/block/blockjob_int.h | 2 +- + qapi/block-core.json | 14 +++++++------- + 7 files changed, 16 insertions(+), 16 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index 9e672bb..c49ea92 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -526,7 +526,7 @@ static const BlockJobDriver backup_job_driver = { + .job_driver = { + .instance_size = sizeof(BackupBlockJob), + }, +- .job_type = BLOCK_JOB_TYPE_BACKUP, ++ .job_type = JOB_TYPE_BACKUP, + .start = backup_run, + .commit = backup_commit, + .abort = backup_abort, +diff --git a/block/commit.c b/block/commit.c +index 18cbb2f..afa2b2b 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -218,7 +218,7 @@ static const BlockJobDriver commit_job_driver = { + .job_driver = { + .instance_size = sizeof(CommitBlockJob), + }, +- .job_type = BLOCK_JOB_TYPE_COMMIT, ++ .job_type = JOB_TYPE_COMMIT, + .start = commit_run, + }; + +diff --git a/block/mirror.c b/block/mirror.c +index aa1d6b7..ed72656 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -989,7 +989,7 @@ static const BlockJobDriver mirror_job_driver = { + .job_driver = { + .instance_size = sizeof(MirrorBlockJob), + }, +- .job_type = BLOCK_JOB_TYPE_MIRROR, ++ .job_type = JOB_TYPE_MIRROR, + .start = mirror_run, + .complete = mirror_complete, + .pause = mirror_pause, +@@ -1001,7 +1001,7 @@ static const BlockJobDriver commit_active_job_driver = { + .job_driver = { + .instance_size = sizeof(MirrorBlockJob), + }, +- .job_type = BLOCK_JOB_TYPE_COMMIT, ++ .job_type = JOB_TYPE_COMMIT, + .start = mirror_run, + .complete = mirror_complete, + .pause = mirror_pause, +diff --git a/block/stream.c b/block/stream.c +index f88fc75..048bceb 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -212,7 +212,7 @@ static const BlockJobDriver stream_job_driver = { + .job_driver = { + .instance_size = sizeof(StreamBlockJob), + }, +- .job_type = BLOCK_JOB_TYPE_STREAM, ++ .job_type = JOB_TYPE_STREAM, + .start = stream_run, + }; + +diff --git a/blockjob.c b/blockjob.c +index 1464856..2a38447 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -310,7 +310,7 @@ static char *child_job_get_parent_desc(BdrvChild *c) + { + BlockJob *job = c->opaque; + return g_strdup_printf("%s job '%s'", +- BlockJobType_str(job->driver->job_type), ++ JobType_str(job->driver->job_type), + job->job.id); + } + +@@ -847,7 +847,7 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) + return NULL; + } + info = g_new0(BlockJobInfo, 1); +- info->type = g_strdup(BlockJobType_str(job->driver->job_type)); ++ info->type = g_strdup(JobType_str(job->driver->job_type)); + info->device = g_strdup(job->job.id); + info->len = job->len; + info->busy = atomic_read(&job->busy); +@@ -980,7 +980,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, + block_job_sleep_timer_cb, job); + + error_setg(&job->blocker, "block device is in use by block job: %s", +- BlockJobType_str(driver->job_type)); ++ JobType_str(driver->job_type)); + block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort); + bs->job = job; + +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index e8eca44..8e7e0a2 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -39,7 +39,7 @@ struct BlockJobDriver { + JobDriver job_driver; + + /** String describing the operation, part of query-block-jobs QMP API */ +- BlockJobType job_type; ++ JobType job_type; + + /** Mandatory: Entrypoint for the Coroutine. */ + CoroutineEntry *start; +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 1f6d4bb..607e5c5 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -1050,9 +1050,9 @@ + 'data': ['top', 'full', 'none', 'incremental'] } + + ## +-# @BlockJobType: ++# @JobType: + # +-# Type of a block job. ++# Type of a background job. + # + # @commit: block commit job type, see "block-commit" + # +@@ -1064,7 +1064,7 @@ + # + # Since: 1.7 + ## +-{ 'enum': 'BlockJobType', ++{ 'enum': 'JobType', + 'data': ['commit', 'stream', 'mirror', 'backup'] } + + ## +@@ -4529,7 +4529,7 @@ + # + ## + { 'event': 'BLOCK_JOB_COMPLETED', +- 'data': { 'type' : 'BlockJobType', ++ 'data': { 'type' : 'JobType', + 'device': 'str', + 'len' : 'int', + 'offset': 'int', +@@ -4565,7 +4565,7 @@ + # + ## + { 'event': 'BLOCK_JOB_CANCELLED', +- 'data': { 'type' : 'BlockJobType', ++ 'data': { 'type' : 'JobType', + 'device': 'str', + 'len' : 'int', + 'offset': 'int', +@@ -4630,7 +4630,7 @@ + # + ## + { 'event': 'BLOCK_JOB_READY', +- 'data': { 'type' : 'BlockJobType', ++ 'data': { 'type' : 'JobType', + 'device': 'str', + 'len' : 'int', + 'offset': 'int', +@@ -4657,7 +4657,7 @@ + # + ## + { 'event': 'BLOCK_JOB_PENDING', +- 'data': { 'type' : 'BlockJobType', ++ 'data': { 'type' : 'JobType', + 'id' : 'str' } } + + ## +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Replace-BlockJob.completed-with-job_is_completed.patch b/SOURCES/kvm-job-Replace-BlockJob.completed-with-job_is_completed.patch new file mode 100644 index 0000000..6f78849 --- /dev/null +++ b/SOURCES/kvm-job-Replace-BlockJob.completed-with-job_is_completed.patch @@ -0,0 +1,185 @@ +From bafcb1e3ffe5b9af344a7891f6a62eb296687a43 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:12 +0200 +Subject: [PATCH 43/89] job: Replace BlockJob.completed with job_is_completed() + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-30-kwolf@redhat.com> +Patchwork-id: 81094 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 29/73] job: Replace BlockJob.completed with job_is_completed() +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Since we introduced an explicit status to block job, BlockJob.completed +is redundant because it can be derived from the status. Remove the field +from BlockJob and add a function to derive it from the status at the Job +level. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: John Snow +(cherry picked from commit dbe5e6c1f73b41282624b78a2375a5c3ee59e905) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + blockjob.c | 16 +++++++--------- + include/block/blockjob.h | 3 --- + include/qemu/job.h | 3 +++ + job.c | 22 ++++++++++++++++++++++ + qemu-img.c | 4 ++-- + 5 files changed, 34 insertions(+), 14 deletions(-) + +diff --git a/blockjob.c b/blockjob.c +index 6334a54..a1d1f48 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -193,7 +193,7 @@ static void block_job_detach_aio_context(void *opaque) + + job_pause(&job->job); + +- while (!job->job.paused && !job->completed) { ++ while (!job->job.paused && !job_is_completed(&job->job)) { + block_job_drain(job); + } + +@@ -269,7 +269,6 @@ const BlockJobDriver *block_job_driver(BlockJob *job) + static void block_job_decommission(BlockJob *job) + { + assert(job); +- job->completed = true; + job->job.busy = false; + job->job.paused = false; + job->job.deferred_to_main_loop = true; +@@ -334,7 +333,7 @@ static void block_job_clean(BlockJob *job) + + static int block_job_finalize_single(BlockJob *job) + { +- assert(job->completed); ++ assert(job_is_completed(&job->job)); + + /* Ensure abort is called for late-transactional failures */ + block_job_update_rc(job); +@@ -427,10 +426,10 @@ static int block_job_finish_sync(BlockJob *job, + /* block_job_drain calls block_job_enter, and it should be enough to + * induce progress until the job completes or moves to the main thread. + */ +- while (!job->job.deferred_to_main_loop && !job->completed) { ++ while (!job->job.deferred_to_main_loop && !job_is_completed(&job->job)) { + block_job_drain(job); + } +- while (!job->completed) { ++ while (!job_is_completed(&job->job)) { + aio_poll(qemu_get_aio_context(), true); + } + ret = (job_is_cancelled(&job->job) && job->ret == 0) +@@ -471,7 +470,7 @@ static void block_job_completed_txn_abort(BlockJob *job) + while (!QLIST_EMPTY(&txn->jobs)) { + other_job = QLIST_FIRST(&txn->jobs); + ctx = blk_get_aio_context(other_job->blk); +- if (!other_job->completed) { ++ if (!job_is_completed(&other_job->job)) { + assert(job_is_cancelled(&other_job->job)); + block_job_finish_sync(other_job, NULL, NULL); + } +@@ -513,7 +512,7 @@ static void block_job_completed_txn_success(BlockJob *job) + * txn. + */ + QLIST_FOREACH(other_job, &txn->jobs, txn_list) { +- if (!other_job->completed) { ++ if (!job_is_completed(&other_job->job)) { + return; + } + assert(other_job->ret == 0); +@@ -847,9 +846,8 @@ void block_job_early_fail(BlockJob *job) + + void block_job_completed(BlockJob *job, int ret) + { +- assert(job && job->txn && !job->completed); ++ assert(job && job->txn && !job_is_completed(&job->job)); + assert(blk_bs(job->blk)->job == job); +- job->completed = true; + job->ret = ret; + block_job_update_rc(job); + trace_block_job_completed(job, ret, job->ret); +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 556a8f6..3e94e18 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -88,9 +88,6 @@ typedef struct BlockJob { + /** The opaque value that is passed to the completion function. */ + void *opaque; + +- /** True when job has reported completion by calling block_job_completed. */ +- bool completed; +- + /** ret code passed to block_job_completed. */ + int ret; + +diff --git a/include/qemu/job.h b/include/qemu/job.h +index bc63985..858f3be 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -214,6 +214,9 @@ const char *job_type_str(const Job *job); + /** Returns whether the job is scheduled for cancellation. */ + bool job_is_cancelled(Job *job); + ++/** Returns whether the job is in a completed state. */ ++bool job_is_completed(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 +diff --git a/job.c b/job.c +index fd10b1d..aaacfcc 100644 +--- a/job.c ++++ b/job.c +@@ -121,6 +121,28 @@ bool job_is_cancelled(Job *job) + return job->cancelled; + } + ++bool job_is_completed(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; ++} ++ + bool job_started(Job *job) + { + return job->co; +diff --git a/qemu-img.c b/qemu-img.c +index f745919..f5bb0ef 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -878,9 +878,9 @@ static void run_block_job(BlockJob *job, Error **errp) + aio_poll(aio_context, true); + qemu_progress_print(job->len ? + ((float)job->offset / job->len * 100.f) : 0.0f, 0); +- } while (!job->ready && !job->completed); ++ } while (!job->ready && !job_is_completed(&job->job)); + +- if (!job->completed) { ++ if (!job_is_completed(&job->job)) { + ret = block_job_complete_sync(job, errp); + } else { + ret = job->ret; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Switch-transactions-to-JobTxn.patch b/SOURCES/kvm-job-Switch-transactions-to-JobTxn.patch new file mode 100644 index 0000000..3f5ebdc --- /dev/null +++ b/SOURCES/kvm-job-Switch-transactions-to-JobTxn.patch @@ -0,0 +1,423 @@ +From cf8f50fd39b467c3f23f78e7bec351e07b826344 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:21 +0200 +Subject: [PATCH 52/89] job: Switch transactions to JobTxn + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-39-kwolf@redhat.com> +Patchwork-id: 81086 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 38/73] job: Switch transactions to JobTxn +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This doesn't actually move any transaction code to Job yet, but it +renames the type for transactions from BlockJobTxn to JobTxn and makes +them contain Jobs rather than BlockJobs + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit 62c9e4162a7bc26a1389e50d17d3b2637028bbc3) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 2 +- + blockdev.c | 14 +++++------ + blockjob.c | 60 +++++++++++++++++++++++--------------------- + include/block/block_int.h | 2 +- + include/block/blockjob.h | 11 ++++---- + include/block/blockjob_int.h | 2 +- + include/qemu/job.h | 3 +++ + tests/test-blockjob-txn.c | 8 +++--- + 8 files changed, 54 insertions(+), 48 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index ca7d990..6172f90 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -547,7 +547,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, + BlockdevOnError on_target_error, + int creation_flags, + BlockCompletionFunc *cb, void *opaque, +- BlockJobTxn *txn, Error **errp) ++ JobTxn *txn, Error **errp) + { + int64_t len; + BlockDriverInfo bdi; +diff --git a/blockdev.c b/blockdev.c +index c2b2be1..b6f3e92 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -1488,7 +1488,7 @@ typedef struct BlkActionOps { + struct BlkActionState { + TransactionAction *action; + const BlkActionOps *ops; +- BlockJobTxn *block_job_txn; ++ JobTxn *block_job_txn; + TransactionProperties *txn_props; + QSIMPLEQ_ENTRY(BlkActionState) entry; + }; +@@ -1906,7 +1906,7 @@ typedef struct DriveBackupState { + BlockJob *job; + } DriveBackupState; + +-static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, ++static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, + Error **errp); + + static void drive_backup_prepare(BlkActionState *common, Error **errp) +@@ -1996,7 +1996,7 @@ typedef struct BlockdevBackupState { + BlockJob *job; + } BlockdevBackupState; + +-static BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, ++static BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, + Error **errp); + + static void blockdev_backup_prepare(BlkActionState *common, Error **errp) +@@ -2285,7 +2285,7 @@ void qmp_transaction(TransactionActionList *dev_list, + Error **errp) + { + TransactionActionList *dev_entry = dev_list; +- BlockJobTxn *block_job_txn = NULL; ++ JobTxn *block_job_txn = NULL; + BlkActionState *state, *next; + Error *local_err = NULL; + +@@ -2293,7 +2293,7 @@ void qmp_transaction(TransactionActionList *dev_list, + QSIMPLEQ_INIT(&snap_bdrv_states); + + /* Does this transaction get canceled as a group on failure? +- * If not, we don't really need to make a BlockJobTxn. ++ * If not, we don't really need to make a JobTxn. + */ + props = get_transaction_properties(props); + if (props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) { +@@ -3311,7 +3311,7 @@ out: + aio_context_release(aio_context); + } + +-static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, ++static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, + Error **errp) + { + BlockDriverState *bs; +@@ -3481,7 +3481,7 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) + return bdrv_named_nodes_list(errp); + } + +-BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, ++BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, + Error **errp) + { + BlockDriverState *bs; +diff --git a/blockjob.c b/blockjob.c +index 1ed3e9c..bd35c4f 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -37,13 +37,13 @@ + #include "qemu/timer.h" + + /* Transactional group of block jobs */ +-struct BlockJobTxn { ++struct JobTxn { + + /* Is this txn being cancelled? */ + bool aborting; + + /* List of jobs */ +- QLIST_HEAD(, BlockJob) jobs; ++ QLIST_HEAD(, Job) jobs; + + /* Reference count */ + int refcnt; +@@ -94,27 +94,27 @@ BlockJob *block_job_get(const char *id) + } + } + +-BlockJobTxn *block_job_txn_new(void) ++JobTxn *block_job_txn_new(void) + { +- BlockJobTxn *txn = g_new0(BlockJobTxn, 1); ++ JobTxn *txn = g_new0(JobTxn, 1); + QLIST_INIT(&txn->jobs); + txn->refcnt = 1; + return txn; + } + +-static void block_job_txn_ref(BlockJobTxn *txn) ++static void block_job_txn_ref(JobTxn *txn) + { + txn->refcnt++; + } + +-void block_job_txn_unref(BlockJobTxn *txn) ++void block_job_txn_unref(JobTxn *txn) + { + if (txn && --txn->refcnt == 0) { + g_free(txn); + } + } + +-void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job) ++void block_job_txn_add_job(JobTxn *txn, BlockJob *job) + { + if (!txn) { + return; +@@ -123,14 +123,14 @@ void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job) + assert(!job->txn); + job->txn = txn; + +- QLIST_INSERT_HEAD(&txn->jobs, job, txn_list); ++ QLIST_INSERT_HEAD(&txn->jobs, &job->job, txn_list); + block_job_txn_ref(txn); + } + + void block_job_txn_del_job(BlockJob *job) + { + if (job->txn) { +- QLIST_REMOVE(job, txn_list); ++ QLIST_REMOVE(&job->job, txn_list); + block_job_txn_unref(job->txn); + job->txn = NULL; + } +@@ -285,18 +285,22 @@ static void job_cancel_async(Job *job, bool force) + job->force_cancel |= force; + } + +-static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock) ++static int block_job_txn_apply(JobTxn *txn, int fn(BlockJob *), bool lock) + { + AioContext *ctx; +- BlockJob *job, *next; ++ Job *job, *next; ++ BlockJob *bjob; + int rc = 0; + + QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) { ++ assert(is_block_job(job)); ++ bjob = container_of(job, BlockJob, job); ++ + if (lock) { +- ctx = blk_get_aio_context(job->blk); ++ ctx = job->aio_context; + aio_context_acquire(ctx); + } +- rc = fn(job); ++ rc = fn(bjob); + if (lock) { + aio_context_release(ctx); + } +@@ -310,8 +314,8 @@ static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock) + static void block_job_completed_txn_abort(BlockJob *job) + { + AioContext *ctx; +- BlockJobTxn *txn = job->txn; +- BlockJob *other_job; ++ JobTxn *txn = job->txn; ++ Job *other_job; + + if (txn->aborting) { + /* +@@ -324,7 +328,7 @@ static void block_job_completed_txn_abort(BlockJob *job) + + /* We are the first failed job. Cancel other jobs. */ + QLIST_FOREACH(other_job, &txn->jobs, txn_list) { +- ctx = blk_get_aio_context(other_job->blk); ++ ctx = other_job->aio_context; + aio_context_acquire(ctx); + } + +@@ -332,18 +336,18 @@ static void block_job_completed_txn_abort(BlockJob *job) + * 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) { +- job_cancel_async(&other_job->job, false); ++ if (other_job != &job->job) { ++ job_cancel_async(other_job, false); + } + } + while (!QLIST_EMPTY(&txn->jobs)) { + other_job = QLIST_FIRST(&txn->jobs); +- ctx = blk_get_aio_context(other_job->blk); +- if (!job_is_completed(&other_job->job)) { +- assert(job_is_cancelled(&other_job->job)); +- job_finish_sync(&other_job->job, NULL, NULL); ++ ctx = other_job->aio_context; ++ if (!job_is_completed(other_job)) { ++ assert(job_is_cancelled(other_job)); ++ job_finish_sync(other_job, NULL, NULL); + } +- job_finalize_single(&other_job->job); ++ job_finalize_single(other_job); + aio_context_release(ctx); + } + +@@ -385,8 +389,8 @@ static int block_job_transition_to_pending(BlockJob *job) + + static void block_job_completed_txn_success(BlockJob *job) + { +- BlockJobTxn *txn = job->txn; +- BlockJob *other_job; ++ JobTxn *txn = job->txn; ++ Job *other_job; + + job_state_transition(&job->job, JOB_STATUS_WAITING); + +@@ -395,10 +399,10 @@ static void block_job_completed_txn_success(BlockJob *job) + * txn. + */ + QLIST_FOREACH(other_job, &txn->jobs, txn_list) { +- if (!job_is_completed(&other_job->job)) { ++ if (!job_is_completed(other_job)) { + return; + } +- assert(other_job->job.ret == 0); ++ assert(other_job->ret == 0); + } + + block_job_txn_apply(txn, block_job_transition_to_pending, false); +@@ -628,7 +632,7 @@ static void block_job_event_pending(Notifier *n, void *opaque) + */ + + void *block_job_create(const char *job_id, const BlockJobDriver *driver, +- BlockJobTxn *txn, BlockDriverState *bs, uint64_t perm, ++ JobTxn *txn, BlockDriverState *bs, uint64_t perm, + uint64_t shared_perm, int64_t speed, int flags, + BlockCompletionFunc *cb, void *opaque, Error **errp) + { +diff --git a/include/block/block_int.h b/include/block/block_int.h +index d913ed1..ad2b852 100644 +--- a/include/block/block_int.h ++++ b/include/block/block_int.h +@@ -1018,7 +1018,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, + BlockdevOnError on_target_error, + int creation_flags, + BlockCompletionFunc *cb, void *opaque, +- BlockJobTxn *txn, Error **errp); ++ JobTxn *txn, Error **errp); + + void hmp_drive_add_node(Monitor *mon, const char *optstr); + +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index 85ce18a..44df025 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -33,7 +33,7 @@ + #define BLOCK_JOB_SLICE_TIME 100000000ULL /* ns */ + + typedef struct BlockJobDriver BlockJobDriver; +-typedef struct BlockJobTxn BlockJobTxn; ++typedef struct JobTxn JobTxn; + + /** + * BlockJob: +@@ -85,8 +85,7 @@ typedef struct BlockJob { + /** BlockDriverStates that are involved in this block job */ + GSList *nodes; + +- BlockJobTxn *txn; +- QLIST_ENTRY(BlockJob) txn_list; ++ JobTxn *txn; + } BlockJob; + + /** +@@ -273,7 +272,7 @@ void block_job_iostatus_reset(BlockJob *job); + * group. Jobs wait for each other before completing. Cancelling one job + * cancels all jobs in the transaction. + */ +-BlockJobTxn *block_job_txn_new(void); ++JobTxn *block_job_txn_new(void); + + /** + * block_job_txn_unref: +@@ -282,7 +281,7 @@ BlockJobTxn *block_job_txn_new(void); + * or block_job_txn_new. If it's the last reference to the object, it will be + * freed. + */ +-void block_job_txn_unref(BlockJobTxn *txn); ++void block_job_txn_unref(JobTxn *txn); + + /** + * block_job_txn_add_job: +@@ -293,7 +292,7 @@ void block_job_txn_unref(BlockJobTxn *txn); + * The caller must call either block_job_txn_unref() or block_job_completed() + * to release the reference that is automatically grabbed here. + */ +-void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job); ++void block_job_txn_add_job(JobTxn *txn, BlockJob *job); + + /** + * block_job_is_internal: +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +index b8ca7bb..ce66a9b 100644 +--- a/include/block/blockjob_int.h ++++ b/include/block/blockjob_int.h +@@ -91,7 +91,7 @@ struct BlockJobDriver { + * called from a wrapper that is specific to the job type. + */ + void *block_job_create(const char *job_id, const BlockJobDriver *driver, +- BlockJobTxn *txn, BlockDriverState *bs, uint64_t perm, ++ JobTxn *txn, BlockDriverState *bs, uint64_t perm, + uint64_t shared_perm, int64_t speed, int flags, + BlockCompletionFunc *cb, void *opaque, Error **errp); + +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 17e2cec..d4aa7fa 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -132,6 +132,9 @@ typedef struct Job { + + /** Element of the list of jobs */ + QLIST_ENTRY(Job) job_list; ++ ++ /** Element of the list of jobs in a job transaction */ ++ QLIST_ENTRY(Job) txn_list; + } Job; + + /** +diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c +index 1572f8d..ec5d592 100644 +--- a/tests/test-blockjob-txn.c ++++ b/tests/test-blockjob-txn.c +@@ -93,7 +93,7 @@ static const BlockJobDriver test_block_job_driver = { + */ + static BlockJob *test_block_job_start(unsigned int iterations, + bool use_timer, +- int rc, int *result, BlockJobTxn *txn) ++ int rc, int *result, JobTxn *txn) + { + BlockDriverState *bs; + TestBlockJob *s; +@@ -122,7 +122,7 @@ static BlockJob *test_block_job_start(unsigned int iterations, + static void test_single_job(int expected) + { + BlockJob *job; +- BlockJobTxn *txn; ++ JobTxn *txn; + int result = -EINPROGRESS; + + txn = block_job_txn_new(); +@@ -160,7 +160,7 @@ static void test_pair_jobs(int expected1, int expected2) + { + BlockJob *job1; + BlockJob *job2; +- BlockJobTxn *txn; ++ JobTxn *txn; + int result1 = -EINPROGRESS; + int result2 = -EINPROGRESS; + +@@ -222,7 +222,7 @@ static void test_pair_jobs_fail_cancel_race(void) + { + BlockJob *job1; + BlockJob *job2; +- BlockJobTxn *txn; ++ JobTxn *txn; + int result1 = -EINPROGRESS; + int result2 = -EINPROGRESS; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-job-Use-AIO_WAIT_WHILE-in-job_finish_sync.patch b/SOURCES/kvm-job-Use-AIO_WAIT_WHILE-in-job_finish_sync.patch new file mode 100644 index 0000000..9bf6503 --- /dev/null +++ b/SOURCES/kvm-job-Use-AIO_WAIT_WHILE-in-job_finish_sync.patch @@ -0,0 +1,75 @@ +From 1c16ba251ec8d0bf64ee34a0ef71b5912907de12 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:29 +0200 +Subject: [PATCH 38/49] job: Use AIO_WAIT_WHILE() in job_finish_sync() + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-32-kwolf@redhat.com> +Patchwork-id: 82184 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 31/42] job: Use AIO_WAIT_WHILE() in job_finish_sync() +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +job_finish_sync() needs to release the AioContext lock of the job before +calling aio_poll(). Otherwise, callbacks called by aio_poll() would +possibly take the lock a second time and run into a deadlock with a +nested AIO_WAIT_WHILE() call. + +Also, job_drain() without aio_poll() isn't necessarily enough to make +progress on a job, it could depend on bottom halves to be executed. + +Combine both open-coded while loops into a single AIO_WAIT_WHILE() call +that solves both of these problems. + +Signed-off-by: Kevin Wolf +Reviewed-by: Fam Zheng +Reviewed-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + job.c | 14 ++++++-------- + 1 file changed, 6 insertions(+), 8 deletions(-) + +diff --git a/job.c b/job.c +index 5a0ccc7..47b5a11 100644 +--- a/job.c ++++ b/job.c +@@ -29,6 +29,7 @@ + #include "qemu/job.h" + #include "qemu/id.h" + #include "qemu/main-loop.h" ++#include "block/aio-wait.h" + #include "trace-root.h" + #include "qapi/qapi-events-job.h" + +@@ -957,6 +958,7 @@ void job_complete(Job *job, Error **errp) + int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) + { + Error *local_err = NULL; ++ AioWait dummy_wait = {}; + int ret; + + job_ref(job); +@@ -969,14 +971,10 @@ int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) + job_unref(job); + return -EBUSY; + } +- /* job_drain calls job_enter, and it should be enough to induce progress +- * until the job completes or moves to the main thread. */ +- while (!job->deferred_to_main_loop && !job_is_completed(job)) { +- job_drain(job); +- } +- while (!job_is_completed(job)) { +- aio_poll(qemu_get_aio_context(), true); +- } ++ ++ AIO_WAIT_WHILE(&dummy_wait, job->aio_context, ++ (job_drain(job), !job_is_completed(job))); ++ + ret = (job_is_cancelled(job) && job->ret == 0) ? -ECANCELED : job->ret; + job_unref(job); + return ret; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-jobs-add-exit-shim.patch b/SOURCES/kvm-jobs-add-exit-shim.patch new file mode 100644 index 0000000..bef4c76 --- /dev/null +++ b/SOURCES/kvm-jobs-add-exit-shim.patch @@ -0,0 +1,108 @@ +From 8ca94edad4b07d6191bc4761a44d8289b0f8ad5d Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:41 +0200 +Subject: [PATCH 03/25] jobs: add exit shim + +RH-Author: John Snow +Message-id: <20180910181803.11781-4-jsnow@redhat.com> +Patchwork-id: 82104 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 03/25] jobs: add exit shim +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +All jobs do the same thing when they leave their running loop: +- Store the return code in a structure +- wait to receive this structure in the main thread +- signal job completion via job_completed + +Few jobs do anything beyond exactly this. Consolidate this exit +logic for a net reduction in SLOC. + +More seriously, when we utilize job_defer_to_main_loop_bh to call +a function that calls job_completed, job_finalize_single will run +in a context where it has recursively taken the aio_context lock, +which can cause hangs if it puts down a reference that causes a flush. + +You can observe this in practice by looking at mirror_exit's careful +placement of job_completed and bdrv_unref calls. + +If we centralize job exiting, we can signal job completion from outside +of the aio_context, which should allow for job cleanup code to run with +only one lock, which makes cleanup callbacks less tricky to write. + +Signed-off-by: John Snow +Reviewed-by: Max Reitz +Message-id: 20180830015734.19765-4-jsnow@redhat.com +Reviewed-by: Jeff Cody +Signed-off-by: Max Reitz +(cherry picked from commit 00359a71d45a414ee47d8e423104dc0afd24ec65) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + include/qemu/job.h | 11 +++++++++++ + job.c | 18 ++++++++++++++++++ + 2 files changed, 29 insertions(+) + +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 905dfdd..24b5f3f 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -209,6 +209,17 @@ struct JobDriver { + void (*drain)(Job *job); + + /** ++ * If the callback is not NULL, exit will be invoked from the main thread ++ * when the job's coroutine has finished, but before transactional ++ * convergence; before @prepare or @abort. ++ * ++ * FIXME TODO: This callback is only temporary to transition remaining jobs ++ * to prepare/commit/abort/clean callbacks and will be removed before 3.1. ++ * is released. ++ */ ++ void (*exit)(Job *job); ++ ++ /** + * 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. +diff --git a/job.c b/job.c +index 020ee0d..a814857 100644 +--- a/job.c ++++ b/job.c +@@ -530,6 +530,18 @@ void job_drain(Job *job) + } + } + ++static void job_exit(void *opaque) ++{ ++ Job *job = (Job *)opaque; ++ AioContext *aio_context = job->aio_context; ++ ++ if (job->driver->exit) { ++ aio_context_acquire(aio_context); ++ job->driver->exit(job); ++ aio_context_release(aio_context); ++ } ++ job_completed(job, job->ret); ++} + + /** + * All jobs must allow a pause point before entering their job proper. This +@@ -542,6 +554,12 @@ static void coroutine_fn job_co_entry(void *opaque) + assert(job && job->driver && job->driver->run); + job_pause_point(job); + job->ret = job->driver->run(job, &job->err); ++ if (!job->deferred_to_main_loop) { ++ job->deferred_to_main_loop = true; ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), ++ job_exit, ++ job); ++ } + } + + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-jobs-canonize-Error-object.patch b/SOURCES/kvm-jobs-canonize-Error-object.patch new file mode 100644 index 0000000..6b75ce5 --- /dev/null +++ b/SOURCES/kvm-jobs-canonize-Error-object.patch @@ -0,0 +1,285 @@ +From d384736e7c77d64142f33fabc0aa50878d8f252e Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:40 +0200 +Subject: [PATCH 02/25] jobs: canonize Error object + +RH-Author: John Snow +Message-id: <20180910181803.11781-3-jsnow@redhat.com> +Patchwork-id: 82085 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 02/25] jobs: canonize Error object +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Jobs presently use both an Error object in the case of the create job, +and char strings in the case of generic errors elsewhere. + +Unify the two paths as just j->err, and remove the extra argument from +job_completed. The integer error code for job_completed is kept for now, +to be removed shortly in a separate patch. + +Signed-off-by: John Snow +Message-id: 20180830015734.19765-3-jsnow@redhat.com +[mreitz: Dropped a superfluous g_strdup()] +Reviewed-by: Eric Blake +Signed-off-by: Max Reitz +(cherry picked from commit 3d1f8b07a4c241f81949eff507d9f3a8fd73b87b) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 2 +- + block/commit.c | 2 +- + block/create.c | 5 ++--- + block/mirror.c | 2 +- + block/stream.c | 2 +- + include/qemu/job.h | 14 ++++++++------ + job-qmp.c | 5 +++-- + job.c | 18 ++++++------------ + tests/test-bdrv-drain.c | 2 +- + tests/test-blockjob-txn.c | 2 +- + tests/test-blockjob.c | 2 +- + 11 files changed, 26 insertions(+), 30 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index a142518..a5bf699 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -388,7 +388,7 @@ static void backup_complete(Job *job, void *opaque) + { + BackupCompleteData *data = opaque; + +- job_completed(job, data->ret, NULL); ++ job_completed(job, data->ret); + g_free(data); + } + +diff --git a/block/commit.c b/block/commit.c +index 905a1c5..af7579d 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -117,7 +117,7 @@ static void commit_complete(Job *job, void *opaque) + * bdrv_set_backing_hd() to fail. */ + block_job_remove_all_bdrv(bjob); + +- job_completed(job, ret, NULL); ++ job_completed(job, ret); + g_free(data); + + /* If bdrv_drop_intermediate() didn't already do that, remove the commit +diff --git a/block/create.c b/block/create.c +index 04733c3..26a385c 100644 +--- a/block/create.c ++++ b/block/create.c +@@ -35,14 +35,13 @@ typedef struct BlockdevCreateJob { + BlockDriver *drv; + BlockdevCreateOptions *opts; + int ret; +- Error *err; + } BlockdevCreateJob; + + static void blockdev_create_complete(Job *job, void *opaque) + { + BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); + +- job_completed(job, s->ret, s->err); ++ job_completed(job, s->ret); + } + + static int coroutine_fn blockdev_create_run(Job *job, Error **errp) +@@ -50,7 +49,7 @@ static int coroutine_fn blockdev_create_run(Job *job, Error **errp) + BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); + + job_progress_set_remaining(&s->common, 1); +- s->ret = s->drv->bdrv_co_create(s->opts, &s->err); ++ s->ret = s->drv->bdrv_co_create(s->opts, errp); + job_progress_update(&s->common, 1); + + qapi_free_BlockdevCreateOptions(s->opts); +diff --git a/block/mirror.c b/block/mirror.c +index 03b326d..459f944 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -581,7 +581,7 @@ static void mirror_exit(Job *job, void *opaque) + blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort); + blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort); + +- job_completed(job, data->ret, NULL); ++ job_completed(job, data->ret); + + g_free(data); + bdrv_drained_end(src); +diff --git a/block/stream.c b/block/stream.c +index b4b987d..26a7753 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -93,7 +93,7 @@ out: + } + + g_free(s->backing_file_str); +- job_completed(job, data->ret, NULL); ++ job_completed(job, data->ret); + g_free(data); + } + +diff --git a/include/qemu/job.h b/include/qemu/job.h +index e81cc34..905dfdd 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -124,12 +124,16 @@ typedef struct Job { + /** Estimated progress_current value at the completion of the job */ + int64_t progress_total; + +- /** Error string for a failed job (NULL if, and only if, job->ret == 0) */ +- char *error; +- + /** ret code passed to job_completed. */ + 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; ++ + /** The completion function that will be called when the job completes. */ + BlockCompletionFunc *cb; + +@@ -469,15 +473,13 @@ void job_transition_to_ready(Job *job); + /** + * @job: The job being completed. + * @ret: The status code. +- * @error: The error message for a failing job (only with @ret < 0). If @ret is +- * negative, but NULL is given for @error, strerror() is used. + * + * Marks @job as completed. If @ret is non-zero, the job transaction it is part + * of is aborted. If @ret is 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. + */ +-void job_completed(Job *job, int ret, Error *error); ++void job_completed(Job *job, int ret); + + /** Asynchronously complete the specified @job. */ + void job_complete(Job *job, Error **errp); +diff --git a/job-qmp.c b/job-qmp.c +index 410775d..a969b2b 100644 +--- a/job-qmp.c ++++ b/job-qmp.c +@@ -146,8 +146,9 @@ static JobInfo *job_query_single(Job *job, Error **errp) + .status = job->status, + .current_progress = job->progress_current, + .total_progress = job->progress_total, +- .has_error = !!job->error, +- .error = g_strdup(job->error), ++ .has_error = !!job->err, ++ .error = job->err ? \ ++ g_strdup(error_get_pretty(job->err)) : NULL, + }; + + return info; +diff --git a/job.c b/job.c +index 0d07700..020ee0d 100644 +--- a/job.c ++++ b/job.c +@@ -369,7 +369,7 @@ void job_unref(Job *job) + + QLIST_REMOVE(job, job_list); + +- g_free(job->error); ++ error_free(job->err); + g_free(job->id); + g_free(job); + } +@@ -541,7 +541,7 @@ static void coroutine_fn job_co_entry(void *opaque) + + assert(job && job->driver && job->driver->run); + job_pause_point(job); +- job->ret = job->driver->run(job, NULL); ++ job->ret = job->driver->run(job, &job->err); + } + + +@@ -661,8 +661,8 @@ static void job_update_rc(Job *job) + job->ret = -ECANCELED; + } + if (job->ret) { +- if (!job->error) { +- job->error = g_strdup(strerror(-job->ret)); ++ if (!job->err) { ++ error_setg(&job->err, "%s", strerror(-job->ret)); + } + job_state_transition(job, JOB_STATUS_ABORTING); + } +@@ -860,17 +860,11 @@ static void job_completed_txn_success(Job *job) + } + } + +-void job_completed(Job *job, int ret, Error *error) ++void job_completed(Job *job, int ret) + { + assert(job && job->txn && !job_is_completed(job)); + + job->ret = ret; +- if (error) { +- assert(job->ret < 0); +- job->error = g_strdup(error_get_pretty(error)); +- error_free(error); +- } +- + job_update_rc(job); + trace_job_completed(job, ret, job->ret); + if (job->ret) { +@@ -888,7 +882,7 @@ void job_cancel(Job *job, bool force) + } + job_cancel_async(job, force); + if (!job_started(job)) { +- job_completed(job, -ECANCELED, NULL); ++ job_completed(job, -ECANCELED); + } else if (job->deferred_to_main_loop) { + job_completed_txn_abort(job); + } else { +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index 798445a..8937894 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -498,7 +498,7 @@ typedef struct TestBlockJob { + + static void test_job_completed(Job *job, void *opaque) + { +- job_completed(job, 0, NULL); ++ job_completed(job, 0); + } + + static int coroutine_fn test_job_run(Job *job, Error **errp) +diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c +index 3194924..82cedee 100644 +--- a/tests/test-blockjob-txn.c ++++ b/tests/test-blockjob-txn.c +@@ -34,7 +34,7 @@ static void test_block_job_complete(Job *job, void *opaque) + rc = -ECANCELED; + } + +- job_completed(job, rc, NULL); ++ job_completed(job, rc); + bdrv_unref(bs); + } + +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index b0462bf..408a226 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -167,7 +167,7 @@ static void cancel_job_completed(Job *job, void *opaque) + { + CancelJob *s = opaque; + s->completed = true; +- job_completed(job, 0, NULL); ++ job_completed(job, 0); + } + + static void cancel_job_complete(Job *job, Error **errp) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-jobs-change-start-callback-to-run-callback.patch b/SOURCES/kvm-jobs-change-start-callback-to-run-callback.patch new file mode 100644 index 0000000..d2a9a4e --- /dev/null +++ b/SOURCES/kvm-jobs-change-start-callback-to-run-callback.patch @@ -0,0 +1,372 @@ +From 751387e28f5006dd063010ff0b0912ff9b44c2fe Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:39 +0200 +Subject: [PATCH 01/25] jobs: change start callback to run callback + +RH-Author: John Snow +Message-id: <20180910181803.11781-2-jsnow@redhat.com> +Patchwork-id: 82102 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 01/25] jobs: change start callback to run callback +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Presently we codify the entry point for a job as the "start" callback, +but a more apt name would be "run" to clarify the idea that when this +function returns we consider the job to have "finished," except for +any cleanup which occurs in separate callbacks later. + +As part of this clarification, change the signature to include an error +object and a return code. The error ptr is not yet used, and the return +code while captured, will be overwritten by actions in the job_completed +function. + +Signed-off-by: John Snow +Reviewed-by: Max Reitz +Message-id: 20180830015734.19765-2-jsnow@redhat.com +Reviewed-by: Jeff Cody +Signed-off-by: Max Reitz +(cherry picked from commit f67432a2019caf05b57a146bf45c1024a5cb608e) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 7 ++++--- + block/commit.c | 7 ++++--- + block/create.c | 8 +++++--- + block/mirror.c | 10 ++++++---- + block/stream.c | 7 ++++--- + include/qemu/job.h | 2 +- + job.c | 6 +++--- + tests/test-bdrv-drain.c | 7 ++++--- + tests/test-blockjob-txn.c | 16 ++++++++-------- + tests/test-blockjob.c | 7 ++++--- + 10 files changed, 43 insertions(+), 34 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index 4ba1a6a..a142518 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -480,9 +480,9 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) + bdrv_dirty_iter_free(dbi); + } + +-static void coroutine_fn backup_run(void *opaque) ++static int coroutine_fn backup_run(Job *opaque_job, Error **errp) + { +- BackupBlockJob *job = opaque; ++ BackupBlockJob *job = container_of(opaque_job, BackupBlockJob, common.job); + BackupCompleteData *data; + BlockDriverState *bs = blk_bs(job->common.blk); + int64_t offset, nb_clusters; +@@ -587,6 +587,7 @@ static void coroutine_fn backup_run(void *opaque) + data = g_malloc(sizeof(*data)); + data->ret = ret; + job_defer_to_main_loop(&job->common.job, backup_complete, data); ++ return ret; + } + + static const BlockJobDriver backup_job_driver = { +@@ -596,7 +597,7 @@ static const BlockJobDriver backup_job_driver = { + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, +- .start = backup_run, ++ .run = backup_run, + .commit = backup_commit, + .abort = backup_abort, + .clean = backup_clean, +diff --git a/block/commit.c b/block/commit.c +index e1814d9..905a1c5 100644 +--- a/block/commit.c ++++ b/block/commit.c +@@ -134,9 +134,9 @@ static void commit_complete(Job *job, void *opaque) + bdrv_unref(top); + } + +-static void coroutine_fn commit_run(void *opaque) ++static int coroutine_fn commit_run(Job *job, Error **errp) + { +- CommitBlockJob *s = opaque; ++ CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); + CommitCompleteData *data; + int64_t offset; + uint64_t delay_ns = 0; +@@ -213,6 +213,7 @@ out: + data = g_malloc(sizeof(*data)); + data->ret = ret; + job_defer_to_main_loop(&s->common.job, commit_complete, data); ++ return ret; + } + + static const BlockJobDriver commit_job_driver = { +@@ -222,7 +223,7 @@ static const BlockJobDriver commit_job_driver = { + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, +- .start = commit_run, ++ .run = commit_run, + }, + }; + +diff --git a/block/create.c b/block/create.c +index 915cd41..04733c3 100644 +--- a/block/create.c ++++ b/block/create.c +@@ -45,9 +45,9 @@ static void blockdev_create_complete(Job *job, void *opaque) + job_completed(job, s->ret, s->err); + } + +-static void coroutine_fn blockdev_create_run(void *opaque) ++static int coroutine_fn blockdev_create_run(Job *job, Error **errp) + { +- BlockdevCreateJob *s = opaque; ++ BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); + + job_progress_set_remaining(&s->common, 1); + s->ret = s->drv->bdrv_co_create(s->opts, &s->err); +@@ -55,12 +55,14 @@ static void coroutine_fn blockdev_create_run(void *opaque) + + qapi_free_BlockdevCreateOptions(s->opts); + job_defer_to_main_loop(&s->common, blockdev_create_complete, NULL); ++ ++ return s->ret; + } + + static const JobDriver blockdev_create_job_driver = { + .instance_size = sizeof(BlockdevCreateJob), + .job_type = JOB_TYPE_CREATE, +- .start = blockdev_create_run, ++ .run = blockdev_create_run, + }; + + void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options, +diff --git a/block/mirror.c b/block/mirror.c +index 65cf43d..03b326d 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -683,9 +683,9 @@ static int mirror_flush(MirrorBlockJob *s) + return ret; + } + +-static void coroutine_fn mirror_run(void *opaque) ++static int coroutine_fn mirror_run(Job *job, Error **errp) + { +- MirrorBlockJob *s = opaque; ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); + MirrorExitData *data; + BlockDriverState *bs = s->source; + BlockDriverState *target_bs = blk_bs(s->target); +@@ -902,7 +902,9 @@ immediate_exit: + if (need_drain) { + bdrv_drained_begin(bs); + } ++ + job_defer_to_main_loop(&s->common.job, mirror_exit, data); ++ return ret; + } + + static void mirror_complete(Job *job, Error **errp) +@@ -993,7 +995,7 @@ static const BlockJobDriver mirror_job_driver = { + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, +- .start = mirror_run, ++ .run = mirror_run, + .pause = mirror_pause, + .complete = mirror_complete, + }, +@@ -1008,7 +1010,7 @@ static const BlockJobDriver commit_active_job_driver = { + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, +- .start = mirror_run, ++ .run = mirror_run, + .pause = mirror_pause, + .complete = mirror_complete, + }, +diff --git a/block/stream.c b/block/stream.c +index 9264b68..b4b987d 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -97,9 +97,9 @@ out: + g_free(data); + } + +-static void coroutine_fn stream_run(void *opaque) ++static int coroutine_fn stream_run(Job *job, Error **errp) + { +- StreamBlockJob *s = opaque; ++ StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); + StreamCompleteData *data; + BlockBackend *blk = s->common.blk; + BlockDriverState *bs = blk_bs(blk); +@@ -206,6 +206,7 @@ out: + data = g_malloc(sizeof(*data)); + data->ret = ret; + job_defer_to_main_loop(&s->common.job, stream_complete, data); ++ return ret; + } + + static const BlockJobDriver stream_job_driver = { +@@ -213,7 +214,7 @@ static const BlockJobDriver stream_job_driver = { + .instance_size = sizeof(StreamBlockJob), + .job_type = JOB_TYPE_STREAM, + .free = block_job_free, +- .start = stream_run, ++ .run = stream_run, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + }, +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 1d82053..e81cc34 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -169,7 +169,7 @@ struct JobDriver { + JobType job_type; + + /** Mandatory: Entrypoint for the Coroutine. */ +- CoroutineEntry *start; ++ int coroutine_fn (*run)(Job *job, Error **errp); + + /** + * If the callback is not NULL, it will be invoked when the job transitions +diff --git a/job.c b/job.c +index 95dc998..0d07700 100644 +--- a/job.c ++++ b/job.c +@@ -539,16 +539,16 @@ static void coroutine_fn job_co_entry(void *opaque) + { + Job *job = opaque; + +- assert(job && job->driver && job->driver->start); ++ assert(job && job->driver && job->driver->run); + job_pause_point(job); +- job->driver->start(job); ++ job->ret = job->driver->run(job, NULL); + } + + + void job_start(Job *job) + { + assert(job && !job_started(job) && job->paused && +- job->driver && job->driver->start); ++ job->driver && job->driver->run); + job->co = qemu_coroutine_create(job_co_entry, job); + job->pause_count--; + job->busy = true; +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index a11c4cf..798445a 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -501,9 +501,9 @@ static void test_job_completed(Job *job, void *opaque) + job_completed(job, 0, NULL); + } + +-static void coroutine_fn test_job_start(void *opaque) ++static int coroutine_fn test_job_run(Job *job, Error **errp) + { +- TestBlockJob *s = opaque; ++ TestBlockJob *s = container_of(job, TestBlockJob, common.job); + + job_transition_to_ready(&s->common.job); + while (!s->should_complete) { +@@ -511,6 +511,7 @@ static void coroutine_fn test_job_start(void *opaque) + } + + job_defer_to_main_loop(&s->common.job, test_job_completed, NULL); ++ return 0; + } + + static void test_job_complete(Job *job, Error **errp) +@@ -525,7 +526,7 @@ BlockJobDriver test_job_driver = { + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, +- .start = test_job_start, ++ .run = test_job_run, + .complete = test_job_complete, + }, + }; +diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c +index 58d9b87..3194924 100644 +--- a/tests/test-blockjob-txn.c ++++ b/tests/test-blockjob-txn.c +@@ -38,25 +38,25 @@ static void test_block_job_complete(Job *job, void *opaque) + bdrv_unref(bs); + } + +-static void coroutine_fn test_block_job_run(void *opaque) ++static int coroutine_fn test_block_job_run(Job *job, Error **errp) + { +- TestBlockJob *s = opaque; +- BlockJob *job = &s->common; ++ TestBlockJob *s = container_of(job, TestBlockJob, common.job); + + while (s->iterations--) { + if (s->use_timer) { +- job_sleep_ns(&job->job, 0); ++ job_sleep_ns(job, 0); + } else { +- job_yield(&job->job); ++ job_yield(job); + } + +- if (job_is_cancelled(&job->job)) { ++ if (job_is_cancelled(job)) { + break; + } + } + +- job_defer_to_main_loop(&job->job, test_block_job_complete, ++ job_defer_to_main_loop(job, test_block_job_complete, + (void *)(intptr_t)s->rc); ++ return s->rc; + } + + typedef struct { +@@ -80,7 +80,7 @@ static const BlockJobDriver test_block_job_driver = { + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, +- .start = test_block_job_run, ++ .run = test_block_job_run, + }, + }; + +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index cb42f06..b0462bf 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -176,9 +176,9 @@ static void cancel_job_complete(Job *job, Error **errp) + s->should_complete = true; + } + +-static void coroutine_fn cancel_job_start(void *opaque) ++static int coroutine_fn cancel_job_run(Job *job, Error **errp) + { +- CancelJob *s = opaque; ++ CancelJob *s = container_of(job, CancelJob, common.job); + + while (!s->should_complete) { + if (job_is_cancelled(&s->common.job)) { +@@ -194,6 +194,7 @@ static void coroutine_fn cancel_job_start(void *opaque) + + defer: + job_defer_to_main_loop(&s->common.job, cancel_job_completed, s); ++ return 0; + } + + static const BlockJobDriver test_cancel_driver = { +@@ -202,7 +203,7 @@ static const BlockJobDriver test_cancel_driver = { + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, +- .start = cancel_job_start, ++ .run = cancel_job_run, + .complete = cancel_job_complete, + }, + }; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-jobs-fix-stale-wording.patch b/SOURCES/kvm-jobs-fix-stale-wording.patch new file mode 100644 index 0000000..ad9f936 --- /dev/null +++ b/SOURCES/kvm-jobs-fix-stale-wording.patch @@ -0,0 +1,62 @@ +From 8fa85725f454d8e727763460dcfa63aaa16b18b2 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:53 +0200 +Subject: [PATCH 84/89] jobs: fix stale wording + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-71-kwolf@redhat.com> +Patchwork-id: 81107 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 70/73] jobs: fix stale wording +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +From: John Snow + +During the design for manual completion, we decided not to use the +"manual" property as a shorthand for both auto-dismiss and auto-finalize. + +Fix the wording. + +Signed-off-by: John Snow +Reviewed-by: Jeff Cody +Reviewed-by: Markus Armbruster +Signed-off-by: Kevin Wolf +(cherry picked from commit c5b09f3f2c4db32f39ecbde2cf2f78e7a657a85d) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + qapi/job.json | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/qapi/job.json b/qapi/job.json +index 17d1003..2264435 100644 +--- a/qapi/job.json ++++ b/qapi/job.json +@@ -50,16 +50,17 @@ + # the last job in a transaction. + # + # @pending: The job has finished its work, but has finalization steps that it +-# needs to make prior to completing. These changes may require +-# manual intervention by the management process if manual was set +-# to true. These changes may still fail. ++# needs to make prior to completing. These changes will require ++# manual intervention via @job-finalize if auto-finalize was set to ++# false. These pending changes may still fail. + # + # @aborting: The job is in the process of being aborted, and will finish with + # an error. The job will afterwards report that it is @concluded. + # This status may not be visible to the management process. + # +-# @concluded: The job has finished all work. If manual was set to true, the job +-# will remain in the query list until it is dismissed. ++# @concluded: The job has finished all work. If auto-dismiss was set to false, ++# the job will remain in the query list until it is dismissed via ++# @job-dismiss. + # + # @null: The job is in the process of being dismantled. This state should not + # ever be visible externally. +-- +1.8.3.1 + diff --git a/SOURCES/kvm-jobs-fix-verb-references-in-docs.patch b/SOURCES/kvm-jobs-fix-verb-references-in-docs.patch new file mode 100644 index 0000000..073c7e3 --- /dev/null +++ b/SOURCES/kvm-jobs-fix-verb-references-in-docs.patch @@ -0,0 +1,66 @@ +From b4b170fbc6181b49bae5d5bfa7a45e00356d1230 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:54 +0200 +Subject: [PATCH 85/89] jobs: fix verb references in docs + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-72-kwolf@redhat.com> +Patchwork-id: 81108 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 71/73] jobs: fix verb references in docs +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +From: John Snow + +These point to the job versions now, not the blockjob versions which +don't really exist anymore. + +Except set-speed, which does. It sticks out like a sore thumb. This +patch doesn't fix that, but it doesn't make it any worse, either. + +Signed-off-by: John Snow +Reviewed-by: Jeff Cody +Reviewed-by: Markus Armbruster +Signed-off-by: Kevin Wolf +(cherry picked from commit b8a366feb2aa2200c42fa983a9c607fb78a501db) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + qapi/job.json | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/qapi/job.json b/qapi/job.json +index 2264435..9d074eb 100644 +--- a/qapi/job.json ++++ b/qapi/job.json +@@ -76,19 +76,19 @@ + # + # Represents command verbs that can be applied to a job. + # +-# @cancel: see @block-job-cancel ++# @cancel: see @job-cancel + # +-# @pause: see @block-job-pause ++# @pause: see @job-pause + # +-# @resume: see @block-job-resume ++# @resume: see @job-resume + # + # @set-speed: see @block-job-set-speed + # +-# @complete: see @block-job-complete ++# @complete: see @job-complete + # +-# @dismiss: see @block-job-dismiss ++# @dismiss: see @job-dismiss + # +-# @finalize: see @block-job-finalize ++# @finalize: see @job-finalize + # + # Since: 2.12 + ## +-- +1.8.3.1 + diff --git a/SOURCES/kvm-jobs-remove-.exit-callback.patch b/SOURCES/kvm-jobs-remove-.exit-callback.patch new file mode 100644 index 0000000..1507ad8 --- /dev/null +++ b/SOURCES/kvm-jobs-remove-.exit-callback.patch @@ -0,0 +1,156 @@ +From 583fce2c4839f5b0f3da8794182cc7738311a991 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:58 +0200 +Subject: [PATCH 20/25] jobs: remove .exit callback + +RH-Author: John Snow +Message-id: <20180910181803.11781-21-jsnow@redhat.com> +Patchwork-id: 82101 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 20/25] jobs: remove .exit callback +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Now that all of the jobs use the component finalization callbacks, +there's no use for the heavy-hammer .exit callback anymore. + +job_exit becomes a glorified type shim so that we can call +job_completed from aio_bh_schedule_oneshot. + +Move these three functions down into job.c to eliminate a +forward reference. + +Signed-off-by: John Snow +Reviewed-by: Max Reitz +Message-id: 20180906130225.5118-12-jsnow@redhat.com +Reviewed-by: Jeff Cody +Signed-off-by: Max Reitz +(cherry picked from commit 7e9e7780ef18e902a6458dfa6b024d9e2053923c) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + include/qemu/job.h | 11 -------- + job.c | 77 ++++++++++++++++++++++++------------------------------ + 2 files changed, 34 insertions(+), 54 deletions(-) + +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 04090ba..fdaa06f 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -222,17 +222,6 @@ struct JobDriver { + void (*drain)(Job *job); + + /** +- * If the callback is not NULL, exit will be invoked from the main thread +- * when the job's coroutine has finished, but before transactional +- * convergence; before @prepare or @abort. +- * +- * FIXME TODO: This callback is only temporary to transition remaining jobs +- * to prepare/commit/abort/clean callbacks and will be removed before 3.1. +- * is released. +- */ +- void (*exit)(Job *job); +- +- /** + * 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. +diff --git a/job.c b/job.c +index f56e6a3..dfba4bc 100644 +--- a/job.c ++++ b/job.c +@@ -530,49 +530,6 @@ void job_drain(Job *job) + } + } + +-static void job_completed(Job *job); +- +-static void job_exit(void *opaque) +-{ +- Job *job = (Job *)opaque; +- AioContext *aio_context = job->aio_context; +- +- if (job->driver->exit) { +- aio_context_acquire(aio_context); +- job->driver->exit(job); +- aio_context_release(aio_context); +- } +- job_completed(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; +- +- assert(job && job->driver && job->driver->run); +- job_pause_point(job); +- job->ret = job->driver->run(job, &job->err); +- job->deferred_to_main_loop = true; +- aio_bh_schedule_oneshot(qemu_get_aio_context(), job_exit, job); +-} +- +- +-void job_start(Job *job) +-{ +- assert(job && !job_started(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(job, JOB_STATUS_RUNNING); +- aio_co_enter(job->aio_context, job->co); +-} +- + /* Assumes the block_job_mutex is held */ + static bool job_timer_not_pending(Job *job) + { +@@ -889,6 +846,40 @@ static void job_completed(Job *job) + } + } + ++/** Useful only as a type shim for aio_bh_schedule_oneshot. */ ++static void job_exit(void *opaque) ++{ ++ Job *job = (Job *)opaque; ++ job_completed(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; ++ ++ assert(job && job->driver && job->driver->run); ++ job_pause_point(job); ++ job->ret = job->driver->run(job, &job->err); ++ job->deferred_to_main_loop = true; ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), job_exit, job); ++} ++ ++void job_start(Job *job) ++{ ++ assert(job && !job_started(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(job, JOB_STATUS_RUNNING); ++ aio_co_enter(job->aio_context, job->co); ++} ++ + void job_cancel(Job *job, bool force) + { + if (job->status == JOB_STATUS_CONCLUDED) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-jobs-remove-job_defer_to_main_loop.patch b/SOURCES/kvm-jobs-remove-job_defer_to_main_loop.patch new file mode 100644 index 0000000..6e84315 --- /dev/null +++ b/SOURCES/kvm-jobs-remove-job_defer_to_main_loop.patch @@ -0,0 +1,119 @@ +From 580a6b0332f21a364e2b807dcf63434fddaceada Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:47 +0200 +Subject: [PATCH 09/25] jobs: remove job_defer_to_main_loop + +RH-Author: John Snow +Message-id: <20180910181803.11781-10-jsnow@redhat.com> +Patchwork-id: 82107 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 09/25] jobs: remove job_defer_to_main_loop +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Now that the job infrastructure is handling the job_completed call for +all implemented jobs, we can remove the interface that allowed jobs to +schedule their own completion. + +Signed-off-by: John Snow +Reviewed-by: Max Reitz +Message-id: 20180830015734.19765-10-jsnow@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit e21a1c9831fc80ae3f3c1affdfa43350035d8588) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + include/qemu/job.h | 17 ----------------- + job.c | 40 ++-------------------------------------- + 2 files changed, 2 insertions(+), 55 deletions(-) + +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 952ac3a..04090ba 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -553,23 +553,6 @@ void job_finalize(Job *job, Error **errp); + */ + void job_dismiss(Job **job, Error **errp); + +-typedef void JobDeferToMainLoopFn(Job *job, void *opaque); +- +-/** +- * @job: The job +- * @fn: The function to run in the main loop +- * @opaque: The opaque value that is passed to @fn +- * +- * This function must be called by the main job coroutine just before it +- * returns. @fn is executed in the main loop with the job AioContext acquired. +- * +- * Block jobs must call bdrv_unref(), bdrv_close(), and anything that uses +- * bdrv_drain_all() in the main loop. +- * +- * The @job AioContext is held while @fn executes. +- */ +-void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque); +- + /** + * Synchronously finishes the given @job. If @finish is given, it is called to + * trigger completion or cancellation of the job. +diff --git a/job.c b/job.c +index 1acbcbc..f56e6a3 100644 +--- a/job.c ++++ b/job.c +@@ -556,12 +556,8 @@ static void coroutine_fn job_co_entry(void *opaque) + assert(job && job->driver && job->driver->run); + job_pause_point(job); + job->ret = job->driver->run(job, &job->err); +- if (!job->deferred_to_main_loop) { +- job->deferred_to_main_loop = true; +- aio_bh_schedule_oneshot(qemu_get_aio_context(), +- job_exit, +- job); +- } ++ job->deferred_to_main_loop = true; ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), job_exit, job); + } + + +@@ -964,38 +960,6 @@ void job_complete(Job *job, Error **errp) + job->driver->complete(job, errp); + } + +- +-typedef struct { +- Job *job; +- JobDeferToMainLoopFn *fn; +- void *opaque; +-} JobDeferToMainLoopData; +- +-static void job_defer_to_main_loop_bh(void *opaque) +-{ +- JobDeferToMainLoopData *data = opaque; +- Job *job = data->job; +- AioContext *aio_context = job->aio_context; +- +- aio_context_acquire(aio_context); +- data->fn(data->job, data->opaque); +- aio_context_release(aio_context); +- +- g_free(data); +-} +- +-void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque) +-{ +- JobDeferToMainLoopData *data = g_malloc(sizeof(*data)); +- data->job = job; +- data->fn = fn; +- data->opaque = opaque; +- job->deferred_to_main_loop = true; +- +- aio_bh_schedule_oneshot(qemu_get_aio_context(), +- job_defer_to_main_loop_bh, data); +-} +- + int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) + { + Error *local_err = NULL; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-jobs-remove-ret-argument-to-job_completed-privatize-.patch b/SOURCES/kvm-jobs-remove-ret-argument-to-job_completed-privatize-.patch new file mode 100644 index 0000000..086a750 --- /dev/null +++ b/SOURCES/kvm-jobs-remove-ret-argument-to-job_completed-privatize-.patch @@ -0,0 +1,154 @@ +From 8a39bfe61aa3e30907f583f7f2e90e7d90004f56 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:46 +0200 +Subject: [PATCH 08/25] jobs: remove ret argument to job_completed; privatize + it + +RH-Author: John Snow +Message-id: <20180910181803.11781-9-jsnow@redhat.com> +Patchwork-id: 82095 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 08/25] jobs: remove ret argument to job_completed; privatize it +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Jobs are now expected to return their retcode on the stack, from the +.run callback, so we can remove that argument. + +job_cancel does not need to set -ECANCELED because job_completed will +update the return code itself if the job was canceled. + +While we're here, make job_completed static to job.c and remove it from +job.h; move the documentation of return code to the .run() callback and +to the job->ret property, accordingly. + +Signed-off-by: John Snow +Message-id: 20180830015734.19765-9-jsnow@redhat.com +Reviewed-by: Max Reitz +Signed-off-by: Max Reitz +(cherry picked from commit 404ff28d6ae59fc1c24d631710d4063fc68aed03) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + include/qemu/job.h | 28 +++++++++++++++------------- + job.c | 11 ++++++----- + trace-events | 2 +- + 3 files changed, 22 insertions(+), 19 deletions(-) + +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 24b5f3f..952ac3a 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -124,7 +124,11 @@ typedef struct Job { + /** Estimated progress_current value at the completion of the job */ + int64_t progress_total; + +- /** ret code passed to job_completed. */ ++ /** ++ * 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; + + /** +@@ -172,7 +176,16 @@ struct JobDriver { + /** Enum describing the operation */ + JobType job_type; + +- /** Mandatory: Entrypoint for the Coroutine. */ ++ /** ++ * 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. ++ */ + int coroutine_fn (*run)(Job *job, Error **errp); + + /** +@@ -481,17 +494,6 @@ void job_early_fail(Job *job); + /** Moves the @job from RUNNING to READY */ + void job_transition_to_ready(Job *job); + +-/** +- * @job: The job being completed. +- * @ret: The status code. +- * +- * Marks @job as completed. If @ret is non-zero, the job transaction it is part +- * of is aborted. If @ret is 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. +- */ +-void job_completed(Job *job, int ret); +- + /** Asynchronously complete the specified @job. */ + void job_complete(Job *job, Error **errp); + +diff --git a/job.c b/job.c +index a814857..1acbcbc 100644 +--- a/job.c ++++ b/job.c +@@ -530,6 +530,8 @@ void job_drain(Job *job) + } + } + ++static void job_completed(Job *job); ++ + static void job_exit(void *opaque) + { + Job *job = (Job *)opaque; +@@ -540,7 +542,7 @@ static void job_exit(void *opaque) + job->driver->exit(job); + aio_context_release(aio_context); + } +- job_completed(job, job->ret); ++ job_completed(job); + } + + /** +@@ -878,13 +880,12 @@ static void job_completed_txn_success(Job *job) + } + } + +-void job_completed(Job *job, int ret) ++static void job_completed(Job *job) + { + assert(job && job->txn && !job_is_completed(job)); + +- job->ret = ret; + job_update_rc(job); +- trace_job_completed(job, ret, job->ret); ++ trace_job_completed(job, job->ret); + if (job->ret) { + job_completed_txn_abort(job); + } else { +@@ -900,7 +901,7 @@ void job_cancel(Job *job, bool force) + } + job_cancel_async(job, force); + if (!job_started(job)) { +- job_completed(job, -ECANCELED); ++ job_completed(job); + } else if (job->deferred_to_main_loop) { + job_completed_txn_abort(job); + } else { +diff --git a/trace-events b/trace-events +index c445f54..4fd2cb4 100644 +--- a/trace-events ++++ b/trace-events +@@ -107,7 +107,7 @@ gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packe + # job.c + job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)" + job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)" +-job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d" ++job_completed(void *job, int ret) "job %p ret %d" + + # job-qmp.c + qmp_job_cancel(void *job) "job %p" +-- +1.8.3.1 + diff --git a/SOURCES/kvm-jobs-utilize-job_exit-shim.patch b/SOURCES/kvm-jobs-utilize-job_exit-shim.patch new file mode 100644 index 0000000..aac8e8b --- /dev/null +++ b/SOURCES/kvm-jobs-utilize-job_exit-shim.patch @@ -0,0 +1,307 @@ +From 2986609502d393267e97d2bb874427acef906a61 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:44 +0200 +Subject: [PATCH 06/25] jobs: utilize job_exit shim + +RH-Author: John Snow +Message-id: <20180910181803.11781-7-jsnow@redhat.com> +Patchwork-id: 82106 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 06/25] jobs: utilize job_exit shim +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Utilize the job_exit shim by not calling job_defer_to_main_loop, and +where applicable, converting the deferred callback into the job_exit +callback. + +This converts backup, stream, create, and the unit tests all at once. +Most of these jobs do not see any changes to the order in which they +clean up their resources, except the test-blockjob-txn test, which +now puts down its bs before job_completed is called. + +This is safe for the same reason the reordering in the mirror job is +safe, because job_completed no longer runs under two locks, making +the unref safe even if it causes a flush. + +Signed-off-by: John Snow +Reviewed-by: Max Reitz +Message-id: 20180830015734.19765-7-jsnow@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit eb23654dbe43b549ea2a9ebff9d8edf544d34a73) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/backup.c | 16 ---------------- + block/create.c | 14 +++----------- + block/stream.c | 22 +++++++--------------- + tests/test-bdrv-drain.c | 6 ------ + tests/test-blockjob-txn.c | 11 ++--------- + tests/test-blockjob.c | 10 ++++------ + 6 files changed, 16 insertions(+), 63 deletions(-) + +diff --git a/block/backup.c b/block/backup.c +index a5bf699..08a5b74 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -380,18 +380,6 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job, + } + } + +-typedef struct { +- int ret; +-} BackupCompleteData; +- +-static void backup_complete(Job *job, void *opaque) +-{ +- BackupCompleteData *data = opaque; +- +- job_completed(job, data->ret); +- g_free(data); +-} +- + static bool coroutine_fn yield_and_check(BackupBlockJob *job) + { + uint64_t delay_ns; +@@ -483,7 +471,6 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) + static int coroutine_fn backup_run(Job *opaque_job, Error **errp) + { + BackupBlockJob *job = container_of(opaque_job, BackupBlockJob, common.job); +- BackupCompleteData *data; + BlockDriverState *bs = blk_bs(job->common.blk); + int64_t offset, nb_clusters; + int ret = 0; +@@ -584,9 +571,6 @@ static int coroutine_fn backup_run(Job *opaque_job, Error **errp) + qemu_co_rwlock_unlock(&job->flush_rwlock); + hbitmap_free(job->copy_bitmap); + +- data = g_malloc(sizeof(*data)); +- data->ret = ret; +- job_defer_to_main_loop(&job->common.job, backup_complete, data); + return ret; + } + +diff --git a/block/create.c b/block/create.c +index 26a385c..9534121 100644 +--- a/block/create.c ++++ b/block/create.c +@@ -34,28 +34,20 @@ typedef struct BlockdevCreateJob { + Job common; + BlockDriver *drv; + BlockdevCreateOptions *opts; +- int ret; + } BlockdevCreateJob; + +-static void blockdev_create_complete(Job *job, void *opaque) +-{ +- BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); +- +- job_completed(job, s->ret); +-} +- + static int coroutine_fn blockdev_create_run(Job *job, Error **errp) + { + BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); ++ int ret; + + job_progress_set_remaining(&s->common, 1); +- s->ret = s->drv->bdrv_co_create(s->opts, errp); ++ ret = s->drv->bdrv_co_create(s->opts, errp); + job_progress_update(&s->common, 1); + + qapi_free_BlockdevCreateOptions(s->opts); +- job_defer_to_main_loop(&s->common, blockdev_create_complete, NULL); + +- return s->ret; ++ return ret; + } + + static const JobDriver blockdev_create_job_driver = { +diff --git a/block/stream.c b/block/stream.c +index 26a7753..67e1e72 100644 +--- a/block/stream.c ++++ b/block/stream.c +@@ -54,20 +54,16 @@ static int coroutine_fn stream_populate(BlockBackend *blk, + return blk_co_preadv(blk, offset, qiov.size, &qiov, BDRV_REQ_COPY_ON_READ); + } + +-typedef struct { +- int ret; +-} StreamCompleteData; +- +-static void stream_complete(Job *job, void *opaque) ++static void stream_exit(Job *job) + { + StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); + BlockJob *bjob = &s->common; +- StreamCompleteData *data = opaque; + BlockDriverState *bs = blk_bs(bjob->blk); + BlockDriverState *base = s->base; + Error *local_err = NULL; ++ int ret = job->ret; + +- if (!job_is_cancelled(job) && bs->backing && data->ret == 0) { ++ if (!job_is_cancelled(job) && bs->backing && ret == 0) { + const char *base_id = NULL, *base_fmt = NULL; + if (base) { + base_id = s->backing_file_str; +@@ -75,11 +71,11 @@ static void stream_complete(Job *job, void *opaque) + base_fmt = base->drv->format_name; + } + } +- data->ret = bdrv_change_backing_file(bs, base_id, base_fmt); ++ ret = bdrv_change_backing_file(bs, base_id, base_fmt); + bdrv_set_backing_hd(bs, base, &local_err); + if (local_err) { + error_report_err(local_err); +- data->ret = -EPERM; ++ ret = -EPERM; + goto out; + } + } +@@ -93,14 +89,12 @@ out: + } + + g_free(s->backing_file_str); +- job_completed(job, data->ret); +- g_free(data); ++ job->ret = ret; + } + + static int coroutine_fn stream_run(Job *job, Error **errp) + { + StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); +- StreamCompleteData *data; + BlockBackend *blk = s->common.blk; + BlockDriverState *bs = blk_bs(blk); + BlockDriverState *base = s->base; +@@ -203,9 +197,6 @@ static int coroutine_fn stream_run(Job *job, Error **errp) + + out: + /* Modify backing chain and close BDSes in main loop */ +- data = g_malloc(sizeof(*data)); +- data->ret = ret; +- job_defer_to_main_loop(&s->common.job, stream_complete, data); + return ret; + } + +@@ -215,6 +206,7 @@ static const BlockJobDriver stream_job_driver = { + .job_type = JOB_TYPE_STREAM, + .free = block_job_free, + .run = stream_run, ++ .exit = stream_exit, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + }, +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index 8937894..403446e 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -496,11 +496,6 @@ typedef struct TestBlockJob { + bool should_complete; + } TestBlockJob; + +-static void test_job_completed(Job *job, void *opaque) +-{ +- job_completed(job, 0); +-} +- + static int coroutine_fn test_job_run(Job *job, Error **errp) + { + TestBlockJob *s = container_of(job, TestBlockJob, common.job); +@@ -510,7 +505,6 @@ static int coroutine_fn test_job_run(Job *job, Error **errp) + job_sleep_ns(&s->common.job, 100000); + } + +- job_defer_to_main_loop(&s->common.job, test_job_completed, NULL); + return 0; + } + +diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c +index 82cedee..ef29f35 100644 +--- a/tests/test-blockjob-txn.c ++++ b/tests/test-blockjob-txn.c +@@ -24,17 +24,11 @@ typedef struct { + int *result; + } TestBlockJob; + +-static void test_block_job_complete(Job *job, void *opaque) ++static void test_block_job_exit(Job *job) + { + BlockJob *bjob = container_of(job, BlockJob, job); + BlockDriverState *bs = blk_bs(bjob->blk); +- int rc = (intptr_t)opaque; + +- if (job_is_cancelled(job)) { +- rc = -ECANCELED; +- } +- +- job_completed(job, rc); + bdrv_unref(bs); + } + +@@ -54,8 +48,6 @@ static int coroutine_fn test_block_job_run(Job *job, Error **errp) + } + } + +- job_defer_to_main_loop(job, test_block_job_complete, +- (void *)(intptr_t)s->rc); + return s->rc; + } + +@@ -81,6 +73,7 @@ static const BlockJobDriver test_block_job_driver = { + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .run = test_block_job_run, ++ .exit = test_block_job_exit, + }, + }; + +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index 408a226..ad4a65b 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -163,11 +163,10 @@ typedef struct CancelJob { + bool completed; + } CancelJob; + +-static void cancel_job_completed(Job *job, void *opaque) ++static void cancel_job_exit(Job *job) + { +- CancelJob *s = opaque; ++ CancelJob *s = container_of(job, CancelJob, common.job); + s->completed = true; +- job_completed(job, 0); + } + + static void cancel_job_complete(Job *job, Error **errp) +@@ -182,7 +181,7 @@ static int coroutine_fn cancel_job_run(Job *job, Error **errp) + + while (!s->should_complete) { + if (job_is_cancelled(&s->common.job)) { +- goto defer; ++ return 0; + } + + if (!job_is_ready(&s->common.job) && s->should_converge) { +@@ -192,8 +191,6 @@ static int coroutine_fn cancel_job_run(Job *job, Error **errp) + job_sleep_ns(&s->common.job, 100000); + } + +- defer: +- job_defer_to_main_loop(&s->common.job, cancel_job_completed, s); + return 0; + } + +@@ -204,6 +201,7 @@ static const BlockJobDriver test_cancel_driver = { + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .run = cancel_job_run, ++ .exit = cancel_job_exit, + .complete = cancel_job_complete, + }, + }; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-libvhost-user-support-host-notifier.patch b/SOURCES/kvm-libvhost-user-support-host-notifier.patch new file mode 100644 index 0000000..62099e0 --- /dev/null +++ b/SOURCES/kvm-libvhost-user-support-host-notifier.patch @@ -0,0 +1,239 @@ +From bf38c0c9b897040dddc38456f731006c817e3c39 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Thu, 21 Jun 2018 18:54:45 +0200 +Subject: [PATCH 36/57] libvhost-user: support host notifier + +RH-Author: plai@redhat.com +Message-id: <1529607285-9942-11-git-send-email-plai@redhat.com> +Patchwork-id: 80939 +O-Subject: [RHEL7.6 PATCH BZ 1526645 10/10] libvhost-user: support host notifier +Bugzilla: 1526645 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Maxime Coquelin +RH-Acked-by: Laurent Vivier + +From: Tiwei Bie + +This patch introduces the host notifier support in +libvhost-user. A new API is added to support setting +host notifier for each queue. + +Signed-off-by: Tiwei Bie +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit d84599f56c820d8c1ac9928a76500dcdfbbf194d) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + contrib/libvhost-user/libvhost-user.c | 81 +++++++++++++++++++++++++++++++---- + contrib/libvhost-user/libvhost-user.h | 32 ++++++++++++++ + 2 files changed, 105 insertions(+), 8 deletions(-) + +diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-user.c +index beeed0c..fa06eee 100644 +--- a/contrib/libvhost-user/libvhost-user.c ++++ b/contrib/libvhost-user/libvhost-user.c +@@ -314,11 +314,6 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) + msg.msg_controllen = 0; + } + +- /* Set the version in the flags when sending the reply */ +- vmsg->flags &= ~VHOST_USER_VERSION_MASK; +- vmsg->flags |= VHOST_USER_VERSION; +- vmsg->flags |= VHOST_USER_REPLY_MASK; +- + do { + rc = sendmsg(conn_fd, &msg, 0); + } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); +@@ -339,6 +334,39 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) + return true; + } + ++static bool ++vu_send_reply(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) ++{ ++ /* Set the version in the flags when sending the reply */ ++ vmsg->flags &= ~VHOST_USER_VERSION_MASK; ++ vmsg->flags |= VHOST_USER_VERSION; ++ vmsg->flags |= VHOST_USER_REPLY_MASK; ++ ++ return vu_message_write(dev, conn_fd, vmsg); ++} ++ ++static bool ++vu_process_message_reply(VuDev *dev, const VhostUserMsg *vmsg) ++{ ++ VhostUserMsg msg_reply; ++ ++ if ((vmsg->flags & VHOST_USER_NEED_REPLY_MASK) == 0) { ++ return true; ++ } ++ ++ if (!vu_message_read(dev, dev->slave_fd, &msg_reply)) { ++ return false; ++ } ++ ++ if (msg_reply.request != vmsg->request) { ++ DPRINT("Received unexpected msg type. Expected %d received %d", ++ vmsg->request, msg_reply.request); ++ return false; ++ } ++ ++ return msg_reply.payload.u64 == 0; ++} ++ + /* Kick the log_call_fd if required. */ + static void + vu_log_kick(VuDev *dev) +@@ -534,7 +562,7 @@ vu_set_mem_table_exec_postcopy(VuDev *dev, VhostUserMsg *vmsg) + + /* Send the message back to qemu with the addresses filled in */ + vmsg->fd_num = 0; +- if (!vu_message_write(dev, dev->sock, vmsg)) { ++ if (!vu_send_reply(dev, dev->sock, vmsg)) { + vu_panic(dev, "failed to respond to set-mem-table for postcopy"); + return false; + } +@@ -914,6 +942,41 @@ void vu_set_queue_handler(VuDev *dev, VuVirtq *vq, + } + } + ++bool vu_set_queue_host_notifier(VuDev *dev, VuVirtq *vq, int fd, ++ int size, int offset) ++{ ++ int qidx = vq - dev->vq; ++ int fd_num = 0; ++ VhostUserMsg vmsg = { ++ .request = VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG, ++ .flags = VHOST_USER_VERSION | VHOST_USER_NEED_REPLY_MASK, ++ .size = sizeof(vmsg.payload.area), ++ .payload.area = { ++ .u64 = qidx & VHOST_USER_VRING_IDX_MASK, ++ .size = size, ++ .offset = offset, ++ }, ++ }; ++ ++ if (fd == -1) { ++ vmsg.payload.area.u64 |= VHOST_USER_VRING_NOFD_MASK; ++ } else { ++ vmsg.fds[fd_num++] = fd; ++ } ++ ++ vmsg.fd_num = fd_num; ++ ++ if ((dev->protocol_features & VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD) == 0) { ++ return false; ++ } ++ ++ if (!vu_message_write(dev, dev->slave_fd, &vmsg)) { ++ return false; ++ } ++ ++ return vu_process_message_reply(dev, &vmsg); ++} ++ + static bool + vu_set_vring_call_exec(VuDev *dev, VhostUserMsg *vmsg) + { +@@ -966,7 +1029,9 @@ static bool + vu_get_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg) + { + uint64_t features = 1ULL << VHOST_USER_PROTOCOL_F_LOG_SHMFD | +- 1ULL << VHOST_USER_PROTOCOL_F_SLAVE_REQ; ++ 1ULL << VHOST_USER_PROTOCOL_F_SLAVE_REQ | ++ 1ULL << VHOST_USER_PROTOCOL_F_HOST_NOTIFIER | ++ 1ULL << VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD; + + if (have_userfault()) { + features |= 1ULL << VHOST_USER_PROTOCOL_F_PAGEFAULT; +@@ -1250,7 +1315,7 @@ vu_dispatch(VuDev *dev) + goto end; + } + +- if (!vu_message_write(dev, dev->sock, &vmsg)) { ++ if (!vu_send_reply(dev, dev->sock, &vmsg)) { + goto end; + } + +diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h +index b27075e..4aa55b4 100644 +--- a/contrib/libvhost-user/libvhost-user.h ++++ b/contrib/libvhost-user/libvhost-user.h +@@ -51,6 +51,8 @@ enum VhostUserProtocolFeature { + VHOST_USER_PROTOCOL_F_CRYPTO_SESSION = 7, + VHOST_USER_PROTOCOL_F_PAGEFAULT = 8, + VHOST_USER_PROTOCOL_F_CONFIG = 9, ++ VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD = 10, ++ VHOST_USER_PROTOCOL_F_HOST_NOTIFIER = 11, + + VHOST_USER_PROTOCOL_F_MAX + }; +@@ -92,6 +94,14 @@ typedef enum VhostUserRequest { + VHOST_USER_MAX + } VhostUserRequest; + ++typedef enum VhostUserSlaveRequest { ++ VHOST_USER_SLAVE_NONE = 0, ++ VHOST_USER_SLAVE_IOTLB_MSG = 1, ++ VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2, ++ VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG = 3, ++ VHOST_USER_SLAVE_MAX ++} VhostUserSlaveRequest; ++ + typedef struct VhostUserMemoryRegion { + uint64_t guest_phys_addr; + uint64_t memory_size; +@@ -122,6 +132,12 @@ static VhostUserConfig c __attribute__ ((unused)); + + sizeof(c.size) \ + + sizeof(c.flags)) + ++typedef struct VhostUserVringArea { ++ uint64_t u64; ++ uint64_t size; ++ uint64_t offset; ++} VhostUserVringArea; ++ + #if defined(_WIN32) + # define VU_PACKED __attribute__((gcc_struct, packed)) + #else +@@ -133,6 +149,7 @@ typedef struct VhostUserMsg { + + #define VHOST_USER_VERSION_MASK (0x3) + #define VHOST_USER_REPLY_MASK (0x1 << 2) ++#define VHOST_USER_NEED_REPLY_MASK (0x1 << 3) + uint32_t flags; + uint32_t size; /* the following payload size */ + +@@ -145,6 +162,7 @@ typedef struct VhostUserMsg { + VhostUserMemory memory; + VhostUserLog log; + VhostUserConfig config; ++ VhostUserVringArea area; + } payload; + + int fds[VHOST_MEMORY_MAX_NREGIONS]; +@@ -368,6 +386,20 @@ VuVirtq *vu_get_queue(VuDev *dev, int qidx); + void vu_set_queue_handler(VuDev *dev, VuVirtq *vq, + vu_queue_handler_cb handler); + ++/** ++ * vu_set_queue_host_notifier: ++ * @dev: a VuDev context ++ * @vq: a VuVirtq queue ++ * @fd: a file descriptor ++ * @size: host page size ++ * @offset: notifier offset in @fd file ++ * ++ * Set queue's host notifier. This function may be called several ++ * times for the same queue. If called with -1 @fd, the notifier ++ * is removed. ++ */ ++bool vu_set_queue_host_notifier(VuDev *dev, VuVirtq *vq, int fd, ++ int size, int offset); + + /** + * vu_queue_set_notification: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-linux-headers-asm-s390-kvm.h-header-sync.patch b/SOURCES/kvm-linux-headers-asm-s390-kvm.h-header-sync.patch new file mode 100644 index 0000000..808ce50 --- /dev/null +++ b/SOURCES/kvm-linux-headers-asm-s390-kvm.h-header-sync.patch @@ -0,0 +1,56 @@ +From cbc01f1f70ca63d88bc80b38884a064957c9dc41 Mon Sep 17 00:00:00 2001 +From: Thomas Huth +Date: Tue, 28 Aug 2018 17:03:11 +0200 +Subject: [PATCH 01/29] linux-headers: asm-s390/kvm.h header sync + +RH-Author: Thomas Huth +Message-id: <1535475792-21136-2-git-send-email-thuth@redhat.com> +Patchwork-id: 81947 +O-Subject: [RHEL-7.6 qemu-kvm-ma PATCH 1/2] linux-headers: asm-s390/kvm.h header sync +Bugzilla: 1622962 +RH-Acked-by: David Hildenbrand +RH-Acked-by: Cornelia Huck +RH-Acked-by: Jens Freimann + +This is a header sync with the linux uapi header. The corresponding +kernel commit id is a3da7b4a3be51f37f434f14e11e60491f098b6ea. + +Signed-off-by: Thomas Huth +Signed-off-by: Miroslav Rezanina +--- + linux-headers/asm-s390/kvm.h | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/linux-headers/asm-s390/kvm.h b/linux-headers/asm-s390/kvm.h +index 11def14..1ab9901 100644 +--- a/linux-headers/asm-s390/kvm.h ++++ b/linux-headers/asm-s390/kvm.h +@@ -4,7 +4,7 @@ + /* + * KVM s390 specific structures and definitions + * +- * Copyright IBM Corp. 2008 ++ * Copyright IBM Corp. 2008, 2018 + * + * Author(s): Carsten Otte + * Christian Borntraeger +@@ -225,6 +225,7 @@ struct kvm_guest_debug_arch { + #define KVM_SYNC_FPRS (1UL << 8) + #define KVM_SYNC_GSCB (1UL << 9) + #define KVM_SYNC_BPBC (1UL << 10) ++#define KVM_SYNC_ETOKEN (1UL << 11) + /* length and alignment of the sdnx as a power of two */ + #define SDNXC 8 + #define SDNXL (1UL << SDNXC) +@@ -258,6 +259,8 @@ struct kvm_sync_regs { + struct { + __u64 reserved1[2]; + __u64 gscb[4]; ++ __u64 etoken; ++ __u64 etoken_extension; + }; + }; + }; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-luks-Allow-share-rw-on.patch b/SOURCES/kvm-luks-Allow-share-rw-on.patch new file mode 100644 index 0000000..527a3ff --- /dev/null +++ b/SOURCES/kvm-luks-Allow-share-rw-on.patch @@ -0,0 +1,54 @@ +From baa218a4170b17e3699272a481ee04616c9432f0 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 4 Feb 2019 21:08:22 +0100 +Subject: [PATCH 8/8] luks: Allow share-rw=on +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Max Reitz +Message-id: <20190204210822.10343-2-mreitz@redhat.com> +Patchwork-id: 84228 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/1] luks: Allow share-rw=on +Bugzilla: 1598119 +RH-Acked-by: John Snow +RH-Acked-by: Yash Mankad +RH-Acked-by: Kevin Wolf + +From: Fam Zheng + +Format drivers such as qcow2 don't allow sharing the same image between +two QEMU instances in order to prevent image corruptions, because of +metadata cache. LUKS driver don't modify metadata except for when +creating image, so it is safe to relax the permission. This makes +share-rw=on property work on virtual devices. + +Suggested-by: Daniel P. Berrangé +Signed-off-by: Fam Zheng +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Kevin Wolf +(cherry picked from commit 497da8236ab2663a8108858ba7ea59aac21c5fe6) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/crypto.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/block/crypto.c b/block/crypto.c +index 02f04f3..0bb0db6 100644 +--- a/block/crypto.c ++++ b/block/crypto.c +@@ -698,7 +698,9 @@ BlockDriver bdrv_crypto_luks = { + .bdrv_probe = block_crypto_probe_luks, + .bdrv_open = block_crypto_open_luks, + .bdrv_close = block_crypto_close, +- .bdrv_child_perm = bdrv_format_default_perms, ++ /* This driver doesn't modify LUKS metadata except when creating image. ++ * Allow share-rw=on as a special case. */ ++ .bdrv_child_perm = bdrv_filter_default_perms, + .bdrv_co_create = block_crypto_co_create_luks, + .bdrv_co_create_opts = block_crypto_co_create_opts_luks, + .bdrv_co_truncate = block_crypto_co_truncate, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-main-loop-drop-spin_counter.patch b/SOURCES/kvm-main-loop-drop-spin_counter.patch new file mode 100644 index 0000000..ce519f3 --- /dev/null +++ b/SOURCES/kvm-main-loop-drop-spin_counter.patch @@ -0,0 +1,106 @@ +From d215f56d789b8d6a2cb27501c89a516d213b1af3 Mon Sep 17 00:00:00 2001 +From: Jeffrey Cody +Date: Wed, 20 Jun 2018 17:44:46 +0200 +Subject: [PATCH 51/54] main-loop: drop spin_counter + +RH-Author: Jeffrey Cody +Message-id: <835200bb37bc622f0f6f9c471c809a1b73a08482.1529516334.git.jcody@redhat.com> +Patchwork-id: 80906 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 1/1] main-loop: drop spin_counter +Bugzilla: 1168213 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Markus Armbruster + +From: Stefan Hajnoczi + +Commit d759c951f3287fad04210a52f2dc93f94cf58c7f ("replay: push +replay_mutex_lock up the call tree") removed the !timeout lock +optimization in the main loop. + +The idea of the optimization was to avoid ping-pongs between threads by +keeping the Big QEMU Lock held across non-blocking (!timeout) main loop +iterations. + +A warning is printed when the main loop spins without releasing BQL for +long periods of time. These warnings were supposed to aid debugging but +in practice they just alarm users. They are considered noise because +the cause of spinning is not shown and is hard to find. + +Now that the lock optimization has been removed, there is no danger of +hogging the BQL. Drop the spin counter and the infamous warning. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Jeff Cody +(cherry picked from commit 21891a5a3011608845b5d7f1f9cce60cdc2bcc62) +Note: The original patch series from Stefan makes note that the + patch can be dropped because the lock optimization changed + upstream. + + However, even without that optimization change upstream, this + warning / debug message is not very useful, and is just noise that + does not aid in debugging. Go ahead and remove it anyway. +Signed-off-by: Jeff Cody + +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/common.filter | 1 - + util/main-loop.c | 25 ------------------------- + 2 files changed, 26 deletions(-) + +diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter +index c5f4bcf..7acb454 100644 +--- a/tests/qemu-iotests/common.filter ++++ b/tests/qemu-iotests/common.filter +@@ -77,7 +77,6 @@ _filter_qemu() + { + sed -e "s#\\(^\\|(qemu) \\)$(basename $QEMU_PROG):#\1QEMU_PROG:#" \ + -e 's#^QEMU [0-9]\+\.[0-9]\+\.[0-9]\+ monitor#QEMU X.Y.Z monitor#' \ +- -e '/main-loop: WARNING: I\/O thread spun for [0-9]\+ iterations/d' \ + -e $'s#\r##' # QEMU monitor uses \r\n line endings + } + +diff --git a/util/main-loop.c b/util/main-loop.c +index 992f9b0..affe040 100644 +--- a/util/main-loop.c ++++ b/util/main-loop.c +@@ -222,36 +222,11 @@ static int os_host_main_loop_wait(int64_t timeout) + { + GMainContext *context = g_main_context_default(); + int ret; +- static int spin_counter; + + g_main_context_acquire(context); + + glib_pollfds_fill(&timeout); + +- /* If the I/O thread is very busy or we are incorrectly busy waiting in +- * the I/O thread, this can lead to starvation of the BQL such that the +- * VCPU threads never run. To make sure we can detect the later case, +- * print a message to the screen. If we run into this condition, create +- * a fake timeout in order to give the VCPU threads a chance to run. +- */ +- if (!timeout && (spin_counter > MAX_MAIN_LOOP_SPIN)) { +- static bool notified; +- +- if (!notified && !qtest_enabled() && !qtest_driver()) { +- warn_report("I/O thread spun for %d iterations", +- MAX_MAIN_LOOP_SPIN); +- notified = true; +- } +- +- timeout = SCALE_MS; +- } +- +- +- if (timeout) { +- spin_counter = 0; +- } else { +- spin_counter++; +- } + qemu_mutex_unlock_iothread(); + replay_mutex_unlock(); + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-maint-Allow-for-EXAMPLES-in-texi2pod.patch b/SOURCES/kvm-maint-Allow-for-EXAMPLES-in-texi2pod.patch new file mode 100644 index 0000000..cb99787 --- /dev/null +++ b/SOURCES/kvm-maint-Allow-for-EXAMPLES-in-texi2pod.patch @@ -0,0 +1,62 @@ +From f7e8e465f2c37a28023dea7d3e99a074c0118e73 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:36 +0100 +Subject: [PATCH 098/163] maint: Allow for EXAMPLES in texi2pod + +RH-Author: John Snow +Message-id: <20190327172308.31077-24-jsnow@redhat.com> +Patchwork-id: 85195 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 23/55] maint: Allow for EXAMPLES in texi2pod +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +The next commit will add an EXAMPLES section to qemu-nbd.8; +for that to work, we need to recognize EXAMPLES in texi2pod. +We also need to add a dependency from all man pages against +the generator script, since a change to the generator may +cause the resulting man page to differ. + +Signed-off-by: Eric Blake +Reviewed-by: Richard W.M. Jones +Message-Id: <20190117193658.16413-3-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit ae560cc34f9ff4662d4ca1425b88fd1f85f52817) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + Makefile | 1 + + scripts/texi2pod.pl | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/Makefile b/Makefile +index c95553f..a6711b2 100644 +--- a/Makefile ++++ b/Makefile +@@ -992,6 +992,7 @@ docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \ + docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \ + docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi + ++$(filter %.1 %.7 %.8,$(DOCS)): scripts/texi2pod.pl + + ifdef CONFIG_WIN32 + +diff --git a/scripts/texi2pod.pl b/scripts/texi2pod.pl +index 39ce584..839b791 100755 +--- a/scripts/texi2pod.pl ++++ b/scripts/texi2pod.pl +@@ -398,7 +398,7 @@ $sects{NAME} = "$fn \- $tl\n"; + $sects{FOOTNOTES} .= "=back\n" if exists $sects{FOOTNOTES}; + + for $sect (qw(NAME SYNOPSIS DESCRIPTION OPTIONS ENVIRONMENT FILES +- BUGS NOTES FOOTNOTES SEEALSO AUTHOR COPYRIGHT)) { ++ BUGS NOTES FOOTNOTES EXAMPLES SEEALSO AUTHOR COPYRIGHT)) { + if(exists $sects{$sect}) { + $head = $sect; + $head =~ s/SEEALSO/SEE ALSO/; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-mem-nvdimm-ensure-write-persistence-to-PMEM-in-label.patch b/SOURCES/kvm-mem-nvdimm-ensure-write-persistence-to-PMEM-in-label.patch new file mode 100644 index 0000000..b5b809f --- /dev/null +++ b/SOURCES/kvm-mem-nvdimm-ensure-write-persistence-to-PMEM-in-label.patch @@ -0,0 +1,108 @@ +From 22200b24c1f26602467d47738b4bb3e6da6c4f86 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Fri, 31 Aug 2018 16:25:57 +0200 +Subject: [PATCH 15/29] mem/nvdimm: ensure write persistence to PMEM in label + emulation + +RH-Author: plai@redhat.com +Message-id: <1535732759-22481-8-git-send-email-plai@redhat.com> +Patchwork-id: 82013 +O-Subject: [RHEL7.6 PATCH BZ 1539280 7/9] mem/nvdimm: ensure write persistence to PMEM in label emulation +Bugzilla: 1539280 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Pankaj Gupta +RH-Acked-by: Miroslav Rezanina + +From: Junyan He + +Guest writes to vNVDIMM labels are intercepted and performed on the +backend by QEMU. When the backend is a real persistent memort, QEMU +needs to take proper operations to ensure its write persistence on the +persistent memory. Otherwise, a host power failure may result in the +loss of guest label configurations. + +Signed-off-by: Haozhong Zhang +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Igor Mammedov +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +Reviewed-by: Richard Henderson +(cherry picked from commit faf8a13d80de98b43342a7ec9878b4fd76b18327) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + hw/mem/nvdimm.c | 9 ++++++++- + include/qemu/pmem.h | 30 ++++++++++++++++++++++++++++++ + 2 files changed, 38 insertions(+), 1 deletion(-) + create mode 100644 include/qemu/pmem.h + +diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c +index acb656b..0c962fd 100644 +--- a/hw/mem/nvdimm.c ++++ b/hw/mem/nvdimm.c +@@ -23,6 +23,7 @@ + */ + + #include "qemu/osdep.h" ++#include "qemu/pmem.h" + #include "qapi/error.h" + #include "qapi/visitor.h" + #include "hw/mem/nvdimm.h" +@@ -155,11 +156,17 @@ static void nvdimm_write_label_data(NVDIMMDevice *nvdimm, const void *buf, + { + MemoryRegion *mr; + PCDIMMDevice *dimm = PC_DIMM(nvdimm); ++ bool is_pmem = object_property_get_bool(OBJECT(dimm->hostmem), ++ "pmem", NULL); + uint64_t backend_offset; + + nvdimm_validate_rw_label_data(nvdimm, size, offset); + +- memcpy(nvdimm->label_data + offset, buf, size); ++ if (!is_pmem) { ++ memcpy(nvdimm->label_data + offset, buf, size); ++ } else { ++ pmem_memcpy_persist(nvdimm->label_data + offset, buf, size); ++ } + + mr = host_memory_backend_get_memory(dimm->hostmem, &error_abort); + backend_offset = memory_region_size(mr) - nvdimm->label_size + offset; +diff --git a/include/qemu/pmem.h b/include/qemu/pmem.h +new file mode 100644 +index 0000000..ebdb070 +--- /dev/null ++++ b/include/qemu/pmem.h +@@ -0,0 +1,30 @@ ++/* ++ * QEMU header file for libpmem. ++ * ++ * Copyright (c) 2018 Intel Corporation. ++ * ++ * Author: Haozhong Zhang ++ * ++ * 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_PMEM_H ++#define QEMU_PMEM_H ++ ++#ifdef CONFIG_LIBPMEM ++#include ++#else /* !CONFIG_LIBPMEM */ ++ ++static inline void * ++pmem_memcpy_persist(void *pmemdest, const void *src, size_t len) ++{ ++ /* If 'pmem' option is 'on', we should always have libpmem support, ++ or qemu will report a error and exit, never come here. */ ++ g_assert_not_reached(); ++ return NULL; ++} ++ ++#endif /* CONFIG_LIBPMEM */ ++ ++#endif /* !QEMU_PMEM_H */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-memory-cleanup-side-effects-of-memory_region_init_fo.patch b/SOURCES/kvm-memory-cleanup-side-effects-of-memory_region_init_fo.patch new file mode 100644 index 0000000..15b78ad --- /dev/null +++ b/SOURCES/kvm-memory-cleanup-side-effects-of-memory_region_init_fo.patch @@ -0,0 +1,191 @@ +From 2d02fc0e933a7d4a30e8bb7c665d898d00ac5e00 Mon Sep 17 00:00:00 2001 +From: Igor Mammedov +Date: Fri, 23 Nov 2018 11:58:47 +0100 +Subject: [PATCH 14/34] memory: cleanup side effects of + memory_region_init_foo() on failure +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Igor Mammedov +Message-id: <1542974327-165907-1-git-send-email-imammedo@redhat.com> +Patchwork-id: 83127 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2] memory: cleanup side effects of memory_region_init_foo() on failure +Bugzilla: 1585155 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Philippe Mathieu-Daudé + +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1585155 +Brew: http://brewweb.devel.redhat.com/brew/taskinfo?taskID=19261375 + +if MemoryRegion intialization fails it's left in semi-initialized state, +where it's size is not 0 and attached as child to owner object. +And this leds to crash in following use-case: + (monitor) object_add memory-backend-file,id=mem1,size=99999G,mem-path=/tmp/foo,discard-data=yes + memory.c:2083: memory_region_get_ram_ptr: Assertion `mr->ram_block' failed + Aborted (core dumped) +it happens due to assumption that memory region is intialized when + memory_region_size() != 0 +and therefore it's ok to access it in + file_backend_unparent() + if (memory_region_size() != 0) + memory_region_get_ram_ptr() + +which happens when object_add fails and unparents failed backend making +file_backend_unparent() access invalid memory region. + +Fix it by making sure that memory_region_init_foo() APIs cleanup externally +visible side effects on failure (like set size to 0 and unparenting object) + +Signed-off-by: Igor Mammedov +Message-Id: <1536064777-42312-1-git-send-email-imammedo@redhat.com> +Signed-off-by: Paolo Bonzini +(cherry picked from commit 1cd3d492624da399d66c4c3e6a5eabb8f96bb0a2) +Signed-off-by: Igor Mammedov +--- +v2: + - clean backport since dependency has been backported and merged as + (commit 754dd8f2 memory, exec: switch file ram allocation functions to 'flags' parameters) + + enter the commit message for your changes. Lines starting + +Signed-off-by: Miroslav Rezanina +--- + memory.c | 48 ++++++++++++++++++++++++++++++++++++++++++------ + 1 file changed, 42 insertions(+), 6 deletions(-) + +diff --git a/memory.c b/memory.c +index 7b47f53..4974f97 100644 +--- a/memory.c ++++ b/memory.c +@@ -1519,12 +1519,18 @@ void memory_region_init_ram_shared_nomigrate(MemoryRegion *mr, + bool share, + Error **errp) + { ++ Error *err = NULL; + memory_region_init(mr, owner, name, size); + mr->ram = true; + mr->terminates = true; + mr->destructor = memory_region_destructor_ram; +- mr->ram_block = qemu_ram_alloc(size, share, mr, errp); ++ mr->ram_block = qemu_ram_alloc(size, share, mr, &err); + mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; ++ if (err) { ++ mr->size = int128_zero(); ++ object_unparent(OBJECT(mr)); ++ error_propagate(errp, err); ++ } + } + + void memory_region_init_resizeable_ram(MemoryRegion *mr, +@@ -1537,13 +1543,19 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr, + void *host), + Error **errp) + { ++ Error *err = NULL; + memory_region_init(mr, owner, name, size); + mr->ram = true; + mr->terminates = true; + mr->destructor = memory_region_destructor_ram; + mr->ram_block = qemu_ram_alloc_resizeable(size, max_size, resized, +- mr, errp); ++ mr, &err); + mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; ++ if (err) { ++ mr->size = int128_zero(); ++ object_unparent(OBJECT(mr)); ++ error_propagate(errp, err); ++ } + } + + #ifdef __linux__ +@@ -1556,13 +1568,19 @@ void memory_region_init_ram_from_file(MemoryRegion *mr, + const char *path, + Error **errp) + { ++ Error *err = NULL; + memory_region_init(mr, owner, name, size); + mr->ram = true; + mr->terminates = true; + mr->destructor = memory_region_destructor_ram; + mr->align = align; +- mr->ram_block = qemu_ram_alloc_from_file(size, mr, ram_flags, path, errp); ++ mr->ram_block = qemu_ram_alloc_from_file(size, mr, ram_flags, path, &err); + mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; ++ if (err) { ++ mr->size = int128_zero(); ++ object_unparent(OBJECT(mr)); ++ error_propagate(errp, err); ++ } + } + + void memory_region_init_ram_from_fd(MemoryRegion *mr, +@@ -1573,14 +1591,20 @@ void memory_region_init_ram_from_fd(MemoryRegion *mr, + int fd, + Error **errp) + { ++ Error *err = NULL; + memory_region_init(mr, owner, name, size); + mr->ram = true; + mr->terminates = true; + mr->destructor = memory_region_destructor_ram; + mr->ram_block = qemu_ram_alloc_from_fd(size, mr, + share ? RAM_SHARED : 0, +- fd, errp); ++ fd, &err); + mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; ++ if (err) { ++ mr->size = int128_zero(); ++ object_unparent(OBJECT(mr)); ++ error_propagate(errp, err); ++ } + } + #endif + +@@ -1631,13 +1655,19 @@ void memory_region_init_rom_nomigrate(MemoryRegion *mr, + uint64_t size, + Error **errp) + { ++ Error *err = NULL; + memory_region_init(mr, owner, name, size); + mr->ram = true; + mr->readonly = true; + mr->terminates = true; + mr->destructor = memory_region_destructor_ram; +- mr->ram_block = qemu_ram_alloc(size, false, mr, errp); ++ mr->ram_block = qemu_ram_alloc(size, false, mr, &err); + mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; ++ if (err) { ++ mr->size = int128_zero(); ++ object_unparent(OBJECT(mr)); ++ error_propagate(errp, err); ++ } + } + + void memory_region_init_rom_device_nomigrate(MemoryRegion *mr, +@@ -1648,6 +1678,7 @@ void memory_region_init_rom_device_nomigrate(MemoryRegion *mr, + uint64_t size, + Error **errp) + { ++ Error *err = NULL; + assert(ops); + memory_region_init(mr, owner, name, size); + mr->ops = ops; +@@ -1655,7 +1686,12 @@ void memory_region_init_rom_device_nomigrate(MemoryRegion *mr, + mr->terminates = true; + mr->rom_device = true; + mr->destructor = memory_region_destructor_ram; +- mr->ram_block = qemu_ram_alloc(size, false, mr, errp); ++ mr->ram_block = qemu_ram_alloc(size, false, mr, &err); ++ if (err) { ++ mr->size = int128_zero(); ++ object_unparent(OBJECT(mr)); ++ error_propagate(errp, err); ++ } + } + + void memory_region_init_iommu(void *_iommu_mr, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-memory-exec-Expose-all-memory-block-related-flags.patch b/SOURCES/kvm-memory-exec-Expose-all-memory-block-related-flags.patch new file mode 100644 index 0000000..27652f7 --- /dev/null +++ b/SOURCES/kvm-memory-exec-Expose-all-memory-block-related-flags.patch @@ -0,0 +1,99 @@ +From dfca592a8a3fb1e781775020961ed16b8aea258e Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Fri, 31 Aug 2018 16:25:53 +0200 +Subject: [PATCH 11/29] memory, exec: Expose all memory block related flags. + +RH-Author: plai@redhat.com +Message-id: <1535732759-22481-4-git-send-email-plai@redhat.com> +Patchwork-id: 82006 +O-Subject: [RHEL7.6 PATCH BZ 1539280 3/9] memory, exec: Expose all memory block related flags. +Bugzilla: 1539280 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Pankaj Gupta +RH-Acked-by: Miroslav Rezanina + +From: Junyan He + +We need to use these flags in other files rather than just in exec.c, +For example, RAM_SHARED should be used when create a ram block from file. +We expose them the exec/memory.h + +Signed-off-by: Junyan He +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Igor Mammedov +Reviewed-by: Richard Henderson +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit b0e5de93811077254a536c23b713b49e12efb742) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + exec.c | 20 -------------------- + include/exec/memory.h | 20 ++++++++++++++++++++ + 2 files changed, 20 insertions(+), 20 deletions(-) + +diff --git a/exec.c b/exec.c +index 7323d39..f21be9c 100644 +--- a/exec.c ++++ b/exec.c +@@ -87,26 +87,6 @@ AddressSpace address_space_memory; + + MemoryRegion io_mem_rom, io_mem_notdirty; + static MemoryRegion io_mem_unassigned; +- +-/* RAM is pre-allocated and passed into qemu_ram_alloc_from_ptr */ +-#define RAM_PREALLOC (1 << 0) +- +-/* RAM is mmap-ed with MAP_SHARED */ +-#define RAM_SHARED (1 << 1) +- +-/* Only a portion of RAM (used_length) is actually used, and migrated. +- * This used_length size can change across reboots. +- */ +-#define RAM_RESIZEABLE (1 << 2) +- +-/* UFFDIO_ZEROPAGE is available on this RAMBlock to atomically +- * zero the page and wake waiting processes. +- * (Set during postcopy) +- */ +-#define RAM_UF_ZEROPAGE (1 << 3) +- +-/* RAM can be migrated */ +-#define RAM_MIGRATABLE (1 << 4) + #endif + + #ifdef TARGET_PAGE_BITS_VARY +diff --git a/include/exec/memory.h b/include/exec/memory.h +index 31eae0a..db46501 100644 +--- a/include/exec/memory.h ++++ b/include/exec/memory.h +@@ -102,6 +102,26 @@ struct IOMMUNotifier { + }; + typedef struct IOMMUNotifier IOMMUNotifier; + ++/* RAM is pre-allocated and passed into qemu_ram_alloc_from_ptr */ ++#define RAM_PREALLOC (1 << 0) ++ ++/* RAM is mmap-ed with MAP_SHARED */ ++#define RAM_SHARED (1 << 1) ++ ++/* Only a portion of RAM (used_length) is actually used, and migrated. ++ * This used_length size can change across reboots. ++ */ ++#define RAM_RESIZEABLE (1 << 2) ++ ++/* UFFDIO_ZEROPAGE is available on this RAMBlock to atomically ++ * zero the page and wake waiting processes. ++ * (Set during postcopy) ++ */ ++#define RAM_UF_ZEROPAGE (1 << 3) ++ ++/* RAM can be migrated */ ++#define RAM_MIGRATABLE (1 << 4) ++ + static inline void iommu_notifier_init(IOMMUNotifier *n, IOMMUNotify fn, + IOMMUNotifierFlag flags, + hwaddr start, hwaddr end) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-memory-exec-switch-file-ram-allocation-functions-to-.patch b/SOURCES/kvm-memory-exec-switch-file-ram-allocation-functions-to-.patch new file mode 100644 index 0000000..57c895b --- /dev/null +++ b/SOURCES/kvm-memory-exec-switch-file-ram-allocation-functions-to-.patch @@ -0,0 +1,234 @@ +From 754dd8f2bfcfd5c98710e910f6343b32af7cd3af Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Fri, 31 Aug 2018 16:25:54 +0200 +Subject: [PATCH 12/29] memory, exec: switch file ram allocation functions to + 'flags' parameters + +RH-Author: plai@redhat.com +Message-id: <1535732759-22481-5-git-send-email-plai@redhat.com> +Patchwork-id: 82005 +O-Subject: [RHEL7.6 PATCH BZ 1539280 4/9] memory, exec: switch file ram allocation functions to 'flags' parameters +Bugzilla: 1539280 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Pankaj Gupta +RH-Acked-by: Miroslav Rezanina + +From: Junyan He + +As more flag parameters besides the existing 'share' are going to be +added to following functions +memory_region_init_ram_from_file +qemu_ram_alloc_from_fd +qemu_ram_alloc_from_file +let's switch them to use the 'flags' parameters so as to ease future +flag additions. + +The existing 'share' flag is converted to the RAM_SHARED bit in ram_flags, +and other flag bits are ignored by above functions right now. + +Signed-off-by: Junyan He +Signed-off-by: Haozhong Zhang +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Igor Mammedov +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +Reviewed-by: Richard Henderson +(cherry picked from commit cbfc01710362f3de6fca3010a17b0e1c866fc181) +Signed-off-by: Paul Lai + +Resolved Conflicts: + include/exec/ram_addr.h + +Signed-off-by: Miroslav Rezanina +--- + backends/hostmem-file.c | 3 ++- + exec.c | 10 +++++----- + include/exec/memory.h | 7 +++++-- + include/exec/ram_addr.h | 25 +++++++++++++++++++++++-- + memory.c | 8 +++++--- + numa.c | 2 +- + 6 files changed, 41 insertions(+), 14 deletions(-) + +diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c +index 134b08d..34c68bb 100644 +--- a/backends/hostmem-file.c ++++ b/backends/hostmem-file.c +@@ -58,7 +58,8 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) + path = object_get_canonical_path(OBJECT(backend)); + memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), + path, +- backend->size, fb->align, backend->share, ++ backend->size, fb->align, ++ backend->share ? RAM_SHARED : 0, + fb->mem_path, errp); + g_free(path); + } +diff --git a/exec.c b/exec.c +index f21be9c..295142b 100644 +--- a/exec.c ++++ b/exec.c +@@ -2038,7 +2038,7 @@ static void ram_block_add(RAMBlock *new_block, Error **errp, bool shared) + + #ifdef __linux__ + RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, +- bool share, int fd, ++ uint32_t ram_flags, int fd, + Error **errp) + { + RAMBlock *new_block; +@@ -2080,14 +2080,14 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, + new_block->mr = mr; + new_block->used_length = size; + new_block->max_length = size; +- new_block->flags = share ? RAM_SHARED : 0; ++ new_block->flags = ram_flags; + new_block->host = file_ram_alloc(new_block, size, fd, !file_size, errp); + if (!new_block->host) { + g_free(new_block); + return NULL; + } + +- ram_block_add(new_block, &local_err, share); ++ ram_block_add(new_block, &local_err, ram_flags & RAM_SHARED); + if (local_err) { + g_free(new_block); + error_propagate(errp, local_err); +@@ -2099,7 +2099,7 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, + + + RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, +- bool share, const char *mem_path, ++ uint32_t ram_flags, const char *mem_path, + Error **errp) + { + int fd; +@@ -2111,7 +2111,7 @@ RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, + return NULL; + } + +- block = qemu_ram_alloc_from_fd(size, mr, share, fd, errp); ++ block = qemu_ram_alloc_from_fd(size, mr, ram_flags, fd, errp); + if (!block) { + if (created) { + unlink(mem_path); +diff --git a/include/exec/memory.h b/include/exec/memory.h +index db46501..b3abe61 100644 +--- a/include/exec/memory.h ++++ b/include/exec/memory.h +@@ -527,6 +527,7 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr, + void *host), + Error **errp); + #ifdef __linux__ ++ + /** + * memory_region_init_ram_from_file: Initialize RAM memory region with a + * mmap-ed backend. +@@ -538,7 +539,9 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr, + * @size: size of the region. + * @align: alignment of the region base address; if 0, the default alignment + * (getpagesize()) will be used. +- * @share: %true if memory must be mmaped with the MAP_SHARED flag ++ * @ram_flags: Memory region features: ++ * - RAM_SHARED: memory must be mmaped with the MAP_SHARED flag ++ * Other bits are ignored now. + * @path: the path in which to allocate the RAM. + * @errp: pointer to Error*, to store an error if it happens. + * +@@ -550,7 +553,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr, + const char *name, + uint64_t size, + uint64_t align, +- bool share, ++ uint32_t ram_flags, + const char *path, + Error **errp); + +diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h +index cf2446a..67e163e 100644 +--- a/include/exec/ram_addr.h ++++ b/include/exec/ram_addr.h +@@ -72,12 +72,33 @@ static inline unsigned long int ramblock_recv_bitmap_offset(void *host_addr, + + long qemu_getrampagesize(void); + unsigned long last_ram_page(void); ++ ++/** ++ * qemu_ram_alloc_from_file, ++ * qemu_ram_alloc_from_fd: Allocate a ram block from the specified backing ++ * file or device ++ * ++ * Parameters: ++ * @size: the size in bytes of the ram block ++ * @mr: the memory region where the ram block is ++ * @ram_flags: specify the properties of the ram block, which can be one ++ * or bit-or of following values ++ * - RAM_SHARED: mmap the backing file or device with MAP_SHARED ++ * Other bits are ignored. ++ * @mem_path or @fd: specify the backing file or device ++ * @errp: pointer to Error*, to store an error if it happens ++ * ++ * Return: ++ * On success, return a pointer to the ram block. ++ * On failure, return NULL. ++ */ + RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, +- bool share, const char *mem_path, ++ uint32_t ram_flags, const char *mem_path, + Error **errp); + RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, +- bool share, int fd, ++ uint32_t ram_flags, int fd, + Error **errp); ++ + RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, + MemoryRegion *mr, Error **errp); + RAMBlock *qemu_ram_alloc(ram_addr_t size, bool share, MemoryRegion *mr, +diff --git a/memory.c b/memory.c +index e70b64b..7b47f53 100644 +--- a/memory.c ++++ b/memory.c +@@ -1552,7 +1552,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr, + const char *name, + uint64_t size, + uint64_t align, +- bool share, ++ uint32_t ram_flags, + const char *path, + Error **errp) + { +@@ -1561,7 +1561,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr, + mr->terminates = true; + mr->destructor = memory_region_destructor_ram; + mr->align = align; +- mr->ram_block = qemu_ram_alloc_from_file(size, mr, share, path, errp); ++ mr->ram_block = qemu_ram_alloc_from_file(size, mr, ram_flags, path, errp); + mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; + } + +@@ -1577,7 +1577,9 @@ void memory_region_init_ram_from_fd(MemoryRegion *mr, + mr->ram = true; + mr->terminates = true; + mr->destructor = memory_region_destructor_ram; +- mr->ram_block = qemu_ram_alloc_from_fd(size, mr, share, fd, errp); ++ mr->ram_block = qemu_ram_alloc_from_fd(size, mr, ++ share ? RAM_SHARED : 0, ++ fd, errp); + mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; + } + #endif +diff --git a/numa.c b/numa.c +index bb8f773..e78fba5 100644 +--- a/numa.c ++++ b/numa.c +@@ -456,7 +456,7 @@ static void allocate_system_memory_nonnuma(MemoryRegion *mr, Object *owner, + if (mem_path) { + #ifdef __linux__ + Error *err = NULL; +- memory_region_init_ram_from_file(mr, owner, name, ram_size, 0, false, ++ memory_region_init_ram_from_file(mr, owner, name, ram_size, 0, 0, + mem_path, &err); + if (err) { + error_report_err(err); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-Do-not-re-read-the-clock-on-pre_save-in-ca.patch b/SOURCES/kvm-migration-Do-not-re-read-the-clock-on-pre_save-in-ca.patch new file mode 100644 index 0000000..7b5c874 --- /dev/null +++ b/SOURCES/kvm-migration-Do-not-re-read-the-clock-on-pre_save-in-ca.patch @@ -0,0 +1,97 @@ +From 95c9f56041b8d3b95644d790e2b3f59587805a01 Mon Sep 17 00:00:00 2001 +From: David Gibson +Date: Tue, 17 Sep 2019 03:47:09 +0200 +Subject: [PATCH 3/4] migration: Do not re-read the clock on pre_save in case + of paused guest + +RH-Author: David Gibson +Message-id: <20190917034709.20167-1-dgibson@redhat.com> +Patchwork-id: 90473 +O-Subject: [RHEL-7.8 qemu-kvm-rhev PATCH] migration: Do not re-read the clock on pre_save in case of paused guest +Bugzilla: 1743508 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Dr. David Alan Gilbert + +From: "Maxiwell S. Garcia" + +Re-read the timebase before migrate was ported from x86 commit: + 6053a86fe7bd: kvmclock: reduce kvmclock difference on migration + +The clock move makes the guest knows about the paused time between +the stop and migrate commands. This is an issue in an already-paused +VM because some side effects, like process stalls, could happen +after migration. + +So, this patch checks the runstate of guest in the pre_save handler and +do not re-reads the timebase in case of paused state (cold migration). + +Signed-off-by: Maxiwell S. Garcia +Message-Id: <20190711194702.26598-1-maxiwell@linux.ibm.com> +Signed-off-by: David Gibson +(cherry picked from commit d14f33976282a8744ca1bf1d64e73996c145aa3f) + +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1743508 +Brew: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=23524287 +Testing: Booted a guest with the scratch qemu build + +Signed-off-by: David Gibson +Signed-off-by: Miroslav Rezanina +--- + hw/ppc/ppc.c | 13 +++++++++---- + target/ppc/cpu-qom.h | 1 + + 2 files changed, 10 insertions(+), 4 deletions(-) + +diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c +index ec4be25..fb90d19 100644 +--- a/hw/ppc/ppc.c ++++ b/hw/ppc/ppc.c +@@ -877,6 +877,8 @@ static void timebase_save(PPCTimebase *tb) + * there is no need to update it from KVM here + */ + tb->guest_timebase = ticks + first_ppc_cpu->env.tb_env->tb_offset; ++ ++ tb->runstate_paused = runstate_check(RUN_STATE_PAUSED); + } + + static void timebase_load(PPCTimebase *tb) +@@ -923,9 +925,9 @@ void cpu_ppc_clock_vm_state_change(void *opaque, int running, + } + + /* +- * When migrating, read the clock just before migration, +- * so that the guest clock counts during the events +- * between: ++ * When migrating a running guest, read the clock just ++ * before migration, so that the guest clock counts ++ * during the events between: + * + * * vm_stop() + * * +@@ -940,7 +942,10 @@ static int timebase_pre_save(void *opaque) + { + PPCTimebase *tb = opaque; + +- timebase_save(tb); ++ /* guest_timebase won't be overridden in case of paused guest */ ++ if (!tb->runstate_paused) { ++ timebase_save(tb); ++ } + + return 0; + } +diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h +index deaa46a..b900277 100644 +--- a/target/ppc/cpu-qom.h ++++ b/target/ppc/cpu-qom.h +@@ -210,6 +210,7 @@ typedef struct PowerPCCPUClass { + typedef struct PPCTimebase { + uint64_t guest_timebase; + int64_t time_of_the_day_ns; ++ bool runstate_paused; + } PPCTimebase; + + extern const struct VMStateDescription vmstate_ppc_timebase; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-Don-t-activate-block-devices-if-using-S.patch b/SOURCES/kvm-migration-Don-t-activate-block-devices-if-using-S.patch new file mode 100644 index 0000000..97b26d2 --- /dev/null +++ b/SOURCES/kvm-migration-Don-t-activate-block-devices-if-using-S.patch @@ -0,0 +1,114 @@ +From 62f7b17e6115df60aa9a54e7530e16e5f4c6d2a0 Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Fri, 22 Jun 2018 19:00:03 +0200 +Subject: [PATCH 24/57] migration: Don't activate block devices if using -S + +RH-Author: Dr. David Alan Gilbert +Message-id: <20180622190005.21297-17-dgilbert@redhat.com> +Patchwork-id: 81002 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 16/18] migration: Don't activate block devices if using -S +Bugzilla: 1560854 +RH-Acked-by: Juan Quintela +RH-Acked-by: Peter Xu +RH-Acked-by: Laurent Vivier + +From: "Dr. David Alan Gilbert" + +Activating the block devices causes the locks to be taken on +the backing file. If we're running with -S and the destination libvirt +hasn't started the destination with 'cont', it's expecting the locks are +still untaken. + +Don't activate the block devices if we're not going to autostart the VM; +'cont' already will do that anyway. This change is tied to the new +migration capability 'late-block-activate' that defaults to off, keeping +the old behaviour by default. + +bz: https://bugzilla.redhat.com/show_bug.cgi?id=1560854 +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Juan Quintela +Signed-off-by: Juan Quintela +(cherry picked from commit 0f073f44df109ea0910d67caede70dec95956ff6) +Signed-off-by: Miroslav Rezanina +--- + migration/migration.c | 34 +++++++++++++++++++++++++++------- + qapi/migration.json | 5 ++++- + 2 files changed, 31 insertions(+), 8 deletions(-) + +diff --git a/migration/migration.c b/migration/migration.c +index b6294f6..edf1c06 100644 +--- a/migration/migration.c ++++ b/migration/migration.c +@@ -199,6 +199,16 @@ static void migrate_generate_event(int new_state) + } + } + ++static bool migrate_late_block_activate(void) ++{ ++ MigrationState *s; ++ ++ s = migrate_get_current(); ++ ++ return s->enabled_capabilities[ ++ MIGRATION_CAPABILITY_LATE_BLOCK_ACTIVATE]; ++} ++ + /* + * Called on -incoming with a defer: uri. + * The migration can be started later after any parameters have been +@@ -308,13 +318,23 @@ static void process_incoming_migration_bh(void *opaque) + Error *local_err = NULL; + MigrationIncomingState *mis = opaque; + +- /* Make sure all file formats flush their mutable metadata. +- * If we get an error here, just don't restart the VM yet. */ +- bdrv_invalidate_cache_all(&local_err); +- if (local_err) { +- error_report_err(local_err); +- local_err = NULL; +- autostart = false; ++ /* If capability late_block_activate is set: ++ * Only fire up the block code now if we're going to restart the ++ * VM, else 'cont' will do it. ++ * This causes file locking to happen; so we don't want it to happen ++ * unless we really are starting the VM. ++ */ ++ if (!migrate_late_block_activate() || ++ (autostart && (!global_state_received() || ++ global_state_get_runstate() == RUN_STATE_RUNNING))) { ++ /* Make sure all file formats flush their mutable metadata. ++ * If we get an error here, just don't restart the VM yet. */ ++ bdrv_invalidate_cache_all(&local_err); ++ if (local_err) { ++ error_report_err(local_err); ++ local_err = NULL; ++ autostart = false; ++ } + } + + /* +diff --git a/qapi/migration.json b/qapi/migration.json +index 9d0bf82..618d2de 100644 +--- a/qapi/migration.json ++++ b/qapi/migration.json +@@ -357,13 +357,16 @@ + # @dirty-bitmaps: If enabled, QEMU will migrate named dirty bitmaps. + # (since 2.12) + # ++# @late-block-activate: If enabled, the destination will not activate block ++# devices (and thus take locks) immediately at the end of migration. ++# (since 3.0) + # Since: 1.2 + ## + { 'enum': 'MigrationCapability', + 'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks', + 'compress', 'events', 'postcopy-ram', 'x-colo', 'release-ram', + 'block', 'return-path', 'pause-before-switchover', 'x-multifd', +- 'dirty-bitmaps' ] } ++ 'dirty-bitmaps', 'late-block-activate' ] } + + ## + # @MigrationCapabilityStatus: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-Support-adding-migration-blockers-earlier.patch b/SOURCES/kvm-migration-Support-adding-migration-blockers-earlier.patch new file mode 100644 index 0000000..2cb57ca --- /dev/null +++ b/SOURCES/kvm-migration-Support-adding-migration-blockers-earlier.patch @@ -0,0 +1,81 @@ +From 7d01c30800f4009d770cb44e377886a7a47f3e47 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:20 +0200 +Subject: [PATCH 53/53] migration: Support adding migration blockers earlier +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-32-armbru@redhat.com> +Patchwork-id: 88011 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 31/31] migration: Support adding migration blockers earlier +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +migrate_add_blocker() asserts we have a current_migration object, in +migrate_get_current(). We do only after migration_object_init(). + +This contributes to the following dependency cycle: + +* configure_blockdev() must run before machine_set_property() + so machine properties can refer to block backends + +* machine_set_property() before configure_accelerator() + so machine properties like kvm-irqchip get applied + +* configure_accelerator() before migration_object_init() + so that Xen's accelerator compat properties get applied. + +* migration_object_init() before configure_blockdev() + so configure_blockdev() can add migration blockers + +The cycle was closed when recent commit cda4aa9a5a0 "Create block +backends before setting machine properties" added the first +dependency, and satisfied it by violating the last one. Broke block +backends that add migration blockers, as demonstrated by qemu-iotests +055. + +To fix it, break the last dependency: make migrate_add_blocker() +usable before migration_object_init(). + +The previous commit already removed the use of migrate_get_current() +from migrate_add_blocker() itself. Didn't quite do the trick, as +there's another one hiding in migration_is_idle(). + +The use there isn't actually necessary: when no migration object has +been created yet, migration is surely idle. Make migration_is_idle() +return true then. + +Fixes: cda4aa9a5a08777cf13e164c0543bd4888b8adce +Signed-off-by: Markus Armbruster +Message-Id: <20190401090827.20793-4-armbru@redhat.com> +Reviewed-by: Igor Mammedov +(cherry picked from commit daff7f0bbe9950d045bb5b74f202295f70ab3aaa) +Signed-off-by: Miroslav Rezanina +--- + migration/migration.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/migration/migration.c b/migration/migration.c +index 83b8d6a..69011d1 100644 +--- a/migration/migration.c ++++ b/migration/migration.c +@@ -1270,7 +1270,11 @@ bool migration_in_postcopy_after_devices(MigrationState *s) + + bool migration_is_idle(void) + { +- MigrationState *s = migrate_get_current(); ++ MigrationState *s = current_migration; ++ ++ if (!s) { ++ return true; ++ } + + switch (s->state) { + case MIGRATION_STATUS_NONE: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-block-dirty-bitmap-fix-dirty_bitmap_load.patch b/SOURCES/kvm-migration-block-dirty-bitmap-fix-dirty_bitmap_load.patch new file mode 100644 index 0000000..b9f6f38 --- /dev/null +++ b/SOURCES/kvm-migration-block-dirty-bitmap-fix-dirty_bitmap_load.patch @@ -0,0 +1,51 @@ +From 91c5845da64dbe85892770609c1b04dee3718506 Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Fri, 22 Jun 2018 19:00:05 +0200 +Subject: [PATCH 26/57] migration/block-dirty-bitmap: fix dirty_bitmap_load + +RH-Author: Dr. David Alan Gilbert +Message-id: <20180622190005.21297-19-dgilbert@redhat.com> +Patchwork-id: 81012 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 18/18] migration/block-dirty-bitmap: fix dirty_bitmap_load +Bugzilla: 1584139 +RH-Acked-by: Juan Quintela +RH-Acked-by: Peter Xu +RH-Acked-by: Laurent Vivier + +From: Vladimir Sementsov-Ogievskiy + +dirty_bitmap_load_header return code is obtained but not handled. Fix +this. + +Bug was introduced in b35ebdf076d697bc +"migration: add postcopy migration of dirty bitmaps" with the whole +function. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20180530112424.204835-1-vsementsov@virtuozzo.com> +Reviewed-by: Eric Blake +Reviewed-by: John Snow +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit a36f6ff46f115672cf86d0e1e7cdb1c2fa4d304b) +Signed-off-by: Miroslav Rezanina +--- + migration/block-dirty-bitmap.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c +index 8819aab..2c541c9 100644 +--- a/migration/block-dirty-bitmap.c ++++ b/migration/block-dirty-bitmap.c +@@ -672,6 +672,9 @@ static int dirty_bitmap_load(QEMUFile *f, void *opaque, int version_id) + + do { + ret = dirty_bitmap_load_header(f, &s); ++ if (ret < 0) { ++ return ret; ++ } + + if (s.flags & DIRTY_BITMAP_MIG_FLAG_START) { + ret = dirty_bitmap_load_start(f, &s); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-block-dirty-bitmap-fix-memory-leak-in-dirt.patch b/SOURCES/kvm-migration-block-dirty-bitmap-fix-memory-leak-in-dirt.patch new file mode 100644 index 0000000..2bdba63 --- /dev/null +++ b/SOURCES/kvm-migration-block-dirty-bitmap-fix-memory-leak-in-dirt.patch @@ -0,0 +1,48 @@ +From b03c385c6eee5999f6aadcc516aa0f9fc882df47 Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Fri, 22 Jun 2018 18:59:58 +0200 +Subject: [PATCH 19/57] migration/block-dirty-bitmap: fix memory leak in + dirty_bitmap_load_bits + +RH-Author: Dr. David Alan Gilbert +Message-id: <20180622190005.21297-12-dgilbert@redhat.com> +Patchwork-id: 81009 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 11/18] migration/block-dirty-bitmap: fix memory leak in dirty_bitmap_load_bits +Bugzilla: 1584139 +RH-Acked-by: Peter Xu +RH-Acked-by: Juan Quintela +RH-Acked-by: Laurent Vivier + +From: Vladimir Sementsov-Ogievskiy + +Release buf on error path too. + +Bug was introduced in b35ebdf076d697bc "migration: add postcopy +migration of dirty bitmaps" with the whole function. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20180427142002.21930-3-vsementsov@virtuozzo.com> +Reviewed-by: Eric Blake +CC: qemu-stable@nongnu.org +Signed-off-by: Eric Blake +(cherry picked from commit 16a2227893dc1d5cad78ed376ad1d7e300978fbe) +Signed-off-by: Miroslav Rezanina +--- + migration/block-dirty-bitmap.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c +index dd04f10..8819aab 100644 +--- a/migration/block-dirty-bitmap.c ++++ b/migration/block-dirty-bitmap.c +@@ -600,6 +600,7 @@ static int dirty_bitmap_load_bits(QEMUFile *f, DirtyBitmapLoadState *s) + ret = qemu_get_buffer(f, buf, buf_size); + if (ret != buf_size) { + error_report("Failed to read bitmap bits"); ++ g_free(buf); + return -EIO; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-calculate-expected_downtime-with-ram_bytes.patch b/SOURCES/kvm-migration-calculate-expected_downtime-with-ram_bytes.patch new file mode 100644 index 0000000..f40fa84 --- /dev/null +++ b/SOURCES/kvm-migration-calculate-expected_downtime-with-ram_bytes.patch @@ -0,0 +1,68 @@ +From 4d7902d7277992139d575552f09c2ff6827e9794 Mon Sep 17 00:00:00 2001 +From: Laurent Vivier +Date: Thu, 21 Jun 2018 09:45:26 +0200 +Subject: [PATCH 14/89] migration: calculate expected_downtime with + ram_bytes_remaining() + +RH-Author: Laurent Vivier +Message-id: <20180621094526.5714-1-lvivier@redhat.com> +Patchwork-id: 80931 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH] migration: calculate expected_downtime with ram_bytes_remaining() +Bugzilla: 1564576 +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Thomas Huth +RH-Acked-by: David Gibson + +From: Balamuruhan S + +expected_downtime value is not accurate with dirty_pages_rate * page_size, +using ram_bytes_remaining() would yeild it resonable. + +consider to read the remaining ram just after having updated the dirty +pages count later migration_bitmap_sync_range() in migration_bitmap_sync() +and reuse the `remaining` field in ram_counters to hold ram_bytes_remaining() +for calculating expected_downtime. + +Reported-by: Michael Roth +Signed-off-by: Balamuruhan S +Signed-off-by: Laurent Vivier +Message-Id: <20180612085009.17594-2-bala24@linux.vnet.ibm.com> +Reviewed-by: Dr. David Alan Gilbert +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 650af8907bd567db914b7ce3a7e9df4c323f4619) +Signed-off-by: Laurent Vivier +Signed-off-by: Miroslav Rezanina +--- + migration/migration.c | 3 +-- + migration/ram.c | 1 + + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/migration/migration.c b/migration/migration.c +index ef4bb42..43d8a64 100644 +--- a/migration/migration.c ++++ b/migration/migration.c +@@ -2239,8 +2239,7 @@ static void migration_update_counters(MigrationState *s, + * recalculate. 10000 is a small enough number for our purposes + */ + if (ram_counters.dirty_pages_rate && transferred > 10000) { +- s->expected_downtime = ram_counters.dirty_pages_rate * +- qemu_target_page_size() / bandwidth; ++ s->expected_downtime = ram_counters.remaining / bandwidth; + } + + qemu_file_reset_rate_limit(s->to_dst_file); +diff --git a/migration/ram.c b/migration/ram.c +index 0e90efa..00c06b5 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -852,6 +852,7 @@ static void migration_bitmap_sync(RAMState *rs) + RAMBLOCK_FOREACH(block) { + migration_bitmap_sync_range(rs, block, 0, block->used_length); + } ++ ram_counters.remaining = ram_bytes_remaining(); + rcu_read_unlock(); + qemu_mutex_unlock(&rs->bitmap_mutex); + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-cleanup-in-error-paths-in-loadvm.patch b/SOURCES/kvm-migration-cleanup-in-error-paths-in-loadvm.patch new file mode 100644 index 0000000..49ff72a --- /dev/null +++ b/SOURCES/kvm-migration-cleanup-in-error-paths-in-loadvm.patch @@ -0,0 +1,54 @@ +From 5496cb7128658bd4b3cb24ce741bba4df3c09656 Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Mon, 19 Nov 2018 16:19:21 +0100 +Subject: [PATCH 22/22] migration: cleanup in error paths in loadvm + +RH-Author: Dr. David Alan Gilbert +Message-id: <20181119161921.15191-3-dgilbert@redhat.com> +Patchwork-id: 83049 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 2/2] migration: cleanup in error paths in loadvm +Bugzilla: 1608877 +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Peter Xu +RH-Acked-by: Pankaj Gupta + +From: "Dr. David Alan Gilbert" + +There's a couple of error paths in qemu_loadvm_state +which happen early on but after we've initialised the +load state; that needs to be cleaned up otherwise +we can hit asserts if the state gets reinitialised later. + +Signed-off-by: Dr. David Alan Gilbert +Message-Id: <20180914170430.54271-3-dgilbert@redhat.com> +Reviewed-by: Peter Xu +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 096c83b7219c5a2145435afc8be750281e9cb447) +Signed-off-by: Danilo C. L. de Paula +(cherry picked from commit 020674a9569df103bdd6a8cef29ce8013c92a8b8) +Signed-off-by: Miroslav Rezanina +--- + migration/savevm.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/migration/savevm.c b/migration/savevm.c +index 7a5d9ff..e5d57fa 100644 +--- a/migration/savevm.c ++++ b/migration/savevm.c +@@ -2146,11 +2146,13 @@ int qemu_loadvm_state(QEMUFile *f) + if (migrate_get_current()->send_configuration) { + if (qemu_get_byte(f) != QEMU_VM_CONFIGURATION) { + error_report("Configuration section missing"); ++ qemu_loadvm_state_cleanup(); + return -EINVAL; + } + ret = vmstate_load_state(f, &vmstate_configuration, &savevm_state, 0); + + if (ret) { ++ qemu_loadvm_state_cleanup(); + return ret; + } + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-detect-compression-and-decompression-error.patch b/SOURCES/kvm-migration-detect-compression-and-decompression-error.patch new file mode 100644 index 0000000..4107b2d --- /dev/null +++ b/SOURCES/kvm-migration-detect-compression-and-decompression-error.patch @@ -0,0 +1,235 @@ +From 231178d9b06a3d2bba1e7695071957671d7c08a1 Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Fri, 22 Jun 2018 18:59:51 +0200 +Subject: [PATCH 12/57] migration: detect compression and decompression errors + +RH-Author: Dr. David Alan Gilbert +Message-id: <20180622190005.21297-5-dgilbert@redhat.com> +Patchwork-id: 80996 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 04/18] migration: detect compression and decompression errors +Bugzilla: 1584139 +RH-Acked-by: Peter Xu +RH-Acked-by: Juan Quintela +RH-Acked-by: Laurent Vivier + +From: Xiao Guangrong + +Currently the page being compressed is allowed to be updated by +the VM on the source QEMU, correspondingly the destination QEMU +just ignores the decompression error. However, we completely miss +the chance to catch real errors, then the VM is corrupted silently + +To make the migration more robuster, we copy the page to a buffer +first to avoid it being written by VM, then detect and handle the +errors of both compression and decompression errors properly + +Reviewed-by: Peter Xu +Signed-off-by: Xiao Guangrong +Message-Id: <20180330075128.26919-5-xiaoguangrong@tencent.com> +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 34ab9e9743aeaf265929d930747f101fa5c76fea) +Signed-off-by: Miroslav Rezanina +--- + migration/qemu-file.c | 4 ++-- + migration/ram.c | 56 +++++++++++++++++++++++++++++++++++---------------- + 2 files changed, 41 insertions(+), 19 deletions(-) + +diff --git a/migration/qemu-file.c b/migration/qemu-file.c +index bafe3a0..0463f4c 100644 +--- a/migration/qemu-file.c ++++ b/migration/qemu-file.c +@@ -710,9 +710,9 @@ ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream, + blen = qemu_compress_data(stream, f->buf + f->buf_index + sizeof(int32_t), + blen, p, size); + if (blen < 0) { +- error_report("Compress Failed!"); +- return 0; ++ return -1; + } ++ + qemu_put_be32(f, blen); + if (f->ops->writev_buffer) { + add_to_iovec(f, f->buf + f->buf_index, blen, false); +diff --git a/migration/ram.c b/migration/ram.c +index be89cd8..cd6d98a 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -269,7 +269,10 @@ struct CompressParam { + QemuCond cond; + RAMBlock *block; + ram_addr_t offset; ++ ++ /* internally used fields */ + z_stream stream; ++ uint8_t *originbuf; + }; + typedef struct CompressParam CompressParam; + +@@ -296,13 +299,14 @@ static QemuCond comp_done_cond; + /* The empty QEMUFileOps will be used by file in CompressParam */ + static const QEMUFileOps empty_ops = { }; + ++static QEMUFile *decomp_file; + static DecompressParam *decomp_param; + static QemuThread *decompress_threads; + static QemuMutex decomp_done_lock; + static QemuCond decomp_done_cond; + + static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, +- ram_addr_t offset); ++ ram_addr_t offset, uint8_t *source_buf); + + static void *do_data_compress(void *opaque) + { +@@ -318,7 +322,8 @@ static void *do_data_compress(void *opaque) + param->block = NULL; + qemu_mutex_unlock(¶m->mutex); + +- do_compress_ram_page(param->file, ¶m->stream, block, offset); ++ do_compress_ram_page(param->file, ¶m->stream, block, offset, ++ param->originbuf); + + qemu_mutex_lock(&comp_done_lock); + param->done = true; +@@ -370,6 +375,7 @@ static void compress_threads_save_cleanup(void) + qemu_mutex_destroy(&comp_param[i].mutex); + qemu_cond_destroy(&comp_param[i].cond); + deflateEnd(&comp_param[i].stream); ++ g_free(comp_param[i].originbuf); + qemu_fclose(comp_param[i].file); + comp_param[i].file = NULL; + } +@@ -394,8 +400,14 @@ static int compress_threads_save_setup(void) + qemu_cond_init(&comp_done_cond); + qemu_mutex_init(&comp_done_lock); + for (i = 0; i < thread_count; i++) { ++ comp_param[i].originbuf = g_try_malloc(TARGET_PAGE_SIZE); ++ if (!comp_param[i].originbuf) { ++ goto exit; ++ } ++ + if (deflateInit(&comp_param[i].stream, + migrate_compress_level()) != Z_OK) { ++ g_free(comp_param[i].originbuf); + goto exit; + } + +@@ -1054,7 +1066,7 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) + } + + static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, +- ram_addr_t offset) ++ ram_addr_t offset, uint8_t *source_buf) + { + RAMState *rs = ram_state; + int bytes_sent, blen; +@@ -1062,7 +1074,14 @@ static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, + + bytes_sent = save_page_header(rs, f, block, offset | + RAM_SAVE_FLAG_COMPRESS_PAGE); +- blen = qemu_put_compression_data(f, stream, p, TARGET_PAGE_SIZE); ++ ++ /* ++ * copy it to a internal buffer to avoid it being modified by VM ++ * so that we can catch up the error during compression and ++ * decompression ++ */ ++ memcpy(source_buf, p, TARGET_PAGE_SIZE); ++ blen = qemu_put_compression_data(f, stream, source_buf, TARGET_PAGE_SIZE); + if (blen < 0) { + bytes_sent = 0; + qemu_file_set_error(migrate_get_current()->to_dst_file, blen); +@@ -2556,7 +2575,7 @@ static void *do_data_decompress(void *opaque) + DecompressParam *param = opaque; + unsigned long pagesize; + uint8_t *des; +- int len; ++ int len, ret; + + qemu_mutex_lock(¶m->mutex); + while (!param->quit) { +@@ -2567,13 +2586,13 @@ static void *do_data_decompress(void *opaque) + qemu_mutex_unlock(¶m->mutex); + + pagesize = TARGET_PAGE_SIZE; +- /* qemu_uncompress_data() will return failed in some case, +- * especially when the page is dirtied when doing the compression, +- * it's not a problem because the dirty page will be retransferred +- * and uncompress() won't break the data in other pages. +- */ +- qemu_uncompress_data(¶m->stream, des, pagesize, param->compbuf, +- len); ++ ++ ret = qemu_uncompress_data(¶m->stream, des, pagesize, ++ param->compbuf, len); ++ if (ret < 0) { ++ error_report("decompress data failed"); ++ qemu_file_set_error(decomp_file, ret); ++ } + + qemu_mutex_lock(&decomp_done_lock); + param->done = true; +@@ -2590,12 +2609,12 @@ static void *do_data_decompress(void *opaque) + return NULL; + } + +-static void wait_for_decompress_done(void) ++static int wait_for_decompress_done(void) + { + int idx, thread_count; + + if (!migrate_use_compression()) { +- return; ++ return 0; + } + + thread_count = migrate_decompress_threads(); +@@ -2606,6 +2625,7 @@ static void wait_for_decompress_done(void) + } + } + qemu_mutex_unlock(&decomp_done_lock); ++ return qemu_file_get_error(decomp_file); + } + + static void compress_threads_load_cleanup(void) +@@ -2646,9 +2666,10 @@ static void compress_threads_load_cleanup(void) + g_free(decomp_param); + decompress_threads = NULL; + decomp_param = NULL; ++ decomp_file = NULL; + } + +-static int compress_threads_load_setup(void) ++static int compress_threads_load_setup(QEMUFile *f) + { + int i, thread_count; + +@@ -2661,6 +2682,7 @@ static int compress_threads_load_setup(void) + decomp_param = g_new0(DecompressParam, thread_count); + qemu_mutex_init(&decomp_done_lock); + qemu_cond_init(&decomp_done_cond); ++ decomp_file = f; + for (i = 0; i < thread_count; i++) { + if (inflateInit(&decomp_param[i].stream) != Z_OK) { + goto exit; +@@ -2720,7 +2742,7 @@ static void decompress_data_with_multi_threads(QEMUFile *f, + */ + static int ram_load_setup(QEMUFile *f, void *opaque) + { +- if (compress_threads_load_setup()) { ++ if (compress_threads_load_setup(f)) { + return -1; + } + +@@ -3075,7 +3097,7 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) + } + } + +- wait_for_decompress_done(); ++ ret |= wait_for_decompress_done(); + rcu_read_unlock(); + trace_ram_load_complete(ret, seq_iter); + return ret; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-discard-non-migratable-RAMBlocks.patch b/SOURCES/kvm-migration-discard-non-migratable-RAMBlocks.patch new file mode 100644 index 0000000..9578900 --- /dev/null +++ b/SOURCES/kvm-migration-discard-non-migratable-RAMBlocks.patch @@ -0,0 +1,381 @@ +From 0319afed69c1b3206fce51fd286c3351c6fd6958 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Fri, 31 Aug 2018 16:25:51 +0200 +Subject: [PATCH 09/29] migration: discard non-migratable RAMBlocks +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: plai@redhat.com +Message-id: <1535732759-22481-2-git-send-email-plai@redhat.com> +Patchwork-id: 82012 +O-Subject: [RHEL7.6 PATCH BZ 1539280 1/9] migration: discard non-migratable RAMBlocks +Bugzilla: 1539280 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Pankaj Gupta +RH-Acked-by: Miroslav Rezanina + +From: Cédric Le Goater + +On the POWER9 processor, the XIVE interrupt controller can control +interrupt sources using MMIO to trigger events, to EOI or to turn off +the sources. Priority management and interrupt acknowledgment is also +controlled by MMIO in the presenter sub-engine. + +These MMIO regions are exposed to guests in QEMU with a set of 'ram +device' memory mappings, similarly to VFIO, and the VMAs are populated +dynamically with the appropriate pages using a fault handler. + +But, these regions are an issue for migration. We need to discard the +associated RAMBlocks from the RAM state on the source VM and let the +destination VM rebuild the memory mappings on the new host in the +post_load() operation just before resuming the system. + +To achieve this goal, the following introduces a new RAMBlock flag +RAM_MIGRATABLE which is updated in the vmstate_register_ram() and +vmstate_unregister_ram() routines. This flag is then used by the +migration to identify RAMBlocks to discard on the source. Some checks +are also performed on the destination to make sure nothing invalid was +sent. + +This change impacts the boston, malta and jazz mips boards for which +migration compatibility is broken. + +Signed-off-by: Cédric Le Goater +Reviewed-by: Juan Quintela +Reviewed-by: Dr. David Alan Gilbert +Signed-off-by: Juan Quintela +(cherry picked from commit b895de502717b83b4e5f089df617cb23530c4d2d) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + exec.c | 38 ++++++++++++++++++++++++++++++++++++++ + include/exec/cpu-common.h | 4 ++++ + migration/postcopy-ram.c | 12 ++++++------ + migration/ram.c | 46 ++++++++++++++++++++++++++++++++++------------ + migration/savevm.c | 2 ++ + 5 files changed, 84 insertions(+), 18 deletions(-) + +diff --git a/exec.c b/exec.c +index 02b1efe..7323d39 100644 +--- a/exec.c ++++ b/exec.c +@@ -104,6 +104,9 @@ static MemoryRegion io_mem_unassigned; + * (Set during postcopy) + */ + #define RAM_UF_ZEROPAGE (1 << 3) ++ ++/* RAM can be migrated */ ++#define RAM_MIGRATABLE (1 << 4) + #endif + + #ifdef TARGET_PAGE_BITS_VARY +@@ -1807,6 +1810,21 @@ void qemu_ram_set_uf_zeroable(RAMBlock *rb) + rb->flags |= RAM_UF_ZEROPAGE; + } + ++bool qemu_ram_is_migratable(RAMBlock *rb) ++{ ++ return rb->flags & RAM_MIGRATABLE; ++} ++ ++void qemu_ram_set_migratable(RAMBlock *rb) ++{ ++ rb->flags |= RAM_MIGRATABLE; ++} ++ ++void qemu_ram_unset_migratable(RAMBlock *rb) ++{ ++ rb->flags &= ~RAM_MIGRATABLE; ++} ++ + /* Called with iothread lock held. */ + void qemu_ram_set_idstr(RAMBlock *new_block, const char *name, DeviceState *dev) + { +@@ -3750,6 +3768,26 @@ int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque) + return ret; + } + ++int qemu_ram_foreach_migratable_block(RAMBlockIterFunc func, void *opaque) ++{ ++ RAMBlock *block; ++ int ret = 0; ++ ++ rcu_read_lock(); ++ RAMBLOCK_FOREACH(block) { ++ if (!qemu_ram_is_migratable(block)) { ++ continue; ++ } ++ ret = func(block->idstr, block->host, block->offset, ++ block->used_length, opaque); ++ if (ret) { ++ break; ++ } ++ } ++ rcu_read_unlock(); ++ return ret; ++} ++ + /* + * Unmap pages of memory from start to start+length such that + * they a) read as 0, b) Trigger whatever fault mechanism +diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h +index 24d335f..0b58e26 100644 +--- a/include/exec/cpu-common.h ++++ b/include/exec/cpu-common.h +@@ -75,6 +75,9 @@ const char *qemu_ram_get_idstr(RAMBlock *rb); + bool qemu_ram_is_shared(RAMBlock *rb); + bool qemu_ram_is_uf_zeroable(RAMBlock *rb); + void qemu_ram_set_uf_zeroable(RAMBlock *rb); ++bool qemu_ram_is_migratable(RAMBlock *rb); ++void qemu_ram_set_migratable(RAMBlock *rb); ++void qemu_ram_unset_migratable(RAMBlock *rb); + + size_t qemu_ram_pagesize(RAMBlock *block); + size_t qemu_ram_pagesize_largest(void); +@@ -119,6 +122,7 @@ typedef int (RAMBlockIterFunc)(const char *block_name, void *host_addr, + ram_addr_t offset, ram_addr_t length, void *opaque); + + int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque); ++int qemu_ram_foreach_migratable_block(RAMBlockIterFunc func, void *opaque); + int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length); + + #endif +diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c +index 4a0b33b..001b041 100644 +--- a/migration/postcopy-ram.c ++++ b/migration/postcopy-ram.c +@@ -264,7 +264,7 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis) + } + + /* We don't support postcopy with shared RAM yet */ +- if (qemu_ram_foreach_block(test_ramblock_postcopiable, NULL)) { ++ if (qemu_ram_foreach_migratable_block(test_ramblock_postcopiable, NULL)) { + goto out; + } + +@@ -392,7 +392,7 @@ static int cleanup_range(const char *block_name, void *host_addr, + */ + int postcopy_ram_incoming_init(MigrationIncomingState *mis, size_t ram_pages) + { +- if (qemu_ram_foreach_block(init_range, NULL)) { ++ if (qemu_ram_foreach_migratable_block(init_range, NULL)) { + return -1; + } + +@@ -414,7 +414,7 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) + return -1; + } + +- if (qemu_ram_foreach_block(cleanup_range, mis)) { ++ if (qemu_ram_foreach_migratable_block(cleanup_range, mis)) { + return -1; + } + /* Let the fault thread quit */ +@@ -480,7 +480,7 @@ static int nhp_range(const char *block_name, void *host_addr, + */ + int postcopy_ram_prepare_discard(MigrationIncomingState *mis) + { +- if (qemu_ram_foreach_block(nhp_range, mis)) { ++ if (qemu_ram_foreach_migratable_block(nhp_range, mis)) { + return -1; + } + +@@ -491,7 +491,7 @@ int postcopy_ram_prepare_discard(MigrationIncomingState *mis) + + /* + * Mark the given area of RAM as requiring notification to unwritten areas +- * Used as a callback on qemu_ram_foreach_block. ++ * Used as a callback on qemu_ram_foreach_migratable_block. + * host_addr: Base of area to mark + * offset: Offset in the whole ram arena + * length: Length of the section +@@ -793,7 +793,7 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis) + mis->have_fault_thread = true; + + /* Mark so that we get notified of accesses to unwritten areas */ +- if (qemu_ram_foreach_block(ram_block_enable_notify, mis)) { ++ if (qemu_ram_foreach_migratable_block(ram_block_enable_notify, mis)) { + return -1; + } + +diff --git a/migration/ram.c b/migration/ram.c +index bd563b5..04b5df5 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -153,11 +153,16 @@ out: + return ret; + } + ++/* Should be holding either ram_list.mutex, or the RCU lock. */ ++#define RAMBLOCK_FOREACH_MIGRATABLE(block) \ ++ RAMBLOCK_FOREACH(block) \ ++ if (!qemu_ram_is_migratable(block)) {} else ++ + static void ramblock_recv_map_init(void) + { + RAMBlock *rb; + +- RAMBLOCK_FOREACH(rb) { ++ RAMBLOCK_FOREACH_MIGRATABLE(rb) { + assert(!rb->receivedmap); + rb->receivedmap = bitmap_new(rb->max_length >> qemu_target_page_bits()); + } +@@ -813,6 +818,10 @@ unsigned long migration_bitmap_find_dirty(RAMState *rs, RAMBlock *rb, + unsigned long *bitmap = rb->bmap; + unsigned long next; + ++ if (!qemu_ram_is_migratable(rb)) { ++ return size; ++ } ++ + if (rs->ram_bulk_stage && start > 0) { + next = start + 1; + } else { +@@ -858,7 +867,7 @@ uint64_t ram_pagesize_summary(void) + RAMBlock *block; + uint64_t summary = 0; + +- RAMBLOCK_FOREACH(block) { ++ RAMBLOCK_FOREACH_MIGRATABLE(block) { + summary |= block->page_size; + } + +@@ -882,7 +891,7 @@ static void migration_bitmap_sync(RAMState *rs) + + qemu_mutex_lock(&rs->bitmap_mutex); + rcu_read_lock(); +- RAMBLOCK_FOREACH(block) { ++ RAMBLOCK_FOREACH_MIGRATABLE(block) { + migration_bitmap_sync_range(rs, block, 0, block->used_length); + } + ram_counters.remaining = ram_bytes_remaining(); +@@ -1522,6 +1531,11 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss, + size_t pagesize_bits = + qemu_ram_pagesize(pss->block) >> TARGET_PAGE_BITS; + ++ if (!qemu_ram_is_migratable(pss->block)) { ++ error_report("block %s should not be migrated !", pss->block->idstr); ++ return 0; ++ } ++ + do { + /* Check the pages is dirty and if it is send it */ + if (!migration_bitmap_clear_dirty(rs, pss->block, pss->page)) { +@@ -1620,7 +1634,7 @@ uint64_t ram_bytes_total(void) + uint64_t total = 0; + + rcu_read_lock(); +- RAMBLOCK_FOREACH(block) { ++ RAMBLOCK_FOREACH_MIGRATABLE(block) { + total += block->used_length; + } + rcu_read_unlock(); +@@ -1675,7 +1689,7 @@ static void ram_save_cleanup(void *opaque) + */ + memory_global_dirty_log_stop(); + +- QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { ++ RAMBLOCK_FOREACH_MIGRATABLE(block) { + g_free(block->bmap); + block->bmap = NULL; + g_free(block->unsentmap); +@@ -1738,7 +1752,7 @@ void ram_postcopy_migrated_memory_release(MigrationState *ms) + { + struct RAMBlock *block; + +- RAMBLOCK_FOREACH(block) { ++ RAMBLOCK_FOREACH_MIGRATABLE(block) { + unsigned long *bitmap = block->bmap; + unsigned long range = block->used_length >> TARGET_PAGE_BITS; + unsigned long run_start = find_next_zero_bit(bitmap, range, 0); +@@ -1816,7 +1830,7 @@ static int postcopy_each_ram_send_discard(MigrationState *ms) + struct RAMBlock *block; + int ret; + +- RAMBLOCK_FOREACH(block) { ++ RAMBLOCK_FOREACH_MIGRATABLE(block) { + PostcopyDiscardState *pds = + postcopy_discard_send_init(ms, block->idstr); + +@@ -2024,7 +2038,7 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms) + rs->last_sent_block = NULL; + rs->last_page = 0; + +- QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { ++ RAMBLOCK_FOREACH_MIGRATABLE(block) { + unsigned long pages = block->used_length >> TARGET_PAGE_BITS; + unsigned long *bitmap = block->bmap; + unsigned long *unsentmap = block->unsentmap; +@@ -2183,7 +2197,7 @@ static void ram_list_init_bitmaps(void) + + /* Skip setting bitmap if there is no RAM */ + if (ram_bytes_total()) { +- QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { ++ RAMBLOCK_FOREACH_MIGRATABLE(block) { + pages = block->max_length >> TARGET_PAGE_BITS; + block->bmap = bitmap_new(pages); + bitmap_set(block->bmap, 0, pages); +@@ -2264,7 +2278,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque) + + qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE); + +- RAMBLOCK_FOREACH(block) { ++ RAMBLOCK_FOREACH_MIGRATABLE(block) { + qemu_put_byte(f, strlen(block->idstr)); + qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr)); + qemu_put_be64(f, block->used_length); +@@ -2508,6 +2522,11 @@ static inline RAMBlock *ram_block_from_stream(QEMUFile *f, int flags) + return NULL; + } + ++ if (!qemu_ram_is_migratable(block)) { ++ error_report("block %s should not be migrated !", id); ++ return NULL; ++ } ++ + return block; + } + +@@ -2750,7 +2769,7 @@ static int ram_load_cleanup(void *opaque) + xbzrle_load_cleanup(); + compress_threads_load_cleanup(); + +- RAMBLOCK_FOREACH(rb) { ++ RAMBLOCK_FOREACH_MIGRATABLE(rb) { + g_free(rb->receivedmap); + rb->receivedmap = NULL; + } +@@ -3012,7 +3031,10 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) + length = qemu_get_be64(f); + + block = qemu_ram_block_by_name(id); +- if (block) { ++ if (block && !qemu_ram_is_migratable(block)) { ++ error_report("block %s should not be migrated !", id); ++ ret = -EINVAL; ++ } else if (block) { + if (length != block->used_length) { + Error *local_err = NULL; + +diff --git a/migration/savevm.c b/migration/savevm.c +index 56c9feb..b975d3a 100644 +--- a/migration/savevm.c ++++ b/migration/savevm.c +@@ -2510,11 +2510,13 @@ void vmstate_register_ram(MemoryRegion *mr, DeviceState *dev) + { + qemu_ram_set_idstr(mr->ram_block, + memory_region_name(mr), dev); ++ qemu_ram_set_migratable(mr->ram_block); + } + + void vmstate_unregister_ram(MemoryRegion *mr, DeviceState *dev) + { + qemu_ram_unset_idstr(mr->ram_block); ++ qemu_ram_unset_migratable(mr->ram_block); + } + + void vmstate_register_ram_global(MemoryRegion *mr) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-fix-saving-normal-page-even-if-it-s-been-c.patch b/SOURCES/kvm-migration-fix-saving-normal-page-even-if-it-s-been-c.patch new file mode 100644 index 0000000..2c62e11 --- /dev/null +++ b/SOURCES/kvm-migration-fix-saving-normal-page-even-if-it-s-been-c.patch @@ -0,0 +1,48 @@ +From 156eb2b86b1383e639106650be32ca7d1a004f09 Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Fri, 22 Jun 2018 18:59:59 +0200 +Subject: [PATCH 20/57] migration: fix saving normal page even if it's been + compressed + +RH-Author: Dr. David Alan Gilbert +Message-id: <20180622190005.21297-13-dgilbert@redhat.com> +Patchwork-id: 80997 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 12/18] migration: fix saving normal page even if it's been compressed +Bugzilla: 1584139 +RH-Acked-by: Juan Quintela +RH-Acked-by: Peter Xu +RH-Acked-by: Laurent Vivier + +From: Xiao Guangrong + +Fix the bug introduced by da3f56cb2e767016 (migration: remove +ram_save_compressed_page()), It should be 'return' rather than +'res' + +Sorry for this stupid mistake :( + +Signed-off-by: Xiao Guangrong +Message-Id: <20180428081045.8878-1-xiaoguangrong@tencent.com> +Signed-off-by: Juan Quintela +(cherry picked from commit 701b1876c0fc0c583e4aff300ace5d33a1b97ed6) +Signed-off-by: Miroslav Rezanina +--- + migration/ram.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/migration/ram.c b/migration/ram.c +index 466609f..c982201 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -1491,7 +1491,7 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, + * CPU resource. + */ + if (block == rs->last_sent_block && save_page_use_compression(rs)) { +- res = compress_page_with_multi_thread(rs, block, offset); ++ return compress_page_with_multi_thread(rs, block, offset); + } + + return ram_save_page(rs, pss, last_stage); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-introduce-control_save_page.patch b/SOURCES/kvm-migration-introduce-control_save_page.patch new file mode 100644 index 0000000..250975e --- /dev/null +++ b/SOURCES/kvm-migration-introduce-control_save_page.patch @@ -0,0 +1,254 @@ +From 63c9f44c51353c54e84e4601a87401ac14247207 Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Fri, 22 Jun 2018 18:59:52 +0200 +Subject: [PATCH 13/57] migration: introduce control_save_page() + +RH-Author: Dr. David Alan Gilbert +Message-id: <20180622190005.21297-6-dgilbert@redhat.com> +Patchwork-id: 81001 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 05/18] migration: introduce control_save_page() +Bugzilla: 1584139 +RH-Acked-by: Peter Xu +RH-Acked-by: Juan Quintela +RH-Acked-by: Laurent Vivier + +From: Xiao Guangrong + +Abstract the common function control_save_page() to cleanup the code, +no logic is changed + +Reviewed-by: Peter Xu +Reviewed-by: Dr. David Alan Gilbert +Signed-off-by: Xiao Guangrong +Message-Id: <20180330075128.26919-6-xiaoguangrong@tencent.com> +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 059ff0fb29dd3a56ac2843676915efc279938c6b) +Signed-off-by: Miroslav Rezanina +--- + migration/ram.c | 174 +++++++++++++++++++++++++++++--------------------------- + 1 file changed, 89 insertions(+), 85 deletions(-) + +diff --git a/migration/ram.c b/migration/ram.c +index cd6d98a..8dc98a5 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -975,6 +975,44 @@ static void ram_release_pages(const char *rbname, uint64_t offset, int pages) + ram_discard_range(rbname, offset, pages << TARGET_PAGE_BITS); + } + ++/* ++ * @pages: the number of pages written by the control path, ++ * < 0 - error ++ * > 0 - number of pages written ++ * ++ * Return true if the pages has been saved, otherwise false is returned. ++ */ ++static bool control_save_page(RAMState *rs, RAMBlock *block, ram_addr_t offset, ++ int *pages) ++{ ++ uint64_t bytes_xmit = 0; ++ int ret; ++ ++ *pages = -1; ++ ret = ram_control_save_page(rs->f, block->offset, offset, TARGET_PAGE_SIZE, ++ &bytes_xmit); ++ if (ret == RAM_SAVE_CONTROL_NOT_SUPP) { ++ return false; ++ } ++ ++ if (bytes_xmit) { ++ ram_counters.transferred += bytes_xmit; ++ *pages = 1; ++ } ++ ++ if (ret == RAM_SAVE_CONTROL_DELAYED) { ++ return true; ++ } ++ ++ if (bytes_xmit > 0) { ++ ram_counters.normal++; ++ } else if (bytes_xmit == 0) { ++ ram_counters.duplicate++; ++ } ++ ++ return true; ++} ++ + /** + * ram_save_page: send the given page to the stream + * +@@ -991,56 +1029,36 @@ static void ram_release_pages(const char *rbname, uint64_t offset, int pages) + static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) + { + int pages = -1; +- uint64_t bytes_xmit; +- ram_addr_t current_addr; + uint8_t *p; +- int ret; + bool send_async = true; + RAMBlock *block = pss->block; + ram_addr_t offset = pss->page << TARGET_PAGE_BITS; ++ ram_addr_t current_addr = block->offset + offset; + + p = block->host + offset; + trace_ram_save_page(block->idstr, (uint64_t)offset, p); + +- /* In doubt sent page as normal */ +- bytes_xmit = 0; +- ret = ram_control_save_page(rs->f, block->offset, +- offset, TARGET_PAGE_SIZE, &bytes_xmit); +- if (bytes_xmit) { +- ram_counters.transferred += bytes_xmit; +- pages = 1; ++ if (control_save_page(rs, block, offset, &pages)) { ++ return pages; + } + + XBZRLE_cache_lock(); +- +- current_addr = block->offset + offset; +- +- if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { +- if (ret != RAM_SAVE_CONTROL_DELAYED) { +- if (bytes_xmit > 0) { +- ram_counters.normal++; +- } else if (bytes_xmit == 0) { +- ram_counters.duplicate++; +- } +- } +- } else { +- pages = save_zero_page(rs, block, offset); +- if (pages > 0) { +- /* Must let xbzrle know, otherwise a previous (now 0'd) cached +- * page would be stale ++ pages = save_zero_page(rs, block, offset); ++ if (pages > 0) { ++ /* Must let xbzrle know, otherwise a previous (now 0'd) cached ++ * page would be stale ++ */ ++ xbzrle_cache_zero_page(rs, current_addr); ++ ram_release_pages(block->idstr, offset, pages); ++ } else if (!rs->ram_bulk_stage && ++ !migration_in_postcopy() && migrate_use_xbzrle()) { ++ pages = save_xbzrle_page(rs, &p, current_addr, block, ++ offset, last_stage); ++ if (!last_stage) { ++ /* Can't send this cached data async, since the cache page ++ * might get updated before it gets to the wire + */ +- xbzrle_cache_zero_page(rs, current_addr); +- ram_release_pages(block->idstr, offset, pages); +- } else if (!rs->ram_bulk_stage && +- !migration_in_postcopy() && migrate_use_xbzrle()) { +- pages = save_xbzrle_page(rs, &p, current_addr, block, +- offset, last_stage); +- if (!last_stage) { +- /* Can't send this cached data async, since the cache page +- * might get updated before it gets to the wire +- */ +- send_async = false; +- } ++ send_async = false; + } + } + +@@ -1175,63 +1193,49 @@ static int ram_save_compressed_page(RAMState *rs, PageSearchStatus *pss, + bool last_stage) + { + int pages = -1; +- uint64_t bytes_xmit = 0; + uint8_t *p; +- int ret; + RAMBlock *block = pss->block; + ram_addr_t offset = pss->page << TARGET_PAGE_BITS; + + p = block->host + offset; + +- ret = ram_control_save_page(rs->f, block->offset, +- offset, TARGET_PAGE_SIZE, &bytes_xmit); +- if (bytes_xmit) { +- ram_counters.transferred += bytes_xmit; +- pages = 1; ++ if (control_save_page(rs, block, offset, &pages)) { ++ return pages; + } +- if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { +- if (ret != RAM_SAVE_CONTROL_DELAYED) { +- if (bytes_xmit > 0) { +- ram_counters.normal++; +- } else if (bytes_xmit == 0) { +- ram_counters.duplicate++; +- } ++ ++ /* When starting the process of a new block, the first page of ++ * the block should be sent out before other pages in the same ++ * block, and all the pages in last block should have been sent ++ * out, keeping this order is important, because the 'cont' flag ++ * is used to avoid resending the block name. ++ */ ++ if (block != rs->last_sent_block) { ++ flush_compressed_data(rs); ++ pages = save_zero_page(rs, block, offset); ++ if (pages > 0) { ++ ram_release_pages(block->idstr, offset, pages); ++ } else { ++ /* ++ * Make sure the first page is sent out before other pages. ++ * ++ * we post it as normal page as compression will take much ++ * CPU resource. ++ */ ++ ram_counters.transferred += save_page_header(rs, rs->f, block, ++ offset | RAM_SAVE_FLAG_PAGE); ++ qemu_put_buffer_async(rs->f, p, TARGET_PAGE_SIZE, ++ migrate_release_ram() & ++ migration_in_postcopy()); ++ ram_counters.transferred += TARGET_PAGE_SIZE; ++ ram_counters.normal++; ++ pages = 1; + } + } else { +- /* When starting the process of a new block, the first page of +- * the block should be sent out before other pages in the same +- * block, and all the pages in last block should have been sent +- * out, keeping this order is important, because the 'cont' flag +- * is used to avoid resending the block name. +- */ +- if (block != rs->last_sent_block) { +- flush_compressed_data(rs); +- pages = save_zero_page(rs, block, offset); +- if (pages > 0) { +- ram_release_pages(block->idstr, offset, pages); +- } else { +- /* +- * Make sure the first page is sent out before other pages. +- * +- * we post it as normal page as compression will take much +- * CPU resource. +- */ +- ram_counters.transferred += save_page_header(rs, rs->f, block, +- offset | RAM_SAVE_FLAG_PAGE); +- qemu_put_buffer_async(rs->f, p, TARGET_PAGE_SIZE, +- migrate_release_ram() & +- migration_in_postcopy()); +- ram_counters.transferred += TARGET_PAGE_SIZE; +- ram_counters.normal++; +- pages = 1; +- } ++ pages = save_zero_page(rs, block, offset); ++ if (pages == -1) { ++ pages = compress_page_with_multi_thread(rs, block, offset); + } else { +- pages = save_zero_page(rs, block, offset); +- if (pages == -1) { +- pages = compress_page_with_multi_thread(rs, block, offset); +- } else { +- ram_release_pages(block->idstr, offset, pages); +- } ++ ram_release_pages(block->idstr, offset, pages); + } + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-introduce-decompress-error-check.patch b/SOURCES/kvm-migration-introduce-decompress-error-check.patch new file mode 100644 index 0000000..dc39e3b --- /dev/null +++ b/SOURCES/kvm-migration-introduce-decompress-error-check.patch @@ -0,0 +1,164 @@ +From 79faa485281ca351111a0461bbffe10a17f30c64 Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Fri, 22 Jun 2018 19:00:02 +0200 +Subject: [PATCH 23/57] migration: introduce decompress-error-check + +RH-Author: Dr. David Alan Gilbert +Message-id: <20180622190005.21297-16-dgilbert@redhat.com> +Patchwork-id: 81013 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 15/18] migration: introduce decompress-error-check +Bugzilla: 1584139 +RH-Acked-by: Juan Quintela +RH-Acked-by: Peter Xu +RH-Acked-by: Laurent Vivier + +From: Xiao Guangrong + +QEMU 3.0 enables strict check for compression & decompression to +make the migration more robust, that depends on the source to fix +the internal design which triggers the unexpected error conditions + +To make it work for migrating old version QEMU to 2.13 QEMU, we +introduce this parameter to disable the error check on the +destination which is the default behavior of the machine type +which is older than 2.13, alternately, the strict check can be +enabled explicitly as followings: + -M pc-q35-2.11 -global migration.decompress-error-check=true + +Signed-off-by: Xiao Guangrong +Reviewed-by: Juan Quintela +Signed-off-by: Juan Quintela +(cherry picked from commit f548222c24342ca74689de7794f9006b43f86a54) + added compat entry to HW_COMPAT_RHEL7_5 +Signed-off-by: Dr. David Alan Gilbert + +Signed-off-by: Miroslav Rezanina +--- + hw/arm/virt.c | 4 ++++ + hw/i386/pc_piix.c | 1 + + hw/i386/pc_q35.c | 1 + + include/hw/compat.h | 5 +++++ + migration/migration.c | 4 ++++ + migration/migration.h | 8 ++++++++ + migration/ram.c | 2 +- + 7 files changed, 24 insertions(+), 1 deletion(-) + +diff --git a/hw/arm/virt.c b/hw/arm/virt.c +index a4d0f52..751a93c 100644 +--- a/hw/arm/virt.c ++++ b/hw/arm/virt.c +@@ -1607,6 +1607,9 @@ static void machvirt_machine_init(void) + } + type_init(machvirt_machine_init); + ++#define VIRT_COMPAT_2_12 \ ++ HW_COMPAT_2_12 ++ + static void virt_2_12_instance_init(Object *obj) + { + VirtMachineState *vms = VIRT_MACHINE(obj); +@@ -1669,6 +1672,7 @@ static void virt_2_12_instance_init(Object *obj) + + static void virt_machine_2_12_options(MachineClass *mc) + { ++ SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_12); + } + DEFINE_VIRT_MACHINE_AS_LATEST(2, 12) + +diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c +index 7b87ef6..9991650 100644 +--- a/hw/i386/pc_piix.c ++++ b/hw/i386/pc_piix.c +@@ -432,6 +432,7 @@ static void pc_i440fx_2_12_machine_options(MachineClass *m) + pc_i440fx_machine_options(m); + m->alias = "pc"; + m->is_default = 1; ++ SET_MACHINE_COMPAT(m, PC_COMPAT_2_12); + } + + DEFINE_I440FX_MACHINE(v2_12, "pc-i440fx-2.12", NULL, +diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c +index 1805f55..a4387e0 100644 +--- a/hw/i386/pc_q35.c ++++ b/hw/i386/pc_q35.c +@@ -313,6 +313,7 @@ static void pc_q35_2_12_machine_options(MachineClass *m) + { + pc_q35_machine_options(m); + m->alias = "q35"; ++ SET_MACHINE_COMPAT(m, PC_COMPAT_2_12); + } + + DEFINE_Q35_MACHINE(v2_12, "pc-q35-2.12", NULL, +diff --git a/include/hw/compat.h b/include/hw/compat.h +index 9e659e9..5802e61 100644 +--- a/include/hw/compat.h ++++ b/include/hw/compat.h +@@ -480,6 +480,11 @@ + .driver = "cirrus-vga",\ + .property = "vgamem_mb",\ + .value = "16",\ ++ },{ /* HW_COMPAT_RHEL7_5 */ \ ++ .driver = "migration",\ ++ .property = "decompress-error-check",\ ++ .value = "off",\ + }, + ++ + #endif /* HW_COMPAT_H */ +diff --git a/migration/migration.c b/migration/migration.c +index 43d8a64..b6294f6 100644 +--- a/migration/migration.c ++++ b/migration/migration.c +@@ -2483,6 +2483,8 @@ void migration_global_dump(Monitor *mon) + ms->send_configuration ? "on" : "off"); + monitor_printf(mon, "send-section-footer: %s\n", + ms->send_section_footer ? "on" : "off"); ++ monitor_printf(mon, "decompress-error-check: %s\n", ++ ms->decompress_error_check ? "on" : "off"); + } + + #define DEFINE_PROP_MIG_CAP(name, x) \ +@@ -2496,6 +2498,8 @@ static Property migration_properties[] = { + send_configuration, true), + DEFINE_PROP_BOOL("send-section-footer", MigrationState, + send_section_footer, true), ++ DEFINE_PROP_BOOL("decompress-error-check", MigrationState, ++ decompress_error_check, true), + + /* Migration parameters */ + DEFINE_PROP_UINT8("x-compress-level", MigrationState, +diff --git a/migration/migration.h b/migration/migration.h +index 06833d7..a9c5c7f 100644 +--- a/migration/migration.h ++++ b/migration/migration.h +@@ -182,6 +182,14 @@ struct MigrationState + bool send_configuration; + /* Whether we send section footer during migration */ + bool send_section_footer; ++ ++ /* ++ * Whether we abort the migration if decompression errors are ++ * detected at the destination. It is left at false for qemu ++ * older than 3.0, since only newer qemu sends streams that ++ * do not trigger spurious decompression errors. ++ */ ++ bool decompress_error_check; + }; + + void migrate_set_state(int *state, int old_state, int new_state); +diff --git a/migration/ram.c b/migration/ram.c +index c982201..bd563b5 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -2582,7 +2582,7 @@ static void *do_data_decompress(void *opaque) + + ret = qemu_uncompress_data(¶m->stream, des, pagesize, + param->compbuf, len); +- if (ret < 0) { ++ if (ret < 0 && migrate_get_current()->decompress_error_check) { + error_report("decompress data failed"); + qemu_file_set_error(decomp_file, ret); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-introduce-save_normal_page.patch b/SOURCES/kvm-migration-introduce-save_normal_page.patch new file mode 100644 index 0000000..06a302f --- /dev/null +++ b/SOURCES/kvm-migration-introduce-save_normal_page.patch @@ -0,0 +1,108 @@ +From 45b68ea328fbac00681b1c2eec1eb1499005020a Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Fri, 22 Jun 2018 18:59:56 +0200 +Subject: [PATCH 17/57] migration: introduce save_normal_page() + +RH-Author: Dr. David Alan Gilbert +Message-id: <20180622190005.21297-10-dgilbert@redhat.com> +Patchwork-id: 81003 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 09/18] migration: introduce save_normal_page() +Bugzilla: 1584139 +RH-Acked-by: Peter Xu +RH-Acked-by: Juan Quintela +RH-Acked-by: Laurent Vivier + +From: Xiao Guangrong + +It directly sends the page to the stream neither checking zero nor +using xbzrle or compression + +Reviewed-by: Peter Xu +Reviewed-by: Dr. David Alan Gilbert +Signed-off-by: Xiao Guangrong +Message-Id: <20180330075128.26919-10-xiaoguangrong@tencent.com> +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 65dacaa04fa7e6104cbcee9251c7845355769a10) +Signed-off-by: Miroslav Rezanina +--- + migration/ram.c | 50 ++++++++++++++++++++++++++++++-------------------- + 1 file changed, 30 insertions(+), 20 deletions(-) + +diff --git a/migration/ram.c b/migration/ram.c +index 6e8a7e2..908879f 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -1013,6 +1013,34 @@ static bool control_save_page(RAMState *rs, RAMBlock *block, ram_addr_t offset, + return true; + } + ++/* ++ * directly send the page to the stream ++ * ++ * Returns the number of pages written. ++ * ++ * @rs: current RAM state ++ * @block: block that contains the page we want to send ++ * @offset: offset inside the block for the page ++ * @buf: the page to be sent ++ * @async: send to page asyncly ++ */ ++static int save_normal_page(RAMState *rs, RAMBlock *block, ram_addr_t offset, ++ uint8_t *buf, bool async) ++{ ++ ram_counters.transferred += save_page_header(rs, rs->f, block, ++ offset | RAM_SAVE_FLAG_PAGE); ++ if (async) { ++ qemu_put_buffer_async(rs->f, buf, TARGET_PAGE_SIZE, ++ migrate_release_ram() & ++ migration_in_postcopy()); ++ } else { ++ qemu_put_buffer(rs->f, buf, TARGET_PAGE_SIZE); ++ } ++ ram_counters.transferred += TARGET_PAGE_SIZE; ++ ram_counters.normal++; ++ return 1; ++} ++ + /** + * ram_save_page: send the given page to the stream + * +@@ -1053,18 +1081,7 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) + + /* XBZRLE overflow or normal page */ + if (pages == -1) { +- ram_counters.transferred += +- save_page_header(rs, rs->f, block, offset | RAM_SAVE_FLAG_PAGE); +- if (send_async) { +- qemu_put_buffer_async(rs->f, p, TARGET_PAGE_SIZE, +- migrate_release_ram() & +- migration_in_postcopy()); +- } else { +- qemu_put_buffer(rs->f, p, TARGET_PAGE_SIZE); +- } +- ram_counters.transferred += TARGET_PAGE_SIZE; +- pages = 1; +- ram_counters.normal++; ++ pages = save_normal_page(rs, block, offset, p, send_async); + } + + XBZRLE_cache_unlock(); +@@ -1195,14 +1212,7 @@ static int ram_save_compressed_page(RAMState *rs, PageSearchStatus *pss, + * we post it as normal page as compression will take much + * CPU resource. + */ +- ram_counters.transferred += save_page_header(rs, rs->f, block, +- offset | RAM_SAVE_FLAG_PAGE); +- qemu_put_buffer_async(rs->f, p, TARGET_PAGE_SIZE, +- migrate_release_ram() & +- migration_in_postcopy()); +- ram_counters.transferred += TARGET_PAGE_SIZE; +- ram_counters.normal++; +- pages = 1; ++ pages = save_normal_page(rs, block, offset, p, true); + } else { + pages = compress_page_with_multi_thread(rs, block, offset); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-move-calling-control_save_page-to-the-comm.patch b/SOURCES/kvm-migration-move-calling-control_save_page-to-the-comm.patch new file mode 100644 index 0000000..cd8f72e --- /dev/null +++ b/SOURCES/kvm-migration-move-calling-control_save_page-to-the-comm.patch @@ -0,0 +1,74 @@ +From 5233d49067a64ecb2c6ab2c91f4345a254f832f2 Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Fri, 22 Jun 2018 18:59:54 +0200 +Subject: [PATCH 15/57] migration: move calling control_save_page to the common + place + +RH-Author: Dr. David Alan Gilbert +Message-id: <20180622190005.21297-8-dgilbert@redhat.com> +Patchwork-id: 81000 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 07/18] migration: move calling control_save_page to the common place +Bugzilla: 1584139 +RH-Acked-by: Peter Xu +RH-Acked-by: Juan Quintela +RH-Acked-by: Laurent Vivier + +From: Xiao Guangrong + +The function is called by both ram_save_page and ram_save_target_page, +so move it to the common caller to cleanup the code + +Reviewed-by: Peter Xu +Signed-off-by: Xiao Guangrong +Message-Id: <20180330075128.26919-8-xiaoguangrong@tencent.com> +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit a8ec91f941c5f83123796331c09333d3557eb5fc) +Signed-off-by: Miroslav Rezanina +--- + migration/ram.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/migration/ram.c b/migration/ram.c +index 106fcf1..9d6c41c 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -1038,10 +1038,6 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) + p = block->host + offset; + trace_ram_save_page(block->idstr, (uint64_t)offset, p); + +- if (control_save_page(rs, block, offset, &pages)) { +- return pages; +- } +- + XBZRLE_cache_lock(); + pages = save_zero_page(rs, block, offset); + if (pages > 0) { +@@ -1199,10 +1195,6 @@ static int ram_save_compressed_page(RAMState *rs, PageSearchStatus *pss, + + p = block->host + offset; + +- if (control_save_page(rs, block, offset, &pages)) { +- return pages; +- } +- + /* When starting the process of a new block, the first page of + * the block should be sent out before other pages in the same + * block, and all the pages in last block should have been sent +@@ -1490,6 +1482,14 @@ err: + static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, + bool last_stage) + { ++ RAMBlock *block = pss->block; ++ ram_addr_t offset = pss->page << TARGET_PAGE_BITS; ++ int res; ++ ++ if (control_save_page(rs, block, offset, &res)) { ++ return res; ++ } ++ + /* + * If xbzrle is on, stop using the data compression after first + * round of migration even if compression is enabled. In theory, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-move-calling-save_zero_page-to-the-common-.patch b/SOURCES/kvm-migration-move-calling-save_zero_page-to-the-common-.patch new file mode 100644 index 0000000..7a3a726 --- /dev/null +++ b/SOURCES/kvm-migration-move-calling-save_zero_page-to-the-common-.patch @@ -0,0 +1,175 @@ +From 81bfa8602be9c36955c451db641e66546732c59a Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Fri, 22 Jun 2018 18:59:55 +0200 +Subject: [PATCH 16/57] migration: move calling save_zero_page to the common + place + +RH-Author: Dr. David Alan Gilbert +Message-id: <20180622190005.21297-9-dgilbert@redhat.com> +Patchwork-id: 81006 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 08/18] migration: move calling save_zero_page to the common place +Bugzilla: 1584139 +RH-Acked-by: Peter Xu +RH-Acked-by: Juan Quintela +RH-Acked-by: Laurent Vivier + +From: Xiao Guangrong + +save_zero_page() is always our first approach to try, move it to +the common place before calling ram_save_compressed_page +and ram_save_page + +Reviewed-by: Peter Xu +Reviewed-by: Dr. David Alan Gilbert +Signed-off-by: Xiao Guangrong +Message-Id: <20180330075128.26919-9-xiaoguangrong@tencent.com> +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit d7400a3409982a52ac451cd3ca9caee9db670ca7) +Signed-off-by: Miroslav Rezanina +--- + migration/ram.c | 105 +++++++++++++++++++++++++++++++------------------------- + 1 file changed, 59 insertions(+), 46 deletions(-) + +diff --git a/migration/ram.c b/migration/ram.c +index 9d6c41c..6e8a7e2 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -1039,15 +1039,8 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) + trace_ram_save_page(block->idstr, (uint64_t)offset, p); + + XBZRLE_cache_lock(); +- pages = save_zero_page(rs, block, offset); +- if (pages > 0) { +- /* Must let xbzrle know, otherwise a previous (now 0'd) cached +- * page would be stale +- */ +- xbzrle_cache_zero_page(rs, current_addr); +- ram_release_pages(block->idstr, offset, pages); +- } else if (!rs->ram_bulk_stage && +- !migration_in_postcopy() && migrate_use_xbzrle()) { ++ if (!rs->ram_bulk_stage && !migration_in_postcopy() && ++ migrate_use_xbzrle()) { + pages = save_xbzrle_page(rs, &p, current_addr, block, + offset, last_stage); + if (!last_stage) { +@@ -1195,40 +1188,23 @@ static int ram_save_compressed_page(RAMState *rs, PageSearchStatus *pss, + + p = block->host + offset; + +- /* When starting the process of a new block, the first page of +- * the block should be sent out before other pages in the same +- * block, and all the pages in last block should have been sent +- * out, keeping this order is important, because the 'cont' flag +- * is used to avoid resending the block name. +- */ + if (block != rs->last_sent_block) { +- flush_compressed_data(rs); +- pages = save_zero_page(rs, block, offset); +- if (pages > 0) { +- ram_release_pages(block->idstr, offset, pages); +- } else { +- /* +- * Make sure the first page is sent out before other pages. +- * +- * we post it as normal page as compression will take much +- * CPU resource. +- */ +- ram_counters.transferred += save_page_header(rs, rs->f, block, +- offset | RAM_SAVE_FLAG_PAGE); +- qemu_put_buffer_async(rs->f, p, TARGET_PAGE_SIZE, +- migrate_release_ram() & +- migration_in_postcopy()); +- ram_counters.transferred += TARGET_PAGE_SIZE; +- ram_counters.normal++; +- pages = 1; +- } ++ /* ++ * Make sure the first page is sent out before other pages. ++ * ++ * we post it as normal page as compression will take much ++ * CPU resource. ++ */ ++ ram_counters.transferred += save_page_header(rs, rs->f, block, ++ offset | RAM_SAVE_FLAG_PAGE); ++ qemu_put_buffer_async(rs->f, p, TARGET_PAGE_SIZE, ++ migrate_release_ram() & ++ migration_in_postcopy()); ++ ram_counters.transferred += TARGET_PAGE_SIZE; ++ ram_counters.normal++; ++ pages = 1; + } else { +- pages = save_zero_page(rs, block, offset); +- if (pages == -1) { +- pages = compress_page_with_multi_thread(rs, block, offset); +- } else { +- ram_release_pages(block->idstr, offset, pages); +- } ++ pages = compress_page_with_multi_thread(rs, block, offset); + } + + return pages; +@@ -1470,6 +1446,24 @@ err: + return -1; + } + ++static bool save_page_use_compression(RAMState *rs) ++{ ++ if (!migrate_use_compression()) { ++ return false; ++ } ++ ++ /* ++ * If xbzrle is on, stop using the data compression after first ++ * round of migration even if compression is enabled. In theory, ++ * xbzrle can do better than compression. ++ */ ++ if (rs->ram_bulk_stage || !migrate_use_xbzrle()) { ++ return true; ++ } ++ ++ return false; ++} ++ + /** + * ram_save_target_page: save one target page + * +@@ -1491,12 +1485,31 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, + } + + /* +- * If xbzrle is on, stop using the data compression after first +- * round of migration even if compression is enabled. In theory, +- * xbzrle can do better than compression. ++ * When starting the process of a new block, the first page of ++ * the block should be sent out before other pages in the same ++ * block, and all the pages in last block should have been sent ++ * out, keeping this order is important, because the 'cont' flag ++ * is used to avoid resending the block name. + */ +- if (migrate_use_compression() && +- (rs->ram_bulk_stage || !migrate_use_xbzrle())) { ++ if (block != rs->last_sent_block && save_page_use_compression(rs)) { ++ flush_compressed_data(rs); ++ } ++ ++ res = save_zero_page(rs, block, offset); ++ if (res > 0) { ++ /* Must let xbzrle know, otherwise a previous (now 0'd) cached ++ * page would be stale ++ */ ++ if (!save_page_use_compression(rs)) { ++ XBZRLE_cache_lock(); ++ xbzrle_cache_zero_page(rs, block->offset + offset); ++ XBZRLE_cache_unlock(); ++ } ++ ram_release_pages(block->idstr, offset, res); ++ return res; ++ } ++ ++ if (save_page_use_compression(rs)) { + return ram_save_compressed_page(rs, pss, last_stage); + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-move-some-code-to-ram_save_host_page.patch b/SOURCES/kvm-migration-move-some-code-to-ram_save_host_page.patch new file mode 100644 index 0000000..2bb7c69 --- /dev/null +++ b/SOURCES/kvm-migration-move-some-code-to-ram_save_host_page.patch @@ -0,0 +1,108 @@ +From cb638b3d14fb19db26f6108a08f1a3b62a96b6c3 Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Fri, 22 Jun 2018 18:59:53 +0200 +Subject: [PATCH 14/57] migration: move some code to ram_save_host_page + +RH-Author: Dr. David Alan Gilbert +Message-id: <20180622190005.21297-7-dgilbert@redhat.com> +Patchwork-id: 81007 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 06/18] migration: move some code to ram_save_host_page +Bugzilla: 1584139 +RH-Acked-by: Peter Xu +RH-Acked-by: Juan Quintela +RH-Acked-by: Laurent Vivier + +From: Xiao Guangrong + +Move some code from ram_save_target_page() to ram_save_host_page() +to make it be more readable for latter patches that dramatically +clean ram_save_target_page() up + +Reviewed-by: Peter Xu +Signed-off-by: Xiao Guangrong +Message-Id: <20180330075128.26919-7-xiaoguangrong@tencent.com> +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 1faa5665c0f1df2eff291454a3a85625a3bc93dd) +Signed-off-by: Miroslav Rezanina +--- + migration/ram.c | 43 +++++++++++++++++++------------------------ + 1 file changed, 19 insertions(+), 24 deletions(-) + +diff --git a/migration/ram.c b/migration/ram.c +index 8dc98a5..106fcf1 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -1484,38 +1484,23 @@ err: + * Returns the number of pages written + * + * @rs: current RAM state +- * @ms: current migration state + * @pss: data about the page we want to send + * @last_stage: if we are at the completion stage + */ + static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, + bool last_stage) + { +- int res = 0; +- +- /* Check the pages is dirty and if it is send it */ +- if (migration_bitmap_clear_dirty(rs, pss->block, pss->page)) { +- /* +- * If xbzrle is on, stop using the data compression after first +- * round of migration even if compression is enabled. In theory, +- * xbzrle can do better than compression. +- */ +- if (migrate_use_compression() && +- (rs->ram_bulk_stage || !migrate_use_xbzrle())) { +- res = ram_save_compressed_page(rs, pss, last_stage); +- } else { +- res = ram_save_page(rs, pss, last_stage); +- } +- +- if (res < 0) { +- return res; +- } +- if (pss->block->unsentmap) { +- clear_bit(pss->page, pss->block->unsentmap); +- } ++ /* ++ * If xbzrle is on, stop using the data compression after first ++ * round of migration even if compression is enabled. In theory, ++ * xbzrle can do better than compression. ++ */ ++ if (migrate_use_compression() && ++ (rs->ram_bulk_stage || !migrate_use_xbzrle())) { ++ return ram_save_compressed_page(rs, pss, last_stage); + } + +- return res; ++ return ram_save_page(rs, pss, last_stage); + } + + /** +@@ -1544,12 +1529,22 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss, + qemu_ram_pagesize(pss->block) >> TARGET_PAGE_BITS; + + do { ++ /* Check the pages is dirty and if it is send it */ ++ if (!migration_bitmap_clear_dirty(rs, pss->block, pss->page)) { ++ pss->page++; ++ continue; ++ } ++ + tmppages = ram_save_target_page(rs, pss, last_stage); + if (tmppages < 0) { + return tmppages; + } + + pages += tmppages; ++ if (pss->block->unsentmap) { ++ clear_bit(pss->page, pss->block->unsentmap); ++ } ++ + pss->page++; + } while ((pss->page & (pagesize_bits - 1)) && + offset_in_ramblock(pss->block, pss->page << TARGET_PAGE_BITS)); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-not-wait-RDMA_CM_EVENT_DISCONNECTED-event-.patch b/SOURCES/kvm-migration-not-wait-RDMA_CM_EVENT_DISCONNECTED-event-.patch new file mode 100644 index 0000000..18acc71 --- /dev/null +++ b/SOURCES/kvm-migration-not-wait-RDMA_CM_EVENT_DISCONNECTED-event-.patch @@ -0,0 +1,110 @@ +From 1465b08194e396304b2425d86bcfda85f3b98b9f Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Fri, 22 Jun 2018 19:00:04 +0200 +Subject: [PATCH 25/57] migration: not wait RDMA_CM_EVENT_DISCONNECTED event + after rdma_disconnect + +RH-Author: Dr. David Alan Gilbert +Message-id: <20180622190005.21297-18-dgilbert@redhat.com> +Patchwork-id: 81008 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 17/18] migration: not wait RDMA_CM_EVENT_DISCONNECTED event after rdma_disconnect +Bugzilla: 1584139 +RH-Acked-by: Juan Quintela +RH-Acked-by: Peter Xu +RH-Acked-by: Laurent Vivier + +From: Lidong Chen + +When cancel migration during RDMA precopy, the source qemu main thread hangs sometime. + +The backtrace is: + (gdb) bt + #0 0x00007f249eabd43d in write () from /lib64/libpthread.so.0 + #1 0x00007f24a1ce98e4 in rdma_get_cm_event (channel=0x4675d10, event=0x7ffe2f643dd0) at src/cma.c:2189 + #2 0x00000000007b6166 in qemu_rdma_cleanup (rdma=0x6784000) at migration/rdma.c:2296 + #3 0x00000000007b7cae in qio_channel_rdma_close (ioc=0x3bfcc30, errp=0x0) at migration/rdma.c:2999 + #4 0x00000000008db60e in qio_channel_close (ioc=0x3bfcc30, errp=0x0) at io/channel.c:273 + #5 0x00000000007a8765 in channel_close (opaque=0x3bfcc30) at migration/qemu-file-channel.c:98 + #6 0x00000000007a71f9 in qemu_fclose (f=0x527c000) at migration/qemu-file.c:334 + #7 0x0000000000795b96 in migrate_fd_cleanup (opaque=0x3b46280) at migration/migration.c:1162 + #8 0x000000000093a71b in aio_bh_call (bh=0x3db7a20) at util/async.c:90 + #9 0x000000000093a7b2 in aio_bh_poll (ctx=0x3b121c0) at util/async.c:118 + #10 0x000000000093f2ad in aio_dispatch (ctx=0x3b121c0) at util/aio-posix.c:436 + #11 0x000000000093ab41 in aio_ctx_dispatch (source=0x3b121c0, callback=0x0, user_data=0x0) + at util/async.c:261 + #12 0x00007f249f73c7aa in g_main_context_dispatch () from /lib64/libglib-2.0.so.0 + #13 0x000000000093dc5e in glib_pollfds_poll () at util/main-loop.c:215 + #14 0x000000000093dd4e in os_host_main_loop_wait (timeout=28000000) at util/main-loop.c:263 + #15 0x000000000093de05 in main_loop_wait (nonblocking=0) at util/main-loop.c:522 + #16 0x00000000005bc6a5 in main_loop () at vl.c:1944 + #17 0x00000000005c39b5 in main (argc=56, argv=0x7ffe2f6443f8, envp=0x3ad0030) at vl.c:4752 + +It does not get the RDMA_CM_EVENT_DISCONNECTED event after rdma_disconnect sometime. + +According to IB Spec once active side send DREQ message, it should wait for DREP message +and only once it arrived it should trigger a DISCONNECT event. DREP message can be dropped +due to network issues. +For that case the spec defines a DREP_timeout state in the CM state machine, if the DREP is +dropped we should get a timeout and a TIMEWAIT_EXIT event will be trigger. +Unfortunately the current kernel CM implementation doesn't include the DREP_timeout state +and in above scenario we will not get DISCONNECT or TIMEWAIT_EXIT events. + +So it should not invoke rdma_get_cm_event which may hang forever, and the event channel +is also destroyed in qemu_rdma_cleanup. + +Signed-off-by: Lidong Chen +Reviewed-by: Juan Quintela +Reviewed-by: Dr. David Alan Gilbert +Signed-off-by: Juan Quintela +(cherry picked from commit c5e76115ccb4979cec795a8ae38becd07c2fde9f) +Signed-off-by: Miroslav Rezanina +--- + migration/rdma.c | 12 ++---------- + migration/trace-events | 1 - + 2 files changed, 2 insertions(+), 11 deletions(-) + +diff --git a/migration/rdma.c b/migration/rdma.c +index 7d233b0..6e29ebf 100644 +--- a/migration/rdma.c ++++ b/migration/rdma.c +@@ -2268,8 +2268,7 @@ static int qemu_rdma_write(QEMUFile *f, RDMAContext *rdma, + + static void qemu_rdma_cleanup(RDMAContext *rdma) + { +- struct rdma_cm_event *cm_event; +- int ret, idx; ++ int idx; + + if (rdma->cm_id && rdma->connected) { + if ((rdma->error_state || +@@ -2283,14 +2282,7 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) + qemu_rdma_post_send_control(rdma, NULL, &head); + } + +- ret = rdma_disconnect(rdma->cm_id); +- if (!ret) { +- trace_qemu_rdma_cleanup_waiting_for_disconnect(); +- ret = rdma_get_cm_event(rdma->channel, &cm_event); +- if (!ret) { +- rdma_ack_cm_event(cm_event); +- } +- } ++ rdma_disconnect(rdma->cm_id); + trace_qemu_rdma_cleanup_disconnect(); + rdma->connected = false; + } +diff --git a/migration/trace-events b/migration/trace-events +index a180d7b..92b3179 100644 +--- a/migration/trace-events ++++ b/migration/trace-events +@@ -123,7 +123,6 @@ qemu_rdma_accept_pin_state(bool pin) "%d" + qemu_rdma_accept_pin_verbsc(void *verbs) "Verbs context after listen: %p" + qemu_rdma_block_for_wrid_miss(const char *wcompstr, int wcomp, const char *gcompstr, uint64_t req) "A Wanted wrid %s (%d) but got %s (%" PRIu64 ")" + qemu_rdma_cleanup_disconnect(void) "" +-qemu_rdma_cleanup_waiting_for_disconnect(void) "" + qemu_rdma_close(void) "" + qemu_rdma_connect_pin_all_requested(void) "" + qemu_rdma_connect_pin_all_outcome(bool pin) "%d" +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-postcopy-Clear-have_listen_thread.patch b/SOURCES/kvm-migration-postcopy-Clear-have_listen_thread.patch new file mode 100644 index 0000000..dcc86ec --- /dev/null +++ b/SOURCES/kvm-migration-postcopy-Clear-have_listen_thread.patch @@ -0,0 +1,53 @@ +From c6b775765f1b05c78fdaf24966c3c4f1d768a9ac Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Mon, 19 Nov 2018 16:19:20 +0100 +Subject: [PATCH 21/22] migration/postcopy: Clear have_listen_thread + +RH-Author: Dr. David Alan Gilbert +Message-id: <20181119161921.15191-2-dgilbert@redhat.com> +Patchwork-id: 83047 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/2] migration/postcopy: Clear have_listen_thread +Bugzilla: 1608877 +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Peter Xu +RH-Acked-by: Pankaj Gupta + +From: "Dr. David Alan Gilbert" + +Clear have_listen_thread when we exit the thread. +The fallout from this was that various things thought there was +an ongoing postcopy after the postcopy had finished. + +The case that failed was postcopy->savevm->loadvm. + +This corresponds to RH bug https://bugzilla.redhat.com/show_bug.cgi?id=1608765 + +Signed-off-by: Dr. David Alan Gilbert +Message-Id: <20180914170430.54271-2-dgilbert@redhat.com> +Reviewed-by: Peter Xu +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 9cf4bb8730c669c40550e635a9e2b8ee4f1664ca) + Manual merge due to context + +Signed-off-by: Danilo C. L. de Paula +(cherry picked from commit 66e37a444b4b4818957dabadcc4580f1877e4ebb) +Signed-off-by: Miroslav Rezanina +--- + migration/savevm.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/migration/savevm.c b/migration/savevm.c +index b975d3a..7a5d9ff 100644 +--- a/migration/savevm.c ++++ b/migration/savevm.c +@@ -1621,6 +1621,7 @@ static void *postcopy_ram_listen_thread(void *opaque) + migration_incoming_state_destroy(); + qemu_loadvm_state_cleanup(); + ++ mis->have_listen_thread = false; + return NULL; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-ram-Add-check-and-info-message-to-nvdimm-p.patch b/SOURCES/kvm-migration-ram-Add-check-and-info-message-to-nvdimm-p.patch new file mode 100644 index 0000000..e3eff46 --- /dev/null +++ b/SOURCES/kvm-migration-ram-Add-check-and-info-message-to-nvdimm-p.patch @@ -0,0 +1,56 @@ +From 4251549eca8b113b508f6bf6a7aaca2541acda61 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Fri, 31 Aug 2018 16:25:58 +0200 +Subject: [PATCH 16/29] migration/ram: Add check and info message to nvdimm + post copy. + +RH-Author: plai@redhat.com +Message-id: <1535732759-22481-9-git-send-email-plai@redhat.com> +Patchwork-id: 82009 +O-Subject: [RHEL7.6 PATCH BZ 1539280 8/9] migration/ram: Add check and info message to nvdimm post copy. +Bugzilla: 1539280 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Pankaj Gupta +RH-Acked-by: Miroslav Rezanina + +From: Junyan He + +The nvdimm kind memory does not support post copy now. +We disable post copy if we have nvdimm memory and print some +log hint to user. + +Signed-off-by: Junyan He +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Igor Mammedov +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 469dd51bc664979f159d47885997d482991394b8) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + migration/ram.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/migration/ram.c b/migration/ram.c +index 04b5df5..f850fd0 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -3120,6 +3120,15 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) + + static bool ram_has_postcopy(void *opaque) + { ++ RAMBlock *rb; ++ RAMBLOCK_FOREACH_MIGRATABLE(rb) { ++ if (ramblock_is_pmem(rb)) { ++ info_report("Block: %s, host: %p is a nvdimm memory, postcopy" ++ "is not supported now!", rb->idstr, rb->host); ++ return false; ++ } ++ } ++ + return migrate_postcopy_ram(); + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-ram-ensure-write-persistence-on-loading-al.patch b/SOURCES/kvm-migration-ram-ensure-write-persistence-on-loading-al.patch new file mode 100644 index 0000000..afa614b --- /dev/null +++ b/SOURCES/kvm-migration-ram-ensure-write-persistence-on-loading-al.patch @@ -0,0 +1,81 @@ +From 3d60cee570db578cc7589cac9844129e9ac9d460 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Fri, 31 Aug 2018 16:25:59 +0200 +Subject: [PATCH 17/29] migration/ram: ensure write persistence on loading all + data to PMEM. + +RH-Author: plai@redhat.com +Message-id: <1535732759-22481-10-git-send-email-plai@redhat.com> +Patchwork-id: 82008 +O-Subject: [RHEL7.6 PATCH BZ 1539280 9/9] migration/ram: ensure write persistence on loading all data to PMEM. +Bugzilla: 1539280 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Pankaj Gupta +RH-Acked-by: Miroslav Rezanina + +From: Junyan He + +Because we need to make sure the pmem kind memory data is synced +after migration, we choose to call pmem_persist() when the migration +finish. This will make sure the data of pmem is safe and will not +lose if power is off. + +Signed-off-by: Junyan He +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Igor Mammedov +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 56eb90af39abf66c0e80588a9f50c31e7df7320b) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + include/qemu/pmem.h | 6 ++++++ + migration/ram.c | 8 ++++++++ + 2 files changed, 14 insertions(+) + +diff --git a/include/qemu/pmem.h b/include/qemu/pmem.h +index ebdb070..dfb6d0d 100644 +--- a/include/qemu/pmem.h ++++ b/include/qemu/pmem.h +@@ -25,6 +25,12 @@ pmem_memcpy_persist(void *pmemdest, const void *src, size_t len) + return NULL; + } + ++static inline void ++pmem_persist(const void *addr, size_t len) ++{ ++ g_assert_not_reached(); ++} ++ + #endif /* CONFIG_LIBPMEM */ + + #endif /* !QEMU_PMEM_H */ +diff --git a/migration/ram.c b/migration/ram.c +index f850fd0..aba0f70 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -33,6 +33,7 @@ + #include "qemu/bitops.h" + #include "qemu/bitmap.h" + #include "qemu/main-loop.h" ++#include "qemu/pmem.h" + #include "xbzrle.h" + #include "ram.h" + #include "migration.h" +@@ -2766,6 +2767,13 @@ static int ram_load_setup(QEMUFile *f, void *opaque) + static int ram_load_cleanup(void *opaque) + { + RAMBlock *rb; ++ ++ RAMBLOCK_FOREACH_MIGRATABLE(rb) { ++ if (ramblock_is_pmem(rb)) { ++ pmem_persist(rb->host, rb->used_length); ++ } ++ } ++ + xbzrle_load_cleanup(); + compress_threads_load_cleanup(); + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-remove-ram_save_compressed_page.patch b/SOURCES/kvm-migration-remove-ram_save_compressed_page.patch new file mode 100644 index 0000000..e703fc3 --- /dev/null +++ b/SOURCES/kvm-migration-remove-ram_save_compressed_page.patch @@ -0,0 +1,97 @@ +From 5c9d6d50076df3032b1d09196e36c33004eb52fb Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Fri, 22 Jun 2018 18:59:57 +0200 +Subject: [PATCH 18/57] migration: remove ram_save_compressed_page() + +RH-Author: Dr. David Alan Gilbert +Message-id: <20180622190005.21297-11-dgilbert@redhat.com> +Patchwork-id: 81011 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 10/18] migration: remove ram_save_compressed_page() +Bugzilla: 1584139 +RH-Acked-by: Juan Quintela +RH-Acked-by: Peter Xu +RH-Acked-by: Laurent Vivier + +From: Xiao Guangrong + +Now, we can reuse the path in ram_save_page() to post the page out +as normal, then the only thing remained in ram_save_compressed_page() +is compression that we can move it out to the caller + +Reviewed-by: Peter Xu +Reviewed-by: Dr. David Alan Gilbert +Signed-off-by: Xiao Guangrong +Message-Id: <20180330075128.26919-11-xiaoguangrong@tencent.com> +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit da3f56cb2e767016d3f204837a77caf35b463f90) +Signed-off-by: Miroslav Rezanina +--- + migration/ram.c | 45 ++++++++------------------------------------- + 1 file changed, 8 insertions(+), 37 deletions(-) + +diff --git a/migration/ram.c b/migration/ram.c +index 908879f..466609f 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -1186,41 +1186,6 @@ static int compress_page_with_multi_thread(RAMState *rs, RAMBlock *block, + } + + /** +- * ram_save_compressed_page: compress the given page and send it to the stream +- * +- * Returns the number of pages written. +- * +- * @rs: current RAM state +- * @block: block that contains the page we want to send +- * @offset: offset inside the block for the page +- * @last_stage: if we are at the completion stage +- */ +-static int ram_save_compressed_page(RAMState *rs, PageSearchStatus *pss, +- bool last_stage) +-{ +- int pages = -1; +- uint8_t *p; +- RAMBlock *block = pss->block; +- ram_addr_t offset = pss->page << TARGET_PAGE_BITS; +- +- p = block->host + offset; +- +- if (block != rs->last_sent_block) { +- /* +- * Make sure the first page is sent out before other pages. +- * +- * we post it as normal page as compression will take much +- * CPU resource. +- */ +- pages = save_normal_page(rs, block, offset, p, true); +- } else { +- pages = compress_page_with_multi_thread(rs, block, offset); +- } +- +- return pages; +-} +- +-/** + * find_dirty_block: find the next dirty page and update any state + * associated with the search process. + * +@@ -1519,8 +1484,14 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, + return res; + } + +- if (save_page_use_compression(rs)) { +- return ram_save_compressed_page(rs, pss, last_stage); ++ /* ++ * Make sure the first page is sent out before other pages. ++ * ++ * we post it as normal page as compression will take much ++ * CPU resource. ++ */ ++ if (block == rs->last_sent_block && save_page_use_compression(rs)) { ++ res = compress_page_with_multi_thread(rs, block, offset); + } + + return ram_save_page(rs, pss, last_stage); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-stop-compressing-page-in-migration-thread.patch b/SOURCES/kvm-migration-stop-compressing-page-in-migration-thread.patch new file mode 100644 index 0000000..1b17689 --- /dev/null +++ b/SOURCES/kvm-migration-stop-compressing-page-in-migration-thread.patch @@ -0,0 +1,86 @@ +From 991ad76f13534cd2f22b30ba8b556f284f28c5c6 Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Fri, 22 Jun 2018 18:59:48 +0200 +Subject: [PATCH 09/57] migration: stop compressing page in migration thread + +RH-Author: Dr. David Alan Gilbert +Message-id: <20180622190005.21297-2-dgilbert@redhat.com> +Patchwork-id: 81005 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 01/18] migration: stop compressing page in migration thread +Bugzilla: 1584139 +RH-Acked-by: Juan Quintela +RH-Acked-by: Peter Xu +RH-Acked-by: Laurent Vivier + +From: Xiao Guangrong + +As compression is a heavy work, do not do it in migration thread, +instead, we post it out as a normal page + +Reviewed-by: Wei Wang +Reviewed-by: Peter Xu +Reviewed-by: Dr. David Alan Gilbert +Signed-off-by: Xiao Guangrong +Message-Id: <20180330075128.26919-2-xiaoguangrong@tencent.com> +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 263a289ae61c8344a417a95b0142650fdff3af56) +Signed-off-by: Miroslav Rezanina +--- + migration/ram.c | 32 ++++++++++++++++---------------- + 1 file changed, 16 insertions(+), 16 deletions(-) + +diff --git a/migration/ram.c b/migration/ram.c +index 00c06b5..f27038a 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -1138,7 +1138,7 @@ static int ram_save_compressed_page(RAMState *rs, PageSearchStatus *pss, + int pages = -1; + uint64_t bytes_xmit = 0; + uint8_t *p; +- int ret, blen; ++ int ret; + RAMBlock *block = pss->block; + ram_addr_t offset = pss->page << TARGET_PAGE_BITS; + +@@ -1168,23 +1168,23 @@ static int ram_save_compressed_page(RAMState *rs, PageSearchStatus *pss, + if (block != rs->last_sent_block) { + flush_compressed_data(rs); + pages = save_zero_page(rs, block, offset); +- if (pages == -1) { +- /* Make sure the first page is sent out before other pages */ +- bytes_xmit = save_page_header(rs, rs->f, block, offset | +- RAM_SAVE_FLAG_COMPRESS_PAGE); +- blen = qemu_put_compression_data(rs->f, p, TARGET_PAGE_SIZE, +- migrate_compress_level()); +- if (blen > 0) { +- ram_counters.transferred += bytes_xmit + blen; +- ram_counters.normal++; +- pages = 1; +- } else { +- qemu_file_set_error(rs->f, blen); +- error_report("compressed data failed!"); +- } +- } + if (pages > 0) { + ram_release_pages(block->idstr, offset, pages); ++ } else { ++ /* ++ * Make sure the first page is sent out before other pages. ++ * ++ * we post it as normal page as compression will take much ++ * CPU resource. ++ */ ++ ram_counters.transferred += save_page_header(rs, rs->f, block, ++ offset | RAM_SAVE_FLAG_PAGE); ++ qemu_put_buffer_async(rs->f, p, TARGET_PAGE_SIZE, ++ migrate_release_ram() & ++ migration_in_postcopy()); ++ ram_counters.transferred += TARGET_PAGE_SIZE; ++ ram_counters.normal++; ++ pages = 1; + } + } else { + pages = save_zero_page(rs, block, offset); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-stop-compression-to-allocate-and-free-memo.patch b/SOURCES/kvm-migration-stop-compression-to-allocate-and-free-memo.patch new file mode 100644 index 0000000..609e340 --- /dev/null +++ b/SOURCES/kvm-migration-stop-compression-to-allocate-and-free-memo.patch @@ -0,0 +1,265 @@ +From 6a6e25b0dff81b5a6dc4272a176f5987b7474a93 Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Fri, 22 Jun 2018 18:59:49 +0200 +Subject: [PATCH 10/57] migration: stop compression to allocate and free memory + frequently + +RH-Author: Dr. David Alan Gilbert +Message-id: <20180622190005.21297-3-dgilbert@redhat.com> +Patchwork-id: 80998 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 02/18] migration: stop compression to allocate and free memory frequently +Bugzilla: 1584139 +RH-Acked-by: Juan Quintela +RH-Acked-by: Peter Xu +RH-Acked-by: Laurent Vivier + +From: Xiao Guangrong + +Current code uses compress2() to compress memory which manages memory +internally, that causes huge memory is allocated and freed very +frequently + +More worse, frequently returning memory to kernel will flush TLBs +and trigger invalidation callbacks on mmu-notification which +interacts with KVM MMU, that dramatically reduce the performance +of VM + +So, we maintain the memory by ourselves and reuse it for each +compression + +Reviewed-by: Peter Xu +Reviewed-by: Jiang Biao +Signed-off-by: Xiao Guangrong +Message-Id: <20180330075128.26919-3-xiaoguangrong@tencent.com> +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit dcaf446ebda5d87e05eb41cdbafb7ae4a7cc4a62) +Signed-off-by: Miroslav Rezanina +--- + migration/qemu-file.c | 39 ++++++++++++++++++++++++++++++++------- + migration/qemu-file.h | 6 ++++-- + migration/ram.c | 41 ++++++++++++++++++++++++++++++++--------- + 3 files changed, 68 insertions(+), 18 deletions(-) + +diff --git a/migration/qemu-file.c b/migration/qemu-file.c +index bb63c77..bafe3a0 100644 +--- a/migration/qemu-file.c ++++ b/migration/qemu-file.c +@@ -658,8 +658,32 @@ uint64_t qemu_get_be64(QEMUFile *f) + return v; + } + +-/* Compress size bytes of data start at p with specific compression +- * level and store the compressed data to the buffer of f. ++/* return the size after compression, or negative value on error */ ++static int qemu_compress_data(z_stream *stream, uint8_t *dest, size_t dest_len, ++ const uint8_t *source, size_t source_len) ++{ ++ int err; ++ ++ err = deflateReset(stream); ++ if (err != Z_OK) { ++ return -1; ++ } ++ ++ stream->avail_in = source_len; ++ stream->next_in = (uint8_t *)source; ++ stream->avail_out = dest_len; ++ stream->next_out = dest; ++ ++ err = deflate(stream, Z_FINISH); ++ if (err != Z_STREAM_END) { ++ return -1; ++ } ++ ++ return stream->next_out - dest; ++} ++ ++/* Compress size bytes of data start at p and store the compressed ++ * data to the buffer of f. + * + * When f is not writable, return -1 if f has no space to save the + * compressed data. +@@ -667,9 +691,8 @@ uint64_t qemu_get_be64(QEMUFile *f) + * do fflush first, if f still has no space to save the compressed + * data, return -1. + */ +- +-ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size, +- int level) ++ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream, ++ const uint8_t *p, size_t size) + { + ssize_t blen = IO_BUF_SIZE - f->buf_index - sizeof(int32_t); + +@@ -683,8 +706,10 @@ ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size, + return -1; + } + } +- if (compress2(f->buf + f->buf_index + sizeof(int32_t), (uLongf *)&blen, +- (Bytef *)p, size, level) != Z_OK) { ++ ++ blen = qemu_compress_data(stream, f->buf + f->buf_index + sizeof(int32_t), ++ blen, p, size); ++ if (blen < 0) { + error_report("Compress Failed!"); + return 0; + } +diff --git a/migration/qemu-file.h b/migration/qemu-file.h +index f4f356a..2ccfcfb 100644 +--- a/migration/qemu-file.h ++++ b/migration/qemu-file.h +@@ -25,6 +25,8 @@ + #ifndef MIGRATION_QEMU_FILE_H + #define MIGRATION_QEMU_FILE_H + ++#include ++ + /* Read a chunk of data from a file at the given position. The pos argument + * can be ignored if the file is only be used for streaming. The number of + * bytes actually read should be returned. +@@ -132,8 +134,8 @@ bool qemu_file_is_writable(QEMUFile *f); + + size_t qemu_peek_buffer(QEMUFile *f, uint8_t **buf, size_t size, size_t offset); + size_t qemu_get_buffer_in_place(QEMUFile *f, uint8_t **buf, size_t size); +-ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size, +- int level); ++ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream, ++ const uint8_t *p, size_t size); + int qemu_put_qemu_file(QEMUFile *f_des, QEMUFile *f_src); + + /* +diff --git a/migration/ram.c b/migration/ram.c +index f27038a..7d3b1da 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -269,6 +269,7 @@ struct CompressParam { + QemuCond cond; + RAMBlock *block; + ram_addr_t offset; ++ z_stream stream; + }; + typedef struct CompressParam CompressParam; + +@@ -299,7 +300,7 @@ static QemuThread *decompress_threads; + static QemuMutex decomp_done_lock; + static QemuCond decomp_done_cond; + +-static int do_compress_ram_page(QEMUFile *f, RAMBlock *block, ++static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, + ram_addr_t offset); + + static void *do_data_compress(void *opaque) +@@ -316,7 +317,7 @@ static void *do_data_compress(void *opaque) + param->block = NULL; + qemu_mutex_unlock(¶m->mutex); + +- do_compress_ram_page(param->file, block, offset); ++ do_compress_ram_page(param->file, ¶m->stream, block, offset); + + qemu_mutex_lock(&comp_done_lock); + param->done = true; +@@ -357,10 +358,19 @@ static void compress_threads_save_cleanup(void) + terminate_compression_threads(); + thread_count = migrate_compress_threads(); + for (i = 0; i < thread_count; i++) { ++ /* ++ * we use it as a indicator which shows if the thread is ++ * properly init'd or not ++ */ ++ if (!comp_param[i].file) { ++ break; ++ } + qemu_thread_join(compress_threads + i); +- qemu_fclose(comp_param[i].file); + qemu_mutex_destroy(&comp_param[i].mutex); + qemu_cond_destroy(&comp_param[i].cond); ++ deflateEnd(&comp_param[i].stream); ++ qemu_fclose(comp_param[i].file); ++ comp_param[i].file = NULL; + } + qemu_mutex_destroy(&comp_done_lock); + qemu_cond_destroy(&comp_done_cond); +@@ -370,12 +380,12 @@ static void compress_threads_save_cleanup(void) + comp_param = NULL; + } + +-static void compress_threads_save_setup(void) ++static int compress_threads_save_setup(void) + { + int i, thread_count; + + if (!migrate_use_compression()) { +- return; ++ return 0; + } + thread_count = migrate_compress_threads(); + compress_threads = g_new0(QemuThread, thread_count); +@@ -383,6 +393,11 @@ static void compress_threads_save_setup(void) + qemu_cond_init(&comp_done_cond); + qemu_mutex_init(&comp_done_lock); + for (i = 0; i < thread_count; i++) { ++ if (deflateInit(&comp_param[i].stream, ++ migrate_compress_level()) != Z_OK) { ++ goto exit; ++ } ++ + /* comp_param[i].file is just used as a dummy buffer to save data, + * set its ops to empty. + */ +@@ -395,6 +410,11 @@ static void compress_threads_save_setup(void) + do_data_compress, comp_param + i, + QEMU_THREAD_JOINABLE); + } ++ return 0; ++ ++exit: ++ compress_threads_save_cleanup(); ++ return -1; + } + + /* Multiple fd's */ +@@ -1032,7 +1052,7 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) + return pages; + } + +-static int do_compress_ram_page(QEMUFile *f, RAMBlock *block, ++static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, + ram_addr_t offset) + { + RAMState *rs = ram_state; +@@ -1041,8 +1061,7 @@ static int do_compress_ram_page(QEMUFile *f, RAMBlock *block, + + bytes_sent = save_page_header(rs, f, block, offset | + RAM_SAVE_FLAG_COMPRESS_PAGE); +- blen = qemu_put_compression_data(f, p, TARGET_PAGE_SIZE, +- migrate_compress_level()); ++ blen = qemu_put_compression_data(f, stream, p, TARGET_PAGE_SIZE); + if (blen < 0) { + bytes_sent = 0; + qemu_file_set_error(migrate_get_current()->to_dst_file, blen); +@@ -2215,9 +2234,14 @@ static int ram_save_setup(QEMUFile *f, void *opaque) + RAMState **rsp = opaque; + RAMBlock *block; + ++ if (compress_threads_save_setup()) { ++ return -1; ++ } ++ + /* migration has already setup the bitmap, reuse it. */ + if (!migration_in_colo_state()) { + if (ram_init_all(rsp) != 0) { ++ compress_threads_save_cleanup(); + return -1; + } + } +@@ -2237,7 +2261,6 @@ static int ram_save_setup(QEMUFile *f, void *opaque) + } + + rcu_read_unlock(); +- compress_threads_save_setup(); + + ram_control_before_iterate(f, RAM_CONTROL_SETUP); + ram_control_after_iterate(f, RAM_CONTROL_SETUP); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-stop-decompression-to-allocate-and-free-me.patch b/SOURCES/kvm-migration-stop-decompression-to-allocate-and-free-me.patch new file mode 100644 index 0000000..832a40e --- /dev/null +++ b/SOURCES/kvm-migration-stop-decompression-to-allocate-and-free-me.patch @@ -0,0 +1,217 @@ +From b835044f9cbe56874b199c6091784cc9c51c7dd5 Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Fri, 22 Jun 2018 18:59:50 +0200 +Subject: [PATCH 11/57] migration: stop decompression to allocate and free + memory frequently + +RH-Author: Dr. David Alan Gilbert +Message-id: <20180622190005.21297-4-dgilbert@redhat.com> +Patchwork-id: 80995 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 03/18] migration: stop decompression to allocate and free memory frequently +Bugzilla: 1584139 +RH-Acked-by: Juan Quintela +RH-Acked-by: Peter Xu +RH-Acked-by: Laurent Vivier + +From: Xiao Guangrong + +Current code uses uncompress() to decompress memory which manages +memory internally, that causes huge memory is allocated and freed +very frequently, more worse, frequently returning memory to kernel +will flush TLBs + +So, we maintain the memory by ourselves and reuse it for each +decompression + +Reviewed-by: Peter Xu +Reviewed-by: Jiang Biao +Signed-off-by: Xiao Guangrong +Message-Id: <20180330075128.26919-4-xiaoguangrong@tencent.com> +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 797ca154b4c68dbd8e93382f714388ab311f672d) +Signed-off-by: Miroslav Rezanina +--- + migration/ram.c | 112 +++++++++++++++++++++++++++++++++++++++++--------------- + 1 file changed, 82 insertions(+), 30 deletions(-) + +diff --git a/migration/ram.c b/migration/ram.c +index 7d3b1da..be89cd8 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -281,6 +281,7 @@ struct DecompressParam { + void *des; + uint8_t *compbuf; + int len; ++ z_stream stream; + }; + typedef struct DecompressParam DecompressParam; + +@@ -2525,6 +2526,31 @@ void ram_handle_compressed(void *host, uint8_t ch, uint64_t size) + } + } + ++/* return the size after decompression, or negative value on error */ ++static int ++qemu_uncompress_data(z_stream *stream, uint8_t *dest, size_t dest_len, ++ const uint8_t *source, size_t source_len) ++{ ++ int err; ++ ++ err = inflateReset(stream); ++ if (err != Z_OK) { ++ return -1; ++ } ++ ++ stream->avail_in = source_len; ++ stream->next_in = (uint8_t *)source; ++ stream->avail_out = dest_len; ++ stream->next_out = dest; ++ ++ err = inflate(stream, Z_NO_FLUSH); ++ if (err != Z_STREAM_END) { ++ return -1; ++ } ++ ++ return stream->total_out; ++} ++ + static void *do_data_decompress(void *opaque) + { + DecompressParam *param = opaque; +@@ -2541,13 +2567,13 @@ static void *do_data_decompress(void *opaque) + qemu_mutex_unlock(¶m->mutex); + + pagesize = TARGET_PAGE_SIZE; +- /* uncompress() will return failed in some case, especially +- * when the page is dirted when doing the compression, it's +- * not a problem because the dirty page will be retransferred ++ /* qemu_uncompress_data() will return failed in some case, ++ * especially when the page is dirtied when doing the compression, ++ * it's not a problem because the dirty page will be retransferred + * and uncompress() won't break the data in other pages. + */ +- uncompress((Bytef *)des, &pagesize, +- (const Bytef *)param->compbuf, len); ++ qemu_uncompress_data(¶m->stream, des, pagesize, param->compbuf, ++ len); + + qemu_mutex_lock(&decomp_done_lock); + param->done = true; +@@ -2582,30 +2608,6 @@ static void wait_for_decompress_done(void) + qemu_mutex_unlock(&decomp_done_lock); + } + +-static void compress_threads_load_setup(void) +-{ +- int i, thread_count; +- +- if (!migrate_use_compression()) { +- return; +- } +- thread_count = migrate_decompress_threads(); +- decompress_threads = g_new0(QemuThread, thread_count); +- decomp_param = g_new0(DecompressParam, thread_count); +- qemu_mutex_init(&decomp_done_lock); +- qemu_cond_init(&decomp_done_cond); +- for (i = 0; i < thread_count; i++) { +- qemu_mutex_init(&decomp_param[i].mutex); +- qemu_cond_init(&decomp_param[i].cond); +- decomp_param[i].compbuf = g_malloc0(compressBound(TARGET_PAGE_SIZE)); +- decomp_param[i].done = true; +- decomp_param[i].quit = false; +- qemu_thread_create(decompress_threads + i, "decompress", +- do_data_decompress, decomp_param + i, +- QEMU_THREAD_JOINABLE); +- } +-} +- + static void compress_threads_load_cleanup(void) + { + int i, thread_count; +@@ -2615,16 +2617,30 @@ static void compress_threads_load_cleanup(void) + } + thread_count = migrate_decompress_threads(); + for (i = 0; i < thread_count; i++) { ++ /* ++ * we use it as a indicator which shows if the thread is ++ * properly init'd or not ++ */ ++ if (!decomp_param[i].compbuf) { ++ break; ++ } ++ + qemu_mutex_lock(&decomp_param[i].mutex); + decomp_param[i].quit = true; + qemu_cond_signal(&decomp_param[i].cond); + qemu_mutex_unlock(&decomp_param[i].mutex); + } + for (i = 0; i < thread_count; i++) { ++ if (!decomp_param[i].compbuf) { ++ break; ++ } ++ + qemu_thread_join(decompress_threads + i); + qemu_mutex_destroy(&decomp_param[i].mutex); + qemu_cond_destroy(&decomp_param[i].cond); ++ inflateEnd(&decomp_param[i].stream); + g_free(decomp_param[i].compbuf); ++ decomp_param[i].compbuf = NULL; + } + g_free(decompress_threads); + g_free(decomp_param); +@@ -2632,6 +2648,39 @@ static void compress_threads_load_cleanup(void) + decomp_param = NULL; + } + ++static int compress_threads_load_setup(void) ++{ ++ int i, thread_count; ++ ++ if (!migrate_use_compression()) { ++ return 0; ++ } ++ ++ thread_count = migrate_decompress_threads(); ++ decompress_threads = g_new0(QemuThread, thread_count); ++ decomp_param = g_new0(DecompressParam, thread_count); ++ qemu_mutex_init(&decomp_done_lock); ++ qemu_cond_init(&decomp_done_cond); ++ for (i = 0; i < thread_count; i++) { ++ if (inflateInit(&decomp_param[i].stream) != Z_OK) { ++ goto exit; ++ } ++ ++ decomp_param[i].compbuf = g_malloc0(compressBound(TARGET_PAGE_SIZE)); ++ qemu_mutex_init(&decomp_param[i].mutex); ++ qemu_cond_init(&decomp_param[i].cond); ++ decomp_param[i].done = true; ++ decomp_param[i].quit = false; ++ qemu_thread_create(decompress_threads + i, "decompress", ++ do_data_decompress, decomp_param + i, ++ QEMU_THREAD_JOINABLE); ++ } ++ return 0; ++exit: ++ compress_threads_load_cleanup(); ++ return -1; ++} ++ + static void decompress_data_with_multi_threads(QEMUFile *f, + void *host, int len) + { +@@ -2671,8 +2720,11 @@ static void decompress_data_with_multi_threads(QEMUFile *f, + */ + static int ram_load_setup(QEMUFile *f, void *opaque) + { ++ if (compress_threads_load_setup()) { ++ return -1; ++ } ++ + xbzrle_load_setup(); +- compress_threads_load_setup(); + ramblock_recv_map_init(); + return 0; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-migration-update-index-field-when-delete-or-qsort-RD.patch b/SOURCES/kvm-migration-update-index-field-when-delete-or-qsort-RD.patch new file mode 100644 index 0000000..0bca5d9 --- /dev/null +++ b/SOURCES/kvm-migration-update-index-field-when-delete-or-qsort-RD.patch @@ -0,0 +1,66 @@ +From f6aecbe90d02dd675d82194240b1be740ca2c2e7 Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Fri, 22 Jun 2018 19:00:00 +0200 +Subject: [PATCH 21/57] migration: update index field when delete or qsort + RDMALocalBlock + +RH-Author: Dr. David Alan Gilbert +Message-id: <20180622190005.21297-14-dgilbert@redhat.com> +Patchwork-id: 81010 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 13/18] migration: update index field when delete or qsort RDMALocalBlock +Bugzilla: 1584139 +RH-Acked-by: Juan Quintela +RH-Acked-by: Peter Xu +RH-Acked-by: Laurent Vivier + +From: Lidong Chen + +rdma_delete_block function deletes RDMALocalBlock base on index field, +but not update the index field. So when next time invoke rdma_delete_block, +it will not work correctly. + +If start and cancel migration repeatedly, some RDMALocalBlock not invoke +ibv_dereg_mr to decrease kernel mm_struct vmpin. When vmpin is large than +max locked memory limitation, ibv_reg_mr will failed, and migration can not +start successfully again. + +Signed-off-by: Lidong Chen +Reviewed-by: Dr. David Alan Gilbert +Message-Id: <1525618499-1560-1-git-send-email-lidongchen@tencent.com> +Signed-off-by: Juan Quintela + +Signed-off-by: Lidong Chen +(cherry picked from commit 71cd73061c014d04bc6b54936e675347ebc8d964) +Signed-off-by: Miroslav Rezanina +--- + migration/rdma.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/migration/rdma.c b/migration/rdma.c +index da474fc..7d233b0 100644 +--- a/migration/rdma.c ++++ b/migration/rdma.c +@@ -708,6 +708,9 @@ static int rdma_delete_block(RDMAContext *rdma, RDMALocalBlock *block) + memcpy(local->block + block->index, old + (block->index + 1), + sizeof(RDMALocalBlock) * + (local->nb_blocks - (block->index + 1))); ++ for (x = block->index; x < local->nb_blocks - 1; x++) { ++ local->block[x].index--; ++ } + } + } else { + assert(block == local->block); +@@ -3246,6 +3249,10 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) + qsort(rdma->local_ram_blocks.block, + rdma->local_ram_blocks.nb_blocks, + sizeof(RDMALocalBlock), dest_ram_sort_func); ++ for (i = 0; i < local->nb_blocks; i++) { ++ local->block[i].index = i; ++ } ++ + if (rdma->pin_all) { + ret = qemu_rdma_reg_whole_ram_blocks(rdma); + if (ret) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-mips_malta-Clean-up-definition-of-flash-memory-size-.patch b/SOURCES/kvm-mips_malta-Clean-up-definition-of-flash-memory-size-.patch new file mode 100644 index 0000000..75edaee --- /dev/null +++ b/SOURCES/kvm-mips_malta-Clean-up-definition-of-flash-memory-size-.patch @@ -0,0 +1,56 @@ +From 43d46e014933fd009b39dd780ef33ac8cd2081d3 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:04 +0200 +Subject: [PATCH 37/53] mips_malta: Clean up definition of flash memory size + somewhat +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-16-armbru@redhat.com> +Patchwork-id: 88009 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 15/31] mips_malta: Clean up definition of flash memory size somewhat +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +pflash_cfi01_register() takes a size in bytes, a block size in bytes +and a number of blocks. mips_malta_init() passes BIOS_SIZE, 65536, +FLASH_SIZE >> 16. Actually consistent only because BIOS_SIZE (defined +in include/hw/mips/bios.h as (4 * MiB)) matches FLASH_SIZE (defined +locally as 0x400000). Confusing all the same. + +Pass FLASH_SIZE instead of BIOS_SIZE. + +Cc: Aurelien Jarno +Cc: Aleksandar Rikalo +Signed-off-by: Markus Armbruster +Reviewed-by: Alex Bennée +Reviewed-by: Richard Henderson +Message-Id: <20190308094610.21210-14-armbru@redhat.com> +Reviewed-by: Philippe Mathieu-Daudé +Tested-by: Philippe Mathieu-Daudé +(cherry picked from commit 7ebfece56a9370eecd4b425b109dd722b09b9303) +Signed-off-by: Miroslav Rezanina +--- + hw/mips/mips_malta.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c +index 857bdb8..e46a252 100644 +--- a/hw/mips/mips_malta.c ++++ b/hw/mips/mips_malta.c +@@ -1063,7 +1063,7 @@ void mips_malta_init(MachineState *machine) + /* Load firmware in flash / BIOS. */ + dinfo = drive_get(IF_PFLASH, 0, fl_idx); + fl = pflash_cfi01_register(FLASH_ADDRESS, NULL, "mips_malta.bios", +- BIOS_SIZE, ++ FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + 65536, FLASH_SIZE >> 16, + 4, 0x0000, 0x0000, 0x0000, 0x0000, be); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-mips_malta-Delete-disabled-broken-DEBUG_BOARD_INIT-c.patch b/SOURCES/kvm-mips_malta-Delete-disabled-broken-DEBUG_BOARD_INIT-c.patch new file mode 100644 index 0000000..b68dca3 --- /dev/null +++ b/SOURCES/kvm-mips_malta-Delete-disabled-broken-DEBUG_BOARD_INIT-c.patch @@ -0,0 +1,74 @@ +From 186549785eab7b0b7ac1063b942a317c80867766 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:01 +0200 +Subject: [PATCH 34/53] mips_malta: Delete disabled, broken DEBUG_BOARD_INIT + code +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-13-armbru@redhat.com> +Patchwork-id: 88001 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 12/31] mips_malta: Delete disabled, broken DEBUG_BOARD_INIT code +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +The debug code under DEBUG_BOARD_INIT doesn't compile: + + hw/mips/mips_malta.c:1273:16: error: implicit declaration of function ‘blk_name’; did you mean ‘basename’? [-Werror=implicit-function-declaration] + blk_name(dinfo->bdrv), fl_sectors); + ^~~~~~~~ + hw/mips/mips_malta.c:1273:16: error: nested extern declaration of ‘blk_name’ [-Werror=nested-externs] + hw/mips/mips_malta.c:1273:30: error: ‘DriveInfo’ {aka ‘struct DriveInfo’} has no member named ‘bdrv’ + blk_name(dinfo->bdrv), fl_sectors); + ^~ + +Delete it. + +Reported-by: Philippe Mathieu-Daudé +Signed-off-by: Markus Armbruster +Reviewed-by: Aleksandar Markovic +Message-Id: <20190308094610.21210-11-armbru@redhat.com> +Reviewed-by: Alex Bennée +Reviewed-by: Philippe Mathieu-Daudé +Tested-by: Philippe Mathieu-Daudé +(cherry picked from commit 5a4abb197b2ee8e6c545a8ec6e0bf9ca4e470127) +Signed-off-by: Miroslav Rezanina +--- + hw/mips/mips_malta.c | 10 ---------- + 1 file changed, 10 deletions(-) + +diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c +index 88fd7d6..7f19bdc 100644 +--- a/hw/mips/mips_malta.c ++++ b/hw/mips/mips_malta.c +@@ -58,8 +58,6 @@ + #include "exec/semihost.h" + #include "hw/mips/cps.h" + +-//#define DEBUG_BOARD_INIT +- + #define ENVP_ADDR 0x80002000l + #define ENVP_NB_ENTRIES 16 + #define ENVP_ENTRY_SIZE 256 +@@ -1066,14 +1064,6 @@ void mips_malta_init(MachineState *machine) + + /* Load firmware in flash / BIOS. */ + dinfo = drive_get(IF_PFLASH, 0, fl_idx); +-#ifdef DEBUG_BOARD_INIT +- if (dinfo) { +- printf("Register parallel flash %d size " TARGET_FMT_lx " at " +- "addr %08llx '%s' %x\n", +- fl_idx, bios_size, FLASH_ADDRESS, +- blk_name(dinfo->bdrv), fl_sectors); +- } +-#endif + fl = pflash_cfi01_register(FLASH_ADDRESS, NULL, "mips_malta.bios", + BIOS_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-mirror-Confirm-we-re-quiesced-only-if-the-job-is-pau.patch b/SOURCES/kvm-mirror-Confirm-we-re-quiesced-only-if-the-job-is-pau.patch new file mode 100644 index 0000000..04f1fc9 --- /dev/null +++ b/SOURCES/kvm-mirror-Confirm-we-re-quiesced-only-if-the-job-is-pau.patch @@ -0,0 +1,114 @@ +From ba6d773a79eef6f59687771bc5664814d96b6b03 Mon Sep 17 00:00:00 2001 +From: Sergio Lopez Pascual +Date: Mon, 9 Sep 2019 12:46:40 +0200 +Subject: [PATCH 4/4] mirror: Confirm we're quiesced only if the job is paused + or cancelled + +RH-Author: Sergio Lopez Pascual +Message-id: <20190909124640.53625-2-slp@redhat.com> +Patchwork-id: 90339 +O-Subject: [RHEL-7.7.z qemu-kvm-rhev PATCH 1/1] mirror: Confirm we're quiesced only if the job is paused or cancelled +Bugzilla: 1665256 +RH-Acked-by: Kevin Wolf +RH-Acked-by: Stefano Garzarella +RH-Acked-by: John Snow + +While child_job_drained_begin() calls to job_pause(), the job doesn't +actually transition between states until it runs again and reaches a +pause point. This means bdrv_drained_begin() may return with some jobs +using the node still having 'busy == true'. + +As a consequence, block_job_detach_aio_context() may get into a +deadlock, waiting for the job to be actually paused, while the coroutine +servicing the job is yielding and doesn't get the opportunity to get +scheduled again. This situation can be reproduced by issuing a +'block-commit' immediately followed by a 'device_del'. + +To ensure bdrv_drained_begin() only returns when the jobs have been +paused, we change mirror_drained_poll() to only confirm it's quiesced +when job->paused == true and there aren't any in-flight requests, except +if we reached that point by a drained section initiated by the +mirror/commit job itself. + +The other block jobs shouldn't need any changes, as the default +drained_poll() behavior is to only confirm it's quiesced if the job is +not busy or completed. + +Signed-off-by: Sergio Lopez +Signed-off-by: Kevin Wolf +(cherry picked from commit 5e771752a1ffba3a99d7d75b6d492b4a86b59e1b) +Signed-off-by: Sergio Lopez +Signed-off-by: Miroslav Rezanina +--- + block/mirror.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/block/mirror.c b/block/mirror.c +index 55dc94f..48c907f 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -72,6 +72,7 @@ typedef struct MirrorBlockJob { + int max_iov; + bool initial_zeroing_ongoing; + bool prepared; ++ bool in_drain; + } MirrorBlockJob; + + typedef struct MirrorOp { +@@ -551,6 +552,7 @@ static int mirror_exit_common(Job *job) + + /* 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(target_bs); + bdrv_replace_node(to_replace, target_bs, &local_err); + bdrv_drained_end(target_bs); +@@ -587,6 +589,7 @@ static int mirror_exit_common(Job *job) + blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort); + + bdrv_drained_end(src); ++ s->in_drain = false; + bdrv_unref(mirror_top_bs); + bdrv_unref(src); + +@@ -860,10 +863,12 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) + */ + trace_mirror_before_drain(s, cnt); + ++ s->in_drain = true; + bdrv_drained_begin(bs); + cnt = bdrv_get_dirty_count(s->dirty_bitmap); + if (cnt > 0 || mirror_flush(s) < 0) { + bdrv_drained_end(bs); ++ s->in_drain = false; + continue; + } + +@@ -911,6 +916,7 @@ immediate_exit: + bdrv_dirty_iter_free(s->dbi); + + if (need_drain) { ++ s->in_drain = true; + bdrv_drained_begin(bs); + } + +@@ -979,6 +985,16 @@ static void mirror_pause(Job *job) + 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. ++ */ ++ if (!s->common.job.paused && !s->common.job.cancelled && !s->in_drain) { ++ return true; ++ } ++ + return !!s->in_flight; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-mirror-Fail-gracefully-for-source-target.patch b/SOURCES/kvm-mirror-Fail-gracefully-for-source-target.patch new file mode 100644 index 0000000..9b4812b --- /dev/null +++ b/SOURCES/kvm-mirror-Fail-gracefully-for-source-target.patch @@ -0,0 +1,87 @@ +From 5d99c3a36234427da6cc0da9c3b1752c52be31e5 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Mon, 3 Sep 2018 11:15:12 +0200 +Subject: [PATCH] mirror: Fail gracefully for source == target + +RH-Author: Kevin Wolf +Message-id: <20180903111512.21419-2-kwolf@redhat.com> +Patchwork-id: 82034 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/1] mirror: Fail gracefully for source == target +Bugzilla: 1582042 +RH-Acked-by: Miroslav Rezanina +RH-Acked-by: Jeffrey Cody +RH-Acked-by: John Snow + +blockdev-mirror with the same node for source and target segfaults +today: A node is in its own backing chain, so mirror_start_job() decides +that this is an active commit. When adding the intermediate nodes with +block_job_add_bdrv(), it starts the iteration through the subchain with +the backing file of source, though, so it never reaches target and +instead runs into NULL at the base. + +While we could fix that by starting with source itself, there is no +point in allowing mirroring a node into itself and I wouldn't be +surprised if this caused more problems later. + +So just check for this scenario and error out. + +Cc: qemu-stable@nongnu.org +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +(cherry picked from commit 86fae10c64d642256cf019e6829929fa0d259c7a) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/mirror.c | 5 +++++ + tests/qemu-iotests/041 | 6 ++++++ + tests/qemu-iotests/041.out | 4 ++-- + 3 files changed, 13 insertions(+), 2 deletions(-) + +diff --git a/block/mirror.c b/block/mirror.c +index 435268b..65cf43d 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -1133,6 +1133,11 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, + buf_size = DEFAULT_MIRROR_BUF_SIZE; + } + ++ if (bs == target) { ++ error_setg(errp, "Can't mirror node into itself"); ++ return; ++ } ++ + /* 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. */ +diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 +index c20ac7d..9336ab6 100755 +--- a/tests/qemu-iotests/041 ++++ b/tests/qemu-iotests/041 +@@ -234,6 +234,12 @@ class TestSingleBlockdev(TestSingleDrive): + result = self.vm.qmp("blockdev-add", **args) + self.assert_qmp(result, 'return', {}) + ++ def test_mirror_to_self(self): ++ result = self.vm.qmp(self.qmp_cmd, job_id='job0', ++ device=self.qmp_target, sync='full', ++ target=self.qmp_target) ++ self.assert_qmp(result, 'error/class', 'GenericError') ++ + test_large_cluster = None + test_image_not_found = None + test_small_buffer2 = None +diff --git a/tests/qemu-iotests/041.out b/tests/qemu-iotests/041.out +index c28b392..e071d0b 100644 +--- a/tests/qemu-iotests/041.out ++++ b/tests/qemu-iotests/041.out +@@ -1,5 +1,5 @@ +-..................................................................................... ++........................................................................................ + ---------------------------------------------------------------------- +-Ran 85 tests ++Ran 88 tests + + OK +-- +1.8.3.1 + diff --git a/SOURCES/kvm-mirror-Fix-potential-use-after-free-in-active-commit.patch b/SOURCES/kvm-mirror-Fix-potential-use-after-free-in-active-commit.patch new file mode 100644 index 0000000..487e02d --- /dev/null +++ b/SOURCES/kvm-mirror-Fix-potential-use-after-free-in-active-commit.patch @@ -0,0 +1,59 @@ +From 24f8fe9543b18cc484fc43ac1bf5bd7780656fbd Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:35 +0200 +Subject: [PATCH 44/49] mirror: Fix potential use-after-free in active commit + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-38-kwolf@redhat.com> +Patchwork-id: 82188 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 37/42] mirror: Fix potential use-after-free in active commit +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +When starting an active commit job, other callbacks can run before +mirror_start_job() calls bdrv_ref() where needed and cause the nodes to +go away. Add another pair of bdrv_ref/unref() around it to protect +against this case. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/mirror.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/block/mirror.c b/block/mirror.c +index 4b27f71..8658873 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -1335,7 +1335,14 @@ void commit_active_start(const char *job_id, BlockDriverState *bs, + + orig_base_flags = bdrv_get_flags(base); + ++ /* bdrv_reopen() drains, which might make the BDSes go away before a ++ * reference is taken in mirror_start_job(). */ ++ bdrv_ref(bs); ++ bdrv_ref(base); ++ + if (bdrv_reopen(base, bs->open_flags, errp)) { ++ bdrv_unref(bs); ++ bdrv_unref(base); + return; + } + +@@ -1344,6 +1351,10 @@ void commit_active_start(const char *job_id, BlockDriverState *bs, + on_error, on_error, true, cb, opaque, + &commit_active_job_driver, false, base, auto_complete, + filter_node_name, false, &local_err); ++ ++ bdrv_unref(bs); ++ bdrv_unref(base); ++ + if (local_err) { + error_propagate(errp, local_err); + goto error_restore_flags; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-mmap-alloc-fix-hugetlbfs-misaligned-length-in-ppc64.patch b/SOURCES/kvm-mmap-alloc-fix-hugetlbfs-misaligned-length-in-ppc64.patch new file mode 100644 index 0000000..190c954 --- /dev/null +++ b/SOURCES/kvm-mmap-alloc-fix-hugetlbfs-misaligned-length-in-ppc64.patch @@ -0,0 +1,181 @@ +From 6eda983528f3d74ecd4bdecf6aae127e9c93ed48 Mon Sep 17 00:00:00 2001 +From: Sam Bobroff +Date: Tue, 16 Apr 2019 05:29:10 +0200 +Subject: [PATCH 163/163] mmap-alloc: fix hugetlbfs misaligned length in ppc64 + +RH-Author: Sam Bobroff +Message-id: <1555392550-21945-3-git-send-email-sbobroff@redhat.com> +Patchwork-id: 85702 +O-Subject: [RHEL-7.7 qemu-kvm-rhev BZ1672819 PATCH 2/2 REPOST] mmap-alloc: fix hugetlbfs misaligned length in ppc64 +Bugzilla: 1672819 +RH-Acked-by: David Gibson +RH-Acked-by: Thomas Huth +RH-Acked-by: Pankaj Gupta + +From: Murilo Opsfelder Araujo + +The commit 7197fb4058bcb68986bae2bb2c04d6370f3e7218 ("util/mmap-alloc: +fix hugetlb support on ppc64") fixed Huge TLB mappings on ppc64. + +However, we still need to consider the underlying huge page size +during munmap() because it requires that both address and length be a +multiple of the underlying huge page size for Huge TLB mappings. +Quote from "Huge page (Huge TLB) mappings" paragraph under NOTES +section of the munmap(2) manual: + + "For munmap(), addr and length must both be a multiple of the + underlying huge page size." + +On ppc64, the munmap() in qemu_ram_munmap() does not work for Huge TLB +mappings because the mapped segment can be aligned with the underlying +huge page size, not aligned with the native system page size, as +returned by getpagesize(). + +This has the side effect of not releasing huge pages back to the pool +after a hugetlbfs file-backed memory device is hot-unplugged. + +This patch fixes the situation in qemu_ram_mmap() and +qemu_ram_munmap() by considering the underlying page size on ppc64. + +After this patch, memory hot-unplug releases huge pages back to the +pool. + +Fixes: 7197fb4058bcb68986bae2bb2c04d6370f3e7218 +Signed-off-by: Murilo Opsfelder Araujo +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +Reviewed-by: Greg Kurz +(cherry picked from commit 7265c2b9716369b339d778b9ef64a8161eb8f99b) + +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1672819 +Testing: Check that hugepage backed RAM removed from a guest is free'd +on the host. +Signed-off-by: Sam Bobroff +Upstream: Patch is in dgibson/ppc-for-4.0 + +Signed-off-by: Miroslav Rezanina +--- + exec.c | 4 ++-- + include/qemu/mmap-alloc.h | 2 +- + util/mmap-alloc.c | 22 ++++++++++++++++------ + util/oslib-posix.c | 2 +- + 4 files changed, 20 insertions(+), 10 deletions(-) + +diff --git a/exec.c b/exec.c +index d87a51a..82e85ff 100644 +--- a/exec.c ++++ b/exec.c +@@ -1714,7 +1714,7 @@ static void *file_ram_alloc(RAMBlock *block, + if (mem_prealloc) { + os_mem_prealloc(fd, area, memory, smp_cpus, errp); + if (errp && *errp) { +- qemu_ram_munmap(area, memory); ++ qemu_ram_munmap(fd, area, memory); + return NULL; + } + } +@@ -2235,7 +2235,7 @@ static void reclaim_ramblock(RAMBlock *block) + xen_invalidate_map_cache_entry(block->host); + #ifndef _WIN32 + } else if (block->fd >= 0) { +- qemu_ram_munmap(block->host, block->max_length); ++ qemu_ram_munmap(block->fd, block->host, block->max_length); + close(block->fd); + #endif + } else { +diff --git a/include/qemu/mmap-alloc.h b/include/qemu/mmap-alloc.h +index 50385e3..ef04f0e 100644 +--- a/include/qemu/mmap-alloc.h ++++ b/include/qemu/mmap-alloc.h +@@ -9,6 +9,6 @@ size_t qemu_mempath_getpagesize(const char *mem_path); + + void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared); + +-void qemu_ram_munmap(void *ptr, size_t size); ++void qemu_ram_munmap(int fd, void *ptr, size_t size); + + #endif +diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c +index 94ee517..19607c1 100644 +--- a/util/mmap-alloc.c ++++ b/util/mmap-alloc.c +@@ -78,6 +78,7 @@ void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared) + int flags; + int guardfd; + size_t offset; ++ size_t pagesize; + size_t total; + void *guardptr; + void *ptr; +@@ -98,7 +99,8 @@ void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared) + * anonymous memory is OK. + */ + flags = MAP_PRIVATE; +- if (fd == -1 || qemu_fd_getpagesize(fd) == getpagesize()) { ++ pagesize = qemu_fd_getpagesize(fd); ++ if (fd == -1 || pagesize == getpagesize()) { + guardfd = -1; + flags |= MAP_ANONYMOUS; + } else { +@@ -107,6 +109,7 @@ void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared) + } + #else + guardfd = -1; ++ pagesize = getpagesize(); + flags = MAP_PRIVATE | MAP_ANONYMOUS; + #endif + +@@ -118,7 +121,7 @@ void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared) + + assert(is_power_of_2(align)); + /* Always align to host page size */ +- assert(align >= getpagesize()); ++ assert(align >= pagesize); + + flags = MAP_FIXED; + flags |= fd == -1 ? MAP_ANONYMOUS : 0; +@@ -141,17 +144,24 @@ void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared) + * a guard page guarding against potential buffer overflows. + */ + total -= offset; +- if (total > size + getpagesize()) { +- munmap(ptr + size + getpagesize(), total - size - getpagesize()); ++ if (total > size + pagesize) { ++ munmap(ptr + size + pagesize, total - size - pagesize); + } + + return ptr; + } + +-void qemu_ram_munmap(void *ptr, size_t size) ++void qemu_ram_munmap(int fd, void *ptr, size_t size) + { ++ size_t pagesize; ++ + if (ptr) { + /* Unmap both the RAM block and the guard page */ +- munmap(ptr, size + getpagesize()); ++#if defined(__powerpc64__) && defined(__linux__) ++ pagesize = qemu_fd_getpagesize(fd); ++#else ++ pagesize = getpagesize(); ++#endif ++ munmap(ptr, size + pagesize); + } + } +diff --git a/util/oslib-posix.c b/util/oslib-posix.c +index 13b6f8d..a24dd01 100644 +--- a/util/oslib-posix.c ++++ b/util/oslib-posix.c +@@ -153,7 +153,7 @@ void qemu_vfree(void *ptr) + void qemu_anon_ram_free(void *ptr, size_t size) + { + trace_qemu_anon_ram_free(ptr, size); +- qemu_ram_munmap(ptr, size); ++ qemu_ram_munmap(-1, ptr, size); + } + + void qemu_set_block(int fd) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-mmap-alloc-unfold-qemu_ram_mmap.patch b/SOURCES/kvm-mmap-alloc-unfold-qemu_ram_mmap.patch new file mode 100644 index 0000000..b819948 --- /dev/null +++ b/SOURCES/kvm-mmap-alloc-unfold-qemu_ram_mmap.patch @@ -0,0 +1,142 @@ +From 5e5b310f575cab0403a84de8e6e7505d6f167730 Mon Sep 17 00:00:00 2001 +From: Sam Bobroff +Date: Tue, 16 Apr 2019 05:29:09 +0200 +Subject: [PATCH 162/163] mmap-alloc: unfold qemu_ram_mmap() + +RH-Author: Sam Bobroff +Message-id: <1555392550-21945-2-git-send-email-sbobroff@redhat.com> +Patchwork-id: 85701 +O-Subject: [RHEL-7.7 qemu-kvm-rhev BZ1672819 PATCH 1/2 REPOST] mmap-alloc: unfold qemu_ram_mmap() +Bugzilla: 1672819 +RH-Acked-by: David Gibson +RH-Acked-by: Thomas Huth +RH-Acked-by: Pankaj Gupta + +From: Murilo Opsfelder Araujo + +Unfold parts of qemu_ram_mmap() for the sake of understanding, moving +declarations to the top, and keeping architecture-specifics in the +ifdef-else blocks. No changes in the function behaviour. + +Give ptr and ptr1 meaningful names: + ptr -> guardptr : pointer to the PROT_NONE guard region + ptr1 -> ptr : pointer to the mapped memory returned to caller + +Signed-off-by: Murilo Opsfelder Araujo +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +Reviewed-by: Greg Kurz +(cherry picked from commit 94af9e34821c5c47a3c69fe242e32d0b33c2fff6) + +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1672819 +Testing: Check that hugepage backed RAM removed from a guest is free'd +on the host. +Signed-off-by: Sam Bobroff +Upstream: Patch is in dgibson/ppc-for-4.0 + +Signed-off-by: Miroslav Rezanina +--- + util/mmap-alloc.c | 53 ++++++++++++++++++++++++++++++++++------------------- + 1 file changed, 34 insertions(+), 19 deletions(-) + +diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c +index 2fd8cbc..94ee517 100644 +--- a/util/mmap-alloc.c ++++ b/util/mmap-alloc.c +@@ -75,11 +75,19 @@ size_t qemu_mempath_getpagesize(const char *mem_path) + + void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared) + { ++ int flags; ++ int guardfd; ++ size_t offset; ++ size_t total; ++ void *guardptr; ++ void *ptr; ++ + /* + * Note: this always allocates at least one extra page of virtual address + * space, even if size is already aligned. + */ +- size_t total = size + align; ++ total = size + align; ++ + #if defined(__powerpc64__) && defined(__linux__) + /* On ppc64 mappings in the same segment (aka slice) must share the same + * page size. Since we will be re-allocating part of this segment +@@ -89,16 +97,22 @@ void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared) + * We do this unless we are using the system page size, in which case + * anonymous memory is OK. + */ +- int anonfd = fd == -1 || qemu_fd_getpagesize(fd) == getpagesize() ? -1 : fd; +- int flags = anonfd == -1 ? MAP_ANONYMOUS : MAP_NORESERVE; +- void *ptr = mmap(0, total, PROT_NONE, flags | MAP_PRIVATE, anonfd, 0); ++ flags = MAP_PRIVATE; ++ if (fd == -1 || qemu_fd_getpagesize(fd) == getpagesize()) { ++ guardfd = -1; ++ flags |= MAP_ANONYMOUS; ++ } else { ++ guardfd = fd; ++ flags |= MAP_NORESERVE; ++ } + #else +- void *ptr = mmap(0, total, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ++ guardfd = -1; ++ flags = MAP_PRIVATE | MAP_ANONYMOUS; + #endif +- size_t offset; +- void *ptr1; + +- if (ptr == MAP_FAILED) { ++ guardptr = mmap(0, total, PROT_NONE, flags, guardfd, 0); ++ ++ if (guardptr == MAP_FAILED) { + return MAP_FAILED; + } + +@@ -106,19 +120,20 @@ void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared) + /* Always align to host page size */ + assert(align >= getpagesize()); + +- offset = QEMU_ALIGN_UP((uintptr_t)ptr, align) - (uintptr_t)ptr; +- ptr1 = mmap(ptr + offset, size, PROT_READ | PROT_WRITE, +- MAP_FIXED | +- (fd == -1 ? MAP_ANONYMOUS : 0) | +- (shared ? MAP_SHARED : MAP_PRIVATE), +- fd, 0); +- if (ptr1 == MAP_FAILED) { +- munmap(ptr, total); ++ flags = MAP_FIXED; ++ flags |= fd == -1 ? MAP_ANONYMOUS : 0; ++ flags |= shared ? MAP_SHARED : MAP_PRIVATE; ++ offset = QEMU_ALIGN_UP((uintptr_t)guardptr, align) - (uintptr_t)guardptr; ++ ++ ptr = mmap(guardptr + offset, size, PROT_READ | PROT_WRITE, flags, fd, 0); ++ ++ if (ptr == MAP_FAILED) { ++ munmap(guardptr, total); + return MAP_FAILED; + } + + if (offset > 0) { +- munmap(ptr, offset); ++ munmap(guardptr, offset); + } + + /* +@@ -127,10 +142,10 @@ void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared) + */ + total -= offset; + if (total > size + getpagesize()) { +- munmap(ptr1 + size + getpagesize(), total - size - getpagesize()); ++ munmap(ptr + size + getpagesize(), total - size - getpagesize()); + } + +- return ptr1; ++ return ptr; + } + + void qemu_ram_munmap(void *ptr, size_t size) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-monitor-move-init-global-earlier.patch b/SOURCES/kvm-monitor-move-init-global-earlier.patch new file mode 100644 index 0000000..1dac255 --- /dev/null +++ b/SOURCES/kvm-monitor-move-init-global-earlier.patch @@ -0,0 +1,67 @@ +From 6d69ed6b8b79cf43a8e0fa72167e7550fe6a936f Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:50:50 +0200 +Subject: [PATCH 23/53] monitor: move init global earlier +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-2-armbru@redhat.com> +Patchwork-id: 87989 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 01/31] monitor: move init global earlier +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +From: Peter Xu + +Before this patch, monitor fd helpers might be called even earlier than +monitor_init_globals(). This can be problematic. + +After previous work, now monitor_init_globals() does not depend on +accelerator initialization any more. Call it earlier (before CLI +parsing; that's where the monitor APIs might be called) to make sure it +is called before any of the monitor APIs. + +Suggested-by: Markus Armbruster +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Markus Armbruster +Signed-off-by: Peter Xu +Message-Id: <20180608035511.7439-7-peterx@redhat.com> +Signed-off-by: Markus Armbruster +(cherry picked from commit d32749deb61513c5456901f20e19887e1bc3d7f3) +Signed-off-by: Miroslav Rezanina +--- + vl.c | 7 +------ + 1 file changed, 1 insertion(+), 6 deletions(-) + +diff --git a/vl.c b/vl.c +index 92a98ab..a22da93 100644 +--- a/vl.c ++++ b/vl.c +@@ -3157,6 +3157,7 @@ int main(int argc, char **argv, char **envp) + + runstate_init(); + postcopy_infrastructure_init(); ++ monitor_init_globals(); + + if (qcrypto_init(&err) < 0) { + error_reportf_err(err, "cannot initialize crypto: "); +@@ -4591,12 +4592,6 @@ int main(int argc, char **argv, char **envp) + default_drive(default_floppy, snapshot, IF_FLOPPY, 0, FD_OPTS); + default_drive(default_sdcard, snapshot, IF_SD, 0, SD_OPTS); + +- /* +- * Note: qtest_enabled() (which is used in monitor_qapi_event_init()) +- * depends on configure_accelerator() above. +- */ +- monitor_init_globals(); +- + if (qemu_opts_foreach(qemu_find_opts("mon"), + mon_init_func, NULL, NULL)) { + exit(1); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-Add-some-error-case-testing-to-iotests-223.patch b/SOURCES/kvm-nbd-Add-some-error-case-testing-to-iotests-223.patch new file mode 100644 index 0000000..dda30f7 --- /dev/null +++ b/SOURCES/kvm-nbd-Add-some-error-case-testing-to-iotests-223.patch @@ -0,0 +1,131 @@ +From 0237c861ddbb4584443c45436c53e0282ecc10fd Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:26 +0100 +Subject: [PATCH 087/163] nbd: Add some error case testing to iotests 223 + +RH-Author: John Snow +Message-id: <20190327172308.31077-14-jsnow@redhat.com> +Patchwork-id: 85175 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 13/55] nbd: Add some error case testing to iotests 223 +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Testing success paths is important, but it's also nice to highlight +expected failure handling, to show that we don't crash, and so that +upcoming tests that change behavior can demonstrate the resulting +effects on error paths. + +Add the following errors: +Attempting to export without a running server +Attempting to start a second server +Attempting to export a bad node name +Attempting to export a name that is already exported +Attempting to export an enabled bitmap +Attempting to remove an already removed export +Attempting to quit server a second time + +All of these properly complain except for a second server-stop, +which will be fixed next. + +Signed-off-by: Eric Blake +Message-Id: <20190111194720.15671-2-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit 2d2fd67428f0cfbffea16969d2635af3e2d78d3d) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/223 | 19 +++++++++++++++++-- + tests/qemu-iotests/223.out | 7 +++++++ + 2 files changed, 24 insertions(+), 2 deletions(-) + +diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 +index 5513dc6..61b46a2 100755 +--- a/tests/qemu-iotests/223 ++++ b/tests/qemu-iotests/223 +@@ -107,6 +107,7 @@ echo + + _launch_qemu 2> >(_filter_nbd) + ++# Intentionally provoke some errors as well, to check error handling + silent= + _send_qemu_cmd $QEMU_HANDLE '{"execute":"qmp_capabilities"}' "return" + _send_qemu_cmd $QEMU_HANDLE '{"execute":"blockdev-add", +@@ -114,18 +115,29 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"blockdev-add", + "file":{"driver":"file", "filename":"'"$TEST_IMG"'"}}}' "return" + _send_qemu_cmd $QEMU_HANDLE '{"execute":"block-dirty-bitmap-disable", + "arguments":{"node":"n", "name":"b"}}' "return" +-_send_qemu_cmd $QEMU_HANDLE '{"execute":"block-dirty-bitmap-disable", +- "arguments":{"node":"n", "name":"b2"}}' "return" ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", ++ "arguments":{"device":"n"}}' "error" # Attempt add without server + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start", + "arguments":{"addr":{"type":"unix", + "data":{"path":"'"$TEST_DIR/nbd"'"}}}}' "return" ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start", ++ "arguments":{"addr":{"type":"unix", ++ "data":{"path":"'"$TEST_DIR/nbd"1'"}}}}' "error" # Attempt second server + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments":{"device":"n"}}' "return" ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", ++ "arguments":{"device":"nosuch"}}' "error" # Attempt to export missing node ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", ++ "arguments":{"device":"n"}}' "error" # Attempt to export same name twice + _send_qemu_cmd $QEMU_HANDLE '{"execute":"x-nbd-server-add-bitmap", + "arguments":{"name":"n", "bitmap":"b"}}' "return" + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments":{"device":"n", "name":"n2"}}' "return" + _send_qemu_cmd $QEMU_HANDLE '{"execute":"x-nbd-server-add-bitmap", ++ "arguments":{"name":"n2", "bitmap":"b2"}}' "error" # Attempt enabled bitmap ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"block-dirty-bitmap-disable", ++ "arguments":{"node":"n", "name":"b2"}}' "return" ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-nbd-server-add-bitmap", + "arguments":{"name":"n2", "bitmap":"b2"}}' "return" + + echo +@@ -157,7 +169,10 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove", + "arguments":{"name":"n"}}' "return" + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove", + "arguments":{"name":"n2"}}' "return" ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove", ++ "arguments":{"name":"n2"}}' "error" # Attempt duplicate clean + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-stop"}' "return" ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-stop"}' "return" # Oops + _send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' "return" + + # success, all done +diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out +index de41747..c1eed62 100644 +--- a/tests/qemu-iotests/223.out ++++ b/tests/qemu-iotests/223.out +@@ -27,10 +27,15 @@ wrote 2097152/2097152 bytes at offset 2097152 + {"return": {}} + {"return": {}} + {"return": {}} ++{"error": {"class": "GenericError", "desc": "NBD server not running"}} + {"return": {}} ++{"error": {"class": "GenericError", "desc": "NBD server already running"}} + {"return": {}} ++{"error": {"class": "GenericError", "desc": "Cannot find device=nosuch nor node_name=nosuch"}} ++{"error": {"class": "GenericError", "desc": "NBD server already has export named 'n'"}} + {"return": {}} + {"return": {}} ++{"error": {"class": "GenericError", "desc": "Bitmap 'b2' is enabled"}} + {"return": {}} + {"return": {}} + +@@ -62,6 +67,8 @@ read 2097152/2097152 bytes at offset 2097152 + + {"return": {}} + {"return": {}} ++{"error": {"class": "GenericError", "desc": "Export 'n2' is not found"}} ++{"return": {}} + {"return": {}} + {"return": {}} + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-Allow-bitmap-export-during-QMP-nbd-server-add.patch b/SOURCES/kvm-nbd-Allow-bitmap-export-during-QMP-nbd-server-add.patch new file mode 100644 index 0000000..a90ab45 --- /dev/null +++ b/SOURCES/kvm-nbd-Allow-bitmap-export-during-QMP-nbd-server-add.patch @@ -0,0 +1,200 @@ +From d5c834ab75d9e3e67e6f8489fef32dc4d22443a1 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:30 +0100 +Subject: [PATCH 091/163] nbd: Allow bitmap export during QMP nbd-server-add + +RH-Author: John Snow +Message-id: <20190327172308.31077-18-jsnow@redhat.com> +Patchwork-id: 85188 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 17/55] nbd: Allow bitmap export during QMP nbd-server-add +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +With the experimental x-nbd-server-add-bitmap command, there was +a window of time where an NBD client could see the export but not +the associated dirty bitmap, which can cause a client that planned +on using the dirty bitmap to be forced to treat the entire image +as dirty as a safety fallback. Furthermore, if the QMP client +successfully exports a disk but then fails to add the bitmap, it +has to take on the burden of removing the export. Since we don't +allow changing the exposed dirty bitmap (whether to a different +bitmap, or removing advertisement of the bitmap), it is nicer to +make the bitmap tied to the export at the time the export is +created, with automatic failure to export if the bitmap is not +available. + +The experimental command included an optional 'bitmap-export-name' +field for remapping the name exposed over NBD to be different from +the bitmap name stored on disk. However, my libvirt demo code +for implementing differential backups on top of persistent bitmaps +did not need to take advantage of that feature (it is instead +possible to create a new temporary bitmap with the desired name, +use block-dirty-bitmap-merge to merge one or more persistent +bitmaps into the temporary, then associate the temporary with the +NBD export, if control is needed over the exported bitmap name). +Hence, I'm not copying that part of the experiment over to the +stable addition. For more details on the libvirt demo, see +https://www.redhat.com/archives/libvir-list/2018-October/msg01254.html, +https://kvmforum2018.sched.com/event/FzuB/facilitating-incremental-backup-eric-blake-red-hat + +This patch focuses on the user interface, and reduces (but does +not completely eliminate) the window where an NBD client can see +the export but not the dirty bitmap, with less work to clean up +after errors. Later patches will add further cleanups now that +this interface is declared stable via a single QMP command, +including removing the race window. + +Update test 223 to use the new interface. + +Signed-off-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20190111194720.15671-6-eblake@redhat.com> +(cherry picked from commit 5fcbeb06812685a2c6d7e0e6f28f018987d08b79) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev-nbd.c | 12 +++++++++++- + hmp.c | 5 +++-- + qapi/block.json | 7 ++++++- + tests/qemu-iotests/223 | 19 ++++++++----------- + tests/qemu-iotests/223.out | 5 +---- + 5 files changed, 29 insertions(+), 19 deletions(-) + +diff --git a/blockdev-nbd.c b/blockdev-nbd.c +index 582ffde..ec8cf0a 100644 +--- a/blockdev-nbd.c ++++ b/blockdev-nbd.c +@@ -140,7 +140,8 @@ void qmp_nbd_server_start(SocketAddressLegacy *addr, + } + + void qmp_nbd_server_add(const char *device, bool has_name, const char *name, +- bool has_writable, bool writable, Error **errp) ++ bool has_writable, bool writable, ++ bool has_bitmap, const char *bitmap, Error **errp) + { + BlockDriverState *bs = NULL; + BlockBackend *on_eject_blk; +@@ -185,6 +186,15 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name, + * our only way of accessing it is through nbd_export_find(), so we can drop + * the strong reference that is @exp. */ + nbd_export_put(exp); ++ ++ if (has_bitmap) { ++ Error *err = NULL; ++ nbd_export_bitmap(exp, bitmap, bitmap, &err); ++ if (err) { ++ error_propagate(errp, err); ++ nbd_export_remove(exp, NBD_SERVER_REMOVE_MODE_HARD, NULL); ++ } ++ } + } + + void qmp_nbd_server_remove(const char *name, +diff --git a/hmp.c b/hmp.c +index cc088da..59e52b9 100644 +--- a/hmp.c ++++ b/hmp.c +@@ -2212,7 +2212,7 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) + } + + qmp_nbd_server_add(info->value->device, false, NULL, +- true, writable, &local_err); ++ true, writable, false, NULL, &local_err); + + if (local_err != NULL) { + qmp_nbd_server_stop(NULL); +@@ -2233,7 +2233,8 @@ void hmp_nbd_server_add(Monitor *mon, const QDict *qdict) + bool writable = qdict_get_try_bool(qdict, "writable", false); + Error *local_err = NULL; + +- qmp_nbd_server_add(device, !!name, name, true, writable, &local_err); ++ qmp_nbd_server_add(device, !!name, name, true, writable, ++ false, NULL, &local_err); + hmp_handle_error(mon, &local_err); + } + +diff --git a/qapi/block.json b/qapi/block.json +index ba85ceb..b04fcdc 100644 +--- a/qapi/block.json ++++ b/qapi/block.json +@@ -313,6 +313,10 @@ + # + # @writable: Whether clients should be able to write to the device via the + # NBD connection (default false). ++ ++# @bitmap: Also export the dirty bitmap reachable from @device, so the ++# NBD client can use NBD_OPT_SET_META_CONTEXT with ++# "qemu:dirty-bitmap:NAME" to inspect the bitmap. (since 4.0) + # + # Returns: error if the server is not running, or export with the same name + # already exists. +@@ -320,7 +324,8 @@ + # Since: 1.3.0 + ## + { 'command': 'nbd-server-add', +- 'data': {'device': 'str', '*name': 'str', '*writable': 'bool'} } ++ 'data': {'device': 'str', '*name': 'str', '*writable': 'bool', ++ '*bitmap': 'str' } } + + ## + # @NbdServerRemoveMode: +diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 +index f200e31..0bcc98a 100755 +--- a/tests/qemu-iotests/223 ++++ b/tests/qemu-iotests/223 +@@ -126,23 +126,20 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start", + "arguments":{"addr":{"type":"unix", + "data":{"path":"'"$TEST_DIR/nbd"1'"}}}}' "error" # Attempt second server + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", +- "arguments":{"device":"n"}}' "return" ++ "arguments":{"device":"n", "bitmap":"b"}}' "return" + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments":{"device":"nosuch"}}' "error" # Attempt to export missing node + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments":{"device":"n"}}' "error" # Attempt to export same name twice +-_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-nbd-server-add-bitmap", +- "arguments":{"name":"n", "bitmap":"b"}}' "return" + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", +- "arguments":{"device":"n", "name":"n2"}}' "return" +-_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-nbd-server-add-bitmap", +- "arguments":{"name":"n2", "bitmap":"b2"}}' "error" # Enabled vs. read-only +-_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove", +- "arguments":{"name":"n2"}}' "return" ++ "arguments":{"device":"n", "name":"n2", ++ "bitmap":"b2"}}' "error" # enabled vs. read-only ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", ++ "arguments":{"device":"n", "name":"n2", ++ "bitmap":"b3"}}' "error" # Missing bitmap + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", +- "arguments":{"device":"n", "name":"n2", "writable":true}}' "return" +-_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-nbd-server-add-bitmap", +- "arguments":{"name":"n2", "bitmap":"b2"}}' "return" ++ "arguments":{"device":"n", "name":"n2", "writable":true, ++ "bitmap":"b2"}}' "return" + + echo + echo "=== Contrast normal status to large granularity dirty-bitmap ===" +diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out +index 3028857..a0c2dec 100644 +--- a/tests/qemu-iotests/223.out ++++ b/tests/qemu-iotests/223.out +@@ -33,11 +33,8 @@ wrote 2097152/2097152 bytes at offset 2097152 + {"return": {}} + {"error": {"class": "GenericError", "desc": "Cannot find device=nosuch nor node_name=nosuch"}} + {"error": {"class": "GenericError", "desc": "NBD server already has export named 'n'"}} +-{"return": {}} +-{"return": {}} + {"error": {"class": "GenericError", "desc": "Enabled bitmap 'b2' incompatible with readonly export"}} +-{"return": {}} +-{"return": {}} ++{"error": {"class": "GenericError", "desc": "Bitmap 'b3' is not found"}} + {"return": {}} + + === Contrast normal status to large granularity dirty-bitmap === +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-Document-timeline-of-various-features.patch b/SOURCES/kvm-nbd-Document-timeline-of-various-features.patch new file mode 100644 index 0000000..ba8793b --- /dev/null +++ b/SOURCES/kvm-nbd-Document-timeline-of-various-features.patch @@ -0,0 +1,81 @@ +From 627a30ded957f715a9832e8fe93feb3f03575095 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:21 +0100 +Subject: [PATCH 082/163] nbd: Document timeline of various features + +RH-Author: John Snow +Message-id: <20190327172308.31077-9-jsnow@redhat.com> +Patchwork-id: 85169 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 08/55] nbd: Document timeline of various features +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +It can be useful to figure out which NBD protocol features are +exposed by a server, as well as what features a client will +take advantage of if available, for a given qemu release. It's +not always precise to base features on version numbers (thanks +to downstream backports), but any documentation is better than +making users search through git logs themselves. + +This patch originally stemmed from a request to document that +pristine 3.0 has a known bug where NBD_OPT_LIST_META_CONTEXT +with 0 queries forgot to advertise an available +"qemu:dirty-bitmap" context, but documenting bugs like this (or +the fact that 3.0 also botched NBD_CMD_CACHE) gets to be too +much details, especially since buggy releases will be less +likely connection targets over time. Instead, I chose to just +remind users to check stable release branches. + +Suggested-by: Vladimir Sementsov-Ogievskiy +Signed-off-by: Eric Blake +Message-Id: <20181215135324.152629-3-eblake@redhat.com> +Reviewed-by: Richard W.M. Jones +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit ba2d3b3ab217784822e4232f0acd71fc523d571f) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + docs/interop/nbd.txt | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +diff --git a/docs/interop/nbd.txt b/docs/interop/nbd.txt +index 77b5f45..fc64473 100644 +--- a/docs/interop/nbd.txt ++++ b/docs/interop/nbd.txt +@@ -15,7 +15,6 @@ Qemu supports the "base:allocation" metadata context as defined in the + NBD protocol specification, and also defines an additional metadata + namespace "qemu". + +- + == "qemu" namespace == + + The "qemu" namespace currently contains only one type of context, +@@ -36,3 +35,21 @@ in addition to "qemu:dirty-bitmap:": + namespace. + * "qemu:dirty-bitmap:" - returns list of all available dirty-bitmap + metadata contexts. ++ ++= Features by version = ++ ++The following list documents which qemu version first implemented ++various features (both as a server exposing the feature, and as a ++client taking advantage of the feature when present), to make it ++easier to plan for cross-version interoperability. Note that in ++several cases, the initial release containing a feature may require ++additional patches from the corresponding stable branch to fix bugs in ++the operation of that feature. ++ ++* 2.6: NBD_OPT_STARTTLS with TLS X.509 Certificates ++* 2.8: NBD_CMD_WRITE_ZEROES ++* 2.10: NBD_OPT_GO, NBD_INFO_BLOCK ++* 2.11: NBD_OPT_STRUCTURED_REPLY ++* 2.12: NBD_CMD_BLOCK_STATUS for "base:allocation" ++* 3.0: NBD_OPT_STARTTLS with TLS Pre-Shared Keys (PSK), ++NBD_CMD_BLOCK_STATUS for "qemu:dirty-bitmap:", NBD_CMD_CACHE +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-Don-t-lose-server-s-error-to-NBD_CMD_BLOCK_STATU.patch b/SOURCES/kvm-nbd-Don-t-lose-server-s-error-to-NBD_CMD_BLOCK_STATU.patch new file mode 100644 index 0000000..9c4058b --- /dev/null +++ b/SOURCES/kvm-nbd-Don-t-lose-server-s-error-to-NBD_CMD_BLOCK_STATU.patch @@ -0,0 +1,83 @@ +From 436070bc736adc93d90292f5b5a3a2b75c36017d Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 6 May 2019 17:56:13 +0200 +Subject: [PATCH 03/53] nbd: Don't lose server's error to NBD_CMD_BLOCK_STATUS + +RH-Author: John Snow +Message-id: <20190506175629.11079-4-jsnow@redhat.com> +Patchwork-id: 87189 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 03/19] nbd: Don't lose server's error to NBD_CMD_BLOCK_STATUS +Bugzilla: 1692018 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Thomas Huth + +From: Eric Blake + +When the server replies with a (structured [*]) error to +NBD_CMD_BLOCK_STATUS, without any extent information sent first, the +client code was blindly throwing away the server's error code and +instead telling the caller that EIO occurred. This has been broken +since its introduction in 78a33ab5 (v2.12, where we should have called: + error_setg(&local_err, "Server did not reply with any status extents"); + nbd_iter_error(&iter, false, -EIO, &local_err); +to declare the situation as a non-fatal error if no earlier error had +already been flagged, rather than just blindly slamming iter.err and +iter.ret), although it is more noticeable since commit 7f86068d, which +actually tries hard to preserve the server's code thanks to a separate +iter.request_ret. + +[*] The spec is clear that the server is also permitted to reply with +a simple error, but that's a separate fix. + +I was able to provoke this scenario with a hack to the server, then +seeing whether ENOMEM makes it back to the caller: + +| diff --git a/nbd/server.c b/nbd/server.c +| index fd013a2817a..29c7995de02 100644 +| --- a/nbd/server.c +| +++ b/nbd/server.c +| @@ -2269,6 +2269,8 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, +| "discard failed", errp); +| +| case NBD_CMD_BLOCK_STATUS: +| + return nbd_send_generic_reply(client, request->handle, -ENOMEM, +| + "no status for you today", errp); +| if (!request->len) { +| return nbd_send_generic_reply(client, request->handle, -EINVAL, +| "need non-zero length", errp); +| -- + +Signed-off-by: Eric Blake +Message-Id: <20190325190104.30213-2-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit b29f3a3d2a5fab40dbb4a65fa2f91821ebffae51) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 9 +++------ + 1 file changed, 3 insertions(+), 6 deletions(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index f3c31d1..532f90c 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -756,12 +756,9 @@ static int nbd_co_receive_blockstatus_reply(NBDClientSession *s, + payload = NULL; + } + +- if (!extent->length && !iter.err) { +- error_setg(&iter.err, +- "Server did not reply with any status extents"); +- if (!iter.ret) { +- iter.ret = -EIO; +- } ++ 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); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-Don-t-take-address-of-fields-in-packed-structs.patch b/SOURCES/kvm-nbd-Don-t-take-address-of-fields-in-packed-structs.patch new file mode 100644 index 0000000..06c0f28 --- /dev/null +++ b/SOURCES/kvm-nbd-Don-t-take-address-of-fields-in-packed-structs.patch @@ -0,0 +1,308 @@ +From a2bf5f1541eb073c7c4214e71b0f52d3bcc82914 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:22 +0100 +Subject: [PATCH 055/163] nbd: Don't take address of fields in packed structs + +RH-Author: John Snow +Message-id: <20190322032241.8111-10-jsnow@redhat.com> +Patchwork-id: 85095 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 09/28] nbd: Don't take address of fields in packed structs +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Peter Maydell + +Taking the address of a field in a packed struct is a bad idea, because +it might not be actually aligned enough for that pointer type (and +thus cause a crash on dereference on some host architectures). Newer +versions of clang warn about this. Avoid the bug by not using the +"modify in place" byte swapping functions. + +This patch was produced with the following spatch script: +@@ +expression E; +@@ +-be16_to_cpus(&E); ++E = be16_to_cpu(E); +@@ +expression E; +@@ +-be32_to_cpus(&E); ++E = be32_to_cpu(E); +@@ +expression E; +@@ +-be64_to_cpus(&E); ++E = be64_to_cpu(E); +@@ +expression E; +@@ +-cpu_to_be16s(&E); ++E = cpu_to_be16(E); +@@ +expression E; +@@ +-cpu_to_be32s(&E); ++E = cpu_to_be32(E); +@@ +expression E; +@@ +-cpu_to_be64s(&E); ++E = cpu_to_be64(E); + +Signed-off-by: Peter Maydell +Message-Id: <20180927164200.15097-1-peter.maydell@linaro.org> +Reviewed-by: Eric Blake +[eblake: rebase, and squash in missed changes] +Signed-off-by: Eric Blake +(cherry picked from commit 80c7c2b00d607221bb43815d2c1951d54229b3ee) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + nbd/client.c | 44 ++++++++++++++++++++++---------------------- + nbd/server.c | 24 ++++++++++++------------ + 2 files changed, 34 insertions(+), 34 deletions(-) + +diff --git a/nbd/client.c b/nbd/client.c +index 40b74d9..b4d457a 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -117,10 +117,10 @@ static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt, + nbd_send_opt_abort(ioc); + return -1; + } +- be64_to_cpus(&reply->magic); +- be32_to_cpus(&reply->option); +- be32_to_cpus(&reply->type); +- be32_to_cpus(&reply->length); ++ 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), +@@ -396,7 +396,7 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, + return -1; + } + len -= sizeof(type); +- be16_to_cpus(&type); ++ type = be16_to_cpu(type); + switch (type) { + case NBD_INFO_EXPORT: + if (len != sizeof(info->size) + sizeof(info->flags)) { +@@ -410,13 +410,13 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, + nbd_send_opt_abort(ioc); + return -1; + } +- be64_to_cpus(&info->size); ++ info->size = be64_to_cpu(info->size); + if (nbd_read(ioc, &info->flags, sizeof(info->flags), errp) < 0) { + error_prepend(errp, "failed to read info flags: "); + nbd_send_opt_abort(ioc); + return -1; + } +- be16_to_cpus(&info->flags); ++ info->flags = be16_to_cpu(info->flags); + trace_nbd_receive_negotiate_size_flags(info->size, info->flags); + break; + +@@ -433,7 +433,7 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, + nbd_send_opt_abort(ioc); + return -1; + } +- be32_to_cpus(&info->min_block); ++ info->min_block = be32_to_cpu(info->min_block); + 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); +@@ -447,7 +447,7 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, + nbd_send_opt_abort(ioc); + return -1; + } +- be32_to_cpus(&info->opt_block); ++ info->opt_block = be32_to_cpu(info->opt_block); + if (!is_power_of_2(info->opt_block) || + info->opt_block < info->min_block) { + error_setg(errp, "server preferred block size %" PRIu32 +@@ -461,7 +461,7 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, + nbd_send_opt_abort(ioc); + return -1; + } +- be32_to_cpus(&info->max_block); ++ info->max_block = be32_to_cpu(info->max_block); + if (info->max_block < info->min_block) { + error_setg(errp, "server maximum block size %" PRIu32 + " is not valid", info->max_block); +@@ -668,7 +668,7 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, + if (nbd_read(ioc, &received_id, sizeof(received_id), errp) < 0) { + return -1; + } +- be32_to_cpus(&received_id); ++ received_id = be32_to_cpu(received_id); + + reply.length -= sizeof(received_id); + name = g_malloc(reply.length + 1); +@@ -872,13 +872,13 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, + error_prepend(errp, "Failed to read export length: "); + goto fail; + } +- be64_to_cpus(&info->size); ++ info->size = be64_to_cpu(info->size); + + if (nbd_read(ioc, &info->flags, sizeof(info->flags), errp) < 0) { + error_prepend(errp, "Failed to read export flags: "); + goto fail; + } +- be16_to_cpus(&info->flags); ++ info->flags = be16_to_cpu(info->flags); + } else if (magic == NBD_CLIENT_MAGIC) { + uint32_t oldflags; + +@@ -895,13 +895,13 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, + error_prepend(errp, "Failed to read export length: "); + goto fail; + } +- be64_to_cpus(&info->size); ++ info->size = be64_to_cpu(info->size); + + if (nbd_read(ioc, &oldflags, sizeof(oldflags), errp) < 0) { + error_prepend(errp, "Failed to read export flags: "); + goto fail; + } +- be32_to_cpus(&oldflags); ++ oldflags = be32_to_cpu(oldflags); + if (oldflags & ~0xffff) { + error_setg(errp, "Unexpected export flags %0x" PRIx32, oldflags); + goto fail; +@@ -1080,8 +1080,8 @@ static int nbd_receive_simple_reply(QIOChannel *ioc, NBDSimpleReply *reply, + return ret; + } + +- be32_to_cpus(&reply->error); +- be64_to_cpus(&reply->handle); ++ reply->error = be32_to_cpu(reply->error); ++ reply->handle = be64_to_cpu(reply->handle); + + return 0; + } +@@ -1105,10 +1105,10 @@ static int nbd_receive_structured_reply_chunk(QIOChannel *ioc, + return ret; + } + +- be16_to_cpus(&chunk->flags); +- be16_to_cpus(&chunk->type); +- be64_to_cpus(&chunk->handle); +- be32_to_cpus(&chunk->length); ++ chunk->flags = be16_to_cpu(chunk->flags); ++ chunk->type = be16_to_cpu(chunk->type); ++ chunk->handle = be64_to_cpu(chunk->handle); ++ chunk->length = be32_to_cpu(chunk->length); + + return 0; + } +@@ -1128,7 +1128,7 @@ int nbd_receive_reply(QIOChannel *ioc, NBDReply *reply, Error **errp) + return ret; + } + +- be32_to_cpus(&reply->magic); ++ reply->magic = be32_to_cpu(reply->magic); + + switch (reply->magic) { + case NBD_SIMPLE_REPLY_MAGIC: +diff --git a/nbd/server.c b/nbd/server.c +index a9fec45..df76324 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -333,7 +333,7 @@ static int nbd_opt_read_name(NBDClient *client, char *name, uint32_t *length, + if (ret <= 0) { + return ret; + } +- cpu_to_be32s(&len); ++ len = cpu_to_be32(len); + + if (len > NBD_MAX_NAME_SIZE) { + return nbd_opt_invalid(client, errp, +@@ -486,7 +486,7 @@ static int nbd_negotiate_send_info(NBDClient *client, + if (rc < 0) { + return rc; + } +- cpu_to_be16s(&info); ++ info = cpu_to_be16(info); + if (nbd_write(client->ioc, &info, sizeof(info), errp) < 0) { + return -EIO; + } +@@ -551,14 +551,14 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags, + if (rc <= 0) { + return rc; + } +- be16_to_cpus(&requests); ++ requests = be16_to_cpu(requests); + trace_nbd_negotiate_handle_info_requests(requests); + while (requests--) { + rc = nbd_opt_read(client, &request, sizeof(request), errp); + if (rc <= 0) { + return rc; + } +- be16_to_cpus(&request); ++ request = be16_to_cpu(request); + trace_nbd_negotiate_handle_info_request(request, + nbd_info_lookup(request)); + /* We care about NBD_INFO_NAME and NBD_INFO_BLOCK_SIZE; +@@ -618,9 +618,9 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags, + /* maximum - At most 32M, but smaller as appropriate. */ + sizes[2] = MIN(blk_get_max_transfer(exp->blk), NBD_MAX_BUFFER_SIZE); + trace_nbd_negotiate_handle_info_block_size(sizes[0], sizes[1], sizes[2]); +- cpu_to_be32s(&sizes[0]); +- cpu_to_be32s(&sizes[1]); +- cpu_to_be32s(&sizes[2]); ++ sizes[0] = cpu_to_be32(sizes[0]); ++ sizes[1] = cpu_to_be32(sizes[1]); ++ sizes[2] = cpu_to_be32(sizes[2]); + rc = nbd_negotiate_send_info(client, NBD_INFO_BLOCK_SIZE, + sizeof(sizes), sizes, errp); + if (rc < 0) { +@@ -904,7 +904,7 @@ static int nbd_negotiate_meta_query(NBDClient *client, + if (ret <= 0) { + return ret; + } +- cpu_to_be32s(&len); ++ len = cpu_to_be32(len); + + if (len < ns_len) { + trace_nbd_negotiate_meta_query_skip("length too short"); +@@ -971,7 +971,7 @@ static int nbd_negotiate_meta_queries(NBDClient *client, + if (ret <= 0) { + return ret; + } +- cpu_to_be32s(&nb_queries); ++ nb_queries = cpu_to_be32(nb_queries); + trace_nbd_negotiate_meta_context(nbd_opt_lookup(client->opt), + export_name, nb_queries); + +@@ -1049,7 +1049,7 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags, + error_prepend(errp, "read failed: "); + return -EIO; + } +- be32_to_cpus(&flags); ++ flags = be32_to_cpu(flags); + trace_nbd_negotiate_options_flags(flags); + if (flags & NBD_FLAG_C_FIXED_NEWSTYLE) { + fixedNewstyle = true; +@@ -1900,8 +1900,8 @@ static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset, + extents_end = extent + 1; + + for (extent = extents; extent < extents_end; extent++) { +- cpu_to_be32s(&extent->flags); +- cpu_to_be32s(&extent->length); ++ extent->flags = cpu_to_be32(extent->flags); ++ extent->length = cpu_to_be32(extent->length); + } + + *bytes -= remaining_bytes; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-Forbid-nbd-server-stop-when-server-is-not-runnin.patch b/SOURCES/kvm-nbd-Forbid-nbd-server-stop-when-server-is-not-runnin.patch new file mode 100644 index 0000000..68645e1 --- /dev/null +++ b/SOURCES/kvm-nbd-Forbid-nbd-server-stop-when-server-is-not-runnin.patch @@ -0,0 +1,78 @@ +From 4c382f6bee06a3af0b05e321beca89e39b6caa1f Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:27 +0100 +Subject: [PATCH 088/163] nbd: Forbid nbd-server-stop when server is not + running + +RH-Author: John Snow +Message-id: <20190327172308.31077-15-jsnow@redhat.com> +Patchwork-id: 85187 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 14/55] nbd: Forbid nbd-server-stop when server is not running +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Since we already forbid other nbd-server commands when not +in the right state, it is unlikely that any caller was relying +on a second stop to behave as a silent no-op. Update iotest +223 to show the improved behavior. + +Signed-off-by: Eric Blake +Message-Id: <20190111194720.15671-3-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit 7801c3a7fd7042fd2f9435af4b9a6d2094073174) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev-nbd.c | 5 +++++ + tests/qemu-iotests/223 | 2 +- + tests/qemu-iotests/223.out | 2 +- + 3 files changed, 7 insertions(+), 2 deletions(-) + +diff --git a/blockdev-nbd.c b/blockdev-nbd.c +index 1d170c8..ca58491 100644 +--- a/blockdev-nbd.c ++++ b/blockdev-nbd.c +@@ -214,6 +214,11 @@ void qmp_nbd_server_remove(const char *name, + + void qmp_nbd_server_stop(Error **errp) + { ++ if (!nbd_server) { ++ error_setg(errp, "NBD server not running"); ++ return; ++ } ++ + nbd_export_close_all(); + + nbd_server_free(nbd_server); +diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 +index 61b46a2..a401609 100755 +--- a/tests/qemu-iotests/223 ++++ b/tests/qemu-iotests/223 +@@ -172,7 +172,7 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove", + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove", + "arguments":{"name":"n2"}}' "error" # Attempt duplicate clean + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-stop"}' "return" +-_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-stop"}' "return" # Oops ++_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-stop"}' "error" # Again + _send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' "return" + + # success, all done +diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out +index c1eed62..7d28c1a 100644 +--- a/tests/qemu-iotests/223.out ++++ b/tests/qemu-iotests/223.out +@@ -69,6 +69,6 @@ read 2097152/2097152 bytes at offset 2097152 + {"return": {}} + {"error": {"class": "GenericError", "desc": "Export 'n2' is not found"}} + {"return": {}} +-{"return": {}} ++{"error": {"class": "GenericError", "desc": "NBD server not running"}} + {"return": {}} + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-Merge-nbd_export_bitmap-into-nbd_export_new.patch b/SOURCES/kvm-nbd-Merge-nbd_export_bitmap-into-nbd_export_new.patch new file mode 100644 index 0000000..4fb89d2 --- /dev/null +++ b/SOURCES/kvm-nbd-Merge-nbd_export_bitmap-into-nbd_export_new.patch @@ -0,0 +1,226 @@ +From 13ac133e5fbde2559899dc3717b2a2c20ce9aeb1 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:32 +0100 +Subject: [PATCH 093/163] nbd: Merge nbd_export_bitmap into nbd_export_new + +RH-Author: John Snow +Message-id: <20190327172308.31077-20-jsnow@redhat.com> +Patchwork-id: 85190 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 19/55] nbd: Merge nbd_export_bitmap into nbd_export_new +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +We only have one caller that wants to export a bitmap name, +which it does right after creation of the export. But there is +still a brief window of time where an NBD client could see the +export but not the dirty bitmap, which a robust client would +have to interpret as meaning the entire image should be treated +as dirty. Better is to eliminate the window entirely, by +inlining nbd_export_bitmap() into nbd_export_new(), and refusing +to create the bitmap in the first place if the requested bitmap +can't be located. + +We also no longer need logic for setting a different bitmap +name compared to the bitmap being exported. + +Signed-off-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20190111194720.15671-8-eblake@redhat.com> +(cherry picked from commit 678ba275c77b5b12f3bc9bb369a1b824fc9f679f) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev-nbd.c | 11 +------ + include/block/nbd.h | 9 ++---- + nbd/server.c | 87 ++++++++++++++++++++++++----------------------------- + qemu-nbd.c | 5 +-- + 4 files changed, 47 insertions(+), 65 deletions(-) + +diff --git a/blockdev-nbd.c b/blockdev-nbd.c +index cd86b38..c76d541 100644 +--- a/blockdev-nbd.c ++++ b/blockdev-nbd.c +@@ -175,7 +175,7 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name, + writable = false; + } + +- exp = nbd_export_new(bs, 0, -1, name, NULL, ++ exp = nbd_export_new(bs, 0, -1, name, NULL, bitmap, + writable ? 0 : NBD_FLAG_READ_ONLY, + NULL, false, on_eject_blk, errp); + if (!exp) { +@@ -186,15 +186,6 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name, + * our only way of accessing it is through nbd_export_find(), so we can drop + * the strong reference that is @exp. */ + nbd_export_put(exp); +- +- if (has_bitmap) { +- Error *err = NULL; +- nbd_export_bitmap(exp, bitmap, bitmap, &err); +- if (err) { +- error_propagate(errp, err); +- nbd_export_remove(exp, NBD_SERVER_REMOVE_MODE_HARD, NULL); +- } +- } + } + + void qmp_nbd_server_remove(const char *name, +diff --git a/include/block/nbd.h b/include/block/nbd.h +index 2f9a2ae..1971b55 100644 +--- a/include/block/nbd.h ++++ b/include/block/nbd.h +@@ -296,9 +296,9 @@ typedef struct NBDClient NBDClient; + + NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, + const char *name, const char *description, +- uint16_t nbdflags, void (*close)(NBDExport *), +- bool writethrough, BlockBackend *on_eject_blk, +- Error **errp); ++ const char *bitmap, uint16_t nbdflags, ++ void (*close)(NBDExport *), bool writethrough, ++ BlockBackend *on_eject_blk, Error **errp); + void nbd_export_close(NBDExport *exp); + void nbd_export_remove(NBDExport *exp, NbdServerRemoveMode mode, Error **errp); + void nbd_export_get(NBDExport *exp); +@@ -319,9 +319,6 @@ void nbd_client_put(NBDClient *client); + void nbd_server_start(SocketAddress *addr, const char *tls_creds, + Error **errp); + +-void nbd_export_bitmap(NBDExport *exp, const char *bitmap, +- const char *bitmap_export_name, Error **errp); +- + /* nbd_read + * Reads @size bytes from @ioc. Returns 0 on success. + */ +diff --git a/nbd/server.c b/nbd/server.c +index 6c02b57..6b13601 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -1457,9 +1457,9 @@ static void nbd_eject_notifier(Notifier *n, void *data) + + NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, + const char *name, const char *description, +- uint16_t nbdflags, void (*close)(NBDExport *), +- bool writethrough, BlockBackend *on_eject_blk, +- Error **errp) ++ const char *bitmap, uint16_t nbdflags, ++ void (*close)(NBDExport *), bool writethrough, ++ BlockBackend *on_eject_blk, Error **errp) + { + AioContext *ctx; + BlockBackend *blk; +@@ -1507,6 +1507,43 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, + } + exp->size -= exp->size % BDRV_SECTOR_SIZE; + ++ if (bitmap) { ++ BdrvDirtyBitmap *bm = NULL; ++ BlockDriverState *bs = blk_bs(blk); ++ ++ while (true) { ++ bm = bdrv_find_dirty_bitmap(bs, bitmap); ++ if (bm != NULL || bs->backing == NULL) { ++ break; ++ } ++ ++ bs = bs->backing->bs; ++ } ++ ++ if (bm == NULL) { ++ error_setg(errp, "Bitmap '%s' is not found", bitmap); ++ goto fail; ++ } ++ ++ if ((nbdflags & NBD_FLAG_READ_ONLY) && bdrv_is_writable(bs) && ++ bdrv_dirty_bitmap_enabled(bm)) { ++ error_setg(errp, ++ "Enabled bitmap '%s' incompatible with readonly export", ++ bitmap); ++ goto fail; ++ } ++ ++ if (bdrv_dirty_bitmap_user_locked(bm)) { ++ error_setg(errp, "Bitmap '%s' is in use", bitmap); ++ goto fail; ++ } ++ ++ bdrv_dirty_bitmap_set_qmp_locked(bm, true); ++ exp->export_bitmap = bm; ++ exp->export_bitmap_context = g_strdup_printf("qemu:dirty-bitmap:%s", ++ bitmap); ++ } ++ + exp->close = close; + exp->ctx = blk_get_aio_context(blk); + blk_add_aio_context_notifier(blk, blk_aio_attached, blk_aio_detach, exp); +@@ -2424,47 +2461,3 @@ void nbd_client_new(QIOChannelSocket *sioc, + co = qemu_coroutine_create(nbd_co_client_start, client); + qemu_coroutine_enter(co); + } +- +-void nbd_export_bitmap(NBDExport *exp, const char *bitmap, +- const char *bitmap_export_name, Error **errp) +-{ +- BdrvDirtyBitmap *bm = NULL; +- BlockDriverState *bs = blk_bs(exp->blk); +- +- if (exp->export_bitmap) { +- error_setg(errp, "Export bitmap is already set"); +- return; +- } +- +- while (true) { +- bm = bdrv_find_dirty_bitmap(bs, bitmap); +- if (bm != NULL || bs->backing == NULL) { +- break; +- } +- +- bs = bs->backing->bs; +- } +- +- if (bm == NULL) { +- error_setg(errp, "Bitmap '%s' is not found", bitmap); +- return; +- } +- +- if ((exp->nbdflags & NBD_FLAG_READ_ONLY) && bdrv_is_writable(bs) && +- bdrv_dirty_bitmap_enabled(bm)) { +- error_setg(errp, +- "Enabled bitmap '%s' incompatible with readonly export", +- bitmap); +- return; +- } +- +- if (bdrv_dirty_bitmap_user_locked(bm)) { +- error_setg(errp, "Bitmap '%s' is in use", bitmap); +- return; +- } +- +- bdrv_dirty_bitmap_set_qmp_locked(bm, true); +- exp->export_bitmap = bm; +- exp->export_bitmap_context = +- g_strdup_printf("qemu:dirty-bitmap:%s", bitmap_export_name); +-} +diff --git a/qemu-nbd.c b/qemu-nbd.c +index c85aee4..ac4c958 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -1018,8 +1018,9 @@ int main(int argc, char **argv) + } + + export = nbd_export_new(bs, dev_offset, fd_size, export_name, +- export_description, nbdflags, nbd_export_closed, +- writethrough, NULL, &error_fatal); ++ export_description, NULL, nbdflags, ++ nbd_export_closed, writethrough, NULL, ++ &error_fatal); + + if (device) { + #if HAVE_NBD_DEVICE +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-Merge-nbd_export_set_name-into-nbd_export_new.patch b/SOURCES/kvm-nbd-Merge-nbd_export_set_name-into-nbd_export_new.patch new file mode 100644 index 0000000..ce61da8 --- /dev/null +++ b/SOURCES/kvm-nbd-Merge-nbd_export_set_name-into-nbd_export_new.patch @@ -0,0 +1,224 @@ +From 629f726f14a40cfe4edf74ec0d213eed935e5a79 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:29 +0100 +Subject: [PATCH 090/163] nbd: Merge nbd_export_set_name into nbd_export_new + +RH-Author: John Snow +Message-id: <20190327172308.31077-17-jsnow@redhat.com> +Patchwork-id: 85171 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 16/55] nbd: Merge nbd_export_set_name into nbd_export_new +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +The existing NBD code had a weird split where nbd_export_new() +created an export but did not add it to the list of exported +names until a later nbd_export_set_name() came along and grabbed +a second reference on the object; later, the first call to +nbd_export_close() drops the second reference while removing +the export from the list. This is in part because the QAPI +NbdServerRemoveNode enum documents the possibility of adding a +mode where we could do a soft disconnect: preventing new clients, +but waiting for existing clients to gracefully quit, based on +the mode used when calling nbd_export_close(). + +But in spite of all that, note that we never change the name of +an NBD export while it is exposed, which means it is easier to +just inline the process of setting the name as part of creating +the export. + +Inline the contents of nbd_export_set_name() and +nbd_export_set_description() into the two points in an export +lifecycle where they matter, then adjust both callers to pass +the name up front. Note that for creation, all callers pass a +non-NULL name, (passing NULL at creation was for old style +servers, but we removed support for that in commit 7f7dfe2a), +so we can add an assert and do things unconditionally; but for +cleanup, because of the dual nature of nbd_export_close(), we +still have to be careful to avoid use-after-free. Along the +way, add a comment reminding ourselves of the potential of +adding a middle mode disconnect. + +Signed-off-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20190111194720.15671-5-eblake@redhat.com> +(cherry picked from commit 3fa4c76590569f9dc128beb3eee65aaefcb6321e) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev-nbd.c | 5 ++--- + include/block/nbd.h | 3 +-- + nbd/server.c | 52 +++++++++++++++++++++++----------------------------- + qemu-nbd.c | 8 +++----- + 4 files changed, 29 insertions(+), 39 deletions(-) + +diff --git a/blockdev-nbd.c b/blockdev-nbd.c +index ca58491..582ffde 100644 +--- a/blockdev-nbd.c ++++ b/blockdev-nbd.c +@@ -174,14 +174,13 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name, + writable = false; + } + +- exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY, ++ exp = nbd_export_new(bs, 0, -1, name, NULL, ++ writable ? 0 : NBD_FLAG_READ_ONLY, + NULL, false, on_eject_blk, errp); + if (!exp) { + return; + } + +- nbd_export_set_name(exp, name); +- + /* The list of named exports has a strong reference to this export now and + * our only way of accessing it is through nbd_export_find(), so we can drop + * the strong reference that is @exp. */ +diff --git a/include/block/nbd.h b/include/block/nbd.h +index 65402d3..2f9a2ae 100644 +--- a/include/block/nbd.h ++++ b/include/block/nbd.h +@@ -295,6 +295,7 @@ typedef struct NBDExport NBDExport; + typedef struct NBDClient NBDClient; + + NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, ++ const char *name, const char *description, + uint16_t nbdflags, void (*close)(NBDExport *), + bool writethrough, BlockBackend *on_eject_blk, + Error **errp); +@@ -306,8 +307,6 @@ void nbd_export_put(NBDExport *exp); + BlockBackend *nbd_export_get_blockdev(NBDExport *exp); + + NBDExport *nbd_export_find(const char *name); +-void nbd_export_set_name(NBDExport *exp, const char *name); +-void nbd_export_set_description(NBDExport *exp, const char *description); + void nbd_export_close_all(void); + + void nbd_client_new(QIOChannelSocket *sioc, +diff --git a/nbd/server.c b/nbd/server.c +index c0f2e85..6c02b57 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -1456,6 +1456,7 @@ static void nbd_eject_notifier(Notifier *n, void *data) + } + + NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, ++ const char *name, const char *description, + uint16_t nbdflags, void (*close)(NBDExport *), + bool writethrough, BlockBackend *on_eject_blk, + Error **errp) +@@ -1471,6 +1472,7 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, + * that BDRV_O_INACTIVE is cleared and the image is ready for write + * access since the export could be available before migration handover. + */ ++ assert(name); + ctx = bdrv_get_aio_context(bs); + aio_context_acquire(ctx); + bdrv_invalidate_cache(bs, NULL); +@@ -1494,6 +1496,8 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, + QTAILQ_INIT(&exp->clients); + exp->blk = blk; + exp->dev_offset = dev_offset; ++ exp->name = g_strdup(name); ++ exp->description = g_strdup(description); + exp->nbdflags = nbdflags; + exp->size = size < 0 ? blk_getlength(blk) : size; + if (exp->size < 0) { +@@ -1513,10 +1517,14 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, + exp->eject_notifier.notify = nbd_eject_notifier; + blk_add_remove_bs_notifier(on_eject_blk, &exp->eject_notifier); + } ++ QTAILQ_INSERT_TAIL(&exports, exp, next); ++ nbd_export_get(exp); + return exp; + + fail: + blk_unref(blk); ++ g_free(exp->name); ++ g_free(exp->description); + g_free(exp); + return NULL; + } +@@ -1533,43 +1541,29 @@ NBDExport *nbd_export_find(const char *name) + return NULL; + } + +-void nbd_export_set_name(NBDExport *exp, const char *name) +-{ +- if (exp->name == name) { +- return; +- } +- +- nbd_export_get(exp); +- if (exp->name != NULL) { +- g_free(exp->name); +- exp->name = NULL; +- QTAILQ_REMOVE(&exports, exp, next); +- nbd_export_put(exp); +- } +- if (name != NULL) { +- nbd_export_get(exp); +- exp->name = g_strdup(name); +- QTAILQ_INSERT_TAIL(&exports, exp, next); +- } +- nbd_export_put(exp); +-} +- +-void nbd_export_set_description(NBDExport *exp, const char *description) +-{ +- g_free(exp->description); +- exp->description = g_strdup(description); +-} +- + void nbd_export_close(NBDExport *exp) + { + NBDClient *client, *next; + + nbd_export_get(exp); ++ /* ++ * TODO: Should we expand QMP NbdServerRemoveNode enum to allow a ++ * close mode that stops advertising the export to new clients but ++ * still permits existing clients to run to completion? Because of ++ * that possibility, nbd_export_close() can be called more than ++ * once on an export. ++ */ + QTAILQ_FOREACH_SAFE(client, &exp->clients, next, next) { + client_close(client, true); + } +- nbd_export_set_name(exp, NULL); +- nbd_export_set_description(exp, NULL); ++ if (exp->name) { ++ nbd_export_put(exp); ++ g_free(exp->name); ++ exp->name = NULL; ++ QTAILQ_REMOVE(&exports, exp, next); ++ } ++ g_free(exp->description); ++ exp->description = NULL; + nbd_export_put(exp); + } + +diff --git a/qemu-nbd.c b/qemu-nbd.c +index c37defb..c85aee4 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -1017,11 +1017,9 @@ int main(int argc, char **argv) + } + } + +- export = nbd_export_new(bs, dev_offset, fd_size, nbdflags, +- nbd_export_closed, writethrough, +- NULL, &error_fatal); +- nbd_export_set_name(export, export_name); +- nbd_export_set_description(export, export_description); ++ export = nbd_export_new(bs, dev_offset, fd_size, export_name, ++ export_description, nbdflags, nbd_export_closed, ++ writethrough, NULL, &error_fatal); + + if (device) { + #if HAVE_NBD_DEVICE +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-Only-require-disabled-bitmap-for-read-only-expor.patch b/SOURCES/kvm-nbd-Only-require-disabled-bitmap-for-read-only-expor.patch new file mode 100644 index 0000000..a8bb16f --- /dev/null +++ b/SOURCES/kvm-nbd-Only-require-disabled-bitmap-for-read-only-expor.patch @@ -0,0 +1,149 @@ +From 36341fd3c27fd361efe22a7c2758fff1b245867f Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:28 +0100 +Subject: [PATCH 089/163] nbd: Only require disabled bitmap for read-only + exports + +RH-Author: John Snow +Message-id: <20190327172308.31077-16-jsnow@redhat.com> +Patchwork-id: 85180 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 15/55] nbd: Only require disabled bitmap for read-only exports +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Our initial implementation of x-nbd-server-add-bitmap put +in a restriction because of incremental backups: in that +usage, we are exporting one qcow2 file (the temporary overlay +target of a blockdev-backup sync:none job) and a dirty bitmap +owned by a second qcow2 file (the source of the +blockdev-backup, which is the backing file of the temporary). +While both qcow2 files are still writable (the target in +order to capture copy-on-write of old contents, and the +source in order to track live guest writes in the meantime), +the NBD client expects to see constant data, including the +dirty bitmap. An enabled bitmap in the source would be +modified by guest writes, which is at odds with the NBD +export being a read-only constant view, hence the initial +code choice of enforcing a disabled bitmap (the intent is +that the exposed bitmap was disabled in the same transaction +that started the blockdev-backup job, although we don't want +to track enough state to actually enforce that). + +However, consider the case of a bitmap contained in a read-only +node (including when the bitmap is found in a backing layer of +the active image). Because the node can't be modified, the +bitmap won't change due to writes, regardless of whether it is +still enabled. Forbidding the export unless the bitmap is +disabled is awkward, paritcularly since we can't change the +bitmap to be disabled (because the node is read-only). + +Alternatively, consider the case of live storage migration, +where management directs the destination to create a writable +NBD server, then performs a drive-mirror from the source to +the target, prior to doing the rest of the live migration. +Since storage migration can be time-consuming, it may be wise +to let the destination include a dirty bitmap to track which +portions it has already received, where even if the migration +is interrupted and restarted, the source can query the +destination block status in order to potentially minimize +re-sending data that has not changed in the meantime on a +second attempt. Such code has not been written, and might not +be trivial (after all, a cluster being marked dirty in the +bitmap does not necessarily guarantee it has the desired +contents), but it makes sense that letting an active dirty +bitmap be exposed and changing alongside writes may prove +useful in the future. + +Solve both issues by gating the restriction against a +disabled bitmap to only happen when the caller has requested +a read-only export, and where the BDS that owns the bitmap +(whether or not it is the BDS handed to nbd_export_new() or +from its backing chain) is still writable. We could drop +the check altogether (if management apps are prepared to +deal with a changing bitmap even on a read-only image), but +for now keeping a check for the read-only case still stands +a chance of preventing management errors. + +Update iotest 223 to show the looser behavior by leaving +a bitmap enabled the whole run; note that we have to tear +down and re-export a node when handling an error. + +Signed-off-by: Eric Blake +Message-Id: <20190111194720.15671-4-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit 702aa50d61497d29ef3b5e9c75d1404b3e6fd831) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 7 +++++-- + tests/qemu-iotests/223 | 10 +++++++--- + tests/qemu-iotests/223.out | 3 ++- + 3 files changed, 14 insertions(+), 6 deletions(-) + +diff --git a/nbd/server.c b/nbd/server.c +index abf03e8..c0f2e85 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -2456,8 +2456,11 @@ void nbd_export_bitmap(NBDExport *exp, const char *bitmap, + return; + } + +- if (bdrv_dirty_bitmap_enabled(bm)) { +- error_setg(errp, "Bitmap '%s' is enabled", bitmap); ++ if ((exp->nbdflags & NBD_FLAG_READ_ONLY) && bdrv_is_writable(bs) && ++ bdrv_dirty_bitmap_enabled(bm)) { ++ error_setg(errp, ++ "Enabled bitmap '%s' incompatible with readonly export", ++ bitmap); + return; + } + +diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 +index a401609..f200e31 100755 +--- a/tests/qemu-iotests/223 ++++ b/tests/qemu-iotests/223 +@@ -61,6 +61,8 @@ echo "=== Create partially sparse image, then add dirty bitmaps ===" + echo + + # Two bitmaps, to contrast granularity issues ++# Also note that b will be disabled, while b2 is left enabled, to ++# check for read-only interactions + _make_test_img -o cluster_size=4k 4M + $QEMU_IO -c 'w -P 0x11 1M 2M' "$TEST_IMG" | _filter_qemu_io + run_qemu < +Date: Mon, 6 May 2019 17:56:14 +0200 +Subject: [PATCH 04/53] nbd: Permit simple error to NBD_CMD_BLOCK_STATUS + +RH-Author: John Snow +Message-id: <20190506175629.11079-5-jsnow@redhat.com> +Patchwork-id: 87186 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 04/19] nbd: Permit simple error to NBD_CMD_BLOCK_STATUS +Bugzilla: 1692018 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Thomas Huth + +From: Eric Blake + +The NBD spec is clear that when structured replies are active, a +simple error reply is acceptable to any command except for +NBD_CMD_READ. However, we were mistakenly requiring structured errors +for NBD_CMD_BLOCK_STATUS, and hanging up on a server that gave a +simple error (since qemu does not behave as such a server, we didn't +notice the problem until now). Broken since its introduction in +commit 78a33ab5 (v2.12). + +Noticed while debugging a separate failure reported by nbdkit while +working out its initial implementation of BLOCK_STATUS, although it +turns out that nbdkit also chose to send structured error replies for +BLOCK_STATUS, so I had to manually provoke the situation by hacking +qemu's server to send a simple error reply: + +| diff --git i/nbd/server.c w/nbd/server.c +| index fd013a2817a..833288d7c45 100644 +| 00--- i/nbd/server.c +| +++ w/nbd/server.c +| @@ -2269,6 +2269,8 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, +| "discard failed", errp); +| +| case NBD_CMD_BLOCK_STATUS: +| + return nbd_co_send_simple_reply(client, request->handle, ENOMEM, +| + NULL, 0, errp); +| if (!request->len) { +| return nbd_send_generic_reply(client, request->handle, -EINVAL, +| "need non-zero length", errp); +| + +Signed-off-by: Eric Blake +Acked-by: Richard W.M. Jones +Message-Id: <20190325190104.30213-3-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit ebd82cd872726549d0a55d329d22c731e2e660ff) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index 532f90c..eee909f 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -716,9 +716,7 @@ static int nbd_co_receive_blockstatus_reply(NBDClientSession *s, + bool received = false; + + assert(!extent->length); +- NBD_FOREACH_REPLY_CHUNK(s, iter, handle, s->info.structured_reply, +- NULL, &reply, &payload) +- { ++ NBD_FOREACH_REPLY_CHUNK(s, iter, handle, false, NULL, &reply, &payload) { + int ret; + NBDStructuredReplyChunk *chunk = &reply.structured; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-Remove-x-nbd-server-add-bitmap.patch b/SOURCES/kvm-nbd-Remove-x-nbd-server-add-bitmap.patch new file mode 100644 index 0000000..e85273d --- /dev/null +++ b/SOURCES/kvm-nbd-Remove-x-nbd-server-add-bitmap.patch @@ -0,0 +1,101 @@ +From d8f27c2a04ed34863b67d6a7914acc0ad1559dff Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:31 +0100 +Subject: [PATCH 092/163] nbd: Remove x-nbd-server-add-bitmap + +RH-Author: John Snow +Message-id: <20190327172308.31077-19-jsnow@redhat.com> +Patchwork-id: 85178 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 18/55] nbd: Remove x-nbd-server-add-bitmap +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Now that nbd-server-add can do the same functionality (well, other +than making the exported bitmap name different than the underlying +bitamp - but we argued that was not essential, since it is just as +easy to create a new non-persistent bitmap with the desired name), +we no longer need the experimental separate command. + +Signed-off-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20190111194720.15671-7-eblake@redhat.com> +(cherry picked from commit 7dc570b3806e5b0a4c9219061556ed5a4a0de80c) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev-nbd.c | 23 ----------------------- + qapi/block.json | 23 ----------------------- + 2 files changed, 46 deletions(-) + +diff --git a/blockdev-nbd.c b/blockdev-nbd.c +index ec8cf0a..cd86b38 100644 +--- a/blockdev-nbd.c ++++ b/blockdev-nbd.c +@@ -233,26 +233,3 @@ void qmp_nbd_server_stop(Error **errp) + nbd_server_free(nbd_server); + nbd_server = NULL; + } +- +-void qmp_x_nbd_server_add_bitmap(const char *name, const char *bitmap, +- bool has_bitmap_export_name, +- const char *bitmap_export_name, +- Error **errp) +-{ +- NBDExport *exp; +- +- if (!nbd_server) { +- error_setg(errp, "NBD server not running"); +- return; +- } +- +- exp = nbd_export_find(name); +- if (exp == NULL) { +- error_setg(errp, "Export '%s' is not found", name); +- return; +- } +- +- nbd_export_bitmap(exp, bitmap, +- has_bitmap_export_name ? bitmap_export_name : bitmap, +- errp); +-} +diff --git a/qapi/block.json b/qapi/block.json +index b04fcdc..6b1d317 100644 +--- a/qapi/block.json ++++ b/qapi/block.json +@@ -369,29 +369,6 @@ + 'data': {'name': 'str', '*mode': 'NbdServerRemoveMode'} } + + ## +-# @x-nbd-server-add-bitmap: +-# +-# Expose a dirty bitmap associated with the selected export. The bitmap search +-# starts at the device attached to the export, and includes all backing files. +-# The exported bitmap is then locked until the NBD export is removed. +-# +-# @name: Export name. +-# +-# @bitmap: Bitmap name to search for. +-# +-# @bitmap-export-name: How the bitmap will be seen by nbd clients +-# (default @bitmap) +-# +-# Note: the client must use NBD_OPT_SET_META_CONTEXT with a query of +-# "qemu:dirty-bitmap:NAME" (where NAME matches @bitmap-export-name) to access +-# the exposed bitmap. +-# +-# Since: 3.0 +-## +- { 'command': 'x-nbd-server-add-bitmap', +- 'data': {'name': 'str', 'bitmap': 'str', '*bitmap-export-name': 'str'} } +- +-## + # @nbd-server-stop: + # + # Stop QEMU's embedded NBD server, and unregister all devices previously +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-Support-auto-read-only-option.patch b/SOURCES/kvm-nbd-Support-auto-read-only-option.patch new file mode 100644 index 0000000..e9be703 --- /dev/null +++ b/SOURCES/kvm-nbd-Support-auto-read-only-option.patch @@ -0,0 +1,52 @@ +From 02778d4e42af5fa6c9dd985bee2ae04b2646bbcd Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 23 Nov 2018 10:41:47 +0100 +Subject: [PATCH 06/34] nbd: Support auto-read-only option + +RH-Author: Kevin Wolf +Message-id: <20181123104154.13541-6-kwolf@redhat.com> +Patchwork-id: 83121 +O-Subject: [RHEL-7.7/7.6.z qemu-kvm-rhev PATCH v2 05/12] nbd: Support auto-read-only option +Bugzilla: 1623986 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina +RH-Acked-by: John Snow + +If read-only=off, but auto-read-only=on is given, open a read-write NBD +connection if the server provides a read-write export, but instead of +erroring out for read-only exports, just degrade to read-only. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +(cherry picked from commit 6c2e581d4d7751f035e9bac0384703879c8a1538) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index 98637c0..80d3625 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -988,11 +988,11 @@ int nbd_client_init(BlockDriverState *bs, + logout("Failed to negotiate with the NBD server\n"); + return ret; + } +- if (client->info.flags & NBD_FLAG_READ_ONLY && +- !bdrv_is_read_only(bs)) { +- error_setg(errp, +- "request for write access conflicts with read-only export"); +- return -EACCES; ++ if (client->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 (client->info.flags & NBD_FLAG_SEND_FUA) { + bs->supported_write_flags = BDRV_REQ_FUA; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-Tolerate-some-server-non-compliance-in-NBD_CMD_B.patch b/SOURCES/kvm-nbd-Tolerate-some-server-non-compliance-in-NBD_CMD_B.patch new file mode 100644 index 0000000..14128b0 --- /dev/null +++ b/SOURCES/kvm-nbd-Tolerate-some-server-non-compliance-in-NBD_CMD_B.patch @@ -0,0 +1,125 @@ +From 557b85bcd4aedf4550b272abb57f817f7dc8eba1 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 6 May 2019 17:56:12 +0200 +Subject: [PATCH 02/53] nbd: Tolerate some server non-compliance in + NBD_CMD_BLOCK_STATUS + +RH-Author: John Snow +Message-id: <20190506175629.11079-3-jsnow@redhat.com> +Patchwork-id: 87193 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 02/19] nbd: Tolerate some server non-compliance in NBD_CMD_BLOCK_STATUS +Bugzilla: 1692018 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Thomas Huth + +From: Eric Blake + +The NBD spec states that NBD_CMD_FLAG_REQ_ONE (which we currently +always use) should not reply with an extent larger than our request, +and that the server's response should be exactly one extent. Right +now, that means that if a server sends more than one extent, we treat +the server as broken, fail the block status request, and disconnect, +which prevents all further use of the block device. But while good +software should be strict in what it sends, it should be tolerant in +what it receives. + +While trying to implement NBD_CMD_BLOCK_STATUS in nbdkit, we +temporarily had a non-compliant server sending too many extents in +spite of REQ_ONE. Oddly enough, 'qemu-img convert' with qemu 3.1 +failed with a somewhat useful message: + qemu-img: Protocol error: invalid payload for NBD_REPLY_TYPE_BLOCK_STATUS + +which then disappeared with commit d8b4bad8, on the grounds that an +error message flagged only at the time of coroutine teardown is +pointless, and instead we should rely on the actual failed API to +report an error - in other words, the 3.1 behavior was masking the +fact that qemu-img was not reporting an error. That has since been +fixed in the previous patch, where qemu-img convert now fails with: + qemu-img: error while reading block status of sector 0: Invalid argument + +But even that is harsh. Since we already partially relaxed things in +commit acfd8f7a to tolerate a server that exceeds the cap (although +that change was made prior to the NBD spec actually putting a cap on +the extent length during REQ_ONE - in fact, the NBD spec change was +BECAUSE of the qemu behavior prior to that commit), it's not that much +harder to argue that we should also tolerate a server that sends too +many extents. But at the same time, it's nice to trace when we are +being tolerant of server non-compliance, in order to help server +writers fix their implementations to be more portable (if they refer +to our traces, rather than just stderr). + +Reported-by: Richard W.M. Jones +Signed-off-by: Eric Blake +Message-Id: <20190323212639.579-3-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit a39286dd61e455014694cb6aa44cfa9e5c86d101) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 21 ++++++++++++++++----- + block/trace-events | 1 + + 2 files changed, 17 insertions(+), 5 deletions(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index 1230850..f3c31d1 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -227,8 +227,8 @@ static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk, + } + + /* nbd_parse_blockstatus_payload +- * support only one extent in reply and only for +- * base:allocation context ++ * Based on our request, we expect only one extent in reply, for the ++ * base:allocation context. + */ + static int nbd_parse_blockstatus_payload(NBDClientSession *client, + NBDStructuredReplyChunk *chunk, +@@ -237,7 +237,8 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client, + { + uint32_t context_id; + +- if (chunk->length != sizeof(context_id) + sizeof(*extent)) { ++ /* The server succeeded, so it must have sent [at least] one extent */ ++ if (chunk->length < sizeof(context_id) + sizeof(*extent)) { + error_setg(errp, "Protocol error: invalid payload for " + "NBD_REPLY_TYPE_BLOCK_STATUS"); + return -EINVAL; +@@ -263,10 +264,20 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client, + return -EINVAL; + } + +- /* The server is allowed to send us extra information on the final +- * extent; just clamp it to the length we requested. */ ++ /* ++ * 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. 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 (chunk->length > sizeof(context_id) + sizeof(*extent)) { ++ trace_nbd_parse_blockstatus_compliance("more than one extent"); ++ } + if (extent->length > orig_length) { + extent->length = orig_length; ++ trace_nbd_parse_blockstatus_compliance("extent length too large"); + } + + return 0; +diff --git a/block/trace-events b/block/trace-events +index 6d4d399..59c6f54 100644 +--- a/block/trace-events ++++ b/block/trace-events +@@ -152,5 +152,6 @@ nvme_cmd_map_qiov_pages(void *s, int i, uint64_t page) "s %p page[%d] 0x%"PRIx64 + nvme_cmd_map_qiov_iov(void *s, int i, void *page, int pages) "s %p iov[%d] %p pages %d" + + # block/nbd-client.c ++nbd_parse_blockstatus_compliance(const char *err) "ignoring extra data from non-compliant server: %s" + nbd_read_reply_entry_fail(int ret, const char *err) "ret = %d, err: %s" + nbd_co_request_fail(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, uint16_t type, const char *name, int ret, const char *err) "Request failed { .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64 ", .flags = 0x%" PRIx16 ", .type = %" PRIu16 " (%s) } ret = %d, err: %s" +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-change-error-checking-order-for-bitmaps.patch b/SOURCES/kvm-nbd-change-error-checking-order-for-bitmaps.patch new file mode 100644 index 0000000..5be341b --- /dev/null +++ b/SOURCES/kvm-nbd-change-error-checking-order-for-bitmaps.patch @@ -0,0 +1,62 @@ +From 4118dc26c6ad7a69c11522d25ead648ffdf0088e Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 18:18:43 +0200 +Subject: [PATCH 138/163] nbd: change error checking order for bitmaps + +RH-Author: John Snow +Message-id: <20190403181857.9693-8-jsnow@redhat.com> +Patchwork-id: 85414 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 07/21] nbd: change error checking order for bitmaps +Bugzilla: 1677073 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Sergio Lopez Pascual + +Check that the bitmap is not in use prior to it checking if it is +not enabled/recording guest writes. The bitmap being busy was likely +at the behest of the user, so this error has a greater chance of being +understood by the user. + +Signed-off-by: John Snow +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20190223000614.13894-6-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 3b78a92776bf1199b6864f2f38d9d341fb741f36) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/nbd/server.c b/nbd/server.c +index 0910d09..de21c64 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -1510,6 +1510,11 @@ NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset, + goto fail; + } + ++ if (bdrv_dirty_bitmap_user_locked(bm)) { ++ error_setg(errp, "Bitmap '%s' is in use", bitmap); ++ goto fail; ++ } ++ + if ((nbdflags & NBD_FLAG_READ_ONLY) && bdrv_is_writable(bs) && + bdrv_dirty_bitmap_enabled(bm)) { + error_setg(errp, +@@ -1518,11 +1523,6 @@ NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset, + goto fail; + } + +- if (bdrv_dirty_bitmap_user_locked(bm)) { +- error_setg(errp, "Bitmap '%s' is in use", bitmap); +- goto fail; +- } +- + bdrv_dirty_bitmap_set_qmp_locked(bm, true); + exp->export_bitmap = bm; + exp->export_bitmap_context = g_strdup_printf("qemu:dirty-bitmap:%s", +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Add-meta-contexts-to-nbd_receive_export_l.patch b/SOURCES/kvm-nbd-client-Add-meta-contexts-to-nbd_receive_export_l.patch new file mode 100644 index 0000000..8d3e317 --- /dev/null +++ b/SOURCES/kvm-nbd-client-Add-meta-contexts-to-nbd_receive_export_l.patch @@ -0,0 +1,142 @@ +From 1c6d308e59fe4d6f50a0664eea5c0d09b8075f20 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:52 +0100 +Subject: [PATCH 114/163] nbd/client: Add meta contexts to + nbd_receive_export_list() + +RH-Author: John Snow +Message-id: <20190327172308.31077-40-jsnow@redhat.com> +Patchwork-id: 85199 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 39/55] nbd/client: Add meta contexts to nbd_receive_export_list() +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +We want to be able to detect whether a given qemu NBD server is +exposing the right export(s) and dirty bitmaps, at least for +regression testing. We could use 'nbd-client -l' from the upstream +NBD project to list exports, but it's annoying to rely on +out-of-tree binaries; furthermore, nbd-client doesn't necessarily +know about all of the qemu NBD extensions. Thus, we plan on adding +a new mode to qemu-nbd that merely sniffs all possible information +from the server during handshake phase, then disconnects and dumps +the information. + +This patch continues the work of the previous patch, by adding the +ability to track the list of available meta contexts into +NBDExportInfo. It benefits from the recent refactoring patches +with a new nbd_list_meta_contexts() that reuses much of the same +framework as setting a meta context. + +Note: a malicious server could exhaust memory of a client by feeding +an unending loop of contexts; perhaps we could place a limit on how +many we are willing to receive. But this is no different from our +earlier analysis on a server sending an unending list of exports, +and the death of a client due to memory exhaustion when the client +was going to exit soon anyways is not really a denial of service +attack. + +Signed-off-by: Eric Blake +Reviewed-by: Richard W.M. Jones +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20190117193658.16413-19-eblake@redhat.com> +(cherry picked from commit 0b576b6bfb56291bb13db0a54d99adf2f3706030) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + include/block/nbd.h | 2 ++ + nbd/client.c | 41 +++++++++++++++++++++++++++++++++++++++-- + 2 files changed, 41 insertions(+), 2 deletions(-) + +diff --git a/include/block/nbd.h b/include/block/nbd.h +index 19332b4..4faf394 100644 +--- a/include/block/nbd.h ++++ b/include/block/nbd.h +@@ -284,6 +284,8 @@ struct NBDExportInfo { + + /* Set by server results during nbd_receive_export_list() */ + char *description; ++ int n_contexts; ++ char **contexts; + }; + typedef struct NBDExportInfo NBDExportInfo; + +diff --git a/nbd/client.c b/nbd/client.c +index 8a32169..798b82f 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -818,6 +818,36 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, + } + + /* ++ * 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; ++ ++ 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) { ++ return ret; ++ } ++ 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. +@@ -1066,7 +1096,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + /* Clean up result of nbd_receive_export_list */ + void nbd_free_export_list(NBDExportInfo *info, int count) + { +- int i; ++ int i, j; + + if (!info) { + return; +@@ -1075,6 +1105,10 @@ void nbd_free_export_list(NBDExportInfo *info, int count) + 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); + } +@@ -1144,7 +1178,10 @@ int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + break; + } + +- /* TODO: Grab meta contexts */ ++ if (result == 3 && ++ nbd_list_meta_contexts(ioc, &array[i], errp) < 0) { ++ goto out; ++ } + } + + /* Send NBD_OPT_ABORT as a courtesy before hanging up */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Add-nbd_receive_export_list.patch b/SOURCES/kvm-nbd-client-Add-nbd_receive_export_list.patch new file mode 100644 index 0000000..2c690c6 --- /dev/null +++ b/SOURCES/kvm-nbd-client-Add-nbd_receive_export_list.patch @@ -0,0 +1,269 @@ +From 2304c1c3b634b52dcca0a42c4986a04fc2b89369 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:51 +0100 +Subject: [PATCH 113/163] nbd/client: Add nbd_receive_export_list() + +RH-Author: John Snow +Message-id: <20190327172308.31077-39-jsnow@redhat.com> +Patchwork-id: 85212 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 38/55] nbd/client: Add nbd_receive_export_list() +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +We want to be able to detect whether a given qemu NBD server is +exposing the right export(s) and dirty bitmaps, at least for +regression testing. We could use 'nbd-client -l' from the upstream +NBD project to list exports, but it's annoying to rely on +out-of-tree binaries; furthermore, nbd-client doesn't necessarily +know about all of the qemu NBD extensions. Thus, we plan on adding +a new mode to qemu-nbd that merely sniffs all possible information +from the server during handshake phase, then disconnects and dumps +the information. + +This patch adds the low-level client code for grabbing the list +of exports. It benefits from the recent refactoring patches, in +order to share as much code as possible when it comes to doing +validation of server replies. The resulting information is stored +in an array of NBDExportInfo which has been expanded to any +description string, along with a convenience function for freeing +the list. + +Note: a malicious server could exhaust memory of a client by feeding +an unending loop of exports; perhaps we should place a limit on how +many we are willing to receive. But note that a server could +reasonably be serving an export for every file in a large directory, +where an arbitrary limit in the client means we can't list anything +from such a server; the same happens if we just run until the client +fails to malloc() and thus dies by an abort(), where the limit is +no longer arbitrary but determined by available memory. Since the +client is already planning on being short-lived, it's hard to call +this a denial of service attack that would starve off other uses, +so it does not appear to be a security issue. + +Signed-off-by: Eric Blake +Reviewed-by: Richard W.M. Jones +Message-Id: <20190117193658.16413-18-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit d21a2d3451d7f1defea5104ce28938f228fab0d4) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + include/block/nbd.h | 15 +++++- + nbd/client.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 143 insertions(+), 4 deletions(-) + +diff --git a/include/block/nbd.h b/include/block/nbd.h +index be19aac..19332b4 100644 +--- a/include/block/nbd.h ++++ b/include/block/nbd.h +@@ -1,5 +1,5 @@ + /* +- * Copyright (C) 2016-2017 Red Hat, Inc. ++ * Copyright (C) 2016-2019 Red Hat, Inc. + * Copyright (C) 2005 Anthony Liguori + * + * Network Block Device +@@ -262,6 +262,9 @@ 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 +@@ -269,7 +272,8 @@ struct NBDExportInfo { + bool structured_reply; + bool base_allocation; /* base:allocation context for NBD_CMD_BLOCK_STATUS */ + +- /* Set by server results during nbd_receive_negotiate() */ ++ /* Set by server results during nbd_receive_negotiate() and ++ * nbd_receive_export_list() */ + uint64_t size; + uint16_t flags; + uint32_t min_block; +@@ -277,12 +281,19 @@ struct NBDExportInfo { + uint32_t max_block; + + uint32_t context_id; ++ ++ /* Set by server results during nbd_receive_export_list() */ ++ char *description; + }; + typedef struct NBDExportInfo 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); +diff --git a/nbd/client.c b/nbd/client.c +index fa1657a..8a32169 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -836,7 +836,9 @@ static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + + trace_nbd_start_negotiate(tlscreds, hostname ? hostname : ""); + +- *zeroes = true; ++ if (zeroes) { ++ *zeroes = true; ++ } + if (outioc) { + *outioc = NULL; + } +@@ -880,7 +882,9 @@ static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + clientflags |= NBD_FLAG_C_FIXED_NEWSTYLE; + } + if (globalflags & NBD_FLAG_NO_ZEROES) { +- *zeroes = false; ++ if (zeroes) { ++ *zeroes = false; ++ } + clientflags |= NBD_FLAG_C_NO_ZEROES; + } + /* client requested flags */ +@@ -1059,6 +1063,130 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + return 0; + } + ++/* Clean up result of nbd_receive_export_list */ ++void nbd_free_export_list(NBDExportInfo *info, int count) ++{ ++ int i; ++ ++ if (!info) { ++ return; ++ } ++ ++ for (i = 0; i < count; i++) { ++ g_free(info[i].name); ++ g_free(info[i].description); ++ } ++ 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, true, NULL, ++ errp); ++ if (tlscreds && sioc) { ++ ioc = sioc; ++ } ++ ++ switch (result) { ++ case 2: ++ case 3: ++ /* 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].structured_reply = result == 3; ++ } ++ ++ 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; ++ } ++ ++ /* TODO: Grab meta contexts */ ++ } ++ ++ /* Send NBD_OPT_ABORT as a courtesy before hanging up */ ++ nbd_send_opt_abort(ioc); ++ break; ++ case 1: /* newstyle, but limited to 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 0: /* oldstyle, parse length and flags */ ++ array = g_new0(NBDExportInfo, 1); ++ array->name = g_strdup(""); ++ 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 }; ++ ++ nbd_send_request(ioc, &request); ++ } ++ break; ++ default: ++ goto out; ++ } ++ ++ *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) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Add-x-dirty-bitmap-to-query-bitmap-from-s.patch b/SOURCES/kvm-nbd-client-Add-x-dirty-bitmap-to-query-bitmap-from-s.patch new file mode 100644 index 0000000..633eae4 --- /dev/null +++ b/SOURCES/kvm-nbd-client-Add-x-dirty-bitmap-to-query-bitmap-from-s.patch @@ -0,0 +1,174 @@ +From d52addb6a2ca32ce942f348ed47da26cd9524fbd Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:55:10 +0200 +Subject: [PATCH 85/89] nbd/client: Add x-dirty-bitmap to query bitmap from + server + +RH-Author: John Snow +Message-id: <20180718225511.14878-35-jsnow@redhat.com> +Patchwork-id: 81414 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 34/35] nbd/client: Add x-dirty-bitmap to query bitmap from server +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Eric Blake + +In order to test that the NBD server is properly advertising +dirty bitmaps, we need a bare minimum client that can request +and read the context. Since feature freeze for 3.0 is imminent, +this is the smallest workable patch, which replaces the qemu +block status report with the results of the NBD server's dirty +bitmap (making it very easy to use 'qemu-img map --output=json' +to learn where the dirty portions are). Note that the NBD +protocol defines a dirty section with the same bit but opposite +sense that normal "base:allocation" uses to report an allocated +section; so in qemu-img map output, "data":true corresponds to +clean, "data":false corresponds to dirty. + +A more complete solution that allows dirty bitmaps to be queried +at the same time as normal block status will be required before +this addition can lose the x- prefix. Until then, the fact that +this replaces normal status with dirty status means actions +like 'qemu-img convert' will likely misbehave due to treating +dirty regions of the file as if they are unallocated. + +The next patch adds an iotest to exercise this new code. + +Signed-off-by: Eric Blake +Message-Id: <20180702191458.28741-2-eblake@redhat.com> +(cherry picked from commit 216ee3657e14013505abe7853cecb632199fb13e) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 3 +++ + block/nbd-client.h | 1 + + block/nbd.c | 10 ++++++++-- + include/block/nbd.h | 1 + + nbd/client.c | 4 ++-- + qapi/block-core.json | 7 ++++++- + 6 files changed, 21 insertions(+), 5 deletions(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index e7caf49..98637c0 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -966,6 +966,7 @@ int nbd_client_init(BlockDriverState *bs, + const char *export, + QCryptoTLSCreds *tlscreds, + const char *hostname, ++ const char *x_dirty_bitmap, + Error **errp) + { + NBDClientSession *client = nbd_get_client_session(bs); +@@ -978,9 +979,11 @@ int nbd_client_init(BlockDriverState *bs, + client->info.request_sizes = true; + client->info.structured_reply = true; + client->info.base_allocation = true; ++ client->info.x_dirty_bitmap = g_strdup(x_dirty_bitmap); + ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export, + tlscreds, hostname, + &client->ioc, &client->info, errp); ++ g_free(client->info.x_dirty_bitmap); + if (ret < 0) { + logout("Failed to negotiate with the NBD server\n"); + return ret; +diff --git a/block/nbd-client.h b/block/nbd-client.h +index 0ece76e..cfc9055 100644 +--- a/block/nbd-client.h ++++ b/block/nbd-client.h +@@ -45,6 +45,7 @@ int nbd_client_init(BlockDriverState *bs, + const char *export_name, + QCryptoTLSCreds *tlscreds, + const char *hostname, ++ const char *x_dirty_bitmap, + Error **errp); + void nbd_client_close(BlockDriverState *bs); + +diff --git a/block/nbd.c b/block/nbd.c +index 10912c3..f29c10f 100644 +--- a/block/nbd.c ++++ b/block/nbd.c +@@ -378,6 +378,12 @@ static QemuOptsList nbd_runtime_opts = { + .type = QEMU_OPT_STRING, + .help = "ID of the TLS credentials to use", + }, ++ { ++ .name = "x-dirty-bitmap", ++ .type = QEMU_OPT_STRING, ++ .help = "experimental: expose named dirty bitmap in place of " ++ "block status", ++ }, + { /* end of list */ } + }, + }; +@@ -438,8 +444,8 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, + } + + /* NBD handshake */ +- ret = nbd_client_init(bs, sioc, s->export, +- tlscreds, hostname, errp); ++ ret = nbd_client_init(bs, sioc, s->export, tlscreds, hostname, ++ qemu_opt_get(opts, "x-dirty-bitmap"), errp); + error: + if (sioc) { + object_unref(OBJECT(sioc)); +diff --git a/include/block/nbd.h b/include/block/nbd.h +index daaeae6..4638c83 100644 +--- a/include/block/nbd.h ++++ b/include/block/nbd.h +@@ -259,6 +259,7 @@ static inline bool nbd_reply_type_is_error(int type) + struct NBDExportInfo { + /* Set by client before nbd_receive_negotiate() */ + bool request_sizes; ++ char *x_dirty_bitmap; + + /* In-out fields, set by client before nbd_receive_negotiate() and + * updated by server results during nbd_receive_negotiate() */ +diff --git a/nbd/client.c b/nbd/client.c +index b9e175d..25603f2 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -1,5 +1,5 @@ + /* +- * Copyright (C) 2016-2017 Red Hat, Inc. ++ * Copyright (C) 2016-2018 Red Hat, Inc. + * Copyright (C) 2005 Anthony Liguori + * + * Network Block Device Client Side +@@ -825,7 +825,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, + + if (info->structured_reply && base_allocation) { + result = nbd_negotiate_simple_meta_context( +- ioc, name, "base:allocation", ++ ioc, name, info->x_dirty_bitmap ?: "base:allocation", + &info->meta_base_allocation_id, errp); + if (result < 0) { + goto fail; +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 69e0cf8..8a00bec 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -3457,12 +3457,17 @@ + # + # @tls-creds: TLS credentials ID + # ++# @x-dirty-bitmap: A "qemu:dirty-bitmap:NAME" string to query in place of ++# traditional "base:allocation" block status (see ++# NBD_OPT_LIST_META_CONTEXT in the NBD protocol) (since 3.0) ++# + # Since: 2.9 + ## + { 'struct': 'BlockdevOptionsNbd', + 'data': { 'server': 'SocketAddress', + '*export': 'str', +- '*tls-creds': 'str' } } ++ '*tls-creds': 'str', ++ '*x-dirty-bitmap': 'str' } } + + ## + # @BlockdevOptionsRaw: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Change-signature-of-nbd_negotiate_simple_.patch b/SOURCES/kvm-nbd-client-Change-signature-of-nbd_negotiate_simple_.patch new file mode 100644 index 0000000..3c0f64a --- /dev/null +++ b/SOURCES/kvm-nbd-client-Change-signature-of-nbd_negotiate_simple_.patch @@ -0,0 +1,191 @@ +From 6b1be3a2c8f8eb10c46a4d98ada0347569284141 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:44 +0100 +Subject: [PATCH 106/163] nbd/client: Change signature of + nbd_negotiate_simple_meta_context() + +RH-Author: John Snow +Message-id: <20190327172308.31077-32-jsnow@redhat.com> +Patchwork-id: 85194 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 31/55] nbd/client: Change signature of nbd_negotiate_simple_meta_context() +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Pass 'info' instead of three separate parameters related to info, +when requesting the server to set the meta context. Update the +NBDExportInfo struct to rename the received id field to match the +fact that we are currently overloading the field to match whatever +context the user supplied through the x-dirty-bitmap hack, as well +as adding a TODO comment to remind future patches about a desire +to request two contexts at once. + +Signed-off-by: Eric Blake +Reviewed-by: Richard W.M. Jones +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20190117193658.16413-11-eblake@redhat.com> +(cherry picked from commit 2df94eb52b68d16f8a050bc28dd94a8c7d3366ec) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 4 ++-- + include/block/nbd.h | 2 +- + nbd/client.c | 53 +++++++++++++++++++++++++---------------------------- + 3 files changed, 28 insertions(+), 31 deletions(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index 3309376..8135396 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -249,11 +249,11 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client, + } + + context_id = payload_advance32(&payload); +- if (client->info.meta_base_allocation_id != context_id) { ++ if (client->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, +- client->info.meta_base_allocation_id); ++ client->info.context_id); + return -EINVAL; + } + +diff --git a/include/block/nbd.h b/include/block/nbd.h +index 00d3eb5..be19aac 100644 +--- a/include/block/nbd.h ++++ b/include/block/nbd.h +@@ -276,7 +276,7 @@ struct NBDExportInfo { + uint32_t opt_block; + uint32_t max_block; + +- uint32_t meta_base_allocation_id; ++ uint32_t context_id; + }; + typedef struct NBDExportInfo NBDExportInfo; + +diff --git a/nbd/client.c b/nbd/client.c +index 8227e69..7799389 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -630,26 +630,30 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc, + } + + /* nbd_negotiate_simple_meta_context: +- * Set one meta context. Simple means that reply must contain zero (not +- * negotiated) or one (negotiated) contexts. More contexts would be considered +- * as a protocol error. It's also implied that meta-data query equals queried +- * context name, so, if server replies with something different than @context, +- * it is considered an error too. +- * return 1 for successful negotiation, context_id is set ++ * 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, +- const char *export, +- const char *context, +- uint32_t *context_id, ++ 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; + NBDOptionReply reply; +- uint32_t received_id = 0; ++ const char *context = info->x_dirty_bitmap ?: "base:allocation"; + bool received = false; +- uint32_t export_len = strlen(export); ++ uint32_t export_len = strlen(info->name); + uint32_t context_len = strlen(context); + uint32_t data_len = sizeof(export_len) + export_len + + sizeof(uint32_t) + /* number of queries */ +@@ -657,9 +661,9 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, + char *data = g_malloc(data_len); + char *p = data; + +- trace_nbd_opt_meta_request(context, export); ++ trace_nbd_opt_meta_request(context, info->name); + stl_be_p(p, export_len); +- memcpy(p += sizeof(export_len), export, export_len); ++ memcpy(p += sizeof(export_len), info->name, export_len); + stl_be_p(p += export_len, 1); + stl_be_p(p += sizeof(uint32_t), context_len); + memcpy(p += sizeof(context_len), context, context_len); +@@ -685,7 +689,7 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, + if (reply.type == NBD_REP_META_CONTEXT) { + char *name; + +- if (reply.length != sizeof(received_id) + context_len) { ++ if (reply.length != sizeof(info->context_id) + context_len) { + error_setg(errp, "Failed to negotiate meta context '%s', server " + "answered with unexpected length %" PRIu32, context, + reply.length); +@@ -693,12 +697,13 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, + return -1; + } + +- if (nbd_read(ioc, &received_id, sizeof(received_id), errp) < 0) { ++ if (nbd_read(ioc, &info->context_id, sizeof(info->context_id), ++ errp) < 0) { + return -1; + } +- received_id = be32_to_cpu(received_id); ++ info->context_id = be32_to_cpu(info->context_id); + +- reply.length -= sizeof(received_id); ++ reply.length -= sizeof(info->context_id); + name = g_malloc(reply.length + 1); + if (nbd_read(ioc, name, reply.length, errp) < 0) { + g_free(name); +@@ -715,7 +720,7 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, + } + g_free(name); + +- trace_nbd_opt_meta_reply(context, received_id); ++ trace_nbd_opt_meta_reply(context, info->context_id); + received = true; + + /* receive NBD_REP_ACK */ +@@ -744,12 +749,7 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, + return -1; + } + +- if (received) { +- *context_id = received_id; +- return 1; +- } +- +- return 0; ++ return received; + } + + int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, +@@ -848,10 +848,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + } + + if (info->structured_reply && base_allocation) { +- result = nbd_negotiate_simple_meta_context( +- ioc, info->name, +- info->x_dirty_bitmap ?: "base:allocation", +- &info->meta_base_allocation_id, errp); ++ result = nbd_negotiate_simple_meta_context(ioc, info, errp); + if (result < 0) { + goto fail; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Drop-pointless-buf-variable.patch b/SOURCES/kvm-nbd-client-Drop-pointless-buf-variable.patch new file mode 100644 index 0000000..1942f7c --- /dev/null +++ b/SOURCES/kvm-nbd-client-Drop-pointless-buf-variable.patch @@ -0,0 +1,115 @@ +From 4f4e329628b6d530cf74d792f0297f651e18e6ba Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:24 +0100 +Subject: [PATCH 085/163] nbd/client: Drop pointless buf variable + +RH-Author: John Snow +Message-id: <20190327172308.31077-12-jsnow@redhat.com> +Patchwork-id: 85182 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 11/55] nbd/client: Drop pointless buf variable +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +There's no need to read into a temporary buffer (oversized +since commit 7d3123e1) followed by a byteswap into a uint64_t +to check for a magic number via memcmp(), when the code +immediately below demonstrates reading into the uint64_t then +byteswapping in place and checking for a magic number via +integer math. What's more, having a different error message +when the server's first reply byte is 0 is unusual - it's no +different from any other wrong magic number, and we already +detected short reads. That whole strlen() issue has been +present and useless since commit 1d45f8b5 in 2010; perhaps it +was leftover debugging (since the correct magic number happens +to be ASCII)? Make the error messages more consistent and +detailed while touching things. + +Signed-off-by: Eric Blake +Reviewed-by: Richard W.M. Jones +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20181215135324.152629-9-eblake@redhat.com> +(cherry picked from commit ef2e35fcc8e14bcc9366df5fdf53f65d679f8dca) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/client.c | 22 +++++++--------------- + nbd/nbd-internal.h | 3 ++- + 2 files changed, 9 insertions(+), 16 deletions(-) + +diff --git a/nbd/client.c b/nbd/client.c +index 5a03a84..f625c20 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -733,7 +733,6 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, + QIOChannel **outioc, NBDExportInfo *info, + Error **errp) + { +- char buf[256]; + uint64_t magic; + int rc; + bool zeroes = true; +@@ -754,27 +753,20 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, + goto fail; + } + +- if (nbd_read(ioc, buf, 8, errp) < 0) { +- error_prepend(errp, "Failed to read data: "); +- goto fail; +- } +- +- buf[8] = '\0'; +- if (strlen(buf) == 0) { +- error_setg(errp, "Server connection closed unexpectedly"); ++ if (nbd_read(ioc, &magic, sizeof(magic), errp) < 0) { ++ error_prepend(errp, "Failed to read initial magic: "); + goto fail; + } +- +- magic = ldq_be_p(buf); ++ magic = be64_to_cpu(magic); + trace_nbd_receive_negotiate_magic(magic); + +- if (memcmp(buf, "NBDMAGIC", 8) != 0) { +- error_setg(errp, "Invalid magic received"); ++ if (magic != NBD_INIT_MAGIC) { ++ error_setg(errp, "Bad initial magic received: 0x%" PRIx64, magic); + goto fail; + } + + if (nbd_read(ioc, &magic, sizeof(magic), errp) < 0) { +- error_prepend(errp, "Failed to read magic: "); ++ error_prepend(errp, "Failed to read server magic: "); + goto fail; + } + magic = be64_to_cpu(magic); +@@ -913,7 +905,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, + } + info->flags = oldflags; + } else { +- error_setg(errp, "Bad magic received"); ++ error_setg(errp, "Bad server magic received: 0x%" PRIx64, magic); + goto fail; + } + +diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h +index f38be9e..82aa221 100644 +--- a/nbd/nbd-internal.h ++++ b/nbd/nbd-internal.h +@@ -46,8 +46,9 @@ + /* Size of oldstyle negotiation */ + #define NBD_OLDSTYLE_NEGOTIATE_SIZE (8 + 8 + 8 + 4 + 124) + ++#define NBD_INIT_MAGIC 0x4e42444d41474943LL /* ASCII "NBDMAGIC" */ + #define NBD_REQUEST_MAGIC 0x25609513 +-#define NBD_OPTS_MAGIC 0x49484156454F5054LL ++#define NBD_OPTS_MAGIC 0x49484156454F5054LL /* ASCII "IHAVEOPT" */ + #define NBD_CLIENT_MAGIC 0x0000420281861253LL + #define NBD_REP_MAGIC 0x0003e889045565a9LL + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Fix-error-message-for-server-with-unusabl.patch b/SOURCES/kvm-nbd-client-Fix-error-message-for-server-with-unusabl.patch new file mode 100644 index 0000000..26b4fb2 --- /dev/null +++ b/SOURCES/kvm-nbd-client-Fix-error-message-for-server-with-unusabl.patch @@ -0,0 +1,48 @@ +From 4f02070b0278bfaebe0f9af1fcb5f2624693f57c Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 6 May 2019 17:56:28 +0200 +Subject: [PATCH 18/53] nbd/client: Fix error message for server with unusable + sizing + +RH-Author: John Snow +Message-id: <20190506175629.11079-19-jsnow@redhat.com> +Patchwork-id: 87188 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 18/19] nbd/client: Fix error message for server with unusable sizing +Bugzilla: 1692018 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Thomas Huth + +From: Eric Blake + +Add a missing space to the error message used when giving up on a +server that insists on an alignment which renders the last few bytes +of the export unreadable. + +Fixes: 3add3ab78 +Signed-off-by: Eric Blake +Message-Id: <20190404145226.32649-1-eblake@redhat.com> +Reviewed-by: Kevin Wolf +(cherry picked from commit e53f88df77e70350b0eda92a2e5e39f67792008f) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/client.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/nbd/client.c b/nbd/client.c +index 4309569..49754120 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -428,7 +428,7 @@ static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, + } + if (info->min_block && + !QEMU_IS_ALIGNED(info->size, info->min_block)) { +- error_setg(errp, "export size %" PRIu64 "is not multiple of " ++ error_setg(errp, "export size %" PRIu64 " is not multiple of " + "minimum block size %" PRIu32, info->size, + info->min_block); + nbd_send_opt_abort(ioc); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Fix-error-messages-during-NBD_INFO_BLOCK_.patch b/SOURCES/kvm-nbd-client-Fix-error-messages-during-NBD_INFO_BLOCK_.patch new file mode 100644 index 0000000..791c7e2 --- /dev/null +++ b/SOURCES/kvm-nbd-client-Fix-error-messages-during-NBD_INFO_BLOCK_.patch @@ -0,0 +1,75 @@ +From 1180606c147edadc4a7979baae377fb983dc5fd2 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:15 +0100 +Subject: [PATCH 048/163] nbd/client: Fix error messages during + NBD_INFO_BLOCK_SIZE + +RH-Author: John Snow +Message-id: <20190322032241.8111-3-jsnow@redhat.com> +Patchwork-id: 85091 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 02/28] nbd/client: Fix error messages during NBD_INFO_BLOCK_SIZE +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +A missing space makes for poor error messages, and sizes can't +go negative. Also, we missed diagnosing a server that sends +a maximum block size less than the minimum. + +Fixes: 081dd1fe +CC: qemu-stable@nongnu.org +Signed-off-by: Eric Blake +Message-Id: <20180501154654.943782-1-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit e475d108f1b3d3163f0affea67cdedbe5fc9752b) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/client.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/nbd/client.c b/nbd/client.c +index a151fa5..40b74d9 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -435,8 +435,8 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, + } + be32_to_cpus(&info->min_block); + if (!is_power_of_2(info->min_block)) { +- error_setg(errp, "server minimum block size %" PRId32 +- "is not a power of two", 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; + } +@@ -450,8 +450,8 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, + be32_to_cpus(&info->opt_block); + if (!is_power_of_2(info->opt_block) || + info->opt_block < info->min_block) { +- error_setg(errp, "server preferred block size %" PRId32 +- "is not valid", info->opt_block); ++ error_setg(errp, "server preferred block size %" PRIu32 ++ " is not valid", info->opt_block); + nbd_send_opt_abort(ioc); + return -1; + } +@@ -462,6 +462,12 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, + return -1; + } + be32_to_cpus(&info->max_block); ++ 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_go_info_block_size(info->min_block, info->opt_block, + info->max_block); + break; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Lower-min_block-for-block-status-unaligne.patch b/SOURCES/kvm-nbd-client-Lower-min_block-for-block-status-unaligne.patch new file mode 100644 index 0000000..3b7b50e --- /dev/null +++ b/SOURCES/kvm-nbd-client-Lower-min_block-for-block-status-unaligne.patch @@ -0,0 +1,119 @@ +From 7122c1ace9649d525da8670d5e57aaa8b7c6a686 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 6 May 2019 17:56:18 +0200 +Subject: [PATCH 08/53] nbd/client: Lower min_block for block-status, unaligned + size + +RH-Author: John Snow +Message-id: <20190506175629.11079-9-jsnow@redhat.com> +Patchwork-id: 87187 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 08/19] nbd/client: Lower min_block for block-status, unaligned size +Bugzilla: 1692018 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Thomas Huth + +From: Eric Blake + +We have a latent bug in our NBD client code, tickled by the brand new +nbdkit 1.11.10 block status support: + +$ nbdkit --filter=log --filter=truncate -U - \ + data data="1" size=511 truncate=64K logfile=/dev/stdout \ + --run 'qemu-img convert $nbd /var/tmp/out' +... +qemu-img: block/io.c:2122: bdrv_co_block_status: Assertion `*pnum && QEMU_IS_ALIGNED(*pnum, align) && align > offset - aligned_offset' failed. + +The culprit? Our implementation of .bdrv_co_block_status can return +unaligned block status for any server that operates with a lower +actual alignment than what we tell the block layer in +request_alignment, in violation of the block layer's constraints. To +date, we've been unable to trip the bug, because qemu as NBD server +always advertises block sizing (at which point it is a server bug if +the server sends unaligned status - although qemu 3.1 is such a server +and I've sent separate patches for 4.0 both to get the server to obey +the spec, and to let the client to tolerate server oddities at EOF). + +But nbdkit does not (yet) advertise block sizing, and therefore is not +in violation of the spec for returning block status at whatever +boundaries it wants, and those unaligned results can occur anywhere +rather than just at EOF. While we are still wise to avoid sending +sub-sector read/write requests to a server of unknown origin, we MUST +consider that a server telling us block status without an advertised +block size is correct. So, we either have to munge unaligned answers +from the server into aligned ones that we hand back to the block +layer, or we have to tell the block layer about a smaller alignment. + +Similarly, if the server advertises an image size that is not +sector-aligned, we might as well assume that the server intends to let +us access those tail bytes, and therefore supports a minimum block +size of 1, regardless of whether the server supports block status +(although we still need more patches to fix the problem that with an +unaligned image, we can send read or block status requests that exceed +EOF to the server). Again, qemu as server cannot trip this problem +(because it rounds images to sector alignment), but nbdkit advertised +unaligned size even before it gained block status support. + +Solve both alignment problems at once by using better heuristics on +what alignment to report to the block layer when the server did not +give us something to work with. Note that very few NBD servers +implement block status (to date, only qemu and nbdkit are known to do +so); and as the NBD spec mentioned block sizing constraints prior to +documenting block status, it can be assumed that any future +implementations of block status are aware that they must advertise +block size if they want a minimum size other than 1. + +We've had a long history of struggles with picking the right alignment +to use in the block layer, as evidenced by the commit message of +fd8d372d (v2.12) that introduced the current choice of forced 512-byte +alignment. + +There is no iotest coverage for this fix, because qemu can't provoke +it, and I didn't want to make test 241 dependent on nbdkit. + +Fixes: fd8d372d +Reported-by: Richard W.M. Jones +Signed-off-by: Eric Blake +Message-Id: <20190329042750.14704-3-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +Tested-by: Richard W.M. Jones +(cherry picked from commit 7da537f70d929800ba9c657b8a47a7b827695ccc) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/nbd.c | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +diff --git a/block/nbd.c b/block/nbd.c +index 838a8fe..670b9bd 100644 +--- a/block/nbd.c ++++ b/block/nbd.c +@@ -437,7 +437,24 @@ static void nbd_refresh_limits(BlockDriverState *bs, Error **errp) + uint32_t min = s->info.min_block; + uint32_t max = MIN_NON_ZERO(NBD_MAX_BUFFER_SIZE, s->info.max_block); + +- bs->bl.request_alignment = min ? min : BDRV_SECTOR_SIZE; ++ /* ++ * 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 = max; + bs->bl.max_pwrite_zeroes = max; + bs->bl.max_transfer = max; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Make-x-dirty-bitmap-more-reliable.patch b/SOURCES/kvm-nbd-client-Make-x-dirty-bitmap-more-reliable.patch new file mode 100644 index 0000000..17cfd49 --- /dev/null +++ b/SOURCES/kvm-nbd-client-Make-x-dirty-bitmap-more-reliable.patch @@ -0,0 +1,57 @@ +From 4a378f730e73452bf9bf3a5c761f7d0efdae9d27 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:39 +0100 +Subject: [PATCH 072/163] nbd/client: Make x-dirty-bitmap more reliable + +RH-Author: John Snow +Message-id: <20190322032241.8111-27-jsnow@redhat.com> +Patchwork-id: 85106 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 26/28] nbd/client: Make x-dirty-bitmap more reliable +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +The implementation of x-dirty-bitmap in qemu 3.0 (commit 216ee365) +silently falls back to treating the server as not supporting +NBD_CMD_BLOCK_STATUS if a requested meta_context name was not +negotiated, which in turn means treating the _entire_ image as +data. Since our hack relied on using 'qemu-img map' to view +which portions of the image were dirty by seeing what the +redirected bdrv_block_status() treats as holes, this means +that our fallback treats the entire image as clean. Better +would have been to treat the entire image as dirty, or to fail +to connect because the user's request for a specific context +could not be honored. This patch goes with the latter. + +Signed-off-by: Eric Blake +Message-Id: <20181130023232.3079982-3-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit 47829c40794160debdb33b4a042d182e776876d4) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index 76e9ca3..e6e27da 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -992,6 +992,11 @@ int nbd_client_init(BlockDriverState *bs, + logout("Failed to negotiate with the NBD server\n"); + return ret; + } ++ if (x_dirty_bitmap && !client->info.base_allocation) { ++ error_setg(errp, "requested x-dirty-bitmap %s not found", ++ x_dirty_bitmap); ++ return -EINVAL; ++ } + if (client->info.flags & NBD_FLAG_READ_ONLY) { + ret = bdrv_apply_auto_read_only(bs, "NBD export is read-only", errp); + if (ret < 0) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-More-consistent-error-messages.patch b/SOURCES/kvm-nbd-client-More-consistent-error-messages.patch new file mode 100644 index 0000000..d720bb5 --- /dev/null +++ b/SOURCES/kvm-nbd-client-More-consistent-error-messages.patch @@ -0,0 +1,89 @@ +From 1ff1deba5e09af78b2fe8095fc0472dfd0051f1c Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:22 +0100 +Subject: [PATCH 083/163] nbd/client: More consistent error messages + +RH-Author: John Snow +Message-id: <20190327172308.31077-10-jsnow@redhat.com> +Patchwork-id: 85185 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 09/55] nbd/client: More consistent error messages +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Consolidate on using decimal (not hex), on outputting the +option reply name (not just value), and a consistent comma between +clauses, when the client reports protocol discrepancies from the +server. While it won't affect normal operation, it makes +debugging additions easier. + +Signed-off-by: Eric Blake +Reviewed-by: Richard W.M. Jones +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20181215135324.152629-6-eblake@redhat.com> +(cherry picked from commit 6c5c035138218a384a229e7b6b9cf51451621c6a) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/client.c | 21 ++++++++++++--------- + 1 file changed, 12 insertions(+), 9 deletions(-) + +diff --git a/nbd/client.c b/nbd/client.c +index 0ad7147..e774147 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -132,8 +132,9 @@ static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt, + return -1; + } + if (reply->option != opt) { +- error_setg(errp, "Unexpected option type %x expected %x", +- 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; + } +@@ -267,8 +268,9 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match, + } + return 0; + } else if (reply.type != NBD_REP_SERVER) { +- error_setg(errp, "Unexpected reply type %" PRIx32 " expected %x", +- 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; + } +@@ -380,9 +382,9 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, + return 1; + } + if (reply.type != NBD_REP_INFO) { +- error_setg(errp, "unexpected reply type %" PRIu32 +- " (%s), expected %u", +- reply.type, nbd_rep_lookup(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; + } +@@ -706,8 +708,9 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, + } + + if (reply.type != NBD_REP_ACK) { +- error_setg(errp, "Unexpected reply type %" PRIx32 " expected %x", +- reply.type, NBD_REP_ACK); ++ error_setg(errp, "Unexpected reply type %u (%s), expected %u (%s)", ++ reply.type, nbd_rep_lookup(reply.type), ++ NBD_REP_ACK, nbd_rep_lookup(NBD_REP_ACK)); + nbd_send_opt_abort(ioc); + return -1; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Move-export-name-into-NBDExportInfo.patch b/SOURCES/kvm-nbd-client-Move-export-name-into-NBDExportInfo.patch new file mode 100644 index 0000000..fab3d52 --- /dev/null +++ b/SOURCES/kvm-nbd-client-Move-export-name-into-NBDExportInfo.patch @@ -0,0 +1,257 @@ +From 0736b0b842bcd0acefed2523d2e0cda3876ca469 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:43 +0100 +Subject: [PATCH 105/163] nbd/client: Move export name into NBDExportInfo + +RH-Author: John Snow +Message-id: <20190327172308.31077-31-jsnow@redhat.com> +Patchwork-id: 85203 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 30/55] nbd/client: Move export name into NBDExportInfo +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Refactor the 'name' parameter of nbd_receive_negotiate() from +being a separate parameter into being part of the in-out 'info'. +This also spills over to a simplification of nbd_opt_go(). + +The main driver for this refactoring is that an upcoming patch +would like to add support to qemu-nbd to list information about +all exports available on a server, where the name(s) will be +provided by the server instead of the client. But another benefit +is that we can now allow the client to explicitly specify the +empty export name "" even when connecting to an oldstyle server +(even if qemu is no longer such a server after commit 7f7dfe2a). + +Signed-off-by: Eric Blake +Reviewed-by: Richard W.M. Jones +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20190117193658.16413-10-eblake@redhat.com> +(cherry picked from commit 6dc1667d6881add34e9bad48ac2a848134ea8a6d) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 5 +++-- + include/block/nbd.h | 8 ++++---- + nbd/client.c | 39 ++++++++++++++++++--------------------- + nbd/trace-events | 2 +- + qemu-nbd.c | 6 ++++-- + 5 files changed, 30 insertions(+), 30 deletions(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index ef32075..3309376 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -999,10 +999,11 @@ int nbd_client_init(BlockDriverState *bs, + client->info.structured_reply = true; + client->info.base_allocation = true; + client->info.x_dirty_bitmap = g_strdup(x_dirty_bitmap); +- ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export, +- tlscreds, hostname, ++ client->info.name = g_strdup(export ?: ""); ++ ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), tlscreds, hostname, + &client->ioc, &client->info, errp); + g_free(client->info.x_dirty_bitmap); ++ g_free(client->info.name); + if (ret < 0) { + logout("Failed to negotiate with the NBD server\n"); + return ret; +diff --git a/include/block/nbd.h b/include/block/nbd.h +index 24be957..00d3eb5 100644 +--- a/include/block/nbd.h ++++ b/include/block/nbd.h +@@ -262,6 +262,7 @@ struct NBDExportInfo { + /* Set by client before nbd_receive_negotiate() */ + bool request_sizes; + char *x_dirty_bitmap; ++ 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() */ +@@ -279,10 +280,9 @@ struct NBDExportInfo { + }; + typedef struct NBDExportInfo NBDExportInfo; + +-int nbd_receive_negotiate(QIOChannel *ioc, const char *name, +- QCryptoTLSCreds *tlscreds, const char *hostname, +- QIOChannel **outioc, NBDExportInfo *info, +- Error **errp); ++int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, ++ const char *hostname, QIOChannel **outioc, ++ NBDExportInfo *info, Error **errp); + int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, + Error **errp); + int nbd_send_request(QIOChannel *ioc, NBDRequest *request); +diff --git a/nbd/client.c b/nbd/client.c +index fd4ba8d..8227e69 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -330,15 +330,14 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, char **description, + } + + +-/* Returns -1 if NBD_OPT_GO proves the export @wantname cannot be ++/* Returns -1 if NBD_OPT_GO proves the export @info->name cannot be + * used, 0 if NBD_OPT_GO 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 @info populated). */ +-static int nbd_opt_go(QIOChannel *ioc, const char *wantname, +- NBDExportInfo *info, Error **errp) ++ * go (with the rest of @info populated). */ ++static int nbd_opt_go(QIOChannel *ioc, NBDExportInfo *info, Error **errp) + { + NBDOptionReply reply; +- uint32_t len = strlen(wantname); ++ uint32_t len = strlen(info->name); + uint16_t type; + int error; + char *buf; +@@ -348,10 +347,10 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, + * flags still 0 is a witness of a broken server. */ + info->flags = 0; + +- trace_nbd_opt_go_start(wantname); ++ trace_nbd_opt_go_start(info->name); + buf = g_malloc(4 + len + 2 + 2 * info->request_sizes + 1); + stl_be_p(buf, len); +- memcpy(buf + 4, wantname, 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) { +@@ -753,10 +752,9 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, + return 0; + } + +-int nbd_receive_negotiate(QIOChannel *ioc, const char *name, +- QCryptoTLSCreds *tlscreds, const char *hostname, +- QIOChannel **outioc, NBDExportInfo *info, +- Error **errp) ++int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, ++ const char *hostname, QIOChannel **outioc, ++ NBDExportInfo *info, Error **errp) + { + uint64_t magic; + int rc; +@@ -766,6 +764,8 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, + + trace_nbd_receive_negotiate(tlscreds, hostname ? hostname : ""); + ++ assert(info->name); ++ trace_nbd_receive_negotiate_name(info->name); + info->structured_reply = false; + info->base_allocation = false; + rc = -EINVAL; +@@ -834,10 +834,6 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, + goto fail; + } + } +- if (!name) { +- trace_nbd_receive_negotiate_default_name(); +- name = ""; +- } + if (fixedNewStyle) { + int result; + +@@ -853,7 +849,8 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, + + if (info->structured_reply && base_allocation) { + result = nbd_negotiate_simple_meta_context( +- ioc, name, info->x_dirty_bitmap ?: "base:allocation", ++ ioc, info->name, ++ info->x_dirty_bitmap ?: "base:allocation", + &info->meta_base_allocation_id, errp); + if (result < 0) { + goto fail; +@@ -866,7 +863,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, + * 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_go(ioc, name, info, errp); ++ result = nbd_opt_go(ioc, info, errp); + if (result < 0) { + goto fail; + } +@@ -879,12 +876,12 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, + * query gives us better error reporting if the + * export name is not available. + */ +- if (nbd_receive_query_exports(ioc, name, errp) < 0) { ++ if (nbd_receive_query_exports(ioc, info->name, errp) < 0) { + goto fail; + } + } + /* write the export name request */ +- if (nbd_send_option_request(ioc, NBD_OPT_EXPORT_NAME, -1, name, ++ if (nbd_send_option_request(ioc, NBD_OPT_EXPORT_NAME, -1, info->name, + errp) < 0) { + goto fail; + } +@@ -904,8 +901,8 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, + } else if (magic == NBD_CLIENT_MAGIC) { + uint32_t oldflags; + +- if (name) { +- error_setg(errp, "Server does not support export names"); ++ if (*info->name) { ++ error_setg(errp, "Server does not support non-empty export names"); + goto fail; + } + if (tlscreds) { +diff --git a/nbd/trace-events b/nbd/trace-events +index d1e1ca6..c3966d2 100644 +--- a/nbd/trace-events ++++ b/nbd/trace-events +@@ -17,7 +17,7 @@ nbd_opt_meta_reply(const char *context, uint32_t id) "Received mapping of contex + nbd_receive_negotiate(void *tlscreds, const char *hostname) "Receiving negotiation tlscreds=%p hostname=%s" + nbd_receive_negotiate_magic(uint64_t magic) "Magic is 0x%" PRIx64 + nbd_receive_negotiate_server_flags(uint32_t globalflags) "Global flags are 0x%" PRIx32 +-nbd_receive_negotiate_default_name(void) "Using default NBD export name \"\"" ++nbd_receive_negotiate_name(const char *name) "Requesting NBD export name '%s'" + nbd_receive_negotiate_size_flags(uint64_t size, uint16_t flags) "Size is %" PRIu64 ", export flags 0x%" PRIx16 + nbd_init_set_socket(void) "Setting NBD socket" + nbd_init_set_block_size(unsigned long block_size) "Setting block size to %lu" +diff --git a/qemu-nbd.c b/qemu-nbd.c +index efca0e4..3c53870 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -264,7 +264,7 @@ static void *show_parts(void *arg) + static void *nbd_client_thread(void *arg) + { + char *device = arg; +- NBDExportInfo info = { .request_sizes = false, }; ++ NBDExportInfo info = { .request_sizes = false, .name = g_strdup("") }; + QIOChannelSocket *sioc; + int fd; + int ret; +@@ -279,7 +279,7 @@ static void *nbd_client_thread(void *arg) + goto out; + } + +- ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, ++ ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), + NULL, NULL, NULL, &info, &local_error); + if (ret < 0) { + if (local_error) { +@@ -318,6 +318,7 @@ static void *nbd_client_thread(void *arg) + } + close(fd); + object_unref(OBJECT(sioc)); ++ g_free(info.name); + kill(getpid(), SIGTERM); + return (void *) EXIT_SUCCESS; + +@@ -326,6 +327,7 @@ out_fd: + out_socket: + object_unref(OBJECT(sioc)); + out: ++ g_free(info.name); + kill(getpid(), SIGTERM); + return (void *) EXIT_FAILURE; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Pull-out-oldstyle-size-determination.patch b/SOURCES/kvm-nbd-client-Pull-out-oldstyle-size-determination.patch new file mode 100644 index 0000000..b59ae4d --- /dev/null +++ b/SOURCES/kvm-nbd-client-Pull-out-oldstyle-size-determination.patch @@ -0,0 +1,116 @@ +From 4b3d650918d54c4034e0162421410c1a076a057b Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:49 +0100 +Subject: [PATCH 111/163] nbd/client: Pull out oldstyle size determination + +RH-Author: John Snow +Message-id: <20190327172308.31077-37-jsnow@redhat.com> +Patchwork-id: 85197 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 36/55] nbd/client: Pull out oldstyle size determination +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Another refactoring creating nbd_negotiate_finish_oldstyle() +for further reuse during 'qemu-nbd --list'. + +Signed-off-by: Eric Blake +Reviewed-by: Richard W.M. Jones +Message-Id: <20190117193658.16413-16-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit b3c9d33bc4e7c2ab2828b16ebc96f0414b0f1acf) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/client.c | 49 ++++++++++++++++++++++++++++++++----------------- + 1 file changed, 32 insertions(+), 17 deletions(-) + +diff --git a/nbd/client.c b/nbd/client.c +index 77cc123..6829c68 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -814,7 +814,7 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, + * 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 +- * 0: server is oldstyle, client must still parse export size ++ * 0: server is oldstyle, must call nbd_negotiate_finish_oldstyle + * 1: server is newstyle, but can only accept EXPORT_NAME + * 2: server is newstyle, but lacks structured replies + * 3: server is newstyle and set up for structured replies +@@ -921,6 +921,36 @@ static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + } + + /* ++ * 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_read(ioc, &info->size, sizeof(info->size), errp) < 0) { ++ error_prepend(errp, "Failed to read export length: "); ++ return -EINVAL; ++ } ++ info->size = be64_to_cpu(info->size); ++ ++ if (nbd_read(ioc, &oldflags, sizeof(oldflags), errp) < 0) { ++ error_prepend(errp, "Failed to read export flags: "); ++ return -EINVAL; ++ } ++ oldflags = be32_to_cpu(oldflags); ++ 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 +@@ -933,7 +963,6 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + int result; + bool zeroes; + bool base_allocation = info->base_allocation; +- uint32_t oldflags; + + assert(info->name); + trace_nbd_receive_negotiate_name(info->name); +@@ -1006,23 +1035,9 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + error_setg(errp, "Server does not support non-empty export names"); + return -EINVAL; + } +- +- if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) { +- error_prepend(errp, "Failed to read export length: "); +- return -EINVAL; +- } +- info->size = be64_to_cpu(info->size); +- +- if (nbd_read(ioc, &oldflags, sizeof(oldflags), errp) < 0) { +- error_prepend(errp, "Failed to read export flags: "); +- return -EINVAL; +- } +- oldflags = be32_to_cpu(oldflags); +- if (oldflags & ~0xffff) { +- error_setg(errp, "Unexpected export flags %0x" PRIx32, oldflags); ++ if (nbd_negotiate_finish_oldstyle(ioc, info, errp) < 0) { + return -EINVAL; + } +- info->flags = oldflags; + break; + default: + return result; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Refactor-nbd_opt_go-to-support-NBD_OPT_IN.patch b/SOURCES/kvm-nbd-client-Refactor-nbd_opt_go-to-support-NBD_OPT_IN.patch new file mode 100644 index 0000000..1799d93 --- /dev/null +++ b/SOURCES/kvm-nbd-client-Refactor-nbd_opt_go-to-support-NBD_OPT_IN.patch @@ -0,0 +1,154 @@ +From 265ca77d8f1c82a680789bdb26fc7992077de567 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:50 +0100 +Subject: [PATCH 112/163] nbd/client: Refactor nbd_opt_go() to support + NBD_OPT_INFO + +RH-Author: John Snow +Message-id: <20190327172308.31077-38-jsnow@redhat.com> +Patchwork-id: 85198 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 37/55] nbd/client: Refactor nbd_opt_go() to support NBD_OPT_INFO +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Rename the function to nbd_opt_info_or_go() with an added parameter +and slight changes to comments and trace messages, in order to +reuse the function for NBD_OPT_INFO. + +Signed-off-by: Eric Blake +Message-Id: <20190117193658.16413-17-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit 138796d0f545ad4b6c74ad2cbe5f6e08c454a0b9) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/client.c | 36 ++++++++++++++++++++++-------------- + nbd/trace-events | 8 ++++---- + 2 files changed, 26 insertions(+), 18 deletions(-) + +diff --git a/nbd/client.c b/nbd/client.c +index 6829c68..fa1657a 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -330,11 +330,16 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, char **description, + } + + +-/* Returns -1 if NBD_OPT_GO proves the export @info->name cannot be +- * used, 0 if NBD_OPT_GO is unsupported (fall back to NBD_OPT_LIST and ++/* ++ * 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_go(QIOChannel *ioc, NBDExportInfo *info, Error **errp) ++ * go (with the rest of @info populated). ++ */ ++static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, ++ NBDExportInfo *info, Error **errp) + { + NBDOptionReply reply; + uint32_t len = strlen(info->name); +@@ -347,7 +352,8 @@ static int nbd_opt_go(QIOChannel *ioc, NBDExportInfo *info, Error **errp) + * flags still 0 is a witness of a broken server. */ + info->flags = 0; + +- trace_nbd_opt_go_start(info->name); ++ 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); +@@ -356,7 +362,7 @@ static int nbd_opt_go(QIOChannel *ioc, NBDExportInfo *info, Error **errp) + if (info->request_sizes) { + stw_be_p(buf + 4 + len + 2, NBD_INFO_BLOCK_SIZE); + } +- error = nbd_send_option_request(ioc, NBD_OPT_GO, ++ error = nbd_send_option_request(ioc, opt, + 4 + len + 2 + 2 * info->request_sizes, + buf, errp); + g_free(buf); +@@ -365,7 +371,7 @@ static int nbd_opt_go(QIOChannel *ioc, NBDExportInfo *info, Error **errp) + } + + while (1) { +- if (nbd_receive_option_reply(ioc, NBD_OPT_GO, &reply, errp) < 0) { ++ if (nbd_receive_option_reply(ioc, opt, &reply, errp) < 0) { + return -1; + } + error = nbd_handle_reply_err(ioc, &reply, errp); +@@ -375,8 +381,10 @@ static int nbd_opt_go(QIOChannel *ioc, NBDExportInfo *info, Error **errp) + len = reply.length; + + if (reply.type == NBD_REP_ACK) { +- /* Server is done sending info and moved into transmission +- phase, but make sure it sent flags */ ++ /* ++ * 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; +@@ -385,7 +393,7 @@ static int nbd_opt_go(QIOChannel *ioc, NBDExportInfo *info, Error **errp) + error_setg(errp, "broken server omitted NBD_INFO_EXPORT"); + return -1; + } +- trace_nbd_opt_go_success(); ++ trace_nbd_opt_info_go_success(nbd_opt_lookup(opt)); + return 1; + } + if (reply.type != NBD_REP_INFO) { +@@ -479,12 +487,12 @@ static int nbd_opt_go(QIOChannel *ioc, NBDExportInfo *info, Error **errp) + nbd_send_opt_abort(ioc); + return -1; + } +- trace_nbd_opt_go_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: +- trace_nbd_opt_go_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); +@@ -993,7 +1001,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + * 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_go(ioc, info, errp); ++ result = nbd_opt_info_or_go(ioc, NBD_OPT_GO, info, errp); + if (result < 0) { + return -EINVAL; + } +diff --git a/nbd/trace-events b/nbd/trace-events +index 663d116..7f10ebd 100644 +--- a/nbd/trace-events ++++ b/nbd/trace-events +@@ -4,10 +4,10 @@ nbd_receive_option_reply(uint32_t option, const char *optname, uint32_t type, co + nbd_server_error_msg(uint32_t err, const char *type, const char *msg) "server reported error 0x%" PRIx32 " (%s) with additional message: %s" + nbd_reply_err_unsup(uint32_t option, const char *name) "server doesn't understand request %" PRIu32 " (%s), attempting fallback" + nbd_receive_list(const char *name, const char *desc) "export list includes '%s', description '%s'" +-nbd_opt_go_start(const char *name) "Attempting NBD_OPT_GO for export '%s'" +-nbd_opt_go_success(void) "Export is good to go" +-nbd_opt_go_info_unknown(int info, const char *name) "Ignoring unknown info %d (%s)" +-nbd_opt_go_info_block_size(uint32_t minimum, uint32_t preferred, uint32_t maximum) "Block sizes are 0x%" PRIx32 ", 0x%" PRIx32 ", 0x%" PRIx32 ++nbd_opt_info_go_start(const char *opt, const char *name) "Attempting %s for export '%s'" ++nbd_opt_info_go_success(const char *opt) "Export is ready after %s request" ++nbd_opt_info_unknown(int info, const char *name) "Ignoring unknown info %d (%s)" ++nbd_opt_info_block_size(uint32_t minimum, uint32_t preferred, uint32_t maximum) "Block sizes are 0x%" PRIx32 ", 0x%" PRIx32 ", 0x%" PRIx32 + nbd_receive_query_exports_start(const char *wantname) "Querying export list for '%s'" + nbd_receive_query_exports_success(const char *wantname) "Found desired export name '%s'" + nbd_receive_starttls_new_client(void) "Setting up TLS" +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Refactor-nbd_receive_list.patch b/SOURCES/kvm-nbd-client-Refactor-nbd_receive_list.patch new file mode 100644 index 0000000..373b956 --- /dev/null +++ b/SOURCES/kvm-nbd-client-Refactor-nbd_receive_list.patch @@ -0,0 +1,240 @@ +From ff100c505e2a1cdc0a405008513948caa935b6cb Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:42 +0100 +Subject: [PATCH 104/163] nbd/client: Refactor nbd_receive_list() + +RH-Author: John Snow +Message-id: <20190327172308.31077-30-jsnow@redhat.com> +Patchwork-id: 85210 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 29/55] nbd/client: Refactor nbd_receive_list() +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Right now, nbd_receive_list() is only called by +nbd_receive_query_exports(), which in turn is only called if the +server lacks NBD_OPT_GO but has working option negotiation, and is +merely used as a quality-of-implementation trick since servers +can't give decent errors for NBD_OPT_EXPORT_NAME. However, servers +that lack NBD_OPT_GO are becoming increasingly rare (nbdkit was a +latecomer, in Aug 2018, but qemu has been such a server since commit +f37708f6 in July 2017 and released in 2.10), so it no longer makes +sense to micro-optimize that function for performance. + +Furthermore, when debugging a server's implementation, tracing the +full reply (both names and descriptions) is useful, not to mention +that upcoming patches adding 'qemu-nbd --list' will want to collect +that data. And when you consider that a server can send an export +name up to the NBD protocol length limit of 4k; but our current +NBD_MAX_NAME_SIZE is only 256, we can't trace all valid server +names without more storage, but 4k is large enough that the heap +is better than the stack for long names. + +Thus, I'm changing the division of labor, with nbd_receive_list() +now always malloc'ing a result on success (the malloc is bounded +by the fact that we reject servers with a reply length larger +than 32M), and moving the comparison to 'wantname' to the caller. + +There is a minor change in behavior where a server with 0 exports +(an immediate NBD_REP_ACK reply) is now no longer distinguished +from a server without LIST support (NBD_REP_ERR_UNSUP); this +information could be preserved with a complication to the calling +contract to provide a bit more information, but I didn't see the +point. After all, the worst that can happen if our guess at a +match is wrong is that the caller will get a cryptic disconnect +when NBD_OPT_EXPORT_NAME fails (which is no different from what +would happen if we had not tried LIST), while treating an empty +list as immediate failure would prevent connecting to really old +servers that really did lack LIST. Besides, NBD servers with 0 +exports are rare (qemu can do it when using QMP nbd-server-start +without nbd-server-add - but qemu understands NBD_OPT_GO and +thus won't tickle this change in behavior). + +Fix the spelling of foundExport to match coding standards while +in the area. + +Signed-off-by: Eric Blake +Reviewed-by: Richard W.M. Jones +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20190117193658.16413-9-eblake@redhat.com> +(cherry picked from commit 091d0bf3c94737fc451a9b3f4eddf3a4d74c90b8) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/client.c | 91 ++++++++++++++++++++++++++++++++++++-------------------- + nbd/trace-events | 1 + + 2 files changed, 59 insertions(+), 33 deletions(-) + +diff --git a/nbd/client.c b/nbd/client.c +index f625c20..fd4ba8d 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -234,18 +234,24 @@ static int nbd_handle_reply_err(QIOChannel *ioc, NBDOptionReply *reply, + return result; + } + +-/* Process another portion of the NBD_OPT_LIST reply. Set *@match if +- * the current reply matches @want or if the server does not support +- * NBD_OPT_LIST, otherwise leave @match alone. Return 0 if iteration +- * is complete, positive if more replies are expected, or negative +- * with @errp set if an unrecoverable error occurred. */ +-static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match, ++/* 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) + { ++ int ret = -1; + NBDOptionReply reply; + uint32_t len; + uint32_t namelen; +- char name[NBD_MAX_NAME_SIZE + 1]; ++ char *local_name = NULL; ++ char *local_desc = NULL; + int error; + + if (nbd_receive_option_reply(ioc, NBD_OPT_LIST, &reply, errp) < 0) { +@@ -253,9 +259,6 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match, + } + error = nbd_handle_reply_err(ioc, &reply, errp); + if (error <= 0) { +- /* The server did not support NBD_OPT_LIST, so set *match on +- * the assumption that any name will be accepted. */ +- *match = true; + return error; + } + len = reply.length; +@@ -292,33 +295,38 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match, + nbd_send_opt_abort(ioc); + return -1; + } +- if (namelen != strlen(want)) { +- if (nbd_drop(ioc, len, errp) < 0) { +- error_prepend(errp, +- "failed to skip export name with wrong length: "); +- nbd_send_opt_abort(ioc); +- return -1; +- } +- return 1; +- } + +- assert(namelen < sizeof(name)); +- if (nbd_read(ioc, name, namelen, errp) < 0) { ++ local_name = g_malloc(namelen + 1); ++ if (nbd_read(ioc, local_name, namelen, errp) < 0) { + error_prepend(errp, "failed to read export name: "); + nbd_send_opt_abort(ioc); +- return -1; ++ goto out; + } +- name[namelen] = '\0'; ++ local_name[namelen] = '\0'; + len -= namelen; +- if (nbd_drop(ioc, len, errp) < 0) { +- error_prepend(errp, "failed to read export description: "); +- nbd_send_opt_abort(ioc); +- return -1; ++ if (len) { ++ local_desc = g_malloc(len + 1); ++ if (nbd_read(ioc, local_desc, len, errp) < 0) { ++ error_prepend(errp, "failed to read export description: "); ++ nbd_send_opt_abort(ioc); ++ goto out; ++ } ++ local_desc[len] = '\0'; + } +- if (!strcmp(name, want)) { +- *match = true; ++ ++ trace_nbd_receive_list(local_name, local_desc ?: ""); ++ *name = local_name; ++ local_name = NULL; ++ if (description) { ++ *description = local_desc; ++ local_desc = NULL; + } +- return 1; ++ ret = 1; ++ ++ out: ++ g_free(local_name); ++ g_free(local_desc); ++ return ret; + } + + +@@ -493,7 +501,8 @@ static int nbd_receive_query_exports(QIOChannel *ioc, + const char *wantname, + Error **errp) + { +- bool foundExport = false; ++ 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) { +@@ -501,14 +510,25 @@ static int nbd_receive_query_exports(QIOChannel *ioc, + } + + while (1) { +- int ret = nbd_receive_list(ioc, wantname, &foundExport, errp); ++ 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 (!foundExport) { ++ 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); +@@ -517,6 +537,11 @@ static int nbd_receive_query_exports(QIOChannel *ioc, + trace_nbd_receive_query_exports_success(wantname); + return 0; + } ++ list_empty = false; ++ if (!strcmp(name, wantname)) { ++ found_export = true; ++ } ++ g_free(name); + } + } + +diff --git a/nbd/trace-events b/nbd/trace-events +index 5492042..d1e1ca6 100644 +--- a/nbd/trace-events ++++ b/nbd/trace-events +@@ -3,6 +3,7 @@ nbd_send_option_request(uint32_t opt, const char *name, uint32_t len) "Sending o + nbd_receive_option_reply(uint32_t option, const char *optname, uint32_t type, const char *typename, uint32_t length) "Received option reply %" PRIu32" (%s), type %" PRIu32" (%s), len %" PRIu32 + nbd_server_error_msg(uint32_t err, const char *type, const char *msg) "server reported error 0x%" PRIx32 " (%s) with additional message: %s" + nbd_reply_err_unsup(uint32_t option, const char *name) "server doesn't understand request %" PRIu32 " (%s), attempting fallback" ++nbd_receive_list(const char *name, const char *desc) "export list includes '%s', description '%s'" + nbd_opt_go_start(const char *name) "Attempting NBD_OPT_GO for export '%s'" + nbd_opt_go_success(void) "Export is good to go" + nbd_opt_go_info_unknown(int info, const char *name) "Ignoring unknown info %d (%s)" +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Refactor-return-of-nbd_receive_negotiate.patch b/SOURCES/kvm-nbd-client-Refactor-return-of-nbd_receive_negotiate.patch new file mode 100644 index 0000000..f93ad79 --- /dev/null +++ b/SOURCES/kvm-nbd-client-Refactor-return-of-nbd_receive_negotiate.patch @@ -0,0 +1,222 @@ +From c1211f220981f1846895caead20e5aa5ae58ab5e Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:47 +0100 +Subject: [PATCH 109/163] nbd/client: Refactor return of + nbd_receive_negotiate() + +RH-Author: John Snow +Message-id: <20190327172308.31077-35-jsnow@redhat.com> +Patchwork-id: 85196 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 34/55] nbd/client: Refactor return of nbd_receive_negotiate() +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +The function could only ever return 0 or -EINVAL; make this +clearer by dropping a useless 'fail:' label. + +Signed-off-by: Eric Blake +Reviewed-by: Richard W.M. Jones +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20190117193658.16413-14-eblake@redhat.com> +(cherry picked from commit 2b8d0954514192133b0119942edfd7a0c146900d) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/client.c | 51 +++++++++++++++++++++++---------------------------- + 1 file changed, 23 insertions(+), 28 deletions(-) + +diff --git a/nbd/client.c b/nbd/client.c +index c7bb708..9028fd0 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -814,7 +814,6 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + NBDExportInfo *info, Error **errp) + { + uint64_t magic; +- int rc; + bool zeroes = true; + bool structured_reply = info->structured_reply; + bool base_allocation = info->base_allocation; +@@ -825,31 +824,30 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + trace_nbd_receive_negotiate_name(info->name); + info->structured_reply = false; + info->base_allocation = false; +- rc = -EINVAL; + + if (outioc) { + *outioc = NULL; + } + if (tlscreds && !outioc) { + error_setg(errp, "Output I/O channel required for TLS"); +- goto fail; ++ return -EINVAL; + } + + if (nbd_read(ioc, &magic, sizeof(magic), errp) < 0) { + error_prepend(errp, "Failed to read initial magic: "); +- goto fail; ++ return -EINVAL; + } + magic = be64_to_cpu(magic); + trace_nbd_receive_negotiate_magic(magic); + + if (magic != NBD_INIT_MAGIC) { + error_setg(errp, "Bad initial magic received: 0x%" PRIx64, magic); +- goto fail; ++ return -EINVAL; + } + + if (nbd_read(ioc, &magic, sizeof(magic), errp) < 0) { + error_prepend(errp, "Failed to read server magic: "); +- goto fail; ++ return -EINVAL; + } + magic = be64_to_cpu(magic); + trace_nbd_receive_negotiate_magic(magic); +@@ -861,7 +859,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + + if (nbd_read(ioc, &globalflags, sizeof(globalflags), errp) < 0) { + error_prepend(errp, "Failed to read server flags: "); +- goto fail; ++ return -EINVAL; + } + globalflags = be16_to_cpu(globalflags); + trace_nbd_receive_negotiate_server_flags(globalflags); +@@ -877,18 +875,18 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + clientflags = cpu_to_be32(clientflags); + if (nbd_write(ioc, &clientflags, sizeof(clientflags), errp) < 0) { + error_prepend(errp, "Failed to send clientflags field: "); +- goto fail; ++ return -EINVAL; + } + if (tlscreds) { + if (fixedNewStyle) { + *outioc = nbd_receive_starttls(ioc, tlscreds, hostname, errp); + if (!*outioc) { +- goto fail; ++ return -EINVAL; + } + ioc = *outioc; + } else { + error_setg(errp, "Server does not support STARTTLS"); +- goto fail; ++ return -EINVAL; + } + } + if (fixedNewStyle) { +@@ -899,7 +897,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + NBD_OPT_STRUCTURED_REPLY, + errp); + if (result < 0) { +- goto fail; ++ return -EINVAL; + } + info->structured_reply = result == 1; + } +@@ -907,7 +905,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + if (info->structured_reply && base_allocation) { + result = nbd_negotiate_simple_meta_context(ioc, info, errp); + if (result < 0) { +- goto fail; ++ return -EINVAL; + } + info->base_allocation = result == 1; + } +@@ -919,7 +917,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + * export, then use NBD_OPT_EXPORT_NAME. */ + result = nbd_opt_go(ioc, info, errp); + if (result < 0) { +- goto fail; ++ return -EINVAL; + } + if (result > 0) { + return 0; +@@ -931,25 +929,25 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + * export name is not available. + */ + if (nbd_receive_query_exports(ioc, info->name, errp) < 0) { +- goto fail; ++ return -EINVAL; + } + } + /* write the export name request */ + if (nbd_send_option_request(ioc, NBD_OPT_EXPORT_NAME, -1, info->name, + errp) < 0) { +- goto fail; ++ return -EINVAL; + } + + /* Read the response */ + if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) { + error_prepend(errp, "Failed to read export length: "); +- goto fail; ++ return -EINVAL; + } + info->size = be64_to_cpu(info->size); + + if (nbd_read(ioc, &info->flags, sizeof(info->flags), errp) < 0) { + error_prepend(errp, "Failed to read export flags: "); +- goto fail; ++ return -EINVAL; + } + info->flags = be16_to_cpu(info->flags); + } else if (magic == NBD_CLIENT_MAGIC) { +@@ -957,43 +955,40 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + + if (*info->name) { + error_setg(errp, "Server does not support non-empty export names"); +- goto fail; ++ return -EINVAL; + } + if (tlscreds) { + error_setg(errp, "Server does not support STARTTLS"); +- goto fail; ++ return -EINVAL; + } + + if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) { + error_prepend(errp, "Failed to read export length: "); +- goto fail; ++ return -EINVAL; + } + info->size = be64_to_cpu(info->size); + + if (nbd_read(ioc, &oldflags, sizeof(oldflags), errp) < 0) { + error_prepend(errp, "Failed to read export flags: "); +- goto fail; ++ return -EINVAL; + } + oldflags = be32_to_cpu(oldflags); + if (oldflags & ~0xffff) { + error_setg(errp, "Unexpected export flags %0x" PRIx32, oldflags); +- goto fail; ++ return -EINVAL; + } + info->flags = oldflags; + } else { + error_setg(errp, "Bad server magic received: 0x%" PRIx64, magic); +- goto fail; ++ return -EINVAL; + } + + 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: "); +- goto fail; ++ return -EINVAL; + } +- rc = 0; +- +-fail: +- return rc; ++ return 0; + } + + #ifdef __linux__ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Reject-inaccessible-tail-of-inconsistent-.patch b/SOURCES/kvm-nbd-client-Reject-inaccessible-tail-of-inconsistent-.patch new file mode 100644 index 0000000..e22f066 --- /dev/null +++ b/SOURCES/kvm-nbd-client-Reject-inaccessible-tail-of-inconsistent-.patch @@ -0,0 +1,64 @@ +From c03c9a78664b0f3e27bba21167e621d5068feb0b Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 6 May 2019 17:56:20 +0200 +Subject: [PATCH 10/53] nbd/client: Reject inaccessible tail of inconsistent + server + +RH-Author: John Snow +Message-id: <20190506175629.11079-11-jsnow@redhat.com> +Patchwork-id: 87183 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 10/19] nbd/client: Reject inaccessible tail of inconsistent server +Bugzilla: 1692018 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Thomas Huth + +From: Eric Blake + +The NBD spec suggests that a server should never advertise a size +inconsistent with its minimum block alignment, as that tail is +effectively inaccessible to a compliant client obeying those block +constraints. Since we have a habit of rounding up rather than +truncating, to avoid losing the last few bytes of user input, and we +cannot access the tail when the server advertises bogus block sizing, +abort the connection to alert the server to fix their bug. And +rejecting such servers matches what we already did for a min_block +that was not a power of 2 or which was larger than max_block. + +Does not impact either qemu (which always sends properly aligned +sizes) or nbdkit (which does not send minimum block requirements yet); +so this is mostly aimed at new NBD server implementations, and ensures +that the rest of our code can assume the size is aligned. + +Signed-off-by: Eric Blake +Message-Id: <20190330155704.24191-1-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit 3add3ab78247fd347fd6f377a4b951022ac35d35) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/client.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/nbd/client.c b/nbd/client.c +index 10a52ad..4309569 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -426,6 +426,14 @@ static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, + 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; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Relax-handling-of-large-NBD_CMD_BLOCK_STA.patch b/SOURCES/kvm-nbd-client-Relax-handling-of-large-NBD_CMD_BLOCK_STA.patch new file mode 100644 index 0000000..34245a2 --- /dev/null +++ b/SOURCES/kvm-nbd-client-Relax-handling-of-large-NBD_CMD_BLOCK_STA.patch @@ -0,0 +1,69 @@ +From 9e909e2a2640b08e7bf10232321a1b1dc81df69c Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:16 +0100 +Subject: [PATCH 049/163] nbd/client: Relax handling of large + NBD_CMD_BLOCK_STATUS reply + +RH-Author: John Snow +Message-id: <20190322032241.8111-4-jsnow@redhat.com> +Patchwork-id: 85090 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 03/28] nbd/client: Relax handling of large NBD_CMD_BLOCK_STATUS reply +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +The NBD spec is proposing a relaxation of NBD_CMD_BLOCK_STATUS +where a server may have the final extent per context give a +length beyond the original request, if it can easily prove that +subsequent bytes have the same status, on the grounds that a +client can take advantage of this information for fewer block +status requests. Since qemu 2.12 as a client always sends +NBD_CMD_FLAG_REQ_ONE, and rejects a server that sends extra +length, the upstream NBD spec will probably limit this behavior +to clients that don't request REQ_ONE semantics; but it doesn't +hurt to relax qemu to always be permissive of this server +behavior, even if it continues to use REQ_ONE. + +CC: qemu-stable@nongnu.org +Signed-off-by: Eric Blake +Message-Id: <20180503222626.1303410-1-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit acfd8f7a5f92e703d2d046cbe3d510008a697194) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index 80d3625..76e9ca3 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -259,14 +259,18 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client, + + if (extent->length == 0 || + (client->info.min_block && !QEMU_IS_ALIGNED(extent->length, +- client->info.min_block)) || +- extent->length > orig_length) +- { ++ client->info.min_block))) { + error_setg(errp, "Protocol error: server sent status chunk with " + "invalid length"); + return -EINVAL; + } + ++ /* The server is allowed to send us extra information on the final ++ * extent; just clamp it to the length we requested. */ ++ if (extent->length > orig_length) { ++ extent->length = orig_length; ++ } ++ + return 0; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Report-offsets-in-bdrv_block_status.patch b/SOURCES/kvm-nbd-client-Report-offsets-in-bdrv_block_status.patch new file mode 100644 index 0000000..5bccb9e --- /dev/null +++ b/SOURCES/kvm-nbd-client-Report-offsets-in-bdrv_block_status.patch @@ -0,0 +1,166 @@ +From df5a366613d42ec2dc5dae33e10b6fdd5db09e0d Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 6 May 2019 17:56:19 +0200 +Subject: [PATCH 09/53] nbd/client: Report offsets in bdrv_block_status + +RH-Author: John Snow +Message-id: <20190506175629.11079-10-jsnow@redhat.com> +Patchwork-id: 87199 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 09/19] nbd/client: Report offsets in bdrv_block_status +Bugzilla: 1692018 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Thomas Huth + +From: Eric Blake + +It is desirable for 'qemu-img map' to have the same output for a file +whether it is served over file or nbd protocols. However, ever since +we implemented block status for NBD (2.12), the NBD protocol forgot to +inform the block layer that as the final layer in the chain, the +offset is valid; without an offset, the human-readable form of +qemu-img map gives up with the unhelpful: + +$ nbdkit -U - data data="1" size=512 --run 'qemu-img map $nbd' +Offset Length Mapped to File +qemu-img: File contains external, encrypted or compressed clusters. + +The --output=json form always works, because it is reporting the +lower-level bdrv_block_status results directly rather than trying to +filter out sparse ranges for human consumption - but now it also +shows the offset member. + +With this patch, the human output changes to: + +Offset Length Mapped to File +0 0x200 0 nbd+unix://?socket=/tmp/nbdkitOxeoLa/socket + +This change is observable to several iotests. + +Fixes: 78a33ab5 +Reported-by: Richard W.M. Jones +Signed-off-by: Eric Blake +Message-Id: <20190329042750.14704-4-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit a62a85ef5ccd764d03d72d6c3cd558f9755b3457) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 9 +++++++-- + tests/qemu-iotests/209.out | 4 ++-- + tests/qemu-iotests/223.out | 18 +++++++++--------- + tests/qemu-iotests/241.out | 6 +++--- + 4 files changed, 21 insertions(+), 16 deletions(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index 09e20b2..9b5779f 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -959,7 +959,9 @@ int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs, + + if (!client->info.base_allocation) { + *pnum = bytes; +- return BDRV_BLOCK_DATA; ++ *map = offset; ++ *file = bs; ++ return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; + } + + ret = nbd_co_send_request(bs, &request, NULL); +@@ -982,8 +984,11 @@ int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs, + + 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); ++ (extent.flags & NBD_STATE_ZERO ? BDRV_BLOCK_ZERO : 0) | ++ BDRV_BLOCK_OFFSET_VALID; + } + + void nbd_client_detach_aio_context(BlockDriverState *bs) +diff --git a/tests/qemu-iotests/209.out b/tests/qemu-iotests/209.out +index 0d29724..214e27b 100644 +--- a/tests/qemu-iotests/209.out ++++ b/tests/qemu-iotests/209.out +@@ -1,2 +1,2 @@ +-[{ "start": 0, "length": 524288, "depth": 0, "zero": false, "data": true}, +-{ "start": 524288, "length": 524288, "depth": 0, "zero": true, "data": false}] ++[{ "start": 0, "length": 524288, "depth": 0, "zero": false, "data": true, "offset": 0}, ++{ "start": 524288, "length": 524288, "depth": 0, "zero": true, "data": false, "offset": 524288}] +diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out +index 0524ffb..a620f82 100644 +--- a/tests/qemu-iotests/223.out ++++ b/tests/qemu-iotests/223.out +@@ -67,18 +67,18 @@ read 1048576/1048576 bytes at offset 1048576 + 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + read 2097152/2097152 bytes at offset 2097152 + 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +-[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true}, +-{ "start": 4096, "length": 1044480, "depth": 0, "zero": true, "data": false}, +-{ "start": 1048576, "length": 3145728, "depth": 0, "zero": false, "data": true}] ++[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, ++{ "start": 4096, "length": 1044480, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, ++{ "start": 1048576, "length": 3145728, "depth": 0, "zero": false, "data": true, "offset": OFFSET}] + [{ "start": 0, "length": 65536, "depth": 0, "zero": false, "data": false}, +-{ "start": 65536, "length": 2031616, "depth": 0, "zero": false, "data": true}, ++{ "start": 65536, "length": 2031616, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, + { "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}] + + === Contrast to small granularity dirty-bitmap === + +-[{ "start": 0, "length": 512, "depth": 0, "zero": false, "data": true}, ++[{ "start": 0, "length": 512, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, + { "start": 512, "length": 512, "depth": 0, "zero": false, "data": false}, +-{ "start": 1024, "length": 2096128, "depth": 0, "zero": false, "data": true}, ++{ "start": 1024, "length": 2096128, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, + { "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}] + + === End qemu NBD server === +@@ -94,10 +94,10 @@ read 2097152/2097152 bytes at offset 2097152 + === Use qemu-nbd as server === + + [{ "start": 0, "length": 65536, "depth": 0, "zero": false, "data": false}, +-{ "start": 65536, "length": 2031616, "depth": 0, "zero": false, "data": true}, ++{ "start": 65536, "length": 2031616, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, + { "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}] +-[{ "start": 0, "length": 512, "depth": 0, "zero": false, "data": true}, ++[{ "start": 0, "length": 512, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, + { "start": 512, "length": 512, "depth": 0, "zero": false, "data": false}, +-{ "start": 1024, "length": 2096128, "depth": 0, "zero": false, "data": true}, ++{ "start": 1024, "length": 2096128, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, + { "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}] + *** done +diff --git a/tests/qemu-iotests/241.out b/tests/qemu-iotests/241.out +index b76a623..f22eabb 100644 +--- a/tests/qemu-iotests/241.out ++++ b/tests/qemu-iotests/241.out +@@ -4,7 +4,7 @@ QA output created by 241 + + size: 1024 + min block: 512 +-[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true}] ++[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET}] + 1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) + + === Exporting unaligned raw image, forced server sector alignment === +@@ -14,13 +14,13 @@ WARNING: Image format was not specified for '/home/eblake/qemu/tests/qemu-iotest + Specify the 'raw' format explicitly to remove the restrictions. + size: 1024 + min block: 512 +-[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true}] ++[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET}] + 1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) + + === Exporting unaligned raw image, forced client sector alignment === + + size: 1024 + min block: 512 +-[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true}] ++[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET}] + 1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Send-NBD_CMD_DISC-if-open-fails-after-con.patch b/SOURCES/kvm-nbd-client-Send-NBD_CMD_DISC-if-open-fails-after-con.patch new file mode 100644 index 0000000..e4ba4a9 --- /dev/null +++ b/SOURCES/kvm-nbd-client-Send-NBD_CMD_DISC-if-open-fails-after-con.patch @@ -0,0 +1,81 @@ +From fbb8b7e5e064ea6a467119772097242311e65628 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:40 +0100 +Subject: [PATCH 073/163] nbd/client: Send NBD_CMD_DISC if open fails after + connect + +RH-Author: John Snow +Message-id: <20190322032241.8111-28-jsnow@redhat.com> +Patchwork-id: 85114 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 27/28] nbd/client: Send NBD_CMD_DISC if open fails after connect +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +If nbd_client_init() fails after we are already connected, +then the server will spam logs with: + +Disconnect client, due to: Unexpected end-of-file before all bytes were read + +unless we gracefully disconnect before closing the connection. + +Ways to trigger this: + +$ opts=driver=nbd,export=foo,server.type=inet,server.host=localhost,server.port=10809 +$ qemu-img map --output=json --image-opts $opts,read-only=off +$ qemu-img map --output=json --image-opts $opts,x-dirty-bitmap=nosuch: + +Signed-off-by: Eric Blake +Message-Id: <20181130023232.3079982-4-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit c688e6ca7b41a105241054853d250df64addbf8f) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 18 ++++++++++++++++-- + 1 file changed, 16 insertions(+), 2 deletions(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index e6e27da..fc5b7ed 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -995,12 +995,13 @@ int nbd_client_init(BlockDriverState *bs, + if (x_dirty_bitmap && !client->info.base_allocation) { + error_setg(errp, "requested x-dirty-bitmap %s not found", + x_dirty_bitmap); +- return -EINVAL; ++ ret = -EINVAL; ++ goto fail; + } + if (client->info.flags & NBD_FLAG_READ_ONLY) { + ret = bdrv_apply_auto_read_only(bs, "NBD export is read-only", errp); + if (ret < 0) { +- return ret; ++ goto fail; + } + } + if (client->info.flags & NBD_FLAG_SEND_FUA) { +@@ -1029,4 +1030,17 @@ int nbd_client_init(BlockDriverState *bs, + + logout("Established connection with NBD server\n"); + return 0; ++ ++ fail: ++ /* ++ * We have connected, but must fail for other reasons. The ++ * connection is still blocking; send NBD_CMD_DISC as a courtesy ++ * to the server. ++ */ ++ { ++ NBDRequest request = { .type = NBD_CMD_DISC }; ++ ++ nbd_send_request(client->ioc ?: QIO_CHANNEL(sioc), &request); ++ return ret; ++ } + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Split-handshake-into-two-functions.patch b/SOURCES/kvm-nbd-client-Split-handshake-into-two-functions.patch new file mode 100644 index 0000000..438773b --- /dev/null +++ b/SOURCES/kvm-nbd-client-Split-handshake-into-two-functions.patch @@ -0,0 +1,263 @@ +From 17d3f9b474fe65d1b4fc128d18e3a7a52b32c62d Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:48 +0100 +Subject: [PATCH 110/163] nbd/client: Split handshake into two functions + +RH-Author: John Snow +Message-id: <20190327172308.31077-36-jsnow@redhat.com> +Patchwork-id: 85213 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 35/55] nbd/client: Split handshake into two functions +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +An upcoming patch will add the ability for qemu-nbd to list +the services provided by an NBD server. Share the common +code of the TLS handshake by splitting the initial exchange +into a separate function, leaving only the export handling +in the original function. Functionally, there should be no +change in behavior in this patch, although some of the code +motion may be difficult to follow due to indentation changes +(view with 'git diff -w' for a smaller changeset). + +I considered an enum for the return code coordinating state +between the two functions, but in the end just settled with +ample comments. + +Signed-off-by: Eric Blake +Reviewed-by: Richard W.M. Jones +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20190117193658.16413-15-eblake@redhat.com> +(cherry picked from commit 10b89988d6b0f5f2aed794bed5b4e774858548f4) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/client.c | 145 ++++++++++++++++++++++++++++++++++++------------------- + nbd/trace-events | 2 +- + 2 files changed, 96 insertions(+), 51 deletions(-) + +diff --git a/nbd/client.c b/nbd/client.c +index 9028fd0..77cc123 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -809,22 +809,26 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, + return received; + } + +-int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, +- const char *hostname, QIOChannel **outioc, +- NBDExportInfo *info, Error **errp) ++/* ++ * 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 ++ * 0: server is oldstyle, client must still parse export size ++ * 1: server is newstyle, but can only accept EXPORT_NAME ++ * 2: server is newstyle, but lacks structured replies ++ * 3: server is newstyle and set up for structured replies ++ */ ++static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, ++ const char *hostname, QIOChannel **outioc, ++ bool structured_reply, bool *zeroes, ++ Error **errp) + { + uint64_t magic; +- bool zeroes = true; +- bool structured_reply = info->structured_reply; +- bool base_allocation = info->base_allocation; + +- trace_nbd_receive_negotiate(tlscreds, hostname ? hostname : ""); +- +- assert(info->name); +- trace_nbd_receive_negotiate_name(info->name); +- info->structured_reply = false; +- info->base_allocation = false; ++ trace_nbd_start_negotiate(tlscreds, hostname ? hostname : ""); + ++ *zeroes = true; + if (outioc) { + *outioc = NULL; + } +@@ -868,7 +872,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + clientflags |= NBD_FLAG_C_FIXED_NEWSTYLE; + } + if (globalflags & NBD_FLAG_NO_ZEROES) { +- zeroes = false; ++ *zeroes = false; + clientflags |= NBD_FLAG_C_NO_ZEROES; + } + /* client requested flags */ +@@ -890,7 +894,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + } + } + if (fixedNewStyle) { +- int result; ++ int result = 0; + + if (structured_reply) { + result = nbd_request_simple_option(ioc, +@@ -899,39 +903,85 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + if (result < 0) { + return -EINVAL; + } +- info->structured_reply = result == 1; + } ++ return 2 + result; ++ } else { ++ return 1; ++ } ++ } else if (magic == NBD_CLIENT_MAGIC) { ++ if (tlscreds) { ++ error_setg(errp, "Server does not support STARTTLS"); ++ return -EINVAL; ++ } ++ return 0; ++ } else { ++ error_setg(errp, "Bad server magic received: 0x%" PRIx64, magic); ++ return -EINVAL; ++ } ++} + +- if (info->structured_reply && base_allocation) { +- result = nbd_negotiate_simple_meta_context(ioc, info, errp); +- if (result < 0) { +- return -EINVAL; +- } +- info->base_allocation = result == 1; +- } ++/* ++ * 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) ++{ ++ int result; ++ bool zeroes; ++ bool base_allocation = info->base_allocation; ++ uint32_t oldflags; + +- /* 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_go(ioc, info, errp); ++ assert(info->name); ++ trace_nbd_receive_negotiate_name(info->name); ++ ++ result = nbd_start_negotiate(ioc, tlscreds, hostname, outioc, ++ info->structured_reply, &zeroes, errp); ++ ++ info->structured_reply = false; ++ info->base_allocation = false; ++ if (tlscreds && *outioc) { ++ ioc = *outioc; ++ } ++ ++ switch (result) { ++ case 3: /* newstyle, with structured replies */ ++ info->structured_reply = true; ++ if (base_allocation) { ++ result = nbd_negotiate_simple_meta_context(ioc, 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; +- } ++ info->base_allocation = result == 1; ++ } ++ /* fall through */ ++ case 2: /* newstyle, try OPT_GO */ ++ /* 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_go(ioc, 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 1: /* newstyle, but limited to EXPORT_NAME */ + /* write the export name request */ + if (nbd_send_option_request(ioc, NBD_OPT_EXPORT_NAME, -1, info->name, + errp) < 0) { +@@ -950,17 +1000,12 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + return -EINVAL; + } + info->flags = be16_to_cpu(info->flags); +- } else if (magic == NBD_CLIENT_MAGIC) { +- uint32_t oldflags; +- ++ break; ++ case 0: /* oldstyle, parse length and flags */ + if (*info->name) { + error_setg(errp, "Server does not support non-empty export names"); + return -EINVAL; + } +- if (tlscreds) { +- error_setg(errp, "Server does not support STARTTLS"); +- return -EINVAL; +- } + + if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) { + error_prepend(errp, "Failed to read export length: "); +@@ -978,9 +1023,9 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + return -EINVAL; + } + info->flags = oldflags; +- } else { +- error_setg(errp, "Bad server magic received: 0x%" PRIx64, magic); +- return -EINVAL; ++ break; ++ default: ++ return result; + } + + trace_nbd_receive_negotiate_size_flags(info->size, info->flags); +diff --git a/nbd/trace-events b/nbd/trace-events +index b4802c1..663d116 100644 +--- a/nbd/trace-events ++++ b/nbd/trace-events +@@ -14,7 +14,7 @@ nbd_receive_starttls_new_client(void) "Setting up TLS" + nbd_receive_starttls_tls_handshake(void) "Starting TLS handshake" + nbd_opt_meta_request(const char *optname, const char *context, const char *export) "Requesting %s %s for export %s" + nbd_opt_meta_reply(const char *optname, const char *context, uint32_t id) "Received %s mapping of %s to id %" PRIu32 +-nbd_receive_negotiate(void *tlscreds, const char *hostname) "Receiving negotiation tlscreds=%p hostname=%s" ++nbd_start_negotiate(void *tlscreds, const char *hostname) "Receiving negotiation tlscreds=%p hostname=%s" + nbd_receive_negotiate_magic(uint64_t magic) "Magic is 0x%" PRIx64 + nbd_receive_negotiate_server_flags(uint32_t globalflags) "Global flags are 0x%" PRIx32 + nbd_receive_negotiate_name(const char *name) "Requesting NBD export name '%s'" +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Split-out-nbd_receive_one_meta_context.patch b/SOURCES/kvm-nbd-client-Split-out-nbd_receive_one_meta_context.patch new file mode 100644 index 0000000..47716aa --- /dev/null +++ b/SOURCES/kvm-nbd-client-Split-out-nbd_receive_one_meta_context.patch @@ -0,0 +1,240 @@ +From eead0dbc83d4f8e8da21017976a353c513c3b2b3 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:46 +0100 +Subject: [PATCH 108/163] nbd/client: Split out nbd_receive_one_meta_context() + +RH-Author: John Snow +Message-id: <20190327172308.31077-34-jsnow@redhat.com> +Patchwork-id: 85193 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 33/55] nbd/client: Split out nbd_receive_one_meta_context() +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Extract portions of nbd_negotiate_simple_meta_context() to +a new function nbd_receive_one_meta_context() that copies the +pattern of nbd_receive_list() for performing the argument +validation of one reply. The error message when the server +replies with more than one context changes slightly, but +that shouldn't happen in the common case. + +Signed-off-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20190117193658.16413-13-eblake@redhat.com> +(cherry picked from commit 0182c1aed9e6a9314023c7264c5c264da2f4a4ce) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/client.c | 147 ++++++++++++++++++++++++++++++++++--------------------- + nbd/trace-events | 2 +- + 2 files changed, 91 insertions(+), 58 deletions(-) + +diff --git a/nbd/client.c b/nbd/client.c +index 96da68e..c7bb708 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -669,7 +669,86 @@ static int nbd_send_meta_query(QIOChannel *ioc, uint32_t opt, + return ret; + } + +-/* nbd_negotiate_simple_meta_context: ++/* ++ * 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, 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_read(ioc, &local_id, sizeof(local_id), errp) < 0) { ++ return -1; ++ } ++ local_id = be32_to_cpu(local_id); ++ ++ reply.length -= sizeof(local_id); ++ local_name = g_malloc(reply.length + 1); ++ if (nbd_read(ioc, local_name, reply.length, 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 +@@ -690,50 +769,21 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, + * function should lose the term _simple. + */ + int ret; +- NBDOptionReply reply; + 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; + } + +- if (nbd_receive_option_reply(ioc, NBD_OPT_SET_META_CONTEXT, &reply, +- errp) < 0) +- { ++ ret = nbd_receive_one_meta_context(ioc, NBD_OPT_SET_META_CONTEXT, ++ &name, &info->context_id, errp); ++ if (ret < 0) { + return -1; + } +- +- ret = nbd_handle_reply_err(ioc, &reply, errp); +- if (ret <= 0) { +- return ret; +- } +- +- if (reply.type == NBD_REP_META_CONTEXT) { +- char *name; +- +- if (reply.length != sizeof(info->context_id) + strlen(context)) { +- error_setg(errp, "Failed to negotiate meta context '%s', server " +- "answered with unexpected length %" PRIu32, context, +- reply.length); +- nbd_send_opt_abort(ioc); +- return -1; +- } +- +- if (nbd_read(ioc, &info->context_id, sizeof(info->context_id), +- errp) < 0) { +- return -1; +- } +- info->context_id = be32_to_cpu(info->context_id); +- +- reply.length -= sizeof(info->context_id); +- name = g_malloc(reply.length + 1); +- if (nbd_read(ioc, name, reply.length, errp) < 0) { +- g_free(name); +- return -1; +- } +- name[reply.length] = '\0'; ++ if (ret == 1) { + if (strcmp(context, name)) { + error_setg(errp, "Failed to negotiate meta context '%s', server " + "answered with different context '%s'", context, +@@ -743,36 +793,19 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, + return -1; + } + g_free(name); +- +- trace_nbd_opt_meta_reply(context, info->context_id); + received = true; + +- /* receive NBD_REP_ACK */ +- if (nbd_receive_option_reply(ioc, NBD_OPT_SET_META_CONTEXT, &reply, +- errp) < 0) +- { ++ ret = nbd_receive_one_meta_context(ioc, NBD_OPT_SET_META_CONTEXT, ++ NULL, NULL, errp); ++ if (ret < 0) { + return -1; + } +- +- ret = nbd_handle_reply_err(ioc, &reply, errp); +- if (ret <= 0) { +- return ret; +- } + } +- +- if (reply.type != NBD_REP_ACK) { +- error_setg(errp, "Unexpected reply type %u (%s), expected %u (%s)", +- reply.type, nbd_rep_lookup(reply.type), +- NBD_REP_ACK, nbd_rep_lookup(NBD_REP_ACK)); ++ if (ret != 0) { ++ error_setg(errp, "Server answered with more than one context"); + nbd_send_opt_abort(ioc); + return -1; + } +- if (reply.length) { +- error_setg(errp, "Unexpected length to ACK response"); +- nbd_send_opt_abort(ioc); +- return -1; +- } +- + return received; + } + +diff --git a/nbd/trace-events b/nbd/trace-events +index 59521e4..b4802c1 100644 +--- a/nbd/trace-events ++++ b/nbd/trace-events +@@ -13,7 +13,7 @@ nbd_receive_query_exports_success(const char *wantname) "Found desired export na + nbd_receive_starttls_new_client(void) "Setting up TLS" + nbd_receive_starttls_tls_handshake(void) "Starting TLS handshake" + nbd_opt_meta_request(const char *optname, const char *context, const char *export) "Requesting %s %s for export %s" +-nbd_opt_meta_reply(const char *context, uint32_t id) "Received mapping of context %s to id %" PRIu32 ++nbd_opt_meta_reply(const char *optname, const char *context, uint32_t id) "Received %s mapping of %s to id %" PRIu32 + nbd_receive_negotiate(void *tlscreds, const char *hostname) "Receiving negotiation tlscreds=%p hostname=%s" + nbd_receive_negotiate_magic(uint64_t magic) "Magic is 0x%" PRIx64 + nbd_receive_negotiate_server_flags(uint32_t globalflags) "Global flags are 0x%" PRIx32 +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Split-out-nbd_send_meta_query.patch b/SOURCES/kvm-nbd-client-Split-out-nbd_send_meta_query.patch new file mode 100644 index 0000000..729ac33 --- /dev/null +++ b/SOURCES/kvm-nbd-client-Split-out-nbd_send_meta_query.patch @@ -0,0 +1,140 @@ +From 0406c2a938cf46c81c2d6c8ed14a4994993a3860 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:45 +0100 +Subject: [PATCH 107/163] nbd/client: Split out nbd_send_meta_query() + +RH-Author: John Snow +Message-id: <20190327172308.31077-33-jsnow@redhat.com> +Patchwork-id: 85202 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 32/55] nbd/client: Split out nbd_send_meta_query() +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Refactor nbd_negotiate_simple_meta_context() to pull out the +code that can be reused to send a LIST request for 0 or 1 query. +No semantic change. The old comment about 'sizeof(uint32_t)' +being equivalent to '/* number of queries */' is no longer +needed, now that we are computing 'sizeof(queries)' instead. + +Signed-off-by: Eric Blake +Reviewed-by: Richard W.M. Jones +Message-Id: <20190117193658.16413-12-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit 757b3ab989dea1c3143dd0d499441415ac7fcbc0) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/client.c | 64 ++++++++++++++++++++++++++++++++++++++------------------ + nbd/trace-events | 2 +- + 2 files changed, 45 insertions(+), 21 deletions(-) + +diff --git a/nbd/client.c b/nbd/client.c +index 7799389..96da68e 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -629,6 +629,46 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc, + 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 = strlen(export); ++ uint32_t queries = !!query; ++ uint32_t query_len = 0; ++ uint32_t data_len; ++ char *data; ++ char *p; ++ ++ data_len = sizeof(export_len) + export_len + sizeof(queries); ++ if (query) { ++ 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_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", +@@ -653,26 +693,10 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, + NBDOptionReply reply; + const char *context = info->x_dirty_bitmap ?: "base:allocation"; + bool received = false; +- uint32_t export_len = strlen(info->name); +- uint32_t context_len = strlen(context); +- uint32_t data_len = sizeof(export_len) + export_len + +- sizeof(uint32_t) + /* number of queries */ +- sizeof(context_len) + context_len; +- char *data = g_malloc(data_len); +- char *p = data; +- +- trace_nbd_opt_meta_request(context, info->name); +- stl_be_p(p, export_len); +- memcpy(p += sizeof(export_len), info->name, export_len); +- stl_be_p(p += export_len, 1); +- stl_be_p(p += sizeof(uint32_t), context_len); +- memcpy(p += sizeof(context_len), context, context_len); + +- ret = nbd_send_option_request(ioc, NBD_OPT_SET_META_CONTEXT, data_len, data, +- errp); +- g_free(data); +- if (ret < 0) { +- return ret; ++ if (nbd_send_meta_query(ioc, NBD_OPT_SET_META_CONTEXT, ++ info->name, context, errp) < 0) { ++ return -1; + } + + if (nbd_receive_option_reply(ioc, NBD_OPT_SET_META_CONTEXT, &reply, +@@ -689,7 +713,7 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, + if (reply.type == NBD_REP_META_CONTEXT) { + char *name; + +- if (reply.length != sizeof(info->context_id) + context_len) { ++ if (reply.length != sizeof(info->context_id) + strlen(context)) { + error_setg(errp, "Failed to negotiate meta context '%s', server " + "answered with unexpected length %" PRIu32, context, + reply.length); +diff --git a/nbd/trace-events b/nbd/trace-events +index c3966d2..59521e4 100644 +--- a/nbd/trace-events ++++ b/nbd/trace-events +@@ -12,7 +12,7 @@ nbd_receive_query_exports_start(const char *wantname) "Querying export list for + nbd_receive_query_exports_success(const char *wantname) "Found desired export name '%s'" + nbd_receive_starttls_new_client(void) "Setting up TLS" + nbd_receive_starttls_tls_handshake(void) "Starting TLS handshake" +-nbd_opt_meta_request(const char *context, const char *export) "Requesting to set meta context %s for export %s" ++nbd_opt_meta_request(const char *optname, const char *context, const char *export) "Requesting %s %s for export %s" + nbd_opt_meta_reply(const char *context, uint32_t id) "Received mapping of context %s to id %" PRIu32 + nbd_receive_negotiate(void *tlscreds, const char *hostname) "Receiving negotiation tlscreds=%p hostname=%s" + nbd_receive_negotiate_magic(uint64_t magic) "Magic is 0x%" PRIx64 +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Support-qemu-img-convert-from-unaligned-s.patch b/SOURCES/kvm-nbd-client-Support-qemu-img-convert-from-unaligned-s.patch new file mode 100644 index 0000000..f0f059a --- /dev/null +++ b/SOURCES/kvm-nbd-client-Support-qemu-img-convert-from-unaligned-s.patch @@ -0,0 +1,132 @@ +From c443306c82400c6e04fe4af771552e88abb469a8 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 6 May 2019 17:56:21 +0200 +Subject: [PATCH 11/53] nbd/client: Support qemu-img convert from unaligned + size + +RH-Author: John Snow +Message-id: <20190506175629.11079-12-jsnow@redhat.com> +Patchwork-id: 87184 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 11/19] nbd/client: Support qemu-img convert from unaligned size +Bugzilla: 1692018 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Thomas Huth + +From: Eric Blake + +If an NBD server advertises a size that is not a multiple of a sector, +the block layer rounds up that size, even though we set info.size to +the exact byte value sent by the server. The block layer then proceeds +to let us read or query block status on the hole that it added past +EOF, which the NBD server is unlikely to be happy with. Fortunately, +qemu as a server never advertizes an unaligned size, so we generally +don't run into this problem; but the nbdkit server makes it easy to +test: + +$ printf %1000d 1 > f1 +$ ~/nbdkit/nbdkit -fv file f1 & pid=$! +$ qemu-img convert -f raw nbd://localhost:10809 f2 +$ kill $pid +$ qemu-img compare f1 f2 + +Pre-patch, the server attempts a 1024-byte read, which nbdkit +rightfully rejects as going beyond its advertised 1000 byte size; the +conversion fails and the output files differ (not even the first +sector is copied, because qemu-img does not follow ddrescue's habit of +trying smaller reads to get as much information as possible in spite +of errors). Post-patch, the client's attempts to read (and query block +status, for new enough nbdkit) are properly truncated to the server's +length, with sane handling of the hole the block layer forced on +us. Although f2 ends up as a larger file (1024 bytes instead of 1000), +qemu-img compare shows the two images to have identical contents for +display to the guest. + +I didn't add iotests coverage since I didn't want to add a dependency +on nbdkit in iotests. I also did NOT patch write, trim, or write +zeroes - these commands continue to fail (usually with ENOSPC, but +whatever the server chose), because we really can't write to the end +of the file, and because 'qemu-img convert' is the most common case +where we care about being tolerant (which is read-only). Perhaps we +could truncate the request if the client is writing zeros to the tail, +but that seems like more work, especially if the block layer is fixed +in 4.1 to track byte-accurate sizing (in which case this patch would +be reverted as unnecessary). + +Signed-off-by: Eric Blake +Message-Id: <20190329042750.14704-5-eblake@redhat.com> +Tested-by: Richard W.M. Jones +(cherry picked from commit 9cf638508c0090b33ada4155c7cbb684e08e5ee9) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 39 ++++++++++++++++++++++++++++++++++++++- + 1 file changed, 38 insertions(+), 1 deletion(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index 9b5779f..a1a84a8 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -835,6 +835,25 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, + 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 >= client->info.size) { ++ assert(bytes < BDRV_SECTOR_SIZE); ++ qemu_iovec_memset(qiov, 0, 0, bytes); ++ return 0; ++ } ++ if (offset + bytes > client->info.size) { ++ uint64_t slop = offset + bytes - client->info.size; ++ ++ assert(slop < BDRV_SECTOR_SIZE); ++ qemu_iovec_memset(qiov, bytes - slop, 0, slop); ++ request.len -= slop; ++ } ++ + ret = nbd_co_send_request(bs, &request, NULL); + if (ret < 0) { + return ret; +@@ -953,7 +972,8 @@ int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs, + .from = offset, + .len = MIN(MIN_NON_ZERO(QEMU_ALIGN_DOWN(INT_MAX, + bs->bl.request_alignment), +- client->info.max_block), bytes), ++ client->info.max_block), ++ MIN(bytes, client->info.size - offset)), + .flags = NBD_CMD_FLAG_REQ_ONE, + }; + +@@ -964,6 +984,23 @@ int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs, + return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; + } + ++ /* ++ * 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 >= client->info.size) { ++ *pnum = bytes; ++ assert(bytes < BDRV_SECTOR_SIZE); ++ /* Intentionally don't report offset_valid for the hole */ ++ return BDRV_BLOCK_ZERO; ++ } ++ ++ if (client->info.min_block) { ++ assert(QEMU_IS_ALIGNED(request.len, client->info.min_block)); ++ } + ret = nbd_co_send_request(bs, &request, NULL); + if (ret < 0) { + return ret; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Trace-all-server-option-error-messages.patch b/SOURCES/kvm-nbd-client-Trace-all-server-option-error-messages.patch new file mode 100644 index 0000000..9e40b43 --- /dev/null +++ b/SOURCES/kvm-nbd-client-Trace-all-server-option-error-messages.patch @@ -0,0 +1,61 @@ +From 3fb57e060f7c7a01f4061eab781b03ad789f5bf5 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:18 +0100 +Subject: [PATCH 079/163] nbd/client: Trace all server option error messages + +RH-Author: John Snow +Message-id: <20190327172308.31077-6-jsnow@redhat.com> +Patchwork-id: 85181 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 05/55] nbd/client: Trace all server option error messages +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Not all servers send free-form text alongside option error replies, but +for servers that do (such as qemu), we pass the server's message as a +hint alongside our own error reporting. However, it would also be +useful to trace such server messages, since we can't guarantee how the +hint may be consumed. + +Signed-off-by: Eric Blake +Message-Id: <20181218225714.284495-3-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit bee21ef0950c8b109d3bad05a3c3f2d94ec1a3af) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/client.c | 2 ++ + nbd/trace-events | 1 + + 2 files changed, 3 insertions(+) + +diff --git a/nbd/client.c b/nbd/client.c +index b4d457a..0ad7147 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -171,6 +171,8 @@ static int nbd_handle_reply_err(QIOChannel *ioc, NBDOptionReply *reply, + goto cleanup; + } + msg[reply->length] = '\0'; ++ trace_nbd_server_error_msg(reply->type, ++ nbd_reply_type_lookup(reply->type), msg); + } + + switch (reply->type) { +diff --git a/nbd/trace-events b/nbd/trace-events +index 5e1d4af..5492042 100644 +--- a/nbd/trace-events ++++ b/nbd/trace-events +@@ -1,6 +1,7 @@ + # nbd/client.c + nbd_send_option_request(uint32_t opt, const char *name, uint32_t len) "Sending option request %" PRIu32" (%s), len %" PRIu32 + nbd_receive_option_reply(uint32_t option, const char *optname, uint32_t type, const char *typename, uint32_t length) "Received option reply %" PRIu32" (%s), type %" PRIu32" (%s), len %" PRIu32 ++nbd_server_error_msg(uint32_t err, const char *type, const char *msg) "server reported error 0x%" PRIx32 " (%s) with additional message: %s" + nbd_reply_err_unsup(uint32_t option, const char *name) "server doesn't understand request %" PRIu32 " (%s), attempting fallback" + nbd_opt_go_start(const char *name) "Attempting NBD_OPT_GO for export '%s'" + nbd_opt_go_success(void) "Export is good to go" +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Trace-server-noncompliance-on-structured-.patch b/SOURCES/kvm-nbd-client-Trace-server-noncompliance-on-structured-.patch new file mode 100644 index 0000000..9472b2c --- /dev/null +++ b/SOURCES/kvm-nbd-client-Trace-server-noncompliance-on-structured-.patch @@ -0,0 +1,107 @@ +From debd78a4fda361c6a78bfc3f0e3577909bad3555 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 6 May 2019 17:56:24 +0200 +Subject: [PATCH 14/53] nbd/client: Trace server noncompliance on structured + reads + +RH-Author: John Snow +Message-id: <20190506175629.11079-15-jsnow@redhat.com> +Patchwork-id: 87192 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 14/19] nbd/client: Trace server noncompliance on structured reads +Bugzilla: 1692018 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Thomas Huth + +From: Eric Blake + +Just as we recently added a trace for a server sending block status +that doesn't match the server's advertised minimum block alignment, +let's do the same for read chunks. But since qemu 3.1 is such a +server (because it advertised 512-byte alignment, but when serving a +file that ends in data but is not sector-aligned, NBD_CMD_READ would +detect a mid-sector change between data and hole at EOF and the +resulting read chunks are unaligned), we don't want to change our +behavior of otherwise tolerating unaligned reads. + +Note that even though we fixed the server for 4.0 to advertise an +actual block alignment (which gets rid of the unaligned reads at EOF +for posix files), we can still trigger it via other means: + +$ qemu-nbd --image-opts driver=blkdebug,align=512,image.driver=file,image.filename=/path/to/non-aligned-file + +Arguably, that is a bug in the blkdebug block status function, for +leaking a block status that is not aligned. It may also be possible to +observe issues with a backing layer with smaller alignment than the +active layer, although so far I have been unable to write a reliable +iotest for that scenario. + +Signed-off-by: Eric Blake +Message-Id: <20190330165349.32256-1-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit 75d34eb98ca0bb2f49d607fcaefd9c482e56b15d) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 12 ++++++++++-- + block/trace-events | 1 + + 2 files changed, 11 insertions(+), 2 deletions(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index a1a84a8..69e3708 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -198,7 +198,8 @@ static inline uint64_t payload_advance64(uint8_t **payload) + return ldq_be_p(*payload - 8); + } + +-static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk, ++static int nbd_parse_offset_hole_payload(NBDClientSession *client, ++ NBDStructuredReplyChunk *chunk, + uint8_t *payload, uint64_t orig_offset, + QEMUIOVector *qiov, Error **errp) + { +@@ -220,6 +221,10 @@ static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk, + " region"); + return -EINVAL; + } ++ if (client->info.min_block && ++ !QEMU_IS_ALIGNED(hole_size, client->info.min_block)) { ++ trace_nbd_structured_read_compliance("hole"); ++ } + + qemu_iovec_memset(qiov, offset - orig_offset, 0, hole_size); + +@@ -377,6 +382,9 @@ static int nbd_co_receive_offset_data_payload(NBDClientSession *s, + " 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); +@@ -699,7 +707,7 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle, + * in qiov */ + break; + case NBD_REPLY_TYPE_OFFSET_HOLE: +- ret = nbd_parse_offset_hole_payload(&reply.structured, payload, ++ ret = nbd_parse_offset_hole_payload(s, &reply.structured, payload, + offset, qiov, &local_err); + if (ret < 0) { + s->quit = true; +diff --git a/block/trace-events b/block/trace-events +index 59c6f54..19abca2 100644 +--- a/block/trace-events ++++ b/block/trace-events +@@ -153,5 +153,6 @@ nvme_cmd_map_qiov_iov(void *s, int i, void *page, int pages) "s %p iov[%d] %p pa + + # block/nbd-client.c + nbd_parse_blockstatus_compliance(const char *err) "ignoring extra data from non-compliant server: %s" ++nbd_structured_read_compliance(const char *type) "server sent non-compliant unaligned read %s chunk" + nbd_read_reply_entry_fail(int ret, const char *err) "ret = %d, err: %s" + nbd_co_request_fail(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, uint16_t type, const char *name, int ret, const char *err) "Request failed { .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64 ", .flags = 0x%" PRIx16 ", .type = %" PRIu16 " (%s) } ret = %d, err: %s" +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Work-around-3.0-bug-for-listing-meta-cont.patch b/SOURCES/kvm-nbd-client-Work-around-3.0-bug-for-listing-meta-cont.patch new file mode 100644 index 0000000..82b2e35 --- /dev/null +++ b/SOURCES/kvm-nbd-client-Work-around-3.0-bug-for-listing-meta-cont.patch @@ -0,0 +1,99 @@ +From 1d8de67d4435beff1839b868672a05c4195c9d92 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:54 +0100 +Subject: [PATCH 116/163] nbd/client: Work around 3.0 bug for listing meta + contexts + +RH-Author: John Snow +Message-id: <20190327172308.31077-42-jsnow@redhat.com> +Patchwork-id: 85211 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 41/55] nbd/client: Work around 3.0 bug for listing meta contexts +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Commit 3d068aff forgot to advertise available qemu: contexts +when the client requests a list with 0 queries. Furthermore, +3.0 shipped with a qemu-img hack of x-dirty-bitmap (commit +216ee365) that _silently_ acts as though the entire image is +clean if a requested bitmap is not present. Both bugs have +been recently fixed, so that a modern qemu server gives full +context output right away, and the client refuses a +connection if a requested x-dirty-bitmap was not found. + +Still, it is likely that there will be users that have to +work with a mix of old and new qemu versions, depending on +which features get backported where, at which point being +able to rely on 'qemu-img --list' output to know for sure +whether a given NBD export has the desired dirty bitmap is +much nicer than blindly connecting and risking that the +entire image may appear clean. We can make our --list code +smart enough to work around buggy servers by tracking +whether we've seen any qemu: replies in the original 0-query +list; if not, repeat with a single query on "qemu:" (which +may still have no replies, but then we know for sure we +didn't trip up on the server bug). + +Signed-off-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20190117193658.16413-21-eblake@redhat.com> +(cherry picked from commit 7c6f5ddca62905e67025aa0657e8a011cbdffa11) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/client.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/nbd/client.c b/nbd/client.c +index 798b82f..8a083c2 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -21,6 +21,7 @@ + #include "qapi/error.h" + #include "trace.h" + #include "nbd-internal.h" ++#include "qemu/cutils.h" + + /* Definitions for opaque data types */ + +@@ -828,6 +829,8 @@ static int nbd_list_meta_contexts(QIOChannel *ioc, + 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) { +@@ -839,9 +842,25 @@ static int nbd_list_meta_contexts(QIOChannel *ioc, + + 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; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-Work-around-server-BLOCK_STATUS-misalignm.patch b/SOURCES/kvm-nbd-client-Work-around-server-BLOCK_STATUS-misalignm.patch new file mode 100644 index 0000000..5696616 --- /dev/null +++ b/SOURCES/kvm-nbd-client-Work-around-server-BLOCK_STATUS-misalignm.patch @@ -0,0 +1,111 @@ +From 00edcf411c7d8e58faa15f2cb07829b8f7f74a60 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 6 May 2019 17:56:16 +0200 +Subject: [PATCH 06/53] nbd-client: Work around server BLOCK_STATUS + misalignment at EOF + +RH-Author: John Snow +Message-id: <20190506175629.11079-7-jsnow@redhat.com> +Patchwork-id: 87194 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 06/19] nbd-client: Work around server BLOCK_STATUS misalignment at EOF +Bugzilla: 1692018 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Thomas Huth + +From: Eric Blake + +The NBD spec is clear that a server that advertises a minimum block +size should reply to NBD_CMD_BLOCK_STATUS with extents aligned +accordingly. However, we know that the qemu NBD server implementation +has had a corner-case bug where it is not compliant with the spec, +present since the introduction of NBD_CMD_BLOCK_STATUS in qemu 2.12 +(and unlikely to be patched in time for 4.0). Namely, when qemu is +serving a file that is not a multiple of 512 bytes, it rounds the size +advertised over NBD up to the next sector boundary (someday, I'd like +to fix that to be byte-accurate, but it's a much bigger audit not +appropriate for this release); yet if the final sector contains data +prior to EOF, lseek(SEEK_HOLE) will point to the implicit hole +mid-sector which qemu then reported over NBD. + +We are well within our rights to hang up on a server that can't follow +the spec, but it is more useful to try and keep the connection alive +in spite of the problem. Do so by tracing a message about the problem, +and then either truncating the request back to an aligned boundary (if +it covered more than the final sector) or widening it out to the full +boundary with a forced status of data (since truncating would result +in 0 bytes, but we have to make progress, and valid since data is a +default-safe answer). And in practice, since the problem only happens +on a sector that starts with data and ends with a hole, we are going +to want to read that full sector anyway (where qemu as the server +fills in the tail beyond EOF with appropriate NUL bytes). + +Easy reproduction: +$ printf %1000d 1 > file +$ qemu-nbd -f raw -t file & pid=$! +$ qemu-img map --output=json -f raw nbd://localhost:10809 +qemu-img: Could not read file metadata: Invalid argument +$ kill $pid + +where the patched version instead succeeds with: +[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true}] + +Signed-off-by: Eric Blake +Message-Id: <20190326171317.4036-1-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit 737d3f524481bb2ef68d3eba1caa636ff143e16a) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 30 ++++++++++++++++++++++++++---- + 1 file changed, 26 insertions(+), 4 deletions(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index eee909f..09e20b2 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -256,15 +256,37 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client, + extent->length = payload_advance32(&payload); + extent->flags = payload_advance32(&payload); + +- if (extent->length == 0 || +- (client->info.min_block && !QEMU_IS_ALIGNED(extent->length, +- client->info.min_block))) { ++ if (extent->length == 0) { + error_setg(errp, "Protocol error: server sent status chunk with " +- "invalid length"); ++ "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 (client->info.min_block && !QEMU_IS_ALIGNED(extent->length, ++ client->info.min_block)) { ++ trace_nbd_parse_blockstatus_compliance("extent length is unaligned"); ++ if (extent->length > client->info.min_block) { ++ extent->length = QEMU_ALIGN_DOWN(extent->length, ++ client->info.min_block); ++ } else { ++ extent->length = client->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. However, it's easy +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-client-fix-nbd_negotiate_simple_meta_context.patch b/SOURCES/kvm-nbd-client-fix-nbd_negotiate_simple_meta_context.patch new file mode 100644 index 0000000..96e8bc8 --- /dev/null +++ b/SOURCES/kvm-nbd-client-fix-nbd_negotiate_simple_meta_context.patch @@ -0,0 +1,59 @@ +From 8f9f560d48befb6c527e876347d883f8ed3c128a Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:14 +0100 +Subject: [PATCH 047/163] nbd/client: fix nbd_negotiate_simple_meta_context + +RH-Author: John Snow +Message-id: <20190322032241.8111-2-jsnow@redhat.com> +Patchwork-id: 85092 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 01/28] nbd/client: fix nbd_negotiate_simple_meta_context +Bugzilla: 1691563 +RH-Acked-by: Richard Jones +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +Initialize received variable. Otherwise, is is possible for server to +answer without any contexts, but we will set context_id to something +random (received_id is not initialized too) and return 1, which is +wrong. + +To solve it, just initialize received to false. Initialize received_id +too, just to make all possible checkers happy. + +Bug was introduced in 78a33ab58782efdb206de14 "nbd: BLOCK_STATUS for +standard get_block_status function: client part" with the whole +function. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20180427142002.21930-2-vsementsov@virtuozzo.com> +Reviewed-by: Eric Blake +CC: qemu-stable@nongnu.org +Signed-off-by: Eric Blake +(cherry picked from commit 89aa0d87634e2cb98517509dc8bdb876f26ecf8b) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/client.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/nbd/client.c b/nbd/client.c +index 25603f2..a151fa5 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -613,8 +613,8 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, + { + int ret; + NBDOptionReply reply; +- uint32_t received_id; +- bool received; ++ uint32_t received_id = 0; ++ bool received = false; + uint32_t export_len = strlen(export); + uint32_t context_len = strlen(context); + uint32_t data_len = sizeof(export_len) + export_len + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-fix-NBD_FLAG_SEND_CACHE-value.patch b/SOURCES/kvm-nbd-fix-NBD_FLAG_SEND_CACHE-value.patch new file mode 100644 index 0000000..2f0e513 --- /dev/null +++ b/SOURCES/kvm-nbd-fix-NBD_FLAG_SEND_CACHE-value.patch @@ -0,0 +1,96 @@ +From 5d77bd2fe5220f026648c9e2f49e65ff45c5475d Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Tue, 9 Oct 2018 22:14:32 +0200 +Subject: [PATCH 02/22] nbd: fix NBD_FLAG_SEND_CACHE value + +RH-Author: John Snow +Message-id: <20181009221432.18359-3-jsnow@redhat.com> +Patchwork-id: 82509 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 2/2] nbd: fix NBD_FLAG_SEND_CACHE value +Bugzilla: 1636148 +RH-Acked-by: Eric Blake +RH-Acked-by: Thomas Huth +RH-Acked-by: Stefan Hajnoczi + +From: "Denis V. Lunev" + +Commit bc37b06a5 added NBD_CMD_CACHE support, but used the wrong value +for NBD_FLAG_SEND_CACHE flag for negotiation. That commit picked bit 8, +which had already been assigned by the NBD specification to mean +NBD_FLAG_CAN_MULTI_CONN, and which was already implemented in the +Linux kernel as a part of stable userspace-kernel API since 4.10: + +"bit 8, NBD_FLAG_CAN_MULTI_CONN: Indicates that the server operates +entirely without cache, or that the cache it uses is shared among all +connections to the given device. In particular, if this flag is +present, then the effects of NBD_CMD_FLUSH and NBD_CMD_FLAG_FUA +MUST be visible across all connections when the server sends its reply +to that command to the client. In the absense of this flag, clients +SHOULD NOT multiplex their commands over more than one connection to +the export. +... +bit 10, NBD_FLAG_SEND_CACHE: documents that the server understands +NBD_CMD_CACHE; however, note that server implementations exist +which support the command without advertising this bit, and +conversely that this bit does not guarantee that the command will +succeed or have an impact." + +Consequences: +- a client trying to use NBD_CMD_CACHE per the NBD spec will not +see the feature as available from a qemu 3.0 server (not fatal, +clients already have to be prepared for caching to not exist) +- a client accidentally coded to the qemu 3.0 bit value instead +of following the spec may interpret NBD_CMD_CACHE as being available +when it is not (probably not fatal, the spec says the server should +gracefully fail unknown commands, and that clients of NBD_CMD_CACHE +should be prepared for failure even when the feature is advertised); +such clients are unlikely (perhaps only in unreleased Virtuozzo code), +and will disappear over time +- a client prepared to use multiple connections based on +NBD_FLAG_CAN_MULTI_CONN may cause data corruption when it assumes +that caching is consistent when in reality qemu 3.0 did not have +a consistent cache. Partially mitigated by using read-only +connections (where nothing needs to be flushed, so caching is +indeed consistent) or when using qemu-nbd with the default -e 1 +(at most one client at a time); visible only when using -e 2 or +more for a writable export. + +Thus the commit fixes negotiation flag in QEMU according to the +specification. + +Signed-off-by: Denis V. Lunev +CC: Vladimir Sementsov-Ogievskiy +CC: Valery Vdovin +CC: Eric Blake +CC: Paolo Bonzini +CC: qemu-stable@nongnu.org +Message-Id: <20181004100313.4253-1-den@openvz.org> +Reviewed-by: Eric Blake +[eblake: enhance commit message, add defines for unimplemented flags] +Signed-off-by: Eric Blake +(cherry picked from commit df91328adab8490367776d2b21b35d790a606120) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + include/block/nbd.h | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/include/block/nbd.h b/include/block/nbd.h +index 4638c83..a53b0cf 100644 +--- a/include/block/nbd.h ++++ b/include/block/nbd.h +@@ -135,7 +135,9 @@ typedef struct NBDExtent { + #define NBD_FLAG_SEND_TRIM (1 << 5) /* Send TRIM (discard) */ + #define NBD_FLAG_SEND_WRITE_ZEROES (1 << 6) /* Send WRITE_ZEROES */ + #define NBD_FLAG_SEND_DF (1 << 7) /* Send DF (Do not Fragment) */ +-#define NBD_FLAG_SEND_CACHE (1 << 8) /* Send CACHE (prefetch) */ ++#define NBD_FLAG_CAN_MULTI_CONN (1 << 8) /* Multi-client cache consistent */ ++#define NBD_FLAG_SEND_RESIZE (1 << 9) /* Send resize */ ++#define NBD_FLAG_SEND_CACHE (1 << 10) /* Send CACHE (prefetch) */ + + /* New-style handshake (global) flags, sent from server to client, and + control what will happen during handshake phase. */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-fix-whitespace-in-server-error-message.patch b/SOURCES/kvm-nbd-fix-whitespace-in-server-error-message.patch new file mode 100644 index 0000000..c92f125 --- /dev/null +++ b/SOURCES/kvm-nbd-fix-whitespace-in-server-error-message.patch @@ -0,0 +1,64 @@ +From 344f6a6e7dbdd6aae5d3564e46ade0970246eaca Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:29 +0100 +Subject: [PATCH 062/163] nbd: fix whitespace in server error message +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190322032241.8111-17-jsnow@redhat.com> +Patchwork-id: 85107 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 16/28] nbd: fix whitespace in server error message +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Daniel P. Berrangé + +A space was missing after the option number was printed: + + Option 0x8not permitted before TLS + +becomes + + Option 0x8 not permitted before TLS + +This fixes + + commit 3668328303429f3bc93ab3365c66331600b06a2d + Author: Eric Blake + Date: Fri Oct 14 13:33:09 2016 -0500 + + nbd: Send message along with server NBD_REP_ERR errors + +Signed-off-by: Daniel P. Berrangé +Message-Id: <20181116155325.22428-2-berrange@redhat.com> +Reviewed-by: Eric Blake +[eblake: move lone space to next line] +Signed-off-by: Eric Blake +(cherry picked from commit 0b0bb124bb797b2fc3389ffa1e933fc34fe6dacb) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/nbd/server.c b/nbd/server.c +index d414861..418b1d4 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -1136,7 +1136,7 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags, + default: + ret = nbd_opt_drop(client, NBD_REP_ERR_TLS_REQD, errp, + "Option 0x%" PRIx32 +- "not permitted before TLS", option); ++ " not permitted before TLS", option); + /* Let the client keep trying, unless they asked to + * quit. In this mode, we've already sent an error, so + * we can't ack the abort. */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-forbid-use-of-frozen-bitmaps.patch b/SOURCES/kvm-nbd-forbid-use-of-frozen-bitmaps.patch new file mode 100644 index 0000000..269e757 --- /dev/null +++ b/SOURCES/kvm-nbd-forbid-use-of-frozen-bitmaps.patch @@ -0,0 +1,46 @@ +From 7017ebd29ae40208efdf2bad4846c220a6952b60 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:36 +0100 +Subject: [PATCH 26/33] nbd: forbid use of frozen bitmaps + +RH-Author: John Snow +Message-id: <20190206221243.7407-17-jsnow@redhat.com> +Patchwork-id: 84273 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 16/23] nbd: forbid use of frozen bitmaps +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +Whether it's "locked" or "frozen", it's in use and should +not be allowed for the purposes of this operation. + +Signed-off-by: John Snow +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20181002230218.13949-7-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit d9782022bda7f8eccaf961044e9efe980dc90c04) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/nbd/server.c b/nbd/server.c +index 1ce3f44..e094300 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -2435,8 +2435,8 @@ void nbd_export_bitmap(NBDExport *exp, const char *bitmap, + return; + } + +- if (bdrv_dirty_bitmap_qmp_locked(bm)) { +- error_setg(errp, "Bitmap '%s' is locked", bitmap); ++ if (bdrv_dirty_bitmap_user_locked(bm)) { ++ error_setg(errp, "Bitmap '%s' is in use", bitmap); + return; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-generalize-usage-of-nbd_read.patch b/SOURCES/kvm-nbd-generalize-usage-of-nbd_read.patch new file mode 100644 index 0000000..c0f8810 --- /dev/null +++ b/SOURCES/kvm-nbd-generalize-usage-of-nbd_read.patch @@ -0,0 +1,481 @@ +From 3523bdc1d6abb74fdc0a572c9fecd7f2633ec4b9 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:57 +0100 +Subject: [PATCH 119/163] nbd: generalize usage of nbd_read + +RH-Author: John Snow +Message-id: <20190327172308.31077-45-jsnow@redhat.com> +Patchwork-id: 85205 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 44/55] nbd: generalize usage of nbd_read +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +We generally do very similar things around nbd_read: error_prepend +specifying what we have tried to read, and be_to_cpu conversion of +integers. + +So, it seems reasonable to move common things to helper functions, +which: +1. simplify code a bit +2. generalize nbd_read error descriptions, all starting with + "Failed to read" +3. make it more difficult to forget to convert things from BE + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: Eric Blake +Message-Id: <20190128165830.165170-1-vsementsov@virtuozzo.com> +[eblake: rename macro to DEF_NBD_READ_N and formatting tweaks; +checkpatch has false positive complaint] +Signed-off-by: Eric Blake +(cherry picked from commit e6798f06a67a25def45a6636259de38cc38f1414) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + block/nbd-client.c | 5 ++- + include/block/nbd.h | 32 +++++++++++++++++-- + nbd/client.c | 88 ++++++++++++++++++----------------------------------- + nbd/common.c | 2 +- + nbd/server.c | 27 ++++++---------- + 5 files changed, 71 insertions(+), 83 deletions(-) + +diff --git a/block/nbd-client.c b/block/nbd-client.c +index 8135396..5c97052 100644 +--- a/block/nbd-client.c ++++ b/block/nbd-client.c +@@ -337,10 +337,9 @@ static int nbd_co_receive_offset_data_payload(NBDClientSession *s, + return -EINVAL; + } + +- if (nbd_read(s->ioc, &offset, sizeof(offset), errp) < 0) { ++ if (nbd_read64(s->ioc, &offset, "OFFSET_DATA offset", errp) < 0) { + return -EIO; + } +- be64_to_cpus(&offset); + + data_size = chunk->length - sizeof(offset); + assert(data_size); +@@ -387,7 +386,7 @@ static coroutine_fn int nbd_co_receive_structured_payload( + } + + *payload = g_new(char, len); +- ret = nbd_read(s->ioc, *payload, len, errp); ++ ret = nbd_read(s->ioc, *payload, len, "structured payload", errp); + if (ret < 0) { + g_free(*payload); + *payload = NULL; +diff --git a/include/block/nbd.h b/include/block/nbd.h +index 4faf394..96cfb1d 100644 +--- a/include/block/nbd.h ++++ b/include/block/nbd.h +@@ -23,6 +23,7 @@ + #include "qapi/qapi-types-block.h" + #include "io/channel-socket.h" + #include "crypto/tlscreds.h" ++#include "qapi/error.h" + + /* Handshake phase structs - this struct is passed on the wire */ + +@@ -336,11 +337,38 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds, + * Reads @size bytes from @ioc. Returns 0 on success. + */ + static inline int nbd_read(QIOChannel *ioc, void *buffer, size_t size, +- Error **errp) ++ const char *desc, Error **errp) + { +- return qio_channel_read_all(ioc, buffer, size, errp) < 0 ? -EIO : 0; ++ 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 -1; ++ } ++ ++ 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) \ ++{ \ ++ if (nbd_read(ioc, val, sizeof(*val), desc, errp) < 0) { \ ++ return -1; \ ++ } \ ++ *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; +diff --git a/nbd/client.c b/nbd/client.c +index 8a083c2..10a52ad 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -113,8 +113,7 @@ 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), errp) < 0) { +- error_prepend(errp, "failed to read option reply: "); ++ if (nbd_read(ioc, reply, sizeof(*reply), "option reply", errp) < 0) { + nbd_send_opt_abort(ioc); + return -1; + } +@@ -166,8 +165,8 @@ static int nbd_handle_reply_err(QIOChannel *ioc, NBDOptionReply *reply, + goto cleanup; + } + msg = g_malloc(reply->length + 1); +- if (nbd_read(ioc, msg, reply->length, errp) < 0) { +- error_prepend(errp, "failed to read option error %" PRIu32 ++ 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 cleanup; +@@ -284,12 +283,10 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, char **description, + nbd_send_opt_abort(ioc); + return -1; + } +- if (nbd_read(ioc, &namelen, sizeof(namelen), errp) < 0) { +- error_prepend(errp, "failed to read option name length: "); ++ if (nbd_read32(ioc, &namelen, "option name length", errp) < 0) { + nbd_send_opt_abort(ioc); + return -1; + } +- namelen = be32_to_cpu(namelen); + len -= sizeof(namelen); + if (len < namelen) { + error_setg(errp, "incorrect option name length"); +@@ -298,8 +295,7 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, char **description, + } + + local_name = g_malloc(namelen + 1); +- if (nbd_read(ioc, local_name, namelen, errp) < 0) { +- error_prepend(errp, "failed to read export name: "); ++ if (nbd_read(ioc, local_name, namelen, "export name", errp) < 0) { + nbd_send_opt_abort(ioc); + goto out; + } +@@ -307,8 +303,7 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, char **description, + len -= namelen; + if (len) { + local_desc = g_malloc(len + 1); +- if (nbd_read(ioc, local_desc, len, errp) < 0) { +- error_prepend(errp, "failed to read export description: "); ++ if (nbd_read(ioc, local_desc, len, "export description", errp) < 0) { + nbd_send_opt_abort(ioc); + goto out; + } +@@ -410,13 +405,11 @@ static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, + nbd_send_opt_abort(ioc); + return -1; + } +- if (nbd_read(ioc, &type, sizeof(type), errp) < 0) { +- error_prepend(errp, "failed to read info type: "); ++ if (nbd_read16(ioc, &type, "info type", errp) < 0) { + nbd_send_opt_abort(ioc); + return -1; + } + len -= sizeof(type); +- type = be16_to_cpu(type); + switch (type) { + case NBD_INFO_EXPORT: + if (len != sizeof(info->size) + sizeof(info->flags)) { +@@ -425,18 +418,14 @@ static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, + nbd_send_opt_abort(ioc); + return -1; + } +- if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) { +- error_prepend(errp, "failed to read info size: "); ++ if (nbd_read64(ioc, &info->size, "info size", errp) < 0) { + nbd_send_opt_abort(ioc); + return -1; + } +- info->size = be64_to_cpu(info->size); +- if (nbd_read(ioc, &info->flags, sizeof(info->flags), errp) < 0) { +- error_prepend(errp, "failed to read info flags: "); ++ if (nbd_read16(ioc, &info->flags, "info flags", errp) < 0) { + nbd_send_opt_abort(ioc); + return -1; + } +- info->flags = be16_to_cpu(info->flags); + trace_nbd_receive_negotiate_size_flags(info->size, info->flags); + break; + +@@ -447,27 +436,23 @@ static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, + nbd_send_opt_abort(ioc); + return -1; + } +- if (nbd_read(ioc, &info->min_block, sizeof(info->min_block), +- errp) < 0) { +- error_prepend(errp, "failed to read info minimum block size: "); ++ if (nbd_read32(ioc, &info->min_block, "info minimum block size", ++ errp) < 0) { + nbd_send_opt_abort(ioc); + return -1; + } +- info->min_block = be32_to_cpu(info->min_block); + 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_read(ioc, &info->opt_block, sizeof(info->opt_block), +- errp) < 0) { +- error_prepend(errp, +- "failed to read info preferred block size: "); ++ if (nbd_read32(ioc, &info->opt_block, "info preferred block size", ++ errp) < 0) ++ { + nbd_send_opt_abort(ioc); + return -1; + } +- info->opt_block = be32_to_cpu(info->opt_block); + if (!is_power_of_2(info->opt_block) || + info->opt_block < info->min_block) { + error_setg(errp, "server preferred block size %" PRIu32 +@@ -475,13 +460,12 @@ static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, + nbd_send_opt_abort(ioc); + return -1; + } +- if (nbd_read(ioc, &info->max_block, sizeof(info->max_block), +- errp) < 0) { +- error_prepend(errp, "failed to read info maximum block size: "); ++ if (nbd_read32(ioc, &info->max_block, "info maximum block size", ++ errp) < 0) ++ { + nbd_send_opt_abort(ioc); + return -1; + } +- info->max_block = be32_to_cpu(info->max_block); + if (info->max_block < info->min_block) { + error_setg(errp, "server maximum block size %" PRIu32 + " is not valid", info->max_block); +@@ -731,14 +715,13 @@ static int nbd_receive_one_meta_context(QIOChannel *ioc, + return -1; + } + +- if (nbd_read(ioc, &local_id, sizeof(local_id), errp) < 0) { ++ if (nbd_read32(ioc, &local_id, "context id", errp) < 0) { + return -1; + } +- local_id = be32_to_cpu(local_id); + + reply.length -= sizeof(local_id); + local_name = g_malloc(reply.length + 1); +- if (nbd_read(ioc, local_name, reply.length, errp) < 0) { ++ if (nbd_read(ioc, local_name, reply.length, "context name", errp) < 0) { + g_free(local_name); + return -1; + } +@@ -896,11 +879,9 @@ static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + return -EINVAL; + } + +- if (nbd_read(ioc, &magic, sizeof(magic), errp) < 0) { +- error_prepend(errp, "Failed to read initial magic: "); ++ if (nbd_read64(ioc, &magic, "initial magic", errp) < 0) { + return -EINVAL; + } +- magic = be64_to_cpu(magic); + trace_nbd_receive_negotiate_magic(magic); + + if (magic != NBD_INIT_MAGIC) { +@@ -908,11 +889,9 @@ static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + return -EINVAL; + } + +- if (nbd_read(ioc, &magic, sizeof(magic), errp) < 0) { +- error_prepend(errp, "Failed to read server magic: "); ++ if (nbd_read64(ioc, &magic, "server magic", errp) < 0) { + return -EINVAL; + } +- magic = be64_to_cpu(magic); + trace_nbd_receive_negotiate_magic(magic); + + if (magic == NBD_OPTS_MAGIC) { +@@ -920,11 +899,9 @@ static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + uint16_t globalflags; + bool fixedNewStyle = false; + +- if (nbd_read(ioc, &globalflags, sizeof(globalflags), errp) < 0) { +- error_prepend(errp, "Failed to read server flags: "); ++ if (nbd_read16(ioc, &globalflags, "server flags", errp) < 0) { + return -EINVAL; + } +- globalflags = be16_to_cpu(globalflags); + trace_nbd_receive_negotiate_server_flags(globalflags); + if (globalflags & NBD_FLAG_FIXED_NEWSTYLE) { + fixedNewStyle = true; +@@ -992,17 +969,13 @@ static int nbd_negotiate_finish_oldstyle(QIOChannel *ioc, NBDExportInfo *info, + { + uint32_t oldflags; + +- if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) { +- error_prepend(errp, "Failed to read export length: "); ++ if (nbd_read64(ioc, &info->size, "export length", errp) < 0) { + return -EINVAL; + } +- info->size = be64_to_cpu(info->size); + +- if (nbd_read(ioc, &oldflags, sizeof(oldflags), errp) < 0) { +- error_prepend(errp, "Failed to read export flags: "); ++ if (nbd_read32(ioc, &oldflags, "export flags", errp) < 0) { + return -EINVAL; + } +- oldflags = be32_to_cpu(oldflags); + if (oldflags & ~0xffff) { + error_setg(errp, "Unexpected export flags %0x" PRIx32, oldflags); + return -EINVAL; +@@ -1079,17 +1052,13 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + } + + /* Read the response */ +- if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) { +- error_prepend(errp, "Failed to read export length: "); ++ if (nbd_read64(ioc, &info->size, "export length", errp) < 0) { + return -EINVAL; + } +- info->size = be64_to_cpu(info->size); + +- if (nbd_read(ioc, &info->flags, sizeof(info->flags), errp) < 0) { +- error_prepend(errp, "Failed to read export flags: "); ++ if (nbd_read16(ioc, &info->flags, "export flags", errp) < 0) { + return -EINVAL; + } +- info->flags = be16_to_cpu(info->flags); + break; + case 0: /* oldstyle, parse length and flags */ + if (*info->name) { +@@ -1379,7 +1348,7 @@ static int nbd_receive_simple_reply(QIOChannel *ioc, NBDSimpleReply *reply, + assert(reply->magic == NBD_SIMPLE_REPLY_MAGIC); + + ret = nbd_read(ioc, (uint8_t *)reply + sizeof(reply->magic), +- sizeof(*reply) - sizeof(reply->magic), errp); ++ sizeof(*reply) - sizeof(reply->magic), "reply", errp); + if (ret < 0) { + return ret; + } +@@ -1404,7 +1373,8 @@ static int nbd_receive_structured_reply_chunk(QIOChannel *ioc, + assert(chunk->magic == NBD_STRUCTURED_REPLY_MAGIC); + + ret = nbd_read(ioc, (uint8_t *)chunk + sizeof(chunk->magic), +- sizeof(*chunk) - sizeof(chunk->magic), errp); ++ sizeof(*chunk) - sizeof(chunk->magic), "structured chunk", ++ errp); + if (ret < 0) { + return ret; + } +diff --git a/nbd/common.c b/nbd/common.c +index 41f5ed8..cc8b278 100644 +--- a/nbd/common.c ++++ b/nbd/common.c +@@ -31,7 +31,7 @@ int nbd_drop(QIOChannel *ioc, size_t size, Error **errp) + 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), errp); ++ ret = nbd_read(ioc, buffer, MIN(65536, size), NULL, errp); + + if (ret < 0) { + goto cleanup; +diff --git a/nbd/server.c b/nbd/server.c +index cb0d563..838c150 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -438,8 +438,7 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, + error_setg(errp, "Bad length received"); + return -EINVAL; + } +- if (nbd_read(client->ioc, name, client->optlen, errp) < 0) { +- error_prepend(errp, "read failed: "); ++ if (nbd_read(client->ioc, name, client->optlen, "export name", errp) < 0) { + return -EIO; + } + name[client->optlen] = '\0'; +@@ -1046,11 +1045,9 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags, + ... Rest of request + */ + +- if (nbd_read(client->ioc, &flags, sizeof(flags), errp) < 0) { +- error_prepend(errp, "read failed: "); ++ if (nbd_read32(client->ioc, &flags, "flags", errp) < 0) { + return -EIO; + } +- flags = be32_to_cpu(flags); + trace_nbd_negotiate_options_flags(flags); + if (flags & NBD_FLAG_C_FIXED_NEWSTYLE) { + fixedNewstyle = true; +@@ -1070,30 +1067,23 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags, + uint32_t option, length; + uint64_t magic; + +- if (nbd_read(client->ioc, &magic, sizeof(magic), errp) < 0) { +- error_prepend(errp, "read failed: "); ++ if (nbd_read64(client->ioc, &magic, "opts magic", errp) < 0) { + return -EINVAL; + } +- magic = be64_to_cpu(magic); + trace_nbd_negotiate_options_check_magic(magic); + if (magic != NBD_OPTS_MAGIC) { + error_setg(errp, "Bad magic received"); + return -EINVAL; + } + +- if (nbd_read(client->ioc, &option, +- sizeof(option), errp) < 0) { +- error_prepend(errp, "read failed: "); ++ if (nbd_read32(client->ioc, &option, "option", errp) < 0) { + return -EINVAL; + } +- option = be32_to_cpu(option); + client->opt = option; + +- if (nbd_read(client->ioc, &length, sizeof(length), errp) < 0) { +- error_prepend(errp, "read failed: "); ++ if (nbd_read32(client->ioc, &length, "option length", errp) < 0) { + return -EINVAL; + } +- length = be32_to_cpu(length); + assert(!client->optlen); + client->optlen = length; + +@@ -1306,7 +1296,7 @@ static int nbd_receive_request(QIOChannel *ioc, NBDRequest *request, + uint32_t magic; + int ret; + +- ret = nbd_read(ioc, buf, sizeof(buf), errp); ++ ret = nbd_read(ioc, buf, sizeof(buf), "request", errp); + if (ret < 0) { + return ret; + } +@@ -2111,8 +2101,9 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, + } + } + if (request->type == NBD_CMD_WRITE) { +- if (nbd_read(client->ioc, req->data, request->len, errp) < 0) { +- error_prepend(errp, "reading from socket failed: "); ++ if (nbd_read(client->ioc, req->data, request->len, "CMD_WRITE data", ++ errp) < 0) ++ { + return -EIO; + } + req->complete = true; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-publish-_lookup-functions.patch b/SOURCES/kvm-nbd-publish-_lookup-functions.patch new file mode 100644 index 0000000..de1ab32 --- /dev/null +++ b/SOURCES/kvm-nbd-publish-_lookup-functions.patch @@ -0,0 +1,68 @@ +From 9dc089506f5893e309c458e5466566d112a7bb62 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:17 +0100 +Subject: [PATCH 078/163] nbd: publish _lookup functions + +RH-Author: John Snow +Message-id: <20190327172308.31077-5-jsnow@redhat.com> +Patchwork-id: 85172 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 04/55] nbd: publish _lookup functions +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +These functions are used for formatting pretty trace points. We are +going to add some in block/nbd-client, so, let's publish all these +functions at once. Note, that nbd_reply_type_lookup is already +published, and constants, "named" by these functions live in +include/block/nbd.h too. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20181102151152.288399-3-vsementsov@virtuozzo.com> +Reviewed-by: Eric Blake +Signed-off-by: Eric Blake +(cherry picked from commit 757a0d05ea22d63e667db08a80b6ec6f1d53a12f) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + include/block/nbd.h | 5 +++++ + nbd/nbd-internal.h | 5 ----- + 2 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/include/block/nbd.h b/include/block/nbd.h +index 6a5bfe5..65402d3 100644 +--- a/include/block/nbd.h ++++ b/include/block/nbd.h +@@ -343,5 +343,10 @@ static inline bool nbd_reply_is_structured(NBDReply *reply) + } + + 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); + + #endif +diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h +index eeff78d..f38be9e 100644 +--- a/nbd/nbd-internal.h ++++ b/nbd/nbd-internal.h +@@ -100,11 +100,6 @@ struct NBDTLSHandshakeData { + + void nbd_tls_handshake(QIOTask *task, + void *opaque); +-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); + + int nbd_drop(QIOChannel *ioc, size_t size, Error **errp); + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-Advertise-actual-minimum-block-size.patch b/SOURCES/kvm-nbd-server-Advertise-actual-minimum-block-size.patch new file mode 100644 index 0000000..c101dab --- /dev/null +++ b/SOURCES/kvm-nbd-server-Advertise-actual-minimum-block-size.patch @@ -0,0 +1,172 @@ +From 3086eb8eb3a2bf4aec260fea793519899432ad70 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 6 May 2019 17:56:23 +0200 +Subject: [PATCH 13/53] nbd/server: Advertise actual minimum block size + +RH-Author: John Snow +Message-id: <20190506175629.11079-14-jsnow@redhat.com> +Patchwork-id: 87196 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 13/19] nbd/server: Advertise actual minimum block size +Bugzilla: 1692018 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Thomas Huth + +From: Eric Blake + +Both NBD_CMD_BLOCK_STATUS and structured NBD_CMD_READ will split their +reply according to bdrv_block_status() boundaries. If the block device +has a request_alignment smaller than 512, but we advertise a block +alignment of 512 to the client, then this can result in the server +reply violating client expectations by reporting a smaller region of +the export than what the client is permitted to address (although this +is less of an issue for qemu 4.0 clients, given recent client patches +to overlook our non-compliance at EOF). Since it's always better to +be strict in what we send, it is worth advertising the actual minimum +block limit rather than blindly rounding it up to 512. + +Note that this patch is not foolproof - it is still possible to +provoke non-compliant server behavior using: + +$ qemu-nbd --image-opts driver=blkdebug,align=512,image.driver=file,image.filename=/path/to/non-aligned-file + +That is arguably a bug in the blkdebug driver (it should never pass +back block status smaller than its alignment, even if it has to make +multiple bdrv_get_status calls and determine the +least-common-denominator status among the group to return). It may +also be possible to observe issues with a backing layer with smaller +alignment than the active layer, although so far I have been unable to +write a reliable iotest for that scenario (but again, an issue like +that could be argued to be a bug in the block layer, or something +where we need a flag to bdrv_block_status() to state whether the +result must be aligned to the current layer's limits or can be +subdivided for accuracy when chasing backing files). + +Anyways, as blkdebug is not normally used, and as this patch makes our +server more interoperable with qemu 3.1 clients, it is worth applying +now, even while we still work on a larger patch series for the 4.1 +timeframe to have byte-accurate file lengths. + +Note that the iotests output changes - for 223 and 233, we can see the +server's better granularity advertisement; and for 241, the three test +cases have the following effects: +- natural alignment: the server's smaller alignment is now advertised, +and the hole reported at EOF is now the right result; we've gotten rid +of the server's non-compliance +- forced server alignment: the server still advertises 512 bytes, but +still sends a mid-sector hole. This is still a server compliance bug, +which needs to be fixed in the block layer in a later patch; output +does not change because the client is already being tolerant of the +non-compliance +- forced client alignment: the server's smaller alignment means that +the client now sees the server's status change mid-sector without any +protocol violations, but the fact that the map shows an unaligned +mid-sector hole is evidence of the block layer problems with aligned +block status, to be fixed in a later patch + +Signed-off-by: Eric Blake +Message-Id: <20190329042750.14704-7-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +[eblake: rebase to enhanced iotest 241 coverage] +(cherry picked from commit b0245d6478ea5906e3d7a542244d5c015fd47bc7) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 13 ++++++++----- + tests/qemu-iotests/223.out | 4 ++-- + tests/qemu-iotests/233.out | 2 +- + tests/qemu-iotests/241.out | 10 ++++++---- + 4 files changed, 17 insertions(+), 12 deletions(-) + +diff --git a/nbd/server.c b/nbd/server.c +index 9b87c7f..706f95a 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -607,13 +607,16 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags, + /* Send NBD_INFO_BLOCK_SIZE always, but tweak the minimum size + * according to whether the client requested it, and according to + * whether this is OPT_INFO or OPT_GO. */ +- /* minimum - 1 for back-compat, or 512 if client is new enough. +- * TODO: consult blk_bs(blk)->bl.request_alignment? */ +- sizes[0] = +- (client->opt == NBD_OPT_INFO || blocksize) ? BDRV_SECTOR_SIZE : 1; ++ /* minimum - 1 for back-compat, or actual if client will obey it. */ ++ if (client->opt == NBD_OPT_INFO || blocksize) { ++ sizes[0] = blk_get_request_alignment(exp->blk); ++ } else { ++ sizes[0] = 1; ++ } ++ assert(sizes[0] <= NBD_MAX_BUFFER_SIZE); + /* preferred - Hard-code to 4096 for now. + * TODO: is blk_bs(blk)->bl.opt_transfer appropriate? */ +- sizes[1] = 4096; ++ sizes[1] = MAX(4096, sizes[0]); + /* maximum - At most 32M, but smaller as appropriate. */ + sizes[2] = MIN(blk_get_max_transfer(exp->blk), NBD_MAX_BUFFER_SIZE); + trace_nbd_negotiate_handle_info_block_size(sizes[0], sizes[1], sizes[2]); +diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out +index a620f82..805cfbf 100644 +--- a/tests/qemu-iotests/223.out ++++ b/tests/qemu-iotests/223.out +@@ -41,7 +41,7 @@ exports available: 2 + export: 'n' + size: 4194304 + flags: 0x4ef ( readonly flush fua trim zeroes df cache ) +- min block: 512 ++ min block: 1 + opt block: 4096 + max block: 33554432 + available meta contexts: 2 +@@ -50,7 +50,7 @@ exports available: 2 + export: 'n2' + size: 4194304 + flags: 0x4ed ( flush fua trim zeroes df cache ) +- min block: 512 ++ min block: 1 + opt block: 4096 + max block: 33554432 + available meta contexts: 2 +diff --git a/tests/qemu-iotests/233.out b/tests/qemu-iotests/233.out +index 6d45f3b..cd83b86 100644 +--- a/tests/qemu-iotests/233.out ++++ b/tests/qemu-iotests/233.out +@@ -33,7 +33,7 @@ exports available: 1 + export: '' + size: 67108864 + flags: 0x4ed ( flush fua trim zeroes df cache ) +- min block: 512 ++ min block: 1 + opt block: 4096 + max block: 33554432 + available meta contexts: 1 +diff --git a/tests/qemu-iotests/241.out b/tests/qemu-iotests/241.out +index f22eabb..f481074 100644 +--- a/tests/qemu-iotests/241.out ++++ b/tests/qemu-iotests/241.out +@@ -3,8 +3,9 @@ QA output created by 241 + === Exporting unaligned raw image, natural alignment === + + size: 1024 +- min block: 512 +-[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET}] ++ min block: 1 ++[{ "start": 0, "length": 1000, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, ++{ "start": 1000, "length": 24, "depth": 0, "zero": true, "data": true, "offset": OFFSET}] + 1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) + + === Exporting unaligned raw image, forced server sector alignment === +@@ -20,7 +21,8 @@ WARNING: Image format was not specified for '/home/eblake/qemu/tests/qemu-iotest + === Exporting unaligned raw image, forced client sector alignment === + + size: 1024 +- min block: 512 +-[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET}] ++ min block: 1 ++[{ "start": 0, "length": 1000, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, ++{ "start": 1000, "length": 24, "depth": 0, "zero": true, "data": true, "offset": OFFSET}] + 1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-Advertise-all-contexts-in-response-to-bar.patch b/SOURCES/kvm-nbd-server-Advertise-all-contexts-in-response-to-bar.patch new file mode 100644 index 0000000..2b7a55f --- /dev/null +++ b/SOURCES/kvm-nbd-server-Advertise-all-contexts-in-response-to-bar.patch @@ -0,0 +1,48 @@ +From 1ca9e169f34ef1c548b45a05f460f69e97ae0201 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:38 +0100 +Subject: [PATCH 071/163] nbd/server: Advertise all contexts in response to + bare LIST + +RH-Author: John Snow +Message-id: <20190322032241.8111-26-jsnow@redhat.com> +Patchwork-id: 85109 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 25/28] nbd/server: Advertise all contexts in response to bare LIST +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +The NBD spec, and even our code comment, says that if the client +asks for NBD_OPT_LIST_META_CONTEXT with 0 queries, then we should +reply with (a possibly-compressed representation of) ALL contexts +that we are willing to let them try. But commit 3d068aff forgot +to advertise qemu:dirty-bitmap:FOO. + +Signed-off-by: Eric Blake +Message-Id: <20181130023232.3079982-2-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit e31d802479df9daff1994a7ed1e36bbc5bb19d03) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/nbd/server.c b/nbd/server.c +index e8fd6b4..abf03e8 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -978,6 +978,7 @@ static int nbd_negotiate_meta_queries(NBDClient *client, + if (client->opt == NBD_OPT_LIST_META_CONTEXT && !nb_queries) { + /* enable all known contexts */ + meta->base_allocation = true; ++ meta->bitmap = !!meta->exp->export_bitmap; + } else { + for (i = 0; i < nb_queries; ++i) { + ret = nbd_negotiate_meta_query(client, meta, errp); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-Don-t-fail-NBD_OPT_INFO-for-byte-aligned-.patch b/SOURCES/kvm-nbd-server-Don-t-fail-NBD_OPT_INFO-for-byte-aligned-.patch new file mode 100644 index 0000000..f5af51f --- /dev/null +++ b/SOURCES/kvm-nbd-server-Don-t-fail-NBD_OPT_INFO-for-byte-aligned-.patch @@ -0,0 +1,67 @@ +From 2b50f8a038624daf0b38795f33c7e9f5c21798a8 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 6 May 2019 17:56:27 +0200 +Subject: [PATCH 17/53] nbd/server: Don't fail NBD_OPT_INFO for byte-aligned + sources + +RH-Author: John Snow +Message-id: <20190506175629.11079-18-jsnow@redhat.com> +Patchwork-id: 87191 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 17/19] nbd/server: Don't fail NBD_OPT_INFO for byte-aligned sources +Bugzilla: 1692018 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Thomas Huth + +From: Eric Blake + +In commit 0c1d50bd, I added a couple of TODO comments about whether we +consult bl.request_alignment when responding to NBD_OPT_INFO. At the +time, qemu as server was hard-coding an advertised alignment of 512 to +clients that promised to obey constraints, and there was no function +for getting at a device's preferred alignment. But in hindsight, +advertising 512 when the block device prefers 1 caused other +compliance problems, and commit b0245d64 changed one of the two TODO +comments to advertise a more accurate alignment. Time to fix the other +TODO. Doesn't really impact qemu as client (our normal client doesn't +use NBD_OPT_INFO, and qemu-nbd --list promises to obey block sizes), +but it might prove useful to other clients. + +Fixes: b0245d64 +Signed-off-by: Eric Blake +Message-Id: <20190403030526.12258-4-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit 099fbcd65c2064a4ba8251e749bf600055027759) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 13 ++++++++----- + 1 file changed, 8 insertions(+), 5 deletions(-) + +diff --git a/nbd/server.c b/nbd/server.c +index 42f77ae..ad29373 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -643,11 +643,14 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags, + return rc; + } + +- /* If the client is just asking for NBD_OPT_INFO, but forgot to +- * request block sizes, return an error. +- * TODO: consult blk_bs(blk)->request_align, and only error if it +- * is not 1? */ +- if (client->opt == NBD_OPT_INFO && !blocksize) { ++ /* ++ * If the client is just asking for NBD_OPT_INFO, but forgot to ++ * request block sizes in a situation that would impact ++ * performance, then return an error. But for NBD_OPT_GO, we ++ * tolerate all clients, regardless of alignments. ++ */ ++ if (client->opt == NBD_OPT_INFO && !blocksize && ++ blk_get_request_alignment(exp->blk) > 1) { + return nbd_negotiate_send_rep_err(client, + NBD_REP_ERR_BLOCK_SIZE_REQD, + errp, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-Favor-u-int64_t-over-off_t.patch b/SOURCES/kvm-nbd-server-Favor-u-int64_t-over-off_t.patch new file mode 100644 index 0000000..b7fc43e --- /dev/null +++ b/SOURCES/kvm-nbd-server-Favor-u-int64_t-over-off_t.patch @@ -0,0 +1,198 @@ +From 7d033c0f2aa0c3a2c2c2b85cba48cf898284f66b Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:40 +0100 +Subject: [PATCH 102/163] nbd/server: Favor [u]int64_t over off_t + +RH-Author: John Snow +Message-id: <20190327172308.31077-28-jsnow@redhat.com> +Patchwork-id: 85192 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 27/55] nbd/server: Favor [u]int64_t over off_t +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Although our compile-time environment is set up so that we always +support long files with 64-bit off_t, we have no guarantee whether +off_t is the same type as int64_t. This requires casts when +printing values, and prevents us from directly using qemu_strtoi64() +(which will be done in the next patch). Let's just flip to uint64_t +where possible, and stick to int64_t for detecting failure of +blk_getlength(); we also keep the assertions added in the previous +patch that the resulting values fit in 63 bits. The overflow check +in nbd_co_receive_request() was already sane (request->from is +validated to fit in 63 bits, and request->len is 32 bits, so the +addition can't overflow 64 bits), but rewrite it in a form easier +to recognize as a typical overflow check. + +Rename the variable 'description' to keep line lengths reasonable. + +Suggested-by: Vladimir Sementsov-Ogievskiy +Signed-off-by: Eric Blake +Message-Id: <20190117193658.16413-7-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit 9d26dfcbab62746b3e66ec7784d75c13ff499669) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + include/block/nbd.h | 4 ++-- + nbd/server.c | 18 +++++++++--------- + qemu-nbd.c | 29 +++++++++++------------------ + 3 files changed, 22 insertions(+), 29 deletions(-) + +diff --git a/include/block/nbd.h b/include/block/nbd.h +index 1971b55..24be957 100644 +--- a/include/block/nbd.h ++++ b/include/block/nbd.h +@@ -294,8 +294,8 @@ int nbd_errno_to_system_errno(int err); + typedef struct NBDExport NBDExport; + typedef struct NBDClient NBDClient; + +-NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, +- const char *name, const char *description, ++NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset, ++ uint64_t size, const char *name, const char *desc, + const char *bitmap, uint16_t nbdflags, + void (*close)(NBDExport *), bool writethrough, + BlockBackend *on_eject_blk, Error **errp); +diff --git a/nbd/server.c b/nbd/server.c +index 51ee809..cb0d563 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -77,8 +77,8 @@ struct NBDExport { + BlockBackend *blk; + char *name; + char *description; +- off_t dev_offset; +- off_t size; ++ uint64_t dev_offset; ++ uint64_t size; + uint16_t nbdflags; + QTAILQ_HEAD(, NBDClient) clients; + QTAILQ_ENTRY(NBDExport) next; +@@ -1455,8 +1455,8 @@ static void nbd_eject_notifier(Notifier *n, void *data) + nbd_export_close(exp); + } + +-NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, +- const char *name, const char *description, ++NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset, ++ uint64_t size, const char *name, const char *desc, + const char *bitmap, uint16_t nbdflags, + void (*close)(NBDExport *), bool writethrough, + BlockBackend *on_eject_blk, Error **errp) +@@ -1495,12 +1495,12 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, + exp->refcount = 1; + QTAILQ_INIT(&exp->clients); + exp->blk = blk; +- assert(dev_offset >= 0 && dev_offset <= INT64_MAX); ++ assert(dev_offset <= INT64_MAX); + exp->dev_offset = dev_offset; + exp->name = g_strdup(name); +- exp->description = g_strdup(description); ++ exp->description = g_strdup(desc); + exp->nbdflags = nbdflags; +- assert(size >= 0 && size <= INT64_MAX - dev_offset); ++ assert(size <= INT64_MAX - dev_offset); + exp->size = QEMU_ALIGN_DOWN(size, BDRV_SECTOR_SIZE); + + if (bitmap) { +@@ -2130,10 +2130,10 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, + return -EROFS; + } + if (request->from > client->exp->size || +- request->from + request->len > client->exp->size) { ++ request->len > client->exp->size - request->from) { + error_setg(errp, "operation past EOF; From: %" PRIu64 ", Len: %" PRIu32 + ", Size: %" PRIu64, request->from, request->len, +- (uint64_t)client->exp->size); ++ client->exp->size); + return (request->type == NBD_CMD_WRITE || + request->type == NBD_CMD_WRITE_ZEROES) ? -ENOSPC : -EINVAL; + } +diff --git a/qemu-nbd.c b/qemu-nbd.c +index 5c90c5e..598caa6 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -176,7 +176,7 @@ static void read_partition(uint8_t *p, struct partition_record *r) + } + + static int find_partition(BlockBackend *blk, int partition, +- off_t *offset, off_t *size) ++ uint64_t *offset, uint64_t *size) + { + struct partition_record mbr[4]; + uint8_t data[MBR_SIZE]; +@@ -500,14 +500,14 @@ int main(int argc, char **argv) + { + BlockBackend *blk; + BlockDriverState *bs; +- off_t dev_offset = 0; ++ uint64_t dev_offset = 0; + uint16_t nbdflags = 0; + bool disconnect = false; + const char *bindto = NULL; + const char *port = NULL; + char *sockpath = NULL; + char *device = NULL; +- off_t fd_size; ++ int64_t fd_size; + QemuOpts *sn_opts = NULL; + const char *sn_id_or_name = NULL; + const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:B:"; +@@ -665,10 +665,6 @@ int main(int argc, char **argv) + error_report("Invalid offset `%s'", optarg); + exit(EXIT_FAILURE); + } +- if (dev_offset < 0) { +- error_report("Offset must be positive `%s'", optarg); +- exit(EXIT_FAILURE); +- } + break; + case 'l': + if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) { +@@ -1005,15 +1001,14 @@ int main(int argc, char **argv) + } + + if (dev_offset >= fd_size) { +- error_report("Offset (%lld) has to be smaller than the image size " +- "(%lld)", +- (long long int)dev_offset, (long long int)fd_size); ++ error_report("Offset (%" PRIu64 ") has to be smaller than the image " ++ "size (%" PRId64 ")", dev_offset, fd_size); + exit(EXIT_FAILURE); + } + fd_size -= dev_offset; + + if (partition != -1) { +- off_t limit; ++ uint64_t limit; + + if (dev_offset) { + error_report("Cannot request partition and offset together"); +@@ -1027,15 +1022,13 @@ int main(int argc, char **argv) + } + /* + * MBR partition limits are (32-bit << 9); this assert lets +- * the compiler know that we have two positive values that +- * can't overflow 64 bits. ++ * the compiler know that we can't overflow 64 bits. + */ +- assert(dev_offset >= 0 && dev_offset + limit >= dev_offset); ++ assert(dev_offset + limit >= dev_offset); + if (dev_offset + limit > fd_size) { +- error_report("Discovered partition %d at offset %lld size %lld, " +- "but size exceeds file length %lld", partition, +- (long long int) dev_offset, (long long int) limit, +- (long long int) fd_size); ++ error_report("Discovered partition %d at offset %" PRIu64 ++ " size %" PRIu64 ", but size exceeds file length %" ++ PRId64, partition, dev_offset, limit, fd_size); + exit(EXIT_FAILURE); + } + fd_size = limit; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-Fix-blockstatus-trace.patch b/SOURCES/kvm-nbd-server-Fix-blockstatus-trace.patch new file mode 100644 index 0000000..eb575d9 --- /dev/null +++ b/SOURCES/kvm-nbd-server-Fix-blockstatus-trace.patch @@ -0,0 +1,68 @@ +From cd17af9ea6c8ef48387770a2fd65c93b93f0de65 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 6 May 2019 17:56:25 +0200 +Subject: [PATCH 15/53] nbd/server: Fix blockstatus trace + +RH-Author: John Snow +Message-id: <20190506175629.11079-16-jsnow@redhat.com> +Patchwork-id: 87197 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 15/19] nbd/server: Fix blockstatus trace +Bugzilla: 1692018 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Thomas Huth + +From: Eric Blake + +Don't increment remaining_bytes until we know that we will actually be +including the current block status extent in the reply; otherwise, the +value traced will include a bytes value that is oversized by the +length of the next block status extent which did not get sent because +it instead ended the loop. + +Fixes: fb7afc79 +Signed-off-by: Eric Blake +Message-Id: <20190403030526.12258-2-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit 2178a569be16b18f04ed854d24fd2281b6a429c5) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 9 +++------ + 1 file changed, 3 insertions(+), 6 deletions(-) + +diff --git a/nbd/server.c b/nbd/server.c +index 706f95a..6ae2154 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -1880,17 +1880,12 @@ static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset, + + flags = (ret & BDRV_BLOCK_ALLOCATED ? 0 : NBD_STATE_HOLE) | + (ret & BDRV_BLOCK_ZERO ? NBD_STATE_ZERO : 0); +- offset += num; +- remaining_bytes -= num; + + if (first_extent) { + extent->flags = flags; + extent->length = num; + first_extent = false; +- continue; +- } +- +- if (flags == extent->flags) { ++ } else if (flags == extent->flags) { + /* extend current extent */ + extent->length += num; + } else { +@@ -1903,6 +1898,8 @@ static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset, + extent->flags = flags; + extent->length = num; + } ++ offset += num; ++ remaining_bytes -= num; + } + + extents_end = extent + 1; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-Fix-dirty-bitmap-logic-regression.patch b/SOURCES/kvm-nbd-server-Fix-dirty-bitmap-logic-regression.patch new file mode 100644 index 0000000..ef2267b --- /dev/null +++ b/SOURCES/kvm-nbd-server-Fix-dirty-bitmap-logic-regression.patch @@ -0,0 +1,54 @@ +From 05a40305eb495ff14c9bfcb1aa94c168b54d79ce Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:55:08 +0200 +Subject: [PATCH 83/89] nbd/server: Fix dirty bitmap logic regression + +RH-Author: John Snow +Message-id: <20180718225511.14878-33-jsnow@redhat.com> +Patchwork-id: 81427 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 32/35] nbd/server: Fix dirty bitmap logic regression +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Eric Blake + +In my hurry to fix a build failure, I introduced a logic bug. +The assertion conditional is backwards, meaning that qemu will +now abort instead of reporting dirty bitmap status. + +The bug can only be tickled by an NBD client using an exported +dirty bitmap (which is still an experimental QMP command), so +it's not the end of the world for supported usage (and neither +'make check' nor qemu-iotests fails); but it also shows that we +really want qemu-io support for reading dirty bitmaps if only +so that I can add iotests coverage to prevent future +brown-bag-of-shame events like this one. + +Fixes: 45eb6fb6 +Signed-off-by: Eric Blake +Message-Id: <20180622153509.375130-1-eblake@redhat.com> +(cherry picked from commit 7606c99a0421be7e9d984766fe239f7791a2fd9c) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/nbd/server.c b/nbd/server.c +index 50ac8bf..e52b76b 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -1977,7 +1977,7 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset, + + bdrv_dirty_bitmap_unlock(bitmap); + +- assert(offset > end); ++ assert(offset < end); + *length = end - offset; + return i; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-Hoist-length-check-to-qmp_nbd_server_add.patch b/SOURCES/kvm-nbd-server-Hoist-length-check-to-qmp_nbd_server_add.patch new file mode 100644 index 0000000..a4fb3d9 --- /dev/null +++ b/SOURCES/kvm-nbd-server-Hoist-length-check-to-qmp_nbd_server_add.patch @@ -0,0 +1,106 @@ +From 0591484b860fb6e25f86eceec0ba2e8b8d1673c4 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:39 +0100 +Subject: [PATCH 101/163] nbd/server: Hoist length check to qmp_nbd_server_add + +RH-Author: John Snow +Message-id: <20190327172308.31077-27-jsnow@redhat.com> +Patchwork-id: 85201 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 26/55] nbd/server: Hoist length check to qmp_nbd_server_add +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +We only had two callers to nbd_export_new; qemu-nbd.c always +passed a valid offset/length pair (because it already checked +the file length, to ensure that offset was in bounds), while +blockdev-nbd.c always passed 0/-1. Then nbd_export_new reduces +the size to a multiple of BDRV_SECTOR_SIZE (can only happen +when offset is not sector-aligned, since bdrv_getlength() +currently rounds up) (someday, it would be nice to have +byte-accurate lengths - but not today). + +However, I'm finding it easier to work with the code if we are +consistent on having both callers pass in a valid length, and +just assert that things are sane in nbd_export_new, meaning +that no negative values were passed, and that offset+size does +not exceed 63 bits (as that really is a fundamental limit to +later operations, whether we use off_t or uint64_t). + +Signed-off-by: Eric Blake +Message-Id: <20190117193658.16413-6-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit 7596bbb390838359e4789996f349bda0cad56b0e) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev-nbd.c | 10 +++++++++- + nbd/server.c | 10 +++------- + 2 files changed, 12 insertions(+), 8 deletions(-) + +diff --git a/blockdev-nbd.c b/blockdev-nbd.c +index c76d541..d73ac1b 100644 +--- a/blockdev-nbd.c ++++ b/blockdev-nbd.c +@@ -146,6 +146,7 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name, + BlockDriverState *bs = NULL; + BlockBackend *on_eject_blk; + NBDExport *exp; ++ int64_t len; + + if (!nbd_server) { + error_setg(errp, "NBD server not running"); +@@ -168,6 +169,13 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name, + return; + } + ++ len = bdrv_getlength(bs); ++ if (len < 0) { ++ error_setg_errno(errp, -len, ++ "Failed to determine the NBD export's length"); ++ return; ++ } ++ + if (!has_writable) { + writable = false; + } +@@ -175,7 +183,7 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name, + writable = false; + } + +- exp = nbd_export_new(bs, 0, -1, name, NULL, bitmap, ++ exp = nbd_export_new(bs, 0, len, name, NULL, bitmap, + writable ? 0 : NBD_FLAG_READ_ONLY, + NULL, false, on_eject_blk, errp); + if (!exp) { +diff --git a/nbd/server.c b/nbd/server.c +index 6b13601..51ee809 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -1495,17 +1495,13 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, + exp->refcount = 1; + QTAILQ_INIT(&exp->clients); + exp->blk = blk; ++ assert(dev_offset >= 0 && dev_offset <= INT64_MAX); + exp->dev_offset = dev_offset; + exp->name = g_strdup(name); + exp->description = g_strdup(description); + exp->nbdflags = nbdflags; +- exp->size = size < 0 ? blk_getlength(blk) : size; +- if (exp->size < 0) { +- error_setg_errno(errp, -exp->size, +- "Failed to determine the NBD export's length"); +- goto fail; +- } +- exp->size -= exp->size % BDRV_SECTOR_SIZE; ++ assert(size >= 0 && size <= INT64_MAX - dev_offset); ++ exp->size = QEMU_ALIGN_DOWN(size, BDRV_SECTOR_SIZE); + + if (bitmap) { + BdrvDirtyBitmap *bm = NULL; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-Ignore-write-errors-when-replying-to-NBD_.patch b/SOURCES/kvm-nbd-server-Ignore-write-errors-when-replying-to-NBD_.patch new file mode 100644 index 0000000..8163c38 --- /dev/null +++ b/SOURCES/kvm-nbd-server-Ignore-write-errors-when-replying-to-NBD_.patch @@ -0,0 +1,65 @@ +From d706e465682307df95a723e1a74b32c0db93e259 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:30 +0100 +Subject: [PATCH 063/163] nbd/server: Ignore write errors when replying to + NBD_OPT_ABORT +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190322032241.8111-18-jsnow@redhat.com> +Patchwork-id: 85105 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 17/28] nbd/server: Ignore write errors when replying to NBD_OPT_ABORT +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Commit 37ec36f6 intentionally ignores errors when trying to reply +to an NBD_OPT_ABORT request for plaintext clients, but did not make +the same change for a TLS server. Since NBD_OPT_ABORT is +documented as being a potential for an EPIPE when the client hangs +up without waiting for our reply, we don't need to pollute the +server's output with that failure. + +Signed-off-by: Eric Blake +Message-Id: <20181117223221.2198751-1-eblake@redhat.com> +Reviewed-by: Daniel P. Berrangé +(cherry picked from commit 3e99ebb9d3df15ce0ecf1b435816c9c46ee9a1ad) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +diff --git a/nbd/server.c b/nbd/server.c +index 418b1d4..e8fd6b4 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -1134,12 +1134,16 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags, + return -EINVAL; + + default: +- ret = nbd_opt_drop(client, NBD_REP_ERR_TLS_REQD, errp, ++ /* Let the client keep trying, unless they asked to ++ * quit. Always try to give an error back to the ++ * client; but when replying to OPT_ABORT, be aware ++ * that the client may hang up before receiving the ++ * error, in which case we are fine ignoring the ++ * resulting EPIPE. */ ++ ret = nbd_opt_drop(client, NBD_REP_ERR_TLS_REQD, ++ option == NBD_OPT_ABORT ? NULL : errp, + "Option 0x%" PRIx32 + " not permitted before TLS", option); +- /* Let the client keep trying, unless they asked to +- * quit. In this mode, we've already sent an error, so +- * we can't ack the abort. */ + if (option == NBD_OPT_ABORT) { + return 1; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-Kill-pointless-shadowed-variable.patch b/SOURCES/kvm-nbd-server-Kill-pointless-shadowed-variable.patch new file mode 100644 index 0000000..b77828d --- /dev/null +++ b/SOURCES/kvm-nbd-server-Kill-pointless-shadowed-variable.patch @@ -0,0 +1,51 @@ +From 19dc347405263456be2c13750a97ee4567665c58 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:23:04 +0100 +Subject: [PATCH 126/163] nbd/server: Kill pointless shadowed variable + +RH-Author: John Snow +Message-id: <20190327172308.31077-52-jsnow@redhat.com> +Patchwork-id: 85217 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 51/55] nbd/server: Kill pointless shadowed variable +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +lgtm.com pointed out that commit 678ba275 introduced a shadowed +declaration of local variable 'bs'; thankfully, the inner 'bs' +obtained by 'blk_bs(blk)' matches the outer one given that we had +'blk_insert_bs(blk, bs, errp)' a few lines earlier, and there are +no later uses of 'bs' beyond the scope of the 'if (bitmap)' to +care if we change the value stored in 'bs' while traveling the +backing chain to find a bitmap. So simply get rid of the extra +declaration. + +Reported-by: Peter Maydell +Signed-off-by: Eric Blake +Message-Id: <20190207191357.6665-1-eblake@redhat.com> +Signed-off-by: Eric Blake +(cherry picked from commit 269ee27e99cfbff983a9ab067ae22f6182f11fe2) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/nbd/server.c b/nbd/server.c +index 838c150..0910d09 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -1495,7 +1495,6 @@ NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset, + + if (bitmap) { + BdrvDirtyBitmap *bm = NULL; +- BlockDriverState *bs = blk_bs(blk); + + while (true) { + bm = bdrv_find_dirty_bitmap(bs, bitmap); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-Reject-0-length-block-status-request.patch b/SOURCES/kvm-nbd-server-Reject-0-length-block-status-request.patch new file mode 100644 index 0000000..af8d69d --- /dev/null +++ b/SOURCES/kvm-nbd-server-Reject-0-length-block-status-request.patch @@ -0,0 +1,51 @@ +From 32058dd38df873a275bc004c53b5ae7e74af6871 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:54:59 +0200 +Subject: [PATCH 74/89] nbd/server: Reject 0-length block status request + +RH-Author: John Snow +Message-id: <20180718225511.14878-24-jsnow@redhat.com> +Patchwork-id: 81423 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 23/35] nbd/server: Reject 0-length block status request +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Eric Blake + +The NBD spec says that behavior is unspecified if the client +requests 0 length for block status; but since the structured +reply is documenting as returning a non-zero length, it's +easier to just diagnose this with an EINVAL error than to +figure out what to return. + +CC: qemu-stable@nongnu.org +Signed-off-by: Eric Blake +Message-Id: <20180621124937.166549-1-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit d8b20291cba6aa9bb295885a34f2b5f05d59d1b2) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/nbd/server.c b/nbd/server.c +index 9e1f227..493a926 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -2007,6 +2007,10 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, + "discard failed", errp); + + case NBD_CMD_BLOCK_STATUS: ++ if (!request->len) { ++ return nbd_send_generic_reply(client, request->handle, -EINVAL, ++ "need non-zero length", errp); ++ } + if (client->export_meta.valid && client->export_meta.base_allocation) { + return nbd_co_send_block_status(client, request->handle, + blk_bs(exp->blk), request->from, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-Silence-gcc-false-positive.patch b/SOURCES/kvm-nbd-server-Silence-gcc-false-positive.patch new file mode 100644 index 0000000..b5e8b0f --- /dev/null +++ b/SOURCES/kvm-nbd-server-Silence-gcc-false-positive.patch @@ -0,0 +1,56 @@ +From 71b082ce48653cb70bf628f753d6e7750f8d03c2 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:55:07 +0200 +Subject: [PATCH 82/89] nbd/server: Silence gcc false positive + +RH-Author: John Snow +Message-id: <20180718225511.14878-32-jsnow@redhat.com> +Patchwork-id: 81425 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 31/35] nbd/server: Silence gcc false positive +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Eric Blake + +The code has a while() loop that always initialized 'end', and +the loop always executes at least once (as evidenced by the assert() +just prior to the loop). But some versions of gcc still complain +that 'end' is used uninitialized, so silence them. + +Signed-off-by: Eric Blake +Reviewed-by: Peter Maydell +Message-id: 20180622125814.345274-1-eblake@redhat.com +Signed-off-by: Peter Maydell +(cherry picked from commit 45eb6fb6cea28cdc937764aac6585751047bb294) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/nbd/server.c b/nbd/server.c +index 2746046..50ac8bf 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -1937,7 +1937,7 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset, + unsigned int nb_extents, + bool dont_fragment) + { +- uint64_t begin = offset, end; ++ uint64_t begin = offset, end = offset; + uint64_t overall_end = offset + *length; + unsigned int i = 0; + BdrvDirtyBitmapIter *it; +@@ -1977,6 +1977,7 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset, + + bdrv_dirty_bitmap_unlock(bitmap); + ++ assert(offset > end); + *length = end - offset; + return i; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-Trace-client-noncompliance-on-unaligned-r.patch b/SOURCES/kvm-nbd-server-Trace-client-noncompliance-on-unaligned-r.patch new file mode 100644 index 0000000..f3ce0de --- /dev/null +++ b/SOURCES/kvm-nbd-server-Trace-client-noncompliance-on-unaligned-r.patch @@ -0,0 +1,140 @@ +From a3c2d291823493cd39a79e0b2a38c9af29090cf0 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 6 May 2019 17:56:26 +0200 +Subject: [PATCH 16/53] nbd/server: Trace client noncompliance on unaligned + requests + +RH-Author: John Snow +Message-id: <20190506175629.11079-17-jsnow@redhat.com> +Patchwork-id: 87198 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 16/19] nbd/server: Trace client noncompliance on unaligned requests +Bugzilla: 1692018 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Thomas Huth + +From: Eric Blake + +We've recently added traces for clients to flag server non-compliance; +let's do the same for servers to flag client non-compliance. According +to the spec, if the client requests NBD_INFO_BLOCK_SIZE, it is +promising to send all requests aligned to those boundaries. Of +course, if the client does not request NBD_INFO_BLOCK_SIZE, then it +made no promises so we shouldn't flag anything; and because we are +willing to handle clients that made no promises (the spec allows us to +use NBD_REP_ERR_BLOCK_SIZE_REQD if we had been unwilling), we already +have to handle unaligned requests (which the block layer already does +on our behalf). So even though the spec allows us to return EINVAL +for clients that promised to behave, it's easier to always answer +unaligned requests. Still, flagging non-compliance can be useful in +debugging a client that is trying to be maximally portable. + +Qemu as client used to have one spot where it sent non-compliant +requests: if the server sends an unaligned reply to +NBD_CMD_BLOCK_STATUS, and the client was iterating over the entire +disk, the next request would start at that unaligned point; this was +fixed in commit a39286dd when the client was taught to work around +server non-compliance; but is equally fixed if the server is patched +to not send unaligned replies in the first place (yes, qemu 4.0 as +server still has few such bugs, although they will be patched in +4.1). Fortunately, I did not find any more spots where qemu as client +was non-compliant. I was able to test the patch by using the following +hack to convince qemu-io to run various unaligned commands, coupled +with serving 512-byte alignment by intentionally omitting '-f raw' on +the server while viewing server traces. + +| diff --git i/nbd/client.c w/nbd/client.c +| index 427980bdd22..1858b2aac35 100644 +| --- i/nbd/client.c +| +++ w/nbd/client.c +| @@ -449,6 +449,7 @@ static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, +| nbd_send_opt_abort(ioc); +| return -1; +| } +| + info->min_block = 1;//hack +| 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); + +Signed-off-by: Eric Blake +Message-Id: <20190403030526.12258-3-eblake@redhat.com> +[eblake: address minor review nits] +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit 6e280648d21d8c0aa8a101b62d0732cd1e608743) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 17 ++++++++++++++++- + nbd/trace-events | 1 + + 2 files changed, 17 insertions(+), 1 deletion(-) + +diff --git a/nbd/server.c b/nbd/server.c +index 6ae2154..42f77ae 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -124,6 +124,8 @@ struct NBDClient { + int nb_requests; + bool closing; + ++ uint32_t check_align; /* If non-zero, check for aligned client requests */ ++ + bool structured_reply; + NBDExportMetaContexts export_meta; + +@@ -533,6 +535,7 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags, + bool blocksize = false; + uint32_t sizes[3]; + char buf[sizeof(uint64_t) + sizeof(uint16_t)]; ++ uint32_t check_align = 0; + + /* Client sends: + 4 bytes: L, name length (can be 0) +@@ -609,7 +612,7 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags, + * whether this is OPT_INFO or OPT_GO. */ + /* minimum - 1 for back-compat, or actual if client will obey it. */ + if (client->opt == NBD_OPT_INFO || blocksize) { +- sizes[0] = blk_get_request_alignment(exp->blk); ++ check_align = sizes[0] = blk_get_request_alignment(exp->blk); + } else { + sizes[0] = 1; + } +@@ -660,6 +663,7 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags, + + if (client->opt == NBD_OPT_GO) { + client->exp = exp; ++ client->check_align = check_align; + QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); + nbd_export_get(client->exp); + nbd_check_meta_export(client); +@@ -2126,6 +2130,17 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, + return (request->type == NBD_CMD_WRITE || + request->type == NBD_CMD_WRITE_ZEROES) ? -ENOSPC : -EINVAL; + } ++ if (client->check_align && !QEMU_IS_ALIGNED(request->from | request->len, ++ client->check_align)) { ++ /* ++ * The block layer gracefully handles unaligned requests, but ++ * it's still worth tracing client non-compliance ++ */ ++ trace_nbd_co_receive_align_compliance(nbd_cmd_lookup(request->type), ++ request->from, ++ request->len, ++ client->check_align); ++ } + valid_flags = NBD_CMD_FLAG_FUA; + if (request->type == NBD_CMD_READ && client->structured_reply) { + valid_flags |= NBD_CMD_FLAG_DF; +diff --git a/nbd/trace-events b/nbd/trace-events +index 7f10ebd..bd42cc8 100644 +--- a/nbd/trace-events ++++ b/nbd/trace-events +@@ -71,4 +71,5 @@ nbd_co_send_structured_error(uint64_t handle, int err, const char *errname, cons + nbd_co_receive_request_decode_type(uint64_t handle, uint16_t type, const char *name) "Decoding type: handle = %" PRIu64 ", type = %" PRIu16 " (%s)" + nbd_co_receive_request_payload_received(uint64_t handle, uint32_t len) "Payload received: handle = %" PRIu64 ", len = %" PRIu32 + nbd_co_receive_request_cmd_write(uint32_t len) "Reading %" PRIu32 " byte(s)" ++nbd_co_receive_align_compliance(const char *op, uint64_t from, uint32_t len, uint32_t align) "client sent non-compliant unaligned %s request: from=0x%" PRIx64 ", len=0x%" PRIx32 ", align=0x%" PRIx32 + nbd_trip(void) "Reading request" +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-add-nbd_meta_empty_or_pattern-helper.patch b/SOURCES/kvm-nbd-server-add-nbd_meta_empty_or_pattern-helper.patch new file mode 100644 index 0000000..d5a7ff6 --- /dev/null +++ b/SOURCES/kvm-nbd-server-add-nbd_meta_empty_or_pattern-helper.patch @@ -0,0 +1,163 @@ +From 06d93467b8c40769e7b6c39a41594894fe440a9f Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:55:02 +0200 +Subject: [PATCH 77/89] nbd/server: add nbd_meta_empty_or_pattern helper + +RH-Author: John Snow +Message-id: <20180718225511.14878-27-jsnow@redhat.com> +Patchwork-id: 81398 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 26/35] nbd/server: add nbd_meta_empty_or_pattern helper +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +Add nbd_meta_pattern() and nbd_meta_empty_or_pattern() helpers for +metadata query parsing. nbd_meta_pattern() will be reused for the +"qemu" namespace in following patches. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20180609151758.17343-4-vsementsov@virtuozzo.com> +Reviewed-by: Eric Blake +[eblake: comment tweaks] +Signed-off-by: Eric Blake +(cherry picked from commit b0769d8f8df0b51881f1f15c9e29722cf6191a43) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 89 ++++++++++++++++++++++++++++++++++++++++++------------------ + 1 file changed, 62 insertions(+), 27 deletions(-) + +diff --git a/nbd/server.c b/nbd/server.c +index 26cc41a..9171cd4 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -733,53 +733,87 @@ static int nbd_negotiate_send_meta_context(NBDClient *client, + return qio_channel_writev_all(client->ioc, iov, 2, errp) < 0 ? -EIO : 0; + } + +-/* nbd_meta_base_query +- * +- * Handle queries to 'base' namespace. For now, only the base:allocation +- * context is available. 'len' is the amount of text remaining to be read from +- * the current name, after the 'base:' portion has been stripped. ++/* Read strlen(@pattern) bytes, and set @match to true if they match @pattern. ++ * @match is never set to false. + * + * Return -errno on I/O error, 0 if option was completely handled by + * sending a reply about inconsistent lengths, or 1 on success. + * +- * Note: return code = 1 doesn't mean that we've parsed the "base:allocation" +- * namespace. It only means that there are no errors. ++ * Note: return code = 1 doesn't mean that we've read exactly @pattern. ++ * It only means that there are no errors. + */ +-static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta, +- uint32_t len, Error **errp) ++static int nbd_meta_pattern(NBDClient *client, const char *pattern, bool *match, ++ Error **errp) + { + int ret; +- char query[sizeof("allocation") - 1]; +- size_t alen = strlen("allocation"); +- +- if (len == 0) { +- if (client->opt == NBD_OPT_LIST_META_CONTEXT) { +- meta->base_allocation = true; +- } +- trace_nbd_negotiate_meta_query_parse("base:"); +- return 1; +- } ++ char *query; ++ size_t len = strlen(pattern); + +- if (len != alen) { +- trace_nbd_negotiate_meta_query_skip("not base:allocation"); +- return nbd_opt_skip(client, len, errp); +- } ++ assert(len); + ++ query = g_malloc(len); + ret = nbd_opt_read(client, query, len, errp); + if (ret <= 0) { ++ g_free(query); + return ret; + } + +- if (strncmp(query, "allocation", alen) == 0) { +- trace_nbd_negotiate_meta_query_parse("base:allocation"); +- meta->base_allocation = true; ++ if (strncmp(query, pattern, len) == 0) { ++ trace_nbd_negotiate_meta_query_parse(pattern); ++ *match = true; + } else { +- trace_nbd_negotiate_meta_query_skip("not base:allocation"); ++ trace_nbd_negotiate_meta_query_skip("pattern not matched"); + } ++ g_free(query); + + return 1; + } + ++/* ++ * Read @len bytes, and set @match to true if they match @pattern, or if @len ++ * is 0 and the client is performing _LIST_. @match is never set to false. ++ * ++ * Return -errno on I/O error, 0 if option was completely handled by ++ * sending a reply about inconsistent lengths, or 1 on success. ++ * ++ * Note: return code = 1 doesn't mean that we've read exactly @pattern. ++ * It only means that there are no errors. ++ */ ++static int nbd_meta_empty_or_pattern(NBDClient *client, const char *pattern, ++ uint32_t len, bool *match, Error **errp) ++{ ++ if (len == 0) { ++ if (client->opt == NBD_OPT_LIST_META_CONTEXT) { ++ *match = true; ++ } ++ trace_nbd_negotiate_meta_query_parse("empty"); ++ return 1; ++ } ++ ++ if (len != strlen(pattern)) { ++ trace_nbd_negotiate_meta_query_skip("different lengths"); ++ return nbd_opt_skip(client, len, errp); ++ } ++ ++ return nbd_meta_pattern(client, pattern, match, errp); ++} ++ ++/* nbd_meta_base_query ++ * ++ * Handle queries to 'base' namespace. For now, only the base:allocation ++ * context is available. 'len' is the amount of text remaining to be read from ++ * the current name, after the 'base:' portion has been stripped. ++ * ++ * Return -errno on I/O error, 0 if option was completely handled by ++ * sending a reply about inconsistent lengths, or 1 on success. ++ */ ++static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta, ++ uint32_t len, Error **errp) ++{ ++ return nbd_meta_empty_or_pattern(client, "allocation", len, ++ &meta->base_allocation, errp); ++} ++ + /* nbd_negotiate_meta_query + * + * Parse namespace name and call corresponding function to parse body of the +@@ -823,6 +857,7 @@ static int nbd_negotiate_meta_query(NBDClient *client, + return nbd_opt_skip(client, len, errp); + } + ++ trace_nbd_negotiate_meta_query_parse("base:"); + return nbd_meta_base_query(client, meta, len, errp); + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-drop-old-style-negotiation.patch b/SOURCES/kvm-nbd-server-drop-old-style-negotiation.patch new file mode 100644 index 0000000..75d60ad --- /dev/null +++ b/SOURCES/kvm-nbd-server-drop-old-style-negotiation.patch @@ -0,0 +1,177 @@ +From 15a4aec1e61d9b1e1ad212fc60058d9ee95620f7 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:25 +0100 +Subject: [PATCH 058/163] nbd/server: drop old-style negotiation + +RH-Author: John Snow +Message-id: <20190322032241.8111-13-jsnow@redhat.com> +Patchwork-id: 85110 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 12/28] nbd/server: drop old-style negotiation +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +After the previous commit, nbd_client_new's first parameter is always +NULL. Let's drop it with all corresponding old-style negotiation code +path which is unreachable now. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20181003170228.95973-3-vsementsov@virtuozzo.com> +Reviewed-by: Eric Blake +[eblake: re-wrap short line] +Signed-off-by: Eric Blake +(cherry picked from commit 7f7dfe2a53446072c136d349e3150c84d322b2bc) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + blockdev-nbd.c | 3 +-- + include/block/nbd.h | 3 +-- + nbd/server.c | 53 +++++++++++++++-------------------------------------- + qemu-nbd.c | 2 +- + 4 files changed, 18 insertions(+), 43 deletions(-) + +diff --git a/blockdev-nbd.c b/blockdev-nbd.c +index 1ef1104..1d170c8 100644 +--- a/blockdev-nbd.c ++++ b/blockdev-nbd.c +@@ -36,8 +36,7 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc, + gpointer opaque) + { + qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server"); +- nbd_client_new(NULL, cioc, +- nbd_server->tlscreds, NULL, ++ nbd_client_new(cioc, nbd_server->tlscreds, NULL, + nbd_blockdev_client_closed); + } + +diff --git a/include/block/nbd.h b/include/block/nbd.h +index a53b0cf..6a5bfe5 100644 +--- a/include/block/nbd.h ++++ b/include/block/nbd.h +@@ -310,8 +310,7 @@ void nbd_export_set_name(NBDExport *exp, const char *name); + void nbd_export_set_description(NBDExport *exp, const char *description); + void nbd_export_close_all(void); + +-void nbd_client_new(NBDExport *exp, +- QIOChannelSocket *sioc, ++void nbd_client_new(QIOChannelSocket *sioc, + QCryptoTLSCreds *tlscreds, + const char *tlsaclname, + void (*close_fn)(NBDClient *, bool)); +diff --git a/nbd/server.c b/nbd/server.c +index df76324..d414861 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -1253,7 +1253,6 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) + const uint16_t myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM | + NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA | + NBD_FLAG_SEND_WRITE_ZEROES | NBD_FLAG_SEND_CACHE); +- bool oldStyle; + + /* Old style negotiation header, no room for options + [ 0 .. 7] passwd ("NBDMAGIC") +@@ -1274,33 +1273,19 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) + trace_nbd_negotiate_begin(); + memcpy(buf, "NBDMAGIC", 8); + +- oldStyle = client->exp != NULL && !client->tlscreds; +- if (oldStyle) { +- trace_nbd_negotiate_old_style(client->exp->size, +- client->exp->nbdflags | myflags); +- stq_be_p(buf + 8, NBD_CLIENT_MAGIC); +- stq_be_p(buf + 16, client->exp->size); +- stl_be_p(buf + 24, client->exp->nbdflags | myflags); ++ stq_be_p(buf + 8, NBD_OPTS_MAGIC); ++ stw_be_p(buf + 16, NBD_FLAG_FIXED_NEWSTYLE | NBD_FLAG_NO_ZEROES); + +- if (nbd_write(client->ioc, buf, sizeof(buf), errp) < 0) { +- error_prepend(errp, "write failed: "); +- return -EINVAL; +- } +- } else { +- stq_be_p(buf + 8, NBD_OPTS_MAGIC); +- stw_be_p(buf + 16, NBD_FLAG_FIXED_NEWSTYLE | NBD_FLAG_NO_ZEROES); +- +- if (nbd_write(client->ioc, buf, 18, errp) < 0) { +- error_prepend(errp, "write failed: "); +- return -EINVAL; +- } +- ret = nbd_negotiate_options(client, myflags, errp); +- if (ret != 0) { +- if (ret < 0) { +- error_prepend(errp, "option negotiation failed: "); +- } +- return ret; ++ if (nbd_write(client->ioc, buf, 18, errp) < 0) { ++ error_prepend(errp, "write failed: "); ++ return -EINVAL; ++ } ++ ret = nbd_negotiate_options(client, myflags, errp); ++ if (ret != 0) { ++ if (ret < 0) { ++ error_prepend(errp, "option negotiation failed: "); + } ++ return ret; + } + + assert(!client->optlen); +@@ -2396,13 +2381,8 @@ static void nbd_client_receive_next_request(NBDClient *client) + static coroutine_fn void nbd_co_client_start(void *opaque) + { + NBDClient *client = opaque; +- NBDExport *exp = client->exp; + Error *local_err = NULL; + +- if (exp) { +- nbd_export_get(exp); +- QTAILQ_INSERT_TAIL(&exp->clients, client, next); +- } + qemu_co_mutex_init(&client->send_lock); + + if (nbd_negotiate(client, &local_err)) { +@@ -2417,13 +2397,11 @@ static coroutine_fn void nbd_co_client_start(void *opaque) + } + + /* +- * Create a new client listener on the given export @exp, using the +- * given channel @sioc. Begin servicing it in a coroutine. When the +- * connection closes, call @close_fn with an indication of whether the +- * client completed negotiation. ++ * Create a new client listener using the given channel @sioc. ++ * Begin servicing it in a coroutine. When the connection closes, call ++ * @close_fn with an indication of whether the client completed negotiation. + */ +-void nbd_client_new(NBDExport *exp, +- QIOChannelSocket *sioc, ++void nbd_client_new(QIOChannelSocket *sioc, + QCryptoTLSCreds *tlscreds, + const char *tlsaclname, + void (*close_fn)(NBDClient *, bool)) +@@ -2433,7 +2411,6 @@ void nbd_client_new(NBDExport *exp, + + client = g_new0(NBDClient, 1); + client->refcount = 1; +- client->exp = exp; + client->tlscreds = tlscreds; + if (tlscreds) { + object_ref(OBJECT(client->tlscreds)); +diff --git a/qemu-nbd.c b/qemu-nbd.c +index 6aaebe7..e76fe30 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -354,7 +354,7 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc, + + nb_fds++; + nbd_update_server_watch(); +- nbd_client_new(NULL, cioc, tlscreds, NULL, nbd_client_closed); ++ nbd_client_new(cioc, tlscreds, NULL, nbd_client_closed); + } + + static void nbd_update_server_watch(void) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-fix-NBD_CMD_CACHE.patch b/SOURCES/kvm-nbd-server-fix-NBD_CMD_CACHE.patch new file mode 100644 index 0000000..191abe1 --- /dev/null +++ b/SOURCES/kvm-nbd-server-fix-NBD_CMD_CACHE.patch @@ -0,0 +1,52 @@ +From 1abf2fdfb42f49715458e6b3c01b431c9522926a Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Tue, 9 Oct 2018 22:14:31 +0200 +Subject: [PATCH 01/22] nbd/server: fix NBD_CMD_CACHE + +RH-Author: John Snow +Message-id: <20181009221432.18359-2-jsnow@redhat.com> +Patchwork-id: 82508 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/2] nbd/server: fix NBD_CMD_CACHE +Bugzilla: 1636148 +RH-Acked-by: Eric Blake +RH-Acked-by: Thomas Huth +RH-Acked-by: Stefan Hajnoczi + +From: Vladimir Sementsov-Ogievskiy + +We should not go to structured-read branch on CACHE command, fix that. + +Bug introduced in bc37b06a5cde24 "nbd/server: introduce NBD_CMD_CACHE" +with the whole feature and affects 3.0.0 release. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +CC: qemu-stable@nongnu.org +Message-Id: <20181003144738.70670-1-vsementsov@virtuozzo.com> +Reviewed-by: Eric Blake +[eblake: commit message typo fix] +Signed-off-by: Eric Blake +(cherry picked from commit 2f454defc23e1be78f2a96bad2877ce7829f61b4) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/nbd/server.c b/nbd/server.c +index ea5fe0e..1ce3f44 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -2135,7 +2135,8 @@ static coroutine_fn int nbd_do_cmd_read(NBDClient *client, NBDRequest *request, + } + + if (client->structured_reply && !(request->flags & NBD_CMD_FLAG_DF) && +- request->len) { ++ request->len && request->type != NBD_CMD_CACHE) ++ { + return nbd_co_send_sparse_read(client, request->handle, request->from, + data, request->len, errp); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-fix-bitmap-export.patch b/SOURCES/kvm-nbd-server-fix-bitmap-export.patch new file mode 100644 index 0000000..1952899 --- /dev/null +++ b/SOURCES/kvm-nbd-server-fix-bitmap-export.patch @@ -0,0 +1,91 @@ +From d7a0e8fc0484ef18c25e7396baea07dfd6520a30 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:20 +0100 +Subject: [PATCH 053/163] nbd/server: fix bitmap export + +RH-Author: John Snow +Message-id: <20190322032241.8111-8-jsnow@redhat.com> +Patchwork-id: 85094 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 07/28] nbd/server: fix bitmap export +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +bitmap_to_extents function is broken: it switches dirty variable after +every iteration, however it can process only part of dirty (or zero) +area during one iteration in case when this area is too large for one +extent. + +Fortunately, the bug doesn't produce wrong extent flags: it just inserts +a zero-length extent between sequential extents representing large dirty +(or zero) area. However, zero-length extents are forbidden by the NBD +protocol. So, a careful client should consider such a reply as a server +fault, while a less-careful will likely ignore zero-length extents. + +The bug can only be triggered by a client that requests block status +for nearly 4G at once (a request of 4G and larger is impossible per +the protocol, and requests smaller than 4G less the bitmap granularity +cause the loop to quit iterating rather than revisit the tail of the +large area); it also cannot trigger if the client used the +NBD_CMD_FLAG_REQ_ONE flag. Since qemu 3.0 as client (using the +x-dirty-bitmap extension) always passes the flag, it is immune; and +we are not aware of other open-source clients that know how to request +qemu:dirty-bitmap:FOO contexts. Clients that want to avoid the bug +could cap block status requests to a smaller length, such as 2G or 3G. + +Fix this by more careful handling of dirty variable. + +Bug was introduced in 3d068aff16 + "nbd/server: implement dirty bitmap export", with the whole function. +and is present in v3.0.0 release. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20180914165116.23182-1-vsementsov@virtuozzo.com> +CC: qemu-stable@nongnu.org +Reviewed-by: Eric Blake +[eblake: improved commit message] +Signed-off-by: Eric Blake +(cherry picked from commit 6545916d528de7a6b784f4d10e7b236b30bfaced) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/nbd/server.c b/nbd/server.c +index 0ab0dbd..a8ddc4a 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -1951,6 +1951,8 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset, + + assert(begin < overall_end && nb_extents); + while (begin < overall_end && i < nb_extents) { ++ bool next_dirty = !dirty; ++ + if (dirty) { + end = bdrv_dirty_bitmap_next_zero(bitmap, begin, UINT64_MAX); + } else { +@@ -1962,6 +1964,7 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset, + end = MIN(bdrv_dirty_bitmap_size(bitmap), + begin + UINT32_MAX + 1 - + bdrv_dirty_bitmap_granularity(bitmap)); ++ next_dirty = dirty; + } + if (dont_fragment && end > overall_end) { + end = overall_end; +@@ -1971,7 +1974,7 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset, + extents[i].flags = cpu_to_be32(dirty ? NBD_STATE_DIRTY : 0); + i++; + begin = end; +- dirty = !dirty; ++ dirty = next_dirty; + } + + bdrv_dirty_iter_free(it); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-fix-nbd_co_send_block_status.patch b/SOURCES/kvm-nbd-server-fix-nbd_co_send_block_status.patch new file mode 100644 index 0000000..89f87ad --- /dev/null +++ b/SOURCES/kvm-nbd-server-fix-nbd_co_send_block_status.patch @@ -0,0 +1,58 @@ +From 947350f0e713e80320519fb4131f10ed607d657f Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:55:09 +0200 +Subject: [PATCH 84/89] nbd/server: fix nbd_co_send_block_status + +RH-Author: John Snow +Message-id: <20180718225511.14878-34-jsnow@redhat.com> +Patchwork-id: 81418 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 33/35] nbd/server: fix nbd_co_send_block_status +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +Call nbd_co_send_extents() with correct length parameter +(extent.length may be smaller than original length). + +Also, switch length parameter type to uint32_t, to correspond with +request->len and similar nbd_co_send_bitmap(). + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20180704112302.471456-2-vsementsov@virtuozzo.com> +Signed-off-by: Eric Blake +(cherry picked from commit 0c0eaed14721f8a9db334deb35316411c512059a) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/nbd/server.c b/nbd/server.c +index e52b76b..ea5fe0e 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -1910,7 +1910,7 @@ static int nbd_co_send_extents(NBDClient *client, uint64_t handle, + /* Get block status from the exported device and send it to the client */ + static int nbd_co_send_block_status(NBDClient *client, uint64_t handle, + BlockDriverState *bs, uint64_t offset, +- uint64_t length, bool last, ++ uint32_t length, bool last, + uint32_t context_id, Error **errp) + { + int ret; +@@ -1922,7 +1922,8 @@ static int nbd_co_send_block_status(NBDClient *client, uint64_t handle, + client, handle, -ret, "can't get block status", errp); + } + +- return nbd_co_send_extents(client, handle, &extent, 1, length, last, ++ return nbd_co_send_extents(client, handle, &extent, 1, ++ be32_to_cpu(extent.length), last, + context_id, errp); + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-fix-trace.patch b/SOURCES/kvm-nbd-server-fix-trace.patch new file mode 100644 index 0000000..0e7322a --- /dev/null +++ b/SOURCES/kvm-nbd-server-fix-trace.patch @@ -0,0 +1,73 @@ +From 4b09857fcf27b4803336119b06033766d474ea54 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:55:00 +0200 +Subject: [PATCH 75/89] nbd/server: fix trace + +RH-Author: John Snow +Message-id: <20180718225511.14878-25-jsnow@redhat.com> +Patchwork-id: 81401 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 24/35] nbd/server: fix trace +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +Return code = 1 doesn't mean that we parsed base:allocation. Use +correct traces in both -parsed and -skipped cases. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20180609151758.17343-2-vsementsov@virtuozzo.com> +Reviewed-by: Eric Blake +[eblake: comment tweaks] +Signed-off-by: Eric Blake +(cherry picked from commit dbb8b396bb46388cee92e9094c563297d04c43ed) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/nbd/server.c b/nbd/server.c +index 493a926..942c016 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -736,12 +736,16 @@ static int nbd_negotiate_send_meta_context(NBDClient *client, + + /* nbd_meta_base_query + * +- * Handle query to 'base' namespace. For now, only base:allocation context is +- * available in it. 'len' is the amount of text remaining to be read from ++ * Handle queries to 'base' namespace. For now, only the base:allocation ++ * context is available. 'len' is the amount of text remaining to be read from + * the current name, after the 'base:' portion has been stripped. + * + * Return -errno on I/O error, 0 if option was completely handled by +- * sending a reply about inconsistent lengths, or 1 on success. */ ++ * sending a reply about inconsistent lengths, or 1 on success. ++ * ++ * Note: return code = 1 doesn't mean that we've parsed the "base:allocation" ++ * namespace. It only means that there are no errors. ++ */ + static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta, + uint32_t len, Error **errp) + { +@@ -768,10 +772,12 @@ static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta, + } + + if (strncmp(query, "allocation", alen) == 0) { ++ trace_nbd_negotiate_meta_query_parse("base:allocation"); + meta->base_allocation = true; ++ } else { ++ trace_nbd_negotiate_meta_query_skip("not base:allocation"); + } + +- trace_nbd_negotiate_meta_query_parse("base:allocation"); + return 1; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-implement-dirty-bitmap-export.patch b/SOURCES/kvm-nbd-server-implement-dirty-bitmap-export.patch new file mode 100644 index 0000000..d6662f9 --- /dev/null +++ b/SOURCES/kvm-nbd-server-implement-dirty-bitmap-export.patch @@ -0,0 +1,471 @@ +From 59e211a4d669b8950a66bba63e7ae4e58cee648f Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:55:03 +0200 +Subject: [PATCH 78/89] nbd/server: implement dirty bitmap export + +RH-Author: John Snow +Message-id: <20180718225511.14878-28-jsnow@redhat.com> +Patchwork-id: 81412 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 27/35] nbd/server: implement dirty bitmap export +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +Handle a new NBD meta namespace: "qemu", and corresponding queries: +"qemu:dirty-bitmap:". + +With the new metadata context negotiated, BLOCK_STATUS query will reply +with dirty-bitmap data, converted to extents. The new public function +nbd_export_bitmap selects which bitmap to export. For now, only one bitmap +may be exported. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20180609151758.17343-5-vsementsov@virtuozzo.com> +Reviewed-by: Eric Blake +[eblake: wording tweaks, minor cleanups, additional tracing] +Signed-off-by: Eric Blake +(cherry picked from commit 3d068aff16d6dbf066328977c5152847a62f2a0a) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + include/block/nbd.h | 8 +- + nbd/server.c | 278 +++++++++++++++++++++++++++++++++++++++++++++++----- + nbd/trace-events | 1 + + 3 files changed, 262 insertions(+), 25 deletions(-) + +diff --git a/include/block/nbd.h b/include/block/nbd.h +index fcdcd54..8bb9606 100644 +--- a/include/block/nbd.h ++++ b/include/block/nbd.h +@@ -229,11 +229,13 @@ enum { + #define NBD_REPLY_TYPE_ERROR NBD_REPLY_ERR(1) + #define NBD_REPLY_TYPE_ERROR_OFFSET NBD_REPLY_ERR(2) + +-/* Flags for extents (NBDExtent.flags) of NBD_REPLY_TYPE_BLOCK_STATUS, +- * for base:allocation meta context */ ++/* 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) ++ + static inline bool nbd_reply_type_is_error(int type) + { + return type & (1 << 15); +@@ -315,6 +317,8 @@ void nbd_client_put(NBDClient *client); + void nbd_server_start(SocketAddress *addr, const char *tls_creds, + Error **errp); + ++void nbd_export_bitmap(NBDExport *exp, const char *bitmap, ++ const char *bitmap_export_name, Error **errp); + + /* nbd_read + * Reads @size bytes from @ioc. Returns 0 on success. +diff --git a/nbd/server.c b/nbd/server.c +index 9171cd4..2c2d62c 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -23,6 +23,13 @@ + #include "nbd-internal.h" + + #define NBD_META_ID_BASE_ALLOCATION 0 ++#define NBD_META_ID_DIRTY_BITMAP 1 ++ ++/* NBD_MAX_BITMAP_EXTENTS: 1 mb of extents data. An empirical ++ * constant. If an increase is needed, note that the NBD protocol ++ * recommends no larger than 32 mb, so that the client won't consider ++ * the reply as a denial of service attack. */ ++#define NBD_MAX_BITMAP_EXTENTS (0x100000 / 8) + + static int system_errno_to_nbd_errno(int err) + { +@@ -80,6 +87,9 @@ struct NBDExport { + + BlockBackend *eject_notifier_blk; + Notifier eject_notifier; ++ ++ BdrvDirtyBitmap *export_bitmap; ++ char *export_bitmap_context; + }; + + static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); +@@ -92,6 +102,7 @@ typedef struct NBDExportMetaContexts { + bool valid; /* means that negotiation of the option finished without + errors */ + bool base_allocation; /* export base:allocation context (block status) */ ++ bool bitmap; /* export qemu:dirty-bitmap: */ + } NBDExportMetaContexts; + + struct NBDClient { +@@ -814,6 +825,56 @@ static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta, + &meta->base_allocation, errp); + } + ++/* nbd_meta_bitmap_query ++ * ++ * Handle query to 'qemu:' namespace. ++ * @len is the amount of text remaining to be read from the current name, after ++ * the 'qemu:' portion has been stripped. ++ * ++ * Return -errno on I/O error, 0 if option was completely handled by ++ * sending a reply about inconsistent lengths, or 1 on success. */ ++static int nbd_meta_qemu_query(NBDClient *client, NBDExportMetaContexts *meta, ++ uint32_t len, Error **errp) ++{ ++ bool dirty_bitmap = false; ++ size_t dirty_bitmap_len = strlen("dirty-bitmap:"); ++ int ret; ++ ++ if (!meta->exp->export_bitmap) { ++ trace_nbd_negotiate_meta_query_skip("no dirty-bitmap exported"); ++ return nbd_opt_skip(client, len, errp); ++ } ++ ++ if (len == 0) { ++ if (client->opt == NBD_OPT_LIST_META_CONTEXT) { ++ meta->bitmap = true; ++ } ++ trace_nbd_negotiate_meta_query_parse("empty"); ++ return 1; ++ } ++ ++ if (len < dirty_bitmap_len) { ++ trace_nbd_negotiate_meta_query_skip("not dirty-bitmap:"); ++ return nbd_opt_skip(client, len, errp); ++ } ++ ++ len -= dirty_bitmap_len; ++ ret = nbd_meta_pattern(client, "dirty-bitmap:", &dirty_bitmap, errp); ++ if (ret <= 0) { ++ return ret; ++ } ++ if (!dirty_bitmap) { ++ trace_nbd_negotiate_meta_query_skip("not dirty-bitmap:"); ++ return nbd_opt_skip(client, len, errp); ++ } ++ ++ trace_nbd_negotiate_meta_query_parse("dirty-bitmap:"); ++ ++ return nbd_meta_empty_or_pattern( ++ client, meta->exp->export_bitmap_context + ++ strlen("qemu:dirty_bitmap:"), len, &meta->bitmap, errp); ++} ++ + /* nbd_negotiate_meta_query + * + * Parse namespace name and call corresponding function to parse body of the +@@ -829,9 +890,14 @@ static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta, + static int nbd_negotiate_meta_query(NBDClient *client, + NBDExportMetaContexts *meta, Error **errp) + { ++ /* ++ * Both 'qemu' and 'base' namespaces have length = 5 including a ++ * colon. If another length namespace is later introduced, this ++ * should certainly be refactored. ++ */ + int ret; +- char query[sizeof("base:") - 1]; +- size_t baselen = strlen("base:"); ++ size_t ns_len = 5; ++ char ns[5]; + uint32_t len; + + ret = nbd_opt_read(client, &len, sizeof(len), errp); +@@ -840,25 +906,27 @@ static int nbd_negotiate_meta_query(NBDClient *client, + } + cpu_to_be32s(&len); + +- /* The only supported namespace for now is 'base'. So query should start +- * with 'base:'. Otherwise, we can ignore it and skip the remainder. */ +- if (len < baselen) { ++ if (len < ns_len) { + trace_nbd_negotiate_meta_query_skip("length too short"); + return nbd_opt_skip(client, len, errp); + } + +- len -= baselen; +- ret = nbd_opt_read(client, query, baselen, errp); ++ len -= ns_len; ++ ret = nbd_opt_read(client, ns, ns_len, errp); + if (ret <= 0) { + return ret; + } +- if (strncmp(query, "base:", baselen) != 0) { +- trace_nbd_negotiate_meta_query_skip("not for base: namespace"); +- return nbd_opt_skip(client, len, errp); ++ ++ if (!strncmp(ns, "base:", ns_len)) { ++ trace_nbd_negotiate_meta_query_parse("base:"); ++ return nbd_meta_base_query(client, meta, len, errp); ++ } else if (!strncmp(ns, "qemu:", ns_len)) { ++ trace_nbd_negotiate_meta_query_parse("qemu:"); ++ return nbd_meta_qemu_query(client, meta, len, errp); + } + +- trace_nbd_negotiate_meta_query_parse("base:"); +- return nbd_meta_base_query(client, meta, len, errp); ++ trace_nbd_negotiate_meta_query_skip("unknown namespace"); ++ return nbd_opt_skip(client, len, errp); + } + + /* nbd_negotiate_meta_queries +@@ -928,6 +996,16 @@ static int nbd_negotiate_meta_queries(NBDClient *client, + } + } + ++ if (meta->bitmap) { ++ ret = nbd_negotiate_send_meta_context(client, ++ meta->exp->export_bitmap_context, ++ NBD_META_ID_DIRTY_BITMAP, ++ errp); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ + ret = nbd_negotiate_send_rep(client, NBD_REP_ACK, errp); + if (ret == 0) { + meta->valid = true; +@@ -1556,6 +1634,11 @@ void nbd_export_put(NBDExport *exp) + exp->blk = NULL; + } + ++ if (exp->export_bitmap) { ++ bdrv_dirty_bitmap_set_qmp_locked(exp->export_bitmap, false); ++ g_free(exp->export_bitmap_context); ++ } ++ + g_free(exp); + } + } +@@ -1797,9 +1880,15 @@ static int blockstatus_to_extent_be(BlockDriverState *bs, uint64_t offset, + } + + /* nbd_co_send_extents +- * @extents should be in big-endian */ ++ * ++ * @length is only for tracing purposes (and may be smaller or larger ++ * than the client's original request). @last controls whether ++ * NBD_REPLY_FLAG_DONE is sent. @extents should already be in ++ * big-endian format. ++ */ + static int nbd_co_send_extents(NBDClient *client, uint64_t handle, +- NBDExtent *extents, unsigned nb_extents, ++ NBDExtent *extents, unsigned int nb_extents, ++ uint64_t length, bool last, + uint32_t context_id, Error **errp) + { + NBDStructuredMeta chunk; +@@ -1809,7 +1898,9 @@ static int nbd_co_send_extents(NBDClient *client, uint64_t handle, + {.iov_base = extents, .iov_len = nb_extents * sizeof(extents[0])} + }; + +- set_be_chunk(&chunk.h, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_BLOCK_STATUS, ++ trace_nbd_co_send_extents(handle, nb_extents, context_id, length, last); ++ set_be_chunk(&chunk.h, last ? NBD_REPLY_FLAG_DONE : 0, ++ NBD_REPLY_TYPE_BLOCK_STATUS, + handle, sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len); + stl_be_p(&chunk.context_id, context_id); + +@@ -1819,8 +1910,8 @@ static int nbd_co_send_extents(NBDClient *client, uint64_t handle, + /* Get block status from the exported device and send it to the client */ + static int nbd_co_send_block_status(NBDClient *client, uint64_t handle, + BlockDriverState *bs, uint64_t offset, +- uint64_t length, uint32_t context_id, +- Error **errp) ++ uint64_t length, bool last, ++ uint32_t context_id, Error **errp) + { + int ret; + NBDExtent extent; +@@ -1831,7 +1922,84 @@ static int nbd_co_send_block_status(NBDClient *client, uint64_t handle, + client, handle, -ret, "can't get block status", errp); + } + +- return nbd_co_send_extents(client, handle, &extent, 1, context_id, errp); ++ return nbd_co_send_extents(client, handle, &extent, 1, length, last, ++ context_id, errp); ++} ++ ++/* ++ * Populate @extents from a dirty bitmap. Unless @dont_fragment, the ++ * final extent may exceed the original @length. Store in @length the ++ * byte length encoded (which may be smaller or larger than the ++ * original), and return the number of extents used. ++ */ ++static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset, ++ uint64_t *length, NBDExtent *extents, ++ unsigned int nb_extents, ++ bool dont_fragment) ++{ ++ uint64_t begin = offset, end; ++ uint64_t overall_end = offset + *length; ++ unsigned int i = 0; ++ BdrvDirtyBitmapIter *it; ++ bool dirty; ++ ++ bdrv_dirty_bitmap_lock(bitmap); ++ ++ it = bdrv_dirty_iter_new(bitmap); ++ dirty = bdrv_get_dirty_locked(NULL, bitmap, offset); ++ ++ assert(begin < overall_end && nb_extents); ++ while (begin < overall_end && i < nb_extents) { ++ if (dirty) { ++ end = bdrv_dirty_bitmap_next_zero(bitmap, begin); ++ } else { ++ bdrv_set_dirty_iter(it, begin); ++ end = bdrv_dirty_iter_next(it); ++ } ++ if (end == -1 || end - begin > UINT32_MAX) { ++ /* Cap to an aligned value < 4G beyond begin. */ ++ end = MIN(bdrv_dirty_bitmap_size(bitmap), ++ begin + UINT32_MAX + 1 - ++ bdrv_dirty_bitmap_granularity(bitmap)); ++ } ++ if (dont_fragment && end > overall_end) { ++ end = overall_end; ++ } ++ ++ extents[i].length = cpu_to_be32(end - begin); ++ extents[i].flags = cpu_to_be32(dirty ? NBD_STATE_DIRTY : 0); ++ i++; ++ begin = end; ++ dirty = !dirty; ++ } ++ ++ bdrv_dirty_iter_free(it); ++ ++ bdrv_dirty_bitmap_unlock(bitmap); ++ ++ *length = end - offset; ++ return i; ++} ++ ++static int nbd_co_send_bitmap(NBDClient *client, uint64_t handle, ++ BdrvDirtyBitmap *bitmap, uint64_t offset, ++ uint32_t length, bool dont_fragment, bool last, ++ uint32_t context_id, Error **errp) ++{ ++ int ret; ++ unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BITMAP_EXTENTS; ++ NBDExtent *extents = g_new(NBDExtent, nb_extents); ++ uint64_t final_length = length; ++ ++ nb_extents = bitmap_to_extents(bitmap, offset, &final_length, extents, ++ nb_extents, dont_fragment); ++ ++ ret = nbd_co_send_extents(client, handle, extents, nb_extents, ++ final_length, last, context_id, errp); ++ ++ g_free(extents); ++ ++ return ret; + } + + /* nbd_co_receive_request +@@ -2051,11 +2219,34 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, + return nbd_send_generic_reply(client, request->handle, -EINVAL, + "need non-zero length", errp); + } +- if (client->export_meta.valid && client->export_meta.base_allocation) { +- return nbd_co_send_block_status(client, request->handle, +- blk_bs(exp->blk), request->from, +- request->len, +- NBD_META_ID_BASE_ALLOCATION, errp); ++ if (client->export_meta.valid && ++ (client->export_meta.base_allocation || ++ client->export_meta.bitmap)) ++ { ++ if (client->export_meta.base_allocation) { ++ ret = nbd_co_send_block_status(client, request->handle, ++ blk_bs(exp->blk), request->from, ++ request->len, ++ !client->export_meta.bitmap, ++ NBD_META_ID_BASE_ALLOCATION, ++ errp); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ if (client->export_meta.bitmap) { ++ ret = nbd_co_send_bitmap(client, request->handle, ++ client->exp->export_bitmap, ++ request->from, request->len, ++ request->flags & NBD_CMD_FLAG_REQ_ONE, ++ true, NBD_META_ID_DIRTY_BITMAP, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ return ret; + } else { + return nbd_send_generic_reply(client, request->handle, -EINVAL, + "CMD_BLOCK_STATUS not negotiated", +@@ -2207,3 +2398,44 @@ void nbd_client_new(NBDExport *exp, + co = qemu_coroutine_create(nbd_co_client_start, client); + qemu_coroutine_enter(co); + } ++ ++void nbd_export_bitmap(NBDExport *exp, const char *bitmap, ++ const char *bitmap_export_name, Error **errp) ++{ ++ BdrvDirtyBitmap *bm = NULL; ++ BlockDriverState *bs = blk_bs(exp->blk); ++ ++ if (exp->export_bitmap) { ++ error_setg(errp, "Export bitmap is already set"); ++ return; ++ } ++ ++ while (true) { ++ bm = bdrv_find_dirty_bitmap(bs, bitmap); ++ if (bm != NULL || bs->backing == NULL) { ++ break; ++ } ++ ++ bs = bs->backing->bs; ++ } ++ ++ if (bm == NULL) { ++ error_setg(errp, "Bitmap '%s' is not found", bitmap); ++ return; ++ } ++ ++ if (bdrv_dirty_bitmap_enabled(bm)) { ++ error_setg(errp, "Bitmap '%s' is enabled", bitmap); ++ return; ++ } ++ ++ if (bdrv_dirty_bitmap_qmp_locked(bm)) { ++ error_setg(errp, "Bitmap '%s' is locked", bitmap); ++ return; ++ } ++ ++ bdrv_dirty_bitmap_set_qmp_locked(bm, true); ++ exp->export_bitmap = bm; ++ exp->export_bitmap_context = ++ g_strdup_printf("qemu:dirty-bitmap:%s", bitmap_export_name); ++} +diff --git a/nbd/trace-events b/nbd/trace-events +index dee081e..5e1d4af 100644 +--- a/nbd/trace-events ++++ b/nbd/trace-events +@@ -64,6 +64,7 @@ nbd_co_send_simple_reply(uint64_t handle, uint32_t error, const char *errname, i + nbd_co_send_structured_done(uint64_t handle) "Send structured reply done: handle = %" PRIu64 + nbd_co_send_structured_read(uint64_t handle, uint64_t offset, void *data, size_t size) "Send structured read data reply: handle = %" PRIu64 ", offset = %" PRIu64 ", data = %p, len = %zu" + nbd_co_send_structured_read_hole(uint64_t handle, uint64_t offset, size_t size) "Send structured read hole reply: handle = %" PRIu64 ", offset = %" PRIu64 ", len = %zu" ++nbd_co_send_extents(uint64_t handle, unsigned int extents, uint32_t id, uint64_t length, int last) "Send block status reply: handle = %" PRIu64 ", extents = %u, context = %d (extents cover %" PRIu64 " bytes, last chunk = %d)" + nbd_co_send_structured_error(uint64_t handle, int err, const char *errname, const char *msg) "Send structured error reply: handle = %" PRIu64 ", error = %d (%s), msg = '%s'" + nbd_co_receive_request_decode_type(uint64_t handle, uint16_t type, const char *name) "Decoding type: handle = %" PRIu64 ", type = %" PRIu16 " (%s)" + nbd_co_receive_request_payload_received(uint64_t handle, uint32_t len) "Payload received: handle = %" PRIu64 ", len = %" PRIu32 +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-introduce-NBD_CMD_CACHE.patch b/SOURCES/kvm-nbd-server-introduce-NBD_CMD_CACHE.patch new file mode 100644 index 0000000..5026125 --- /dev/null +++ b/SOURCES/kvm-nbd-server-introduce-NBD_CMD_CACHE.patch @@ -0,0 +1,121 @@ +From f834b08ab5b70b9a39bb0b6c351a65ad46b5879f Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:55:06 +0200 +Subject: [PATCH 81/89] nbd/server: introduce NBD_CMD_CACHE + +RH-Author: John Snow +Message-id: <20180718225511.14878-31-jsnow@redhat.com> +Patchwork-id: 81419 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 30/35] nbd/server: introduce NBD_CMD_CACHE +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +Handle nbd CACHE command. Just do read, without sending read data back. +Cache mechanism should be done by exported node driver chain. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20180413143156.11409-1-vsementsov@virtuozzo.com> +Reviewed-by: Eric Blake +[eblake: fix two missing case labels in switch statements] +Signed-off-by: Eric Blake +(cherry picked from commit bc37b06a5cde24fb24c2a2cc44dd86756034ba9d) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + include/block/nbd.h | 3 ++- + nbd/common.c | 2 ++ + nbd/server.c | 11 +++++++---- + 3 files changed, 11 insertions(+), 5 deletions(-) + +diff --git a/include/block/nbd.h b/include/block/nbd.h +index 8bb9606..daaeae6 100644 +--- a/include/block/nbd.h ++++ b/include/block/nbd.h +@@ -135,6 +135,7 @@ typedef struct NBDExtent { + #define NBD_FLAG_SEND_TRIM (1 << 5) /* Send TRIM (discard) */ + #define NBD_FLAG_SEND_WRITE_ZEROES (1 << 6) /* Send WRITE_ZEROES */ + #define NBD_FLAG_SEND_DF (1 << 7) /* Send DF (Do not Fragment) */ ++#define NBD_FLAG_SEND_CACHE (1 << 8) /* Send CACHE (prefetch) */ + + /* New-style handshake (global) flags, sent from server to client, and + control what will happen during handshake phase. */ +@@ -195,7 +196,7 @@ enum { + NBD_CMD_DISC = 2, + NBD_CMD_FLUSH = 3, + NBD_CMD_TRIM = 4, +- /* 5 reserved for failed experiment NBD_CMD_CACHE */ ++ NBD_CMD_CACHE = 5, + NBD_CMD_WRITE_ZEROES = 6, + NBD_CMD_BLOCK_STATUS = 7, + }; +diff --git a/nbd/common.c b/nbd/common.c +index 8c95c1d..41f5ed8 100644 +--- a/nbd/common.c ++++ b/nbd/common.c +@@ -148,6 +148,8 @@ const char *nbd_cmd_lookup(uint16_t cmd) + 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: +diff --git a/nbd/server.c b/nbd/server.c +index 2c2d62c..2746046 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -1252,7 +1252,7 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) + int ret; + const uint16_t myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM | + NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA | +- NBD_FLAG_SEND_WRITE_ZEROES); ++ NBD_FLAG_SEND_WRITE_ZEROES | NBD_FLAG_SEND_CACHE); + bool oldStyle; + + /* Old style negotiation header, no room for options +@@ -2034,7 +2034,9 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, + return -EIO; + } + +- if (request->type == NBD_CMD_READ || request->type == NBD_CMD_WRITE) { ++ if (request->type == NBD_CMD_READ || request->type == NBD_CMD_WRITE || ++ request->type == NBD_CMD_CACHE) ++ { + if (request->len > NBD_MAX_BUFFER_SIZE) { + error_setg(errp, "len (%" PRIu32" ) is larger than max len (%u)", + request->len, NBD_MAX_BUFFER_SIZE); +@@ -2119,7 +2121,7 @@ static coroutine_fn int nbd_do_cmd_read(NBDClient *client, NBDRequest *request, + int ret; + NBDExport *exp = client->exp; + +- assert(request->type == NBD_CMD_READ); ++ assert(request->type == NBD_CMD_READ || request->type == NBD_CMD_CACHE); + + /* XXX: NBD Protocol only documents use of FUA with WRITE */ + if (request->flags & NBD_CMD_FLAG_FUA) { +@@ -2138,7 +2140,7 @@ static coroutine_fn int nbd_do_cmd_read(NBDClient *client, NBDRequest *request, + + ret = blk_pread(exp->blk, request->from + exp->dev_offset, data, + request->len); +- if (ret < 0) { ++ if (ret < 0 || request->type == NBD_CMD_CACHE) { + return nbd_send_generic_reply(client, request->handle, ret, + "reading from file failed", errp); + } +@@ -2171,6 +2173,7 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, + + switch (request->type) { + case NBD_CMD_READ: ++ case NBD_CMD_CACHE: + return nbd_do_cmd_read(client, request, data, errp); + + case NBD_CMD_WRITE: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-refactor-NBDExportMetaContexts.patch b/SOURCES/kvm-nbd-server-refactor-NBDExportMetaContexts.patch new file mode 100644 index 0000000..6280dd2 --- /dev/null +++ b/SOURCES/kvm-nbd-server-refactor-NBDExportMetaContexts.patch @@ -0,0 +1,118 @@ +From 6ba3ec3e20e1980ad6e061a82a101ce0394d6b35 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:55:01 +0200 +Subject: [PATCH 76/89] nbd/server: refactor NBDExportMetaContexts + +RH-Author: John Snow +Message-id: <20180718225511.14878-26-jsnow@redhat.com> +Patchwork-id: 81413 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 25/35] nbd/server: refactor NBDExportMetaContexts +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +Use NBDExport pointer instead of just export name: there is no need to +store a duplicated name in the struct; moreover, NBDExport will be used +further. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20180609151758.17343-3-vsementsov@virtuozzo.com> +Reviewed-by: Eric Blake +[eblake: commit message grammar tweak] +Signed-off-by: Eric Blake +(cherry picked from commit af736e546717d832168dd332a328bfcf74a0ab3d) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 23 +++++++++++------------ + 1 file changed, 11 insertions(+), 12 deletions(-) + +diff --git a/nbd/server.c b/nbd/server.c +index 942c016..26cc41a 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -88,7 +88,7 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); + * as selected by NBD_OPT_SET_META_CONTEXT. Also used for + * NBD_OPT_LIST_META_CONTEXT. */ + typedef struct NBDExportMetaContexts { +- char export_name[NBD_MAX_NAME_SIZE + 1]; ++ NBDExport *exp; + bool valid; /* means that negotiation of the option finished without + errors */ + bool base_allocation; /* export base:allocation context (block status) */ +@@ -399,10 +399,9 @@ static int nbd_negotiate_handle_list(NBDClient *client, Error **errp) + return nbd_negotiate_send_rep(client, NBD_REP_ACK, errp); + } + +-static void nbd_check_meta_export_name(NBDClient *client) ++static void nbd_check_meta_export(NBDClient *client) + { +- client->export_meta.valid &= !strcmp(client->exp->name, +- client->export_meta.export_name); ++ client->export_meta.valid &= client->exp == client->export_meta.exp; + } + + /* Send a reply to NBD_OPT_EXPORT_NAME. +@@ -456,7 +455,7 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, + + QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); + nbd_export_get(client->exp); +- nbd_check_meta_export_name(client); ++ nbd_check_meta_export(client); + + return 0; + } +@@ -650,7 +649,7 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags, + client->exp = exp; + QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); + nbd_export_get(client->exp); +- nbd_check_meta_export_name(client); ++ nbd_check_meta_export(client); + rc = 1; + } + return rc; +@@ -835,7 +834,7 @@ static int nbd_negotiate_meta_queries(NBDClient *client, + NBDExportMetaContexts *meta, Error **errp) + { + int ret; +- NBDExport *exp; ++ char export_name[NBD_MAX_NAME_SIZE + 1]; + NBDExportMetaContexts local_meta; + uint32_t nb_queries; + int i; +@@ -854,15 +853,15 @@ static int nbd_negotiate_meta_queries(NBDClient *client, + + memset(meta, 0, sizeof(*meta)); + +- ret = nbd_opt_read_name(client, meta->export_name, NULL, errp); ++ ret = nbd_opt_read_name(client, export_name, NULL, errp); + if (ret <= 0) { + return ret; + } + +- exp = nbd_export_find(meta->export_name); +- if (exp == NULL) { ++ meta->exp = nbd_export_find(export_name); ++ if (meta->exp == NULL) { + return nbd_opt_drop(client, NBD_REP_ERR_UNKNOWN, errp, +- "export '%s' not present", meta->export_name); ++ "export '%s' not present", export_name); + } + + ret = nbd_opt_read(client, &nb_queries, sizeof(nb_queries), errp); +@@ -871,7 +870,7 @@ static int nbd_negotiate_meta_queries(NBDClient *client, + } + cpu_to_be32s(&nb_queries); + trace_nbd_negotiate_meta_context(nbd_opt_lookup(client->opt), +- meta->export_name, nb_queries); ++ export_name, nb_queries); + + if (client->opt == NBD_OPT_LIST_META_CONTEXT && !nb_queries) { + /* enable all known contexts */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nbd-server-send-more-than-one-extent-of-base-allocat.patch b/SOURCES/kvm-nbd-server-send-more-than-one-extent-of-base-allocat.patch new file mode 100644 index 0000000..90a7fbe --- /dev/null +++ b/SOURCES/kvm-nbd-server-send-more-than-one-extent-of-base-allocat.patch @@ -0,0 +1,178 @@ +From 6f48ea5b06bd3f566d51996f447c205452c6ee70 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:21 +0100 +Subject: [PATCH 054/163] nbd/server: send more than one extent of + base:allocation context + +RH-Author: John Snow +Message-id: <20190322032241.8111-9-jsnow@redhat.com> +Patchwork-id: 85100 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 08/28] nbd/server: send more than one extent of base:allocation context +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +This is necessary for efficient block-status export, for clients which +support it. (qemu is not yet such a client, but could become one.) + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20180704112302.471456-3-vsementsov@virtuozzo.com> +[eblake: grammar tweaks] +Signed-off-by: Eric Blake +(cherry picked from commit fb7afc797e071f2616e1ccc849b39fe43e7033bf) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + nbd/server.c | 79 +++++++++++++++++++++++++++++++++++++++++++++--------------- + 1 file changed, 60 insertions(+), 19 deletions(-) + +diff --git a/nbd/server.c b/nbd/server.c +index a8ddc4a..a9fec45 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -1844,37 +1844,68 @@ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client, + return ret; + } + +-static int blockstatus_to_extent_be(BlockDriverState *bs, uint64_t offset, +- uint64_t bytes, NBDExtent *extent) ++/* ++ * Populate @extents from block status. Update @bytes to be the actual ++ * length encoded (which may be smaller than the original), and update ++ * @nb_extents to the number of extents used. ++ * ++ * Returns zero on success and -errno on bdrv_block_status_above failure. ++ */ ++static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset, ++ uint64_t *bytes, NBDExtent *extents, ++ unsigned int *nb_extents) + { +- uint64_t remaining_bytes = bytes; ++ uint64_t remaining_bytes = *bytes; ++ NBDExtent *extent = extents, *extents_end = extents + *nb_extents; ++ bool first_extent = true; + ++ assert(*nb_extents); + while (remaining_bytes) { + uint32_t flags; + int64_t num; + int ret = bdrv_block_status_above(bs, NULL, offset, remaining_bytes, + &num, NULL, NULL); ++ + if (ret < 0) { + return ret; + } + + flags = (ret & BDRV_BLOCK_ALLOCATED ? 0 : NBD_STATE_HOLE) | + (ret & BDRV_BLOCK_ZERO ? NBD_STATE_ZERO : 0); ++ offset += num; ++ remaining_bytes -= num; + +- if (remaining_bytes == bytes) { ++ if (first_extent) { + extent->flags = flags; ++ extent->length = num; ++ first_extent = false; ++ continue; + } + +- if (flags != extent->flags) { +- break; ++ if (flags == extent->flags) { ++ /* extend current extent */ ++ extent->length += num; ++ } else { ++ if (extent + 1 == extents_end) { ++ break; ++ } ++ ++ /* start new extent */ ++ extent++; ++ extent->flags = flags; ++ extent->length = num; + } ++ } + +- offset += num; +- remaining_bytes -= num; ++ extents_end = extent + 1; ++ ++ for (extent = extents; extent < extents_end; extent++) { ++ cpu_to_be32s(&extent->flags); ++ cpu_to_be32s(&extent->length); + } + +- cpu_to_be32s(&extent->flags); +- extent->length = cpu_to_be32(bytes - remaining_bytes); ++ *bytes -= remaining_bytes; ++ *nb_extents = extents_end - extents; + + return 0; + } +@@ -1910,21 +1941,29 @@ static int nbd_co_send_extents(NBDClient *client, uint64_t handle, + /* Get block status from the exported device and send it to the client */ + static int nbd_co_send_block_status(NBDClient *client, uint64_t handle, + BlockDriverState *bs, uint64_t offset, +- uint32_t length, bool last, +- uint32_t context_id, Error **errp) ++ uint32_t length, bool dont_fragment, ++ bool last, uint32_t context_id, ++ Error **errp) + { + int ret; +- NBDExtent extent; ++ unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BITMAP_EXTENTS; ++ NBDExtent *extents = g_new(NBDExtent, nb_extents); ++ uint64_t final_length = length; + +- ret = blockstatus_to_extent_be(bs, offset, length, &extent); ++ ret = blockstatus_to_extents(bs, offset, &final_length, extents, ++ &nb_extents); + if (ret < 0) { ++ g_free(extents); + return nbd_co_send_structured_error( + client, handle, -ret, "can't get block status", errp); + } + +- return nbd_co_send_extents(client, handle, &extent, 1, +- be32_to_cpu(extent.length), last, +- context_id, errp); ++ ret = nbd_co_send_extents(client, handle, extents, nb_extents, ++ final_length, last, context_id, errp); ++ ++ g_free(extents); ++ ++ return ret; + } + + /* +@@ -2232,10 +2271,12 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, + (client->export_meta.base_allocation || + client->export_meta.bitmap)) + { ++ bool dont_fragment = request->flags & NBD_CMD_FLAG_REQ_ONE; ++ + if (client->export_meta.base_allocation) { + ret = nbd_co_send_block_status(client, request->handle, + blk_bs(exp->blk), request->from, +- request->len, ++ request->len, dont_fragment, + !client->export_meta.bitmap, + NBD_META_ID_BASE_ALLOCATION, + errp); +@@ -2248,7 +2289,7 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, + ret = nbd_co_send_bitmap(client, request->handle, + client->exp->export_bitmap, + request->from, request->len, +- request->flags & NBD_CMD_FLAG_REQ_ONE, ++ dont_fragment, + true, NBD_META_ID_DIRTY_BITMAP, errp); + if (ret < 0) { + return ret; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-net-drop-too-large-packet-early.patch b/SOURCES/kvm-net-drop-too-large-packet-early.patch new file mode 100644 index 0000000..ae44cb3 --- /dev/null +++ b/SOURCES/kvm-net-drop-too-large-packet-early.patch @@ -0,0 +1,149 @@ +From 4898198378a84fc6abd34adf5d0e297f4206b006 Mon Sep 17 00:00:00 2001 +From: Xiao Wang +Date: Fri, 17 May 2019 07:29:37 +0200 +Subject: [PATCH 3/9] net: drop too large packet early + +RH-Author: Xiao Wang +Message-id: <1558078177-372-4-git-send-email-jasowang@redhat.com> +Patchwork-id: 88015 +O-Subject: [RHEL7.7 qemu-kvm-rhev PATCH 3/3] net: drop too large packet early +Bugzilla: 1636780 +RH-Acked-by: Thomas Huth +RH-Acked-by: Jens Freimann +RH-Acked-by: Maxime Coquelin +RH-Acked-by: Michael S. Tsirkin + +Bugzilla: 1636779 + +We try to detect and drop too large packet (>INT_MAX) in 1592a9947036 +("net: ignore packet size greater than INT_MAX") during packet +delivering. Unfortunately, this is not sufficient as we may hit +another integer overflow when trying to queue such large packet in +qemu_net_queue_append_iov(): + +- size of the allocation may overflow on 32bit +- packet->size is integer which may overflow even on 64bit + +Fixing this by moving the check to qemu_sendv_packet_async() which is +the entrance of all networking codes and reduce the limit to +NET_BUFSIZE to be more conservative. This works since: + +- For the callers that call qemu_sendv_packet_async() directly, they + only care about if zero is returned to determine whether to prevent + the source from producing more packets. A callback will be triggered + if peer can accept more then source could be enabled. This is + usually used by high speed networking implementation like virtio-net + or netmap. +- For the callers that call qemu_sendv_packet() that calls + qemu_sendv_packet_async() indirectly, they often ignore the return + value. In this case qemu will just the drop packets if peer can't + receive. + +Qemu will copy the packet if it was queued. So it was safe for both +kinds of the callers to assume the packet was sent. + +Since we move the check from qemu_deliver_packet_iov() to +qemu_sendv_packet_async(), it would be safer to make +qemu_deliver_packet_iov() static to prevent any external user in the +future. + +This is a revised patch of CVE-2018-17963. + +Cc: qemu-stable@nongnu.org +Cc: Li Qiang +Fixes: 1592a9947036 ("net: ignore packet size greater than INT_MAX") +Reported-by: Li Qiang +Reviewed-by: Li Qiang +Signed-off-by: Jason Wang +Reviewed-by: Thomas Huth +Message-id: 20181204035347.6148-2-jasowang@redhat.com +Signed-off-by: Peter Maydell +(cherry picked from commit 25c01bd19d0e4b66f357618aeefda1ef7a41e21a) +Signed-off-by: Miroslav Rezanina +--- + include/net/net.h | 6 ------ + net/net.c | 28 +++++++++++++++++----------- + 2 files changed, 17 insertions(+), 17 deletions(-) + +diff --git a/include/net/net.h b/include/net/net.h +index 1f7341e..df4df25 100644 +--- a/include/net/net.h ++++ b/include/net/net.h +@@ -170,12 +170,6 @@ void qemu_check_nic_model(NICInfo *nd, const char *model); + int qemu_find_nic_model(NICInfo *nd, const char * const *models, + const char *default_model); + +-ssize_t qemu_deliver_packet_iov(NetClientState *sender, +- unsigned flags, +- const struct iovec *iov, +- int iovcnt, +- void *opaque); +- + void print_net_client(Monitor *mon, NetClientState *nc); + void hmp_info_network(Monitor *mon, const QDict *qdict); + void net_socket_rs_init(SocketReadState *rs, +diff --git a/net/net.c b/net/net.c +index c991243..6e5c335 100644 +--- a/net/net.c ++++ b/net/net.c +@@ -231,6 +231,11 @@ static void qemu_net_client_destructor(NetClientState *nc) + { + g_free(nc); + } ++static ssize_t qemu_deliver_packet_iov(NetClientState *sender, ++ unsigned flags, ++ const struct iovec *iov, ++ int iovcnt, ++ void *opaque); + + static void qemu_net_client_setup(NetClientState *nc, + NetClientInfo *info, +@@ -705,22 +710,18 @@ static ssize_t nc_sendv_compat(NetClientState *nc, const struct iovec *iov, + return ret; + } + +-ssize_t qemu_deliver_packet_iov(NetClientState *sender, +- unsigned flags, +- const struct iovec *iov, +- int iovcnt, +- void *opaque) ++static ssize_t qemu_deliver_packet_iov(NetClientState *sender, ++ unsigned flags, ++ const struct iovec *iov, ++ int iovcnt, ++ void *opaque) + { + NetClientState *nc = opaque; +- size_t size = iov_size(iov, iovcnt); + int ret; + +- if (size > INT_MAX) { +- return size; +- } + + if (nc->link_down) { +- return size; ++ return iov_size(iov, iovcnt); + } + + if (nc->receive_disabled) { +@@ -745,10 +746,15 @@ ssize_t qemu_sendv_packet_async(NetClientState *sender, + NetPacketSent *sent_cb) + { + NetQueue *queue; ++ size_t size = iov_size(iov, iovcnt); + int ret; + ++ if (size > NET_BUFSIZE) { ++ return size; ++ } ++ + if (sender->link_down || !sender->peer) { +- return iov_size(iov, iovcnt); ++ return size; + } + + /* Let filters handle the packet first */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-net-ignore-packet-size-greater-than-INT_MAX.patch b/SOURCES/kvm-net-ignore-packet-size-greater-than-INT_MAX.patch new file mode 100644 index 0000000..7a23f62 --- /dev/null +++ b/SOURCES/kvm-net-ignore-packet-size-greater-than-INT_MAX.patch @@ -0,0 +1,55 @@ +From 3abb574bc7328eab1f67a1e470ea44c595b0c0b5 Mon Sep 17 00:00:00 2001 +From: Xiao Wang +Date: Fri, 17 May 2019 07:29:36 +0200 +Subject: [PATCH 2/9] net: ignore packet size greater than INT_MAX + +RH-Author: Xiao Wang +Message-id: <1558078177-372-3-git-send-email-jasowang@redhat.com> +Patchwork-id: 88014 +O-Subject: [RHEL7.7 qemu-kvm-rhev PATCH 2/3] net: ignore packet size greater than INT_MAX +Bugzilla: 1636780 +RH-Acked-by: Thomas Huth +RH-Acked-by: Jens Freimann +RH-Acked-by: Maxime Coquelin +RH-Acked-by: Michael S. Tsirkin + +Bugzilla: 1636779 + +There should not be a reason for passing a packet size greater than +INT_MAX. It's usually a hint of bug somewhere, so ignore packet size +greater than INT_MAX in qemu_deliver_packet_iov() + +CC: qemu-stable@nongnu.org +Reported-by: Daniel Shapira +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Jason Wang +(cherry picked from commit 1592a9947036d60dde5404204a5d45975133caf5) +Signed-off-by: Miroslav Rezanina +--- + net/net.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/net/net.c b/net/net.c +index 29f8398..c991243 100644 +--- a/net/net.c ++++ b/net/net.c +@@ -712,10 +712,15 @@ ssize_t qemu_deliver_packet_iov(NetClientState *sender, + void *opaque) + { + NetClientState *nc = opaque; ++ size_t size = iov_size(iov, iovcnt); + int ret; + ++ if (size > INT_MAX) { ++ return size; ++ } ++ + if (nc->link_down) { +- return iov_size(iov, iovcnt); ++ return size; + } + + if (nc->receive_disabled) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nfs-Fix-error-path-in-nfs_options_qdict_to_qapi.patch b/SOURCES/kvm-nfs-Fix-error-path-in-nfs_options_qdict_to_qapi.patch new file mode 100644 index 0000000..8e33fb8 --- /dev/null +++ b/SOURCES/kvm-nfs-Fix-error-path-in-nfs_options_qdict_to_qapi.patch @@ -0,0 +1,43 @@ +From 05767713c9bd39d7d035b8edce4f7149d23500b6 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:47:47 +0200 +Subject: [PATCH 18/89] nfs: Fix error path in nfs_options_qdict_to_qapi() + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-5-kwolf@redhat.com> +Patchwork-id: 81057 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 04/73] nfs: Fix error path in nfs_options_qdict_to_qapi() +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Don't throw away local_err, but propagate it to errp. + +Signed-off-by: Kevin Wolf +Message-id: 20180516161034.27440-1-kwolf@redhat.com +Reviewed-by: Eric Blake +Reviewed-by: Jeff Cody +Signed-off-by: Jeff Cody +(cherry picked from commit 54b7af4369a37afbd82573d0dcfb27febdb6dd24) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/nfs.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/block/nfs.c b/block/nfs.c +index 1e12958..3059ef2 100644 +--- a/block/nfs.c ++++ b/block/nfs.c +@@ -568,6 +568,7 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options, + visit_free(v); + + if (local_err) { ++ error_propagate(errp, local_err); + return NULL; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nfs-Remove-processed-options-from-QDict.patch b/SOURCES/kvm-nfs-Remove-processed-options-from-QDict.patch new file mode 100644 index 0000000..0cb335f --- /dev/null +++ b/SOURCES/kvm-nfs-Remove-processed-options-from-QDict.patch @@ -0,0 +1,64 @@ +From 27956f992c0e7622feed76bce5c5ff5cc9139e4d Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:47:48 +0200 +Subject: [PATCH 19/89] nfs: Remove processed options from QDict + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-6-kwolf@redhat.com> +Patchwork-id: 81058 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 05/73] nfs: Remove processed options from QDict +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Commit c22a03454 QAPIfied option parsing in the NFS block driver, but +forgot to remove all the options we processed. Therefore, we get an +error in bdrv_open_inherit(), which thinks the remaining options are +invalid. Trying to open an NFS image will result in an error like this: + + Block protocol 'nfs' doesn't support the option 'server.host' + +Remove all options from the QDict to make the NFS driver work again. + +Cc: qemu-stable@nongnu.org +Signed-off-by: Kevin Wolf +Message-id: 20180516160816.26259-1-kwolf@redhat.com +Reviewed-by: Eric Blake +Reviewed-by: Jeff Cody +Signed-off-by: Jeff Cody +(cherry picked from commit c82be42cc803b36fd7aed5dceec68312c7056fd5) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/nfs.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/block/nfs.c b/block/nfs.c +index 3059ef2..743ca04 100644 +--- a/block/nfs.c ++++ b/block/nfs.c +@@ -557,6 +557,7 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options, + { + BlockdevOptionsNfs *opts = NULL; + Visitor *v; ++ const QDictEntry *e; + Error *local_err = NULL; + + v = qobject_input_visitor_new_flat_confused(options, errp); +@@ -572,6 +573,12 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options, + return NULL; + } + ++ /* Remove the processed options from the QDict (the visitor processes ++ * _all_ options in the QDict) */ ++ while ((e = qdict_first(options))) { ++ qdict_del(options, e->key); ++ } ++ + return opts; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-numa-clarify-error-message-when-node-index-is-out-of.patch b/SOURCES/kvm-numa-clarify-error-message-when-node-index-is-out-of.patch new file mode 100644 index 0000000..855fdc1 --- /dev/null +++ b/SOURCES/kvm-numa-clarify-error-message-when-node-index-is-out-of.patch @@ -0,0 +1,57 @@ +From af911371436ef3adf09fca4433f71f8f17631e1b Mon Sep 17 00:00:00 2001 +From: Igor Mammedov +Date: Mon, 2 Jul 2018 13:57:09 +0200 +Subject: [PATCH 52/57] numa: clarify error message when node index is out of + range in -numa dist, ... + +RH-Author: Igor Mammedov +Message-id: <1530539829-260581-1-git-send-email-imammedo@redhat.com> +Patchwork-id: 81184 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH] numa: clarify error message when node index is out of range in -numa dist, ... +Bugzilla: 1578381 +RH-Acked-by: Andrew Jones +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Eduardo Habkost +RH-Acked-by: Pankaj Gupta + +When using following CLI: + -numa dist,src=128,dst=1,val=20 +user gets a rather confusing error message: + "Invalid node 128, max possible could be 128" + +Where 128 is number of nodes that QEMU supports (MAX_NODES), +while src/dst is an index up to that limit, so it should be +MAX_NODES - 1 in error message. +Make error message to explicitly state valid range for node +index to be more clear. + +Signed-off-by: Igor Mammedov +Message-Id: <1526483174-169008-1-git-send-email-imammedo@redhat.com> +Reviewed-by: Eric Blake +Signed-off-by: Eduardo Habkost +(cherry picked from commit 74f38e96b321ef8df2bf7fa1bd4f673ef06aca5b) +Signed-off-by: Igor Mammedov +Signed-off-by: Miroslav Rezanina +--- + numa.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/numa.c b/numa.c +index daf10d8..bb8f773 100644 +--- a/numa.c ++++ b/numa.c +@@ -140,9 +140,8 @@ static void parse_numa_distance(NumaDistOptions *dist, Error **errp) + uint8_t val = dist->val; + + if (src >= MAX_NODES || dst >= MAX_NODES) { +- error_setg(errp, +- "Invalid node %d, max possible could be %d", +- MAX(src, dst), MAX_NODES); ++ error_setg(errp, "Parameter '%s' expects an integer between 0 and %d", ++ src >= MAX_NODES ? "src" : "dst", MAX_NODES - 1); + return; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-nvdimm-no-need-to-overwrite-get_vmstate_memory_regio.patch b/SOURCES/kvm-nvdimm-no-need-to-overwrite-get_vmstate_memory_regio.patch new file mode 100644 index 0000000..b6da0ed --- /dev/null +++ b/SOURCES/kvm-nvdimm-no-need-to-overwrite-get_vmstate_memory_regio.patch @@ -0,0 +1,61 @@ +From cd99d0d882fc24428194547adb29399519ea09d5 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 29 Oct 2018 07:01:35 +0100 +Subject: [PATCH 05/22] nvdimm: no need to overwrite + get_vmstate_memory_region() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20181029070137.21196-2-armbru@redhat.com> +Patchwork-id: 82901 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/3] nvdimm: no need to overwrite get_vmstate_memory_region() +Bugzilla: 1620373 +RH-Acked-by: David Hildenbrand +RH-Acked-by: Igor Mammedov +RH-Acked-by: Laurent Vivier +RH-Acked-by: Marc-André Lureau + +From: David Hildenbrand + +Our parent class (PC_DIMM) provides exactly the same function. + +Reviewed-by: David Gibson +Reviewed-by: Igor Mammedov +Signed-off-by: David Hildenbrand +Message-Id: <20180619134141.29478-7-david@redhat.com> +Signed-off-by: Paolo Bonzini +(cherry picked from commit 4ab56d04ede6e0f979fc8e4a54b381e99cf0a255) +Signed-off-by: Miroslav Rezanina +--- + hw/mem/nvdimm.c | 6 ------ + 1 file changed, 6 deletions(-) + +diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c +index 0c962fd..5f1813d 100644 +--- a/hw/mem/nvdimm.c ++++ b/hw/mem/nvdimm.c +@@ -173,11 +173,6 @@ static void nvdimm_write_label_data(NVDIMMDevice *nvdimm, const void *buf, + memory_region_set_dirty(mr, backend_offset, size); + } + +-static MemoryRegion *nvdimm_get_vmstate_memory_region(PCDIMMDevice *dimm) +-{ +- return host_memory_backend_get_memory(dimm->hostmem, &error_abort); +-} +- + static void nvdimm_class_init(ObjectClass *oc, void *data) + { + PCDIMMDeviceClass *ddc = PC_DIMM_CLASS(oc); +@@ -185,7 +180,6 @@ static void nvdimm_class_init(ObjectClass *oc, void *data) + + ddc->realize = nvdimm_realize; + ddc->get_memory_region = nvdimm_get_memory_region; +- ddc->get_vmstate_memory_region = nvdimm_get_vmstate_memory_region; + + nvc->read_label_data = nvdimm_read_label_data; + nvc->write_label_data = nvdimm_write_label_data; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-object-fix-OBJ_PROP_LINK_UNREF_ON_RELEASE-ambivalenc.patch b/SOURCES/kvm-object-fix-OBJ_PROP_LINK_UNREF_ON_RELEASE-ambivalenc.patch new file mode 100644 index 0000000..4890b4d --- /dev/null +++ b/SOURCES/kvm-object-fix-OBJ_PROP_LINK_UNREF_ON_RELEASE-ambivalenc.patch @@ -0,0 +1,334 @@ +From b2c33dbdec6e1abecee77c28398275a7337d6154 Mon Sep 17 00:00:00 2001 +From: Serhii Popovych +Date: Mon, 9 Jul 2018 11:31:16 +0200 +Subject: [PATCH 26/89] object: fix OBJ_PROP_LINK_UNREF_ON_RELEASE ambivalence +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Serhii Popovych +Message-id: <1531135878-18813-2-git-send-email-spopovyc@redhat.com> +Patchwork-id: 81265 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 1/3] object: fix OBJ_PROP_LINK_UNREF_ON_RELEASE ambivalence +Bugzilla: 1556678 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Marc-André Lureau +RH-Acked-by: David Gibson + +From: Marc-André Lureau + +A link property can be set during creation, with +object_property_add_link() and later with object_property_set_link(). + +add_link() doesn't add a reference to the target object, while +set_link() does. + +Furthemore, OBJ_PROP_LINK_UNREF_ON_RELEASE flags, set during add_link, +says whether a reference must be released when the property is destroyed. +This can lead to leaks if the property was later set_link(), as the +added reference is never released. + +Instead, rename OBJ_PROP_LINK_UNREF_ON_RELEASE to OBJ_PROP_LINK_STRONG +and use that has an indication on how the link handle reference +management in set_link(). + +Signed-off-by: Marc-André Lureau +Message-id: 20180531195119.22021-3-marcandre.lureau@redhat.com +Signed-off-by: Gerd Hoffmann +(cherry picked from commit 265b578c584b1a86c7028790deaa2f4392dd0a65) +Signed-off-by: Serhii Popovych +Signed-off-by: Miroslav Rezanina + +Conflicts: + hw/dma/xlnx-zdma.c + +This file is missing in 2.12.0. +--- + hw/core/qdev-properties.c | 2 +- + hw/core/qdev.c | 2 +- + hw/display/xlnx_dp.c | 2 +- + hw/dma/xilinx_axidma.c | 4 ++-- + hw/i386/pc.c | 2 +- + hw/i386/pc_piix.c | 2 +- + hw/i386/pc_q35.c | 2 +- + hw/ipmi/ipmi.c | 2 +- + hw/net/xilinx_axienet.c | 4 ++-- + hw/ssi/xilinx_spips.c | 2 +- + include/qom/object.h | 12 +++++++++--- + net/can/can_host.c | 2 +- + net/colo-compare.c | 2 +- + qom/object.c | 8 +++++--- + target/arm/cpu.c | 4 ++-- + ui/console.c | 2 +- + 16 files changed, 31 insertions(+), 23 deletions(-) + +diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c +index 5bbc2d9..f3a83a3 100644 +--- a/hw/core/qdev-properties.c ++++ b/hw/core/qdev-properties.c +@@ -1309,7 +1309,7 @@ static void create_link_property(Object *obj, Property *prop, Error **errp) + object_property_add_link(obj, prop->name, prop->link_type, + child, + qdev_prop_allow_set_link_before_realize, +- OBJ_PROP_LINK_UNREF_ON_RELEASE, ++ OBJ_PROP_LINK_STRONG, + errp); + } + +diff --git a/hw/core/qdev.c b/hw/core/qdev.c +index f6f9247..ce7c316 100644 +--- a/hw/core/qdev.c ++++ b/hw/core/qdev.c +@@ -435,7 +435,7 @@ void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins, + object_property_add_link(OBJECT(dev), propname, TYPE_IRQ, + (Object **)&pins[i], + object_property_allow_set_link, +- OBJ_PROP_LINK_UNREF_ON_RELEASE, ++ OBJ_PROP_LINK_STRONG, + &error_abort); + g_free(propname); + } +diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c +index 6715b9c..b737e1d 100644 +--- a/hw/display/xlnx_dp.c ++++ b/hw/display/xlnx_dp.c +@@ -1221,7 +1221,7 @@ static void xlnx_dp_init(Object *obj) + object_property_add_link(obj, "dpdma", TYPE_XLNX_DPDMA, + (Object **) &s->dpdma, + xlnx_dp_set_dpdma, +- OBJ_PROP_LINK_UNREF_ON_RELEASE, ++ OBJ_PROP_LINK_STRONG, + &error_abort); + + /* +diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c +index 9b48103..401a328 100644 +--- a/hw/dma/xilinx_axidma.c ++++ b/hw/dma/xilinx_axidma.c +@@ -525,12 +525,12 @@ static void xilinx_axidma_realize(DeviceState *dev, Error **errp) + object_property_add_link(OBJECT(ds), "dma", TYPE_XILINX_AXI_DMA, + (Object **)&ds->dma, + object_property_allow_set_link, +- OBJ_PROP_LINK_UNREF_ON_RELEASE, ++ OBJ_PROP_LINK_STRONG, + &local_err); + object_property_add_link(OBJECT(cs), "dma", TYPE_XILINX_AXI_DMA, + (Object **)&cs->dma, + object_property_allow_set_link, +- OBJ_PROP_LINK_UNREF_ON_RELEASE, ++ OBJ_PROP_LINK_STRONG, + &local_err); + if (local_err) { + goto xilinx_axidma_realize_fail; +diff --git a/hw/i386/pc.c b/hw/i386/pc.c +index 265decf..7160969 100644 +--- a/hw/i386/pc.c ++++ b/hw/i386/pc.c +@@ -485,7 +485,7 @@ void pc_cmos_init(PCMachineState *pcms, + TYPE_ISA_DEVICE, + (Object **)&pcms->rtc, + object_property_allow_set_link, +- OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); ++ OBJ_PROP_LINK_STRONG, &error_abort); + object_property_set_link(OBJECT(pcms), OBJECT(s), + "rtc_state", &error_abort); + +diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c +index 8a355df..00549c4 100644 +--- a/hw/i386/pc_piix.c ++++ b/hw/i386/pc_piix.c +@@ -290,7 +290,7 @@ static void pc_init1(MachineState *machine, + TYPE_HOTPLUG_HANDLER, + (Object **)&pcms->acpi_dev, + object_property_allow_set_link, +- OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); ++ OBJ_PROP_LINK_STRONG, &error_abort); + object_property_set_link(OBJECT(machine), OBJECT(piix4_pm), + PC_MACHINE_ACPI_DEVICE_PROP, &error_abort); + } +diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c +index e17807e..a273bd5 100644 +--- a/hw/i386/pc_q35.c ++++ b/hw/i386/pc_q35.c +@@ -194,7 +194,7 @@ static void pc_q35_init(MachineState *machine) + TYPE_HOTPLUG_HANDLER, + (Object **)&pcms->acpi_dev, + object_property_allow_set_link, +- OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); ++ OBJ_PROP_LINK_STRONG, &error_abort); + object_property_set_link(OBJECT(machine), OBJECT(lpc), + PC_MACHINE_ACPI_DEVICE_PROP, &error_abort); + +diff --git a/hw/ipmi/ipmi.c b/hw/ipmi/ipmi.c +index 9be281f..63c0317 100644 +--- a/hw/ipmi/ipmi.c ++++ b/hw/ipmi/ipmi.c +@@ -104,7 +104,7 @@ void ipmi_bmc_find_and_link(Object *obj, Object **bmc) + { + object_property_add_link(obj, "bmc", TYPE_IPMI_BMC, bmc, + isa_ipmi_bmc_check, +- OBJ_PROP_LINK_UNREF_ON_RELEASE, ++ OBJ_PROP_LINK_STRONG, + &error_abort); + } + +diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c +index d4c2c89..cc880a3 100644 +--- a/hw/net/xilinx_axienet.c ++++ b/hw/net/xilinx_axienet.c +@@ -951,12 +951,12 @@ static void xilinx_enet_realize(DeviceState *dev, Error **errp) + object_property_add_link(OBJECT(ds), "enet", "xlnx.axi-ethernet", + (Object **) &ds->enet, + object_property_allow_set_link, +- OBJ_PROP_LINK_UNREF_ON_RELEASE, ++ OBJ_PROP_LINK_STRONG, + &local_err); + object_property_add_link(OBJECT(cs), "enet", "xlnx.axi-ethernet", + (Object **) &cs->enet, + object_property_allow_set_link, +- OBJ_PROP_LINK_UNREF_ON_RELEASE, ++ OBJ_PROP_LINK_STRONG, + &local_err); + if (local_err) { + goto xilinx_enet_realize_fail; +diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c +index 426f971..068878c 100644 +--- a/hw/ssi/xilinx_spips.c ++++ b/hw/ssi/xilinx_spips.c +@@ -1345,7 +1345,7 @@ static void xlnx_zynqmp_qspips_init(Object *obj) + object_property_add_link(obj, "stream-connected-dma", TYPE_STREAM_SLAVE, + (Object **)&rq->dma, + object_property_allow_set_link, +- OBJ_PROP_LINK_UNREF_ON_RELEASE, ++ OBJ_PROP_LINK_STRONG, + NULL); + } + +diff --git a/include/qom/object.h b/include/qom/object.h +index 96ce81b..fc4555d 100644 +--- a/include/qom/object.h ++++ b/include/qom/object.h +@@ -1103,6 +1103,11 @@ char *object_property_get_str(Object *obj, const char *name, + * @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. ++ * + */ + void object_property_set_link(Object *obj, Object *value, + const char *name, Error **errp); +@@ -1393,7 +1398,7 @@ void object_property_add_child(Object *obj, const char *name, + + typedef enum { + /* Unref the link pointer when the property is deleted */ +- OBJ_PROP_LINK_UNREF_ON_RELEASE = 0x1, ++ OBJ_PROP_LINK_STRONG = 0x1, + } ObjectPropertyLinkFlags; + + /** +@@ -1431,8 +1436,9 @@ void object_property_allow_set_link(const Object *, const char *, + * 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_UNREF_ON_RELEASE bit is set, +- * the reference count is decremented when the property is deleted. ++ * @flags OBJ_PROP_LINK_STRONG bit is set, ++ * the reference count is decremented when the property is deleted or ++ * modified. + */ + void object_property_add_link(Object *obj, const char *name, + const char *type, Object **child, +diff --git a/net/can/can_host.c b/net/can/can_host.c +index c3d2652..c79347a 100644 +--- a/net/can/can_host.c ++++ b/net/can/can_host.c +@@ -77,7 +77,7 @@ static void can_host_instance_init(Object *obj) + object_property_add_link(obj, "canbus", TYPE_CAN_BUS, + (Object **)&ch->bus, + object_property_allow_set_link, +- OBJ_PROP_LINK_UNREF_ON_RELEASE, ++ OBJ_PROP_LINK_STRONG, + &error_abort); + } + +diff --git a/net/colo-compare.c b/net/colo-compare.c +index 23b2d2c..63469b1 100644 +--- a/net/colo-compare.c ++++ b/net/colo-compare.c +@@ -980,7 +980,7 @@ static void colo_compare_init(Object *obj) + object_property_add_link(obj, "iothread", TYPE_IOTHREAD, + (Object **)&s->iothread, + object_property_allow_set_link, +- OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); ++ OBJ_PROP_LINK_STRONG, NULL); + + s->vnet_hdr = false; + object_property_add_bool(obj, "vnet_hdr_support", compare_get_vnet_hdr, +diff --git a/qom/object.c b/qom/object.c +index 76a89af..c4f1d36 100644 +--- a/qom/object.c ++++ b/qom/object.c +@@ -1564,9 +1564,11 @@ static void object_set_link_property(Object *obj, Visitor *v, + return; + } + +- object_ref(new_target); + *child = new_target; +- object_unref(old_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 gchar *part) +@@ -1581,7 +1583,7 @@ static void object_release_link_property(Object *obj, const char *name, + { + LinkProperty *prop = opaque; + +- if ((prop->flags & OBJ_PROP_LINK_UNREF_ON_RELEASE) && *prop->child) { ++ if ((prop->flags & OBJ_PROP_LINK_STRONG) && *prop->child) { + object_unref(*prop->child); + } + g_free(prop); +diff --git a/target/arm/cpu.c b/target/arm/cpu.c +index 4255e9c..9d030e0 100644 +--- a/target/arm/cpu.c ++++ b/target/arm/cpu.c +@@ -672,7 +672,7 @@ static void arm_cpu_post_init(Object *obj) + TYPE_MEMORY_REGION, + (Object **)&cpu->secure_memory, + qdev_prop_allow_set_link_before_realize, +- OBJ_PROP_LINK_UNREF_ON_RELEASE, ++ OBJ_PROP_LINK_STRONG, + &error_abort); + #endif + } +@@ -700,7 +700,7 @@ static void arm_cpu_post_init(Object *obj) + if (arm_feature(&cpu->env, ARM_FEATURE_M_SECURITY)) { + object_property_add_link(obj, "idau", TYPE_IDAU_INTERFACE, &cpu->idau, + qdev_prop_allow_set_link_before_realize, +- OBJ_PROP_LINK_UNREF_ON_RELEASE, ++ OBJ_PROP_LINK_STRONG, + &error_abort); + qdev_property_add_static(DEVICE(obj), &arm_cpu_initsvtor_property, + &error_abort); +diff --git a/ui/console.c b/ui/console.c +index 73b2d3c..4431e77 100644 +--- a/ui/console.c ++++ b/ui/console.c +@@ -1305,7 +1305,7 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, + object_property_add_link(obj, "device", TYPE_DEVICE, + (Object **)&s->device, + object_property_allow_set_link, +- OBJ_PROP_LINK_UNREF_ON_RELEASE, ++ OBJ_PROP_LINK_STRONG, + &error_abort); + object_property_add_uint32_ptr(obj, "head", + &s->head, &error_abort); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-osdep-add-wait.h-compat-macros.patch b/SOURCES/kvm-osdep-add-wait.h-compat-macros.patch new file mode 100644 index 0000000..6d17462 --- /dev/null +++ b/SOURCES/kvm-osdep-add-wait.h-compat-macros.patch @@ -0,0 +1,59 @@ +From 39f21fd9c1bcd7f085193e5310efa79c3c759acd Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Thu, 21 Jun 2018 18:54:39 +0200 +Subject: [PATCH 30/57] osdep: add wait.h compat macros + +RH-Author: plai@redhat.com +Message-id: <1529607285-9942-5-git-send-email-plai@redhat.com> +Patchwork-id: 80938 +O-Subject: [RHEL7.6 PATCH BZ 1526645 04/10] osdep: add wait.h compat macros +Bugzilla: 1526645 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Maxime Coquelin +RH-Acked-by: Laurent Vivier + +From: "Michael S. Tsirkin" + +Man page for WCOREDUMP says: + + WCOREDUMP(wstatus) returns true if the child produced a core dump. + This macro should be employed only if WIFSIGNALED returned true. + + 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. + +Let's do exactly this. + +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 28012e190e6897cfc2a98364240909d08e90ffc0) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + include/qemu/osdep.h | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h +index 4165806..afc28e5 100644 +--- a/include/qemu/osdep.h ++++ b/include/qemu/osdep.h +@@ -108,6 +108,16 @@ extern int daemon(int, int); + #include "qemu/typedefs.h" + + /* ++ * 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 +-- +1.8.3.1 + diff --git a/SOURCES/kvm-osdep-powerpc64-align-memory-to-allow-2MB-radix-THP-.patch b/SOURCES/kvm-osdep-powerpc64-align-memory-to-allow-2MB-radix-THP-.patch new file mode 100644 index 0000000..15a952d --- /dev/null +++ b/SOURCES/kvm-osdep-powerpc64-align-memory-to-allow-2MB-radix-THP-.patch @@ -0,0 +1,48 @@ +From 7ac01358cfb1a7900cfc4b39714f9affb817dda0 Mon Sep 17 00:00:00 2001 +From: David Gibson +Date: Mon, 16 Jul 2018 05:49:55 +0200 +Subject: [PATCH 45/89] osdep: powerpc64 align memory to allow 2MB radix THP + page tables + +RH-Author: David Gibson +Message-id: <20180716054955.12926-1-dgibson@redhat.com> +Patchwork-id: 81358 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH] osdep: powerpc64 align memory to allow 2MB radix THP page tables +Bugzilla: 1600797 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Thomas Huth +RH-Acked-by: Serhii Popovych + +From: Nicholas Piggin + +This allows KVM with the Book3S radix MMU mode to take advantage of +THP and install larger pages in the partition scope page tables (the +host translation). + +Signed-off-by: Nicholas Piggin +Signed-off-by: David Gibson +(cherry picked from commit 0c1272cc7c72dfe0ef66be8f283cf67c74b58586) + +Signed-off-by: David Gibson +Signed-off-by: Miroslav Rezanina +--- + include/qemu/osdep.h | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h +index afc28e5..9ed6242 100644 +--- a/include/qemu/osdep.h ++++ b/include/qemu/osdep.h +@@ -367,7 +367,8 @@ void qemu_anon_ram_free(void *ptr, size_t size); + #endif + + #if defined(__linux__) && \ +- (defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)) ++ (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. */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pc-Add-rhel7.6.0-machine-types.patch b/SOURCES/kvm-pc-Add-rhel7.6.0-machine-types.patch new file mode 100644 index 0000000..6efbbf1 --- /dev/null +++ b/SOURCES/kvm-pc-Add-rhel7.6.0-machine-types.patch @@ -0,0 +1,116 @@ +From 9476ec6ee439ffc60ddd1478c2ed1fb87253f319 Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Fri, 29 Jun 2018 18:59:37 +0200 +Subject: [PATCH 50/57] pc: Add rhel7.6.0 machine-types + +RH-Author: Eduardo Habkost +Message-id: <20180629185937.24186-2-ehabkost@redhat.com> +Patchwork-id: 81175 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/1] pc: Add rhel7.6.0 machine-types +Bugzilla: 1557051 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Igor Mammedov + +Add rhel7.6.0 machine-types. They will be similar to the +existing 7.5.0 machines, but without the PC_RHEL7_5_COMPAT bits. + +Signed-off-by: Eduardo Habkost +Signed-off-by: Miroslav Rezanina +--- + hw/i386/pc_piix.c | 21 ++++++++++++++++++--- + hw/i386/pc_q35.c | 18 ++++++++++++++++-- + 2 files changed, 34 insertions(+), 5 deletions(-) + +diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c +index 9991650..8a355df 100644 +--- a/hw/i386/pc_piix.c ++++ b/hw/i386/pc_piix.c +@@ -1157,6 +1157,21 @@ static void pc_machine_rhel7_options(MachineClass *m) + m->is_default = 1; + } + ++static void pc_init_rhel760(MachineState *machine) ++{ ++ pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \ ++ TYPE_I440FX_PCI_DEVICE); ++} ++ ++static void pc_machine_rhel760_options(MachineClass *m) ++{ ++ pc_machine_rhel7_options(m); ++ m->desc = "RHEL 7.6.0 PC (i440FX + PIIX, 1996)"; ++} ++ ++DEFINE_PC_MACHINE(rhel760, "pc-i440fx-rhel7.6.0", pc_init_rhel760, ++ pc_machine_rhel760_options); ++ + static void pc_init_rhel750(MachineState *machine) + { + pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \ +@@ -1165,7 +1180,9 @@ static void pc_init_rhel750(MachineState *machine) + + static void pc_machine_rhel750_options(MachineClass *m) + { +- pc_machine_rhel7_options(m); ++ pc_machine_rhel760_options(m); ++ m->alias = NULL; ++ m->is_default = 0; + m->desc = "RHEL 7.5.0 PC (i440FX + PIIX, 1996)"; + m->auto_enable_numa_with_memhp = false; + SET_MACHINE_COMPAT(m, PC_RHEL7_5_COMPAT); +@@ -1184,8 +1201,6 @@ static void pc_machine_rhel740_options(MachineClass *m) + { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pc_machine_rhel750_options(m); +- m->alias = NULL; +- m->is_default = 0; + m->desc = "RHEL 7.4.0 PC (i440FX + PIIX, 1996)"; + m->numa_auto_assign_ram = numa_legacy_auto_assign_ram; + pcmc->pc_rom_ro = false; +diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c +index a4387e0..e17807e 100644 +--- a/hw/i386/pc_q35.c ++++ b/hw/i386/pc_q35.c +@@ -424,6 +424,20 @@ static void pc_q35_machine_rhel7_options(MachineClass *m) + SET_MACHINE_COMPAT(m, PC_RHEL_COMPAT); + } + ++static void pc_q35_init_rhel760(MachineState *machine) ++{ ++ pc_q35_init(machine); ++} ++ ++static void pc_q35_machine_rhel760_options(MachineClass *m) ++{ ++ pc_q35_machine_rhel7_options(m); ++ m->desc = "RHEL-7.6.0 PC (Q35 + ICH9, 2009)"; ++} ++ ++DEFINE_PC_MACHINE(q35_rhel760, "pc-q35-rhel7.6.0", pc_q35_init_rhel760, ++ pc_q35_machine_rhel760_options); ++ + static void pc_q35_init_rhel750(MachineState *machine) + { + pc_q35_init(machine); +@@ -432,7 +446,8 @@ static void pc_q35_init_rhel750(MachineState *machine) + static void pc_q35_machine_rhel750_options(MachineClass *m) + { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); +- pc_q35_machine_rhel7_options(m); ++ pc_q35_machine_rhel760_options(m); ++ m->alias = NULL; + m->desc = "RHEL-7.5.0 PC (Q35 + ICH9, 2009)"; + m->auto_enable_numa_with_memhp = false; + pcmc->default_nic_model = "e1000"; +@@ -451,7 +466,6 @@ static void pc_q35_machine_rhel740_options(MachineClass *m) + { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pc_q35_machine_rhel750_options(m); +- m->alias = NULL; + m->desc = "RHEL-7.4.0 PC (Q35 + ICH9, 2009)"; + m->numa_auto_assign_ram = numa_legacy_auto_assign_ram; + pcmc->pc_rom_ro = false; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pc-Support-firmware-configuration-with-blockdev.patch b/SOURCES/kvm-pc-Support-firmware-configuration-with-blockdev.patch new file mode 100644 index 0000000..d01f708 --- /dev/null +++ b/SOURCES/kvm-pc-Support-firmware-configuration-with-blockdev.patch @@ -0,0 +1,447 @@ +From 056788975c5ebe651d8016ec714d6120da4bedc7 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:17 +0200 +Subject: [PATCH 50/53] pc: Support firmware configuration with -blockdev +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-29-armbru@redhat.com> +Patchwork-id: 87999 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 28/31] pc: Support firmware configuration with -blockdev +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +The PC machines put firmware in ROM by default. To get it put into +flash memory (required by OVMF), you have to use -drive +if=pflash,unit=0,... and optionally -drive if=pflash,unit=1,... + +Why two -drive? This permits setting up one part of the flash memory +read-only, and the other part read/write. It also makes upgrading +firmware on the host easier. Below the hood, it creates two separate +flash devices, because we were too lazy to improve our flash device +models to support sector protection. + +The problem at hand is to do the same with -blockdev somehow, as one +more step towards deprecating -drive. + +Mapping -drive if=none,... to -blockdev is a solved problem. With +if=T other than if=none, -drive additionally configures a block device +frontend. For non-onboard devices, that part maps to -device. Also a +solved problem. For onboard devices such as PC flash memory, we have +an unsolved problem. + +This is actually an instance of a wider problem: our general device +configuration interface doesn't cover onboard devices. Instead, we have +a zoo of ad hoc interfaces that are much more limited. One of them is +-drive, which we'd rather deprecate, but can't until we have suitable +replacements for all its uses. + +Sadly, I can't attack the wider problem today. So back to the narrow +problem. + +My first idea was to reduce it to its solved buddy by using pluggable +instead of onboard devices for the flash memory. Workable, but it +requires some extra smarts in firmware descriptors and libvirt. Paolo +had an idea that is simpler for libvirt: keep the devices onboard, and +add machine properties for their block backends. + +The implementation is less than straightforward, I'm afraid. + +First, block backend properties are *qdev* properties. Machines can't +have those, as they're not devices. I could duplicate these qdev +properties as QOM properties, but I hate that. + +More seriously, the properties do not belong to the machine, they +belong to the onboard flash devices. Adding them to the machine would +then require bad magic to somehow transfer them to the flash devices. +Fortunately, QOM provides the means to handle exactly this case: add +alias properties to the machine that forward to the onboard devices' +properties. + +Properties need to be created in .instance_init() methods. For PC +machines, that's pc_machine_initfn(). To make alias properties work, +we need to create the onboard flash devices there, too. Requires +several bug fixes, in the previous commits. We also have to realize +the devices. More on that below. + +If the user sets pflash0, firmware resides in flash memory. +pc_system_firmware_init() maps and realizes the flash devices. + +Else, firmware resides in ROM. The onboard flash devices aren't used +then. pc_system_firmware_init() destroys them unrealized, along with +the alias properties. + +The existing code to pick up drives defined with -drive if=pflash is +replaced by code to desugar into the machine properties. + +Signed-off-by: Markus Armbruster +Reviewed-by: Philippe Mathieu-Daudé +Reviewed-by: Michael S. Tsirkin +Message-Id: <87ftrtux81.fsf@dusky.pond.sub.org> +(cherry picked from commit ebc29e1beab02646702c8cb9a1d29b68f72ad503) +[Conflict in hw/i386/pc.c due to lack of commit f5878b03811, and in +hw/i386/pc_sysfw.c due to lack of commit d471bf3ebba and due to the +part of downstream commit 84733200df4 that came from c3f813d2f53] + +Signed-off-by: Miroslav Rezanina +--- + hw/i386/pc.c | 2 + + hw/i386/pc_sysfw.c | 248 +++++++++++++++++++++++++++++++++------------------ + include/hw/i386/pc.h | 3 + + 3 files changed, 164 insertions(+), 89 deletions(-) + +diff --git a/hw/i386/pc.c b/hw/i386/pc.c +index 86e9d43..1f55a28 100644 +--- a/hw/i386/pc.c ++++ b/hw/i386/pc.c +@@ -2249,6 +2249,8 @@ static void pc_machine_initfn(Object *obj) + pcms->smbus = true; + pcms->sata = true; + pcms->pit = true; ++ ++ pc_system_flash_create(pcms); + } + + static void pc_machine_reset(void) +diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c +index 2a0e18c..a120342 100644 +--- a/hw/i386/pc_sysfw.c ++++ b/hw/i386/pc_sysfw.c +@@ -28,6 +28,7 @@ + #include "sysemu/block-backend.h" + #include "qemu/error-report.h" + #include "qemu/option.h" ++#include "qemu/units.h" + #include "hw/sysbus.h" + #include "hw/hw.h" + #include "hw/i386/pc.h" +@@ -39,6 +40,17 @@ + + #define BIOS_FILENAME "bios.bin" + ++/* ++ * We don't have a theoretically justifiable exact lower bound on the base ++ * address of any flash mapping. In practice, the IO-APIC MMIO range is ++ * [0xFEE00000..0xFEE01000] -- see IO_APIC_DEFAULT_ADDRESS --, leaving free ++ * only 18MB-4KB below 4G. For now, restrict the cumulative mapping to 8MB in ++ * size. ++ */ ++#define FLASH_SIZE_LIMIT (8 * MiB) ++ ++#define FLASH_SECTOR_SIZE 4096 ++ + static void pc_isa_bios_init(MemoryRegion *rom_memory, + MemoryRegion *flash_mem, + int ram_size) +@@ -70,96 +82,118 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory, + memory_region_set_readonly(isa_bios, true); + } + +-#define FLASH_MAP_UNIT_MAX 2 ++static PFlashCFI01 *pc_pflash_create(PCMachineState *pcms, ++ const char *name, ++ const char *alias_prop_name) ++{ ++ DeviceState *dev = qdev_create(NULL, TYPE_PFLASH_CFI01); + +-/* We don't have a theoretically justifiable exact lower bound on the base +- * address of any flash mapping. In practice, the IO-APIC MMIO range is +- * [0xFEE00000..0xFEE01000[ -- see IO_APIC_DEFAULT_ADDRESS --, leaving free +- * only 18MB-4KB below 4G. For now, restrict the cumulative mapping to 8MB in +- * size. +- */ +-#define FLASH_MAP_BASE_MIN ((hwaddr)(0x100000000ULL - 8*1024*1024)) ++ qdev_prop_set_uint64(dev, "sector-length", FLASH_SECTOR_SIZE); ++ qdev_prop_set_uint8(dev, "width", 1); ++ qdev_prop_set_string(dev, "name", name); ++ object_property_add_child(OBJECT(pcms), name, OBJECT(dev), ++ &error_abort); ++ object_property_add_alias(OBJECT(pcms), alias_prop_name, ++ OBJECT(dev), "drive", &error_abort); ++ return PFLASH_CFI01(dev); ++} + +-/* This function maps flash drives from 4G downward, in order of their unit +- * numbers. The mapping starts at unit#0, with unit number increments of 1, and +- * stops before the first missing flash drive, or before +- * unit#FLASH_MAP_UNIT_MAX, whichever is reached first. +- * +- * Addressing within one flash drive is of course not reversed. +- * +- * An error message is printed and the process exits if: +- * - the size of the backing file for a flash drive is non-positive, or not a +- * multiple of the required sector size, or +- * - the current mapping's base address would fall below FLASH_MAP_BASE_MIN. ++void pc_system_flash_create(PCMachineState *pcms) ++{ ++ PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); ++ ++ if (pcmc->pci_enabled) { ++ pcms->flash[0] = pc_pflash_create(pcms, "system.flash0", ++ "pflash0"); ++ pcms->flash[1] = pc_pflash_create(pcms, "system.flash1", ++ "pflash1"); ++ } ++} ++ ++static void pc_system_flash_cleanup_unused(PCMachineState *pcms) ++{ ++ char *prop_name; ++ int i; ++ Object *dev_obj; ++ ++ assert(PC_MACHINE_GET_CLASS(pcms)->pci_enabled); ++ ++ for (i = 0; i < ARRAY_SIZE(pcms->flash); i++) { ++ dev_obj = OBJECT(pcms->flash[i]); ++ if (!object_property_get_bool(dev_obj, "realized", &error_abort)) { ++ prop_name = g_strdup_printf("pflash%d", i); ++ object_property_del(OBJECT(pcms), prop_name, &error_abort); ++ g_free(prop_name); ++ object_unparent(dev_obj); ++ pcms->flash[i] = NULL; ++ } ++ } ++} ++ ++/* ++ * Map the pcms->flash[] from 4GiB downward, and realize. ++ * Map them in descending order, i.e. pcms->flash[0] at the top, ++ * without gaps. ++ * Stop at the first pcms->flash[0] lacking a block backend. ++ * Set each flash's size from its block backend. Fatal error if the ++ * size isn't a non-zero multiple of 4KiB, or the total size exceeds ++ * FLASH_SIZE_LIMIT. + * +- * The drive with unit#0 (if available) is mapped at the highest address, and +- * it is passed to pc_isa_bios_init(). Merging several drives for isa-bios is ++ * If pcms->flash[0] has a block backend, its memory is passed to ++ * pc_isa_bios_init(). Merging several flash devices for isa-bios is + * not supported. + */ +-static void pc_system_flash_init(MemoryRegion *rom_memory) ++static void pc_system_flash_map(PCMachineState *pcms, ++ MemoryRegion *rom_memory) + { +- int unit; +- DriveInfo *pflash_drv; ++ hwaddr total_size = 0; ++ int i; + BlockBackend *blk; + int64_t size; +- char *fatal_errmsg = NULL; +- hwaddr phys_addr = 0x100000000ULL; +- uint32_t sector_size = 4096; + PFlashCFI01 *system_flash; + MemoryRegion *flash_mem; +- char name[64]; + void *flash_ptr; + int ret, flash_size; + +- for (unit = 0; +- (unit < FLASH_MAP_UNIT_MAX && +- (pflash_drv = drive_get(IF_PFLASH, 0, unit)) != NULL); +- ++unit) { +- blk = blk_by_legacy_dinfo(pflash_drv); ++ assert(PC_MACHINE_GET_CLASS(pcms)->pci_enabled); ++ ++ for (i = 0; i < ARRAY_SIZE(pcms->flash); i++) { ++ system_flash = pcms->flash[i]; ++ blk = pflash_cfi01_get_blk(system_flash); ++ if (!blk) { ++ break; ++ } + size = blk_getlength(blk); + if (size < 0) { +- fatal_errmsg = g_strdup_printf("failed to get backing file size"); +- } else if (size == 0) { +- fatal_errmsg = g_strdup_printf("PC system firmware (pflash) " +- "cannot have zero size"); +- } else if ((size % sector_size) != 0) { +- fatal_errmsg = g_strdup_printf("PC system firmware (pflash) " +- "must be a multiple of 0x%x", sector_size); +- } else if (phys_addr < size || phys_addr - size < FLASH_MAP_BASE_MIN) { +- fatal_errmsg = g_strdup_printf("oversized backing file, pflash " +- "segments cannot be mapped under " +- TARGET_FMT_plx, FLASH_MAP_BASE_MIN); ++ error_report("can't get size of block device %s: %s", ++ blk_name(blk), strerror(-size)); ++ exit(1); + } +- if (fatal_errmsg != NULL) { +- Location loc; +- +- /* push a new, "none" location on the location stack; overwrite its +- * contents with the location saved in the option; print the error +- * (includes location); pop the top +- */ +- loc_push_none(&loc); +- if (pflash_drv->opts != NULL) { +- qemu_opts_loc_restore(pflash_drv->opts); +- } +- error_report("%s", fatal_errmsg); +- loc_pop(&loc); +- g_free(fatal_errmsg); ++ if (size == 0 || size % FLASH_SECTOR_SIZE != 0) { ++ error_report("system firmware block device %s has invalid size " ++ "%" PRId64, ++ blk_name(blk), size); ++ info_report("its size must be a non-zero multiple of 0x%x", ++ FLASH_SECTOR_SIZE); ++ exit(1); ++ } ++ if ((hwaddr)size != size ++ || total_size > HWADDR_MAX - size ++ || total_size + size > FLASH_SIZE_LIMIT) { ++ error_report("combined size of system firmware exceeds " ++ "%" PRIu64 " bytes", ++ FLASH_SIZE_LIMIT); + exit(1); + } + +- phys_addr -= size; +- +- /* pflash_cfi01_register() creates a deep copy of the name */ +- snprintf(name, sizeof name, "system.flash%d", unit); +- system_flash = pflash_cfi01_register(phys_addr, name, +- size, blk, sector_size, +- 1 /* width */, +- 0x0000 /* id0 */, +- 0x0000 /* id1 */, +- 0x0000 /* id2 */, +- 0x0000 /* id3 */, +- 0 /* be */); +- if (unit == 0) { ++ total_size += size; ++ qdev_prop_set_uint32(DEVICE(system_flash), "num-blocks", ++ size / FLASH_SECTOR_SIZE); ++ qdev_init_nofail(DEVICE(system_flash)); ++ sysbus_mmio_map(SYS_BUS_DEVICE(system_flash), 0, ++ 0x100000000ULL - total_size); ++ ++ if (i == 0) { + flash_mem = pflash_cfi01_get_memory(system_flash); + pc_isa_bios_init(rom_memory, flash_mem, size); + +@@ -244,32 +278,68 @@ void pc_system_firmware_init(PCMachineState *pcms, + MemoryRegion *rom_memory) + { + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); +- bool isapc_ram_fw = !pcmc->pci_enabled; ++ int i; + DriveInfo *pflash_drv; ++ BlockBackend *pflash_blk[ARRAY_SIZE(pcms->flash)]; ++ Location loc; + +- pflash_drv = drive_get(IF_PFLASH, 0, 0); +- +- if (isapc_ram_fw || pflash_drv == NULL) { +- /* When a pflash drive is not found, use rom-mode */ +- old_pc_system_rom_init(rom_memory, isapc_ram_fw); ++ if (!pcmc->pci_enabled) { ++ old_pc_system_rom_init(rom_memory, true); + return; + } + +- if (shadow_bios_after_incoming) { +- MachineClass *mc; ++ /* Map legacy -drive if=pflash to machine properties */ ++ for (i = 0; i < ARRAY_SIZE(pcms->flash); i++) { ++ pflash_blk[i] = pflash_cfi01_get_blk(pcms->flash[i]); ++ pflash_drv = drive_get(IF_PFLASH, 0, i); ++ if (!pflash_drv) { ++ continue; ++ } ++ loc_push_none(&loc); ++ qemu_opts_loc_restore(pflash_drv->opts); ++ if (pflash_blk[i]) { ++ error_report("clashes with -machine"); ++ exit(1); ++ } ++ pflash_blk[i] = blk_by_legacy_dinfo(pflash_drv); ++ qdev_prop_set_drive(DEVICE(pcms->flash[i]), ++ "drive", pflash_blk[i], &error_fatal); ++ loc_pop(&loc); ++ } + +- mc = MACHINE_GET_CLASS(current_machine); +- error_report("flash-based firmware is not supported by machine %s", +- mc->name); +- exit(1); ++ /* Reject gaps */ ++ for (i = 1; i < ARRAY_SIZE(pcms->flash); i++) { ++ if (pflash_blk[i] && !pflash_blk[i - 1]) { ++ error_report("pflash%d requires pflash%d", i, i - 1); ++ exit(1); ++ } + } + +- if (kvm_enabled() && !kvm_readonly_mem_enabled()) { +- /* Older KVM cannot execute from device memory. So, flash memory +- * cannot be used unless the readonly memory kvm capability is present. */ +- fprintf(stderr, "qemu: pflash with kvm requires KVM readonly memory support\n"); +- exit(1); ++ if (!pflash_blk[0]) { ++ /* Machine property pflash0 not set, use ROM mode */ ++ old_pc_system_rom_init(rom_memory, false); ++ } else { ++ if (shadow_bios_after_incoming) { ++ MachineClass *mc; ++ ++ mc = MACHINE_GET_CLASS(current_machine); ++ error_report("flash-based firmware is not supported by machine %s", ++ mc->name); ++ exit(1); ++ } ++ ++ if (kvm_enabled() && !kvm_readonly_mem_enabled()) { ++ /* ++ * Older KVM cannot execute from device memory. So, flash ++ * memory cannot be used unless the readonly memory kvm ++ * capability is present. ++ */ ++ error_report("pflash with kvm requires KVM readonly memory support"); ++ exit(1); ++ } ++ ++ pc_system_flash_map(pcms, rom_memory); + } + +- pc_system_flash_init(rom_memory); ++ pc_system_flash_cleanup_unused(pcms); + } +diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h +index 611e111..b91b95d 100644 +--- a/include/hw/i386/pc.h ++++ b/include/hw/i386/pc.h +@@ -6,6 +6,7 @@ + #include "hw/boards.h" + #include "hw/isa/isa.h" + #include "hw/block/fdc.h" ++#include "hw/block/flash.h" + #include "net/net.h" + #include "hw/i386/ioapic.h" + +@@ -41,6 +42,7 @@ struct PCMachineState { + PCIBus *bus; + FWCfgState *fw_cfg; + qemu_irq *gsi; ++ PFlashCFI01 *flash[2]; + + /* Configuration options: */ + uint64_t max_ram_below_4g; +@@ -290,6 +292,7 @@ extern PCIDevice *piix4_dev; + int piix4_init(PCIBus *bus, ISABus **isa_bus, int devfn); + + /* pc_sysfw.c */ ++void pc_system_flash_create(PCMachineState *pcms); + void pc_system_firmware_init(PCMachineState *pcms, MemoryRegion *rom_memory); + + /* acpi-build.c */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pc-acpi-fix-memory-hotplug-regression-by-reducing-st.patch b/SOURCES/kvm-pc-acpi-fix-memory-hotplug-regression-by-reducing-st.patch new file mode 100644 index 0000000..8279315 --- /dev/null +++ b/SOURCES/kvm-pc-acpi-fix-memory-hotplug-regression-by-reducing-st.patch @@ -0,0 +1,101 @@ +From 6d5bc18c3af1969cfcaea9c9bafb5ad0d32bb163 Mon Sep 17 00:00:00 2001 +From: Igor Mammedov +Date: Tue, 7 Aug 2018 12:33:52 +0200 +Subject: [PATCH 05/13] pc: acpi: fix memory hotplug regression by reducing + stub SRAT entry size + +RH-Author: Igor Mammedov +Message-id: <1533645232-98572-1-git-send-email-imammedo@redhat.com> +Patchwork-id: 81662 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH] pc: acpi: fix memory hotplug regression by reducing stub SRAT entry size +Bugzilla: 1609234 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Eduardo Habkost + +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1609234 +Brew: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=17640116 +Branch: rhv7/master-2.12.0 + + 1: there is no coldplugged dimm in the last numa node + but there is a coldplugged dimm in another node + + -m 4096,slots=4,maxmem=32G \ + -object memory-backend-ram,id=m0,size=2G \ + -device pc-dimm,memdev=m0,node=0 \ + -numa node,nodeid=0 \ + -numa node,nodeid=1 + + 2: if order of dimms on CLI is: + 1st plugged dimm in node1 + 2nd plugged dimm in node0 + + -m 4096,slots=4,maxmem=32G \ + -object memory-backend-ram,size=2G,id=m0 \ + -device pc-dimm,memdev=m0,node=1 \ + -object memory-backend-ram,id=m1,size=2G \ + -device pc-dimm,memdev=m1,node=0 \ + -numa node,nodeid=0 \ + -numa node,nodeid=1 + +(qemu) object_add memory-backend-ram,id=m2,size=1G +(qemu) device_add pc-dimm,memdev=m2,node=0 + +the first DIMM hotplug to any node except the last one +fails (Windows is unable to online it). + +Length reduction of stub hotplug memory SRAT entry, +fixes issue for some reason. + +RHBZ: 1609234 + +Signed-off-by: Igor Mammedov +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin + +(cherry picked from commit 10efd7e1088855dc019e6a248ac7f9b24af8dd26) +Signed-off-by: Miroslav Rezanina +--- + hw/i386/acpi-build.c | 19 ++++++++++--------- + 1 file changed, 10 insertions(+), 9 deletions(-) + +diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c +index b309a97..683e5f4 100644 +--- a/hw/i386/acpi-build.c ++++ b/hw/i386/acpi-build.c +@@ -2271,7 +2271,16 @@ static void build_srat_hotpluggable_memory(GArray *table_data, uint64_t base, + numamem = acpi_data_push(table_data, sizeof *numamem); + + if (!info) { +- build_srat_memory(numamem, cur, end - cur, default_node, ++ /* ++ * Entry is required for Windows to enable memory hotplug in OS ++ * and for Linux to enable SWIOTLB when booted with less than ++ * 4G of RAM. Windows works better if the entry sets proximity ++ * to the highest NUMA node in the machine at the end of the ++ * reserved space. ++ * Memory devices may override proximity set by this entry, ++ * providing _PXM method if necessary. ++ */ ++ build_srat_memory(numamem, end - 1, 1, default_node, + MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); + break; + } +@@ -2404,14 +2413,6 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) + build_srat_memory(numamem, 0, 0, 0, MEM_AFFINITY_NOFLAGS); + } + +- /* +- * Entry is required for Windows to enable memory hotplug in OS +- * and for Linux to enable SWIOTLB when booted with less than +- * 4G of RAM. Windows works better if the entry sets proximity +- * to the highest NUMA node in the machine. +- * Memory devices may override proximity set by this entry, +- * providing _PXM method if necessary. +- */ + if (hotplugabble_address_space_size) { + build_srat_hotpluggable_memory(table_data, pcms->hotplug_memory.base, + hotplugabble_address_space_size, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pc-acpi-revert-back-to-1-SRAT-entry-for-hotpluggable.patch b/SOURCES/kvm-pc-acpi-revert-back-to-1-SRAT-entry-for-hotpluggable.patch new file mode 100644 index 0000000..ce02fe8 --- /dev/null +++ b/SOURCES/kvm-pc-acpi-revert-back-to-1-SRAT-entry-for-hotpluggable.patch @@ -0,0 +1,156 @@ +From cffbabd734488678614832ff26222aaa10920472 Mon Sep 17 00:00:00 2001 +From: Igor Mammedov +Date: Wed, 12 Sep 2018 15:21:41 +0200 +Subject: [PATCH 06/49] pc: acpi: revert back to 1 SRAT entry for hotpluggable + area + +RH-Author: Igor Mammedov +Message-id: <1536765701-266415-1-git-send-email-imammedo@redhat.com> +Patchwork-id: 82145 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH] pc: acpi: revert back to 1 SRAT entry for hotpluggable area +Bugzilla: 1626059 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Eduardo Habkost + +Commit + 10efd7e108 "pc: acpi: fix memory hotplug regression by reducing stub SRAT entry size" +attemped to fix hotplug regression introduced by + 848a1cc1e "hw/acpi-build: build SRAT memory affinity structures for DIMM devices" + +fixed issue for Windows/3.0+ linux kernels, however it regressed 2.6 based +kernels (RHEL6) to the point where guest might crash at boot. +Reason is that 2.6 kernel discards SRAT table due too small last entry +which down the road leads to crashes. Hack I've tried in 10efd7e108 is also +not ACPI spec compliant according to which whole possible RAM should be +described in SRAT. Revert 10efd7e108 to fix regression for 2.6 based kernels. + +With 10efd7e108 reverted, I've also tried splitting SRAT table statically +in different ways %/node and %/slot but Windows still fails to online +2nd pc-dimm hot-plugged into node 0 (as described in 10efd7e108) and +sometimes even coldplugged pc-dimms where affected with static SRAT +partitioning. +The only known so far way where Windows stays happy is when we have 1 +SRAT entry in the last node covering all hotplug area. + +Revert 848a1cc1e until we come up with a way to avoid regression +on Windows with hotplug area split in several entries. +Tested this with 2.6/3.0 based kernels (RHEL6/7) and WS20[08/12/12R2/16]). + +Signed-off-by: Igor Mammedov +Acked-by: Laszlo Ersek +Reviewed-by: Eduardo Habkost +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(pull request https://www.mail-archive.com/qemu-devel@nongnu.org/msg560638.html) + +Signed-off-by: Miroslav Rezanina + +Conflicts: + hw/i386/acpi-build.c + - contextual conflict since we do not have + (d471bf3e hw/i386: Use the IEC binary prefix definitions) + (b0c14ec4 machine: make MemoryHotplugState accessible via the machine) + +Since it's blocker and urgent, sending patch without waiting for upstream +commit id (pull req is out there but it looks like Peter is absent to merge it) +--- + hw/i386/acpi-build.c | 74 +++++++++------------------------------------------- + 1 file changed, 13 insertions(+), 61 deletions(-) + +diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c +index 683e5f4..2bc8117 100644 +--- a/hw/i386/acpi-build.c ++++ b/hw/i386/acpi-build.c +@@ -2253,64 +2253,6 @@ build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog) + #define HOLE_640K_START (640 * 1024) + #define HOLE_640K_END (1024 * 1024) + +-static void build_srat_hotpluggable_memory(GArray *table_data, uint64_t base, +- uint64_t len, int default_node) +-{ +- MemoryDeviceInfoList *info_list = qmp_pc_dimm_device_list(); +- MemoryDeviceInfoList *info; +- MemoryDeviceInfo *mi; +- PCDIMMDeviceInfo *di; +- uint64_t end = base + len, cur, size; +- bool is_nvdimm; +- AcpiSratMemoryAffinity *numamem; +- MemoryAffinityFlags flags; +- +- for (cur = base, info = info_list; +- cur < end; +- cur += size, info = info->next) { +- numamem = acpi_data_push(table_data, sizeof *numamem); +- +- if (!info) { +- /* +- * Entry is required for Windows to enable memory hotplug in OS +- * and for Linux to enable SWIOTLB when booted with less than +- * 4G of RAM. Windows works better if the entry sets proximity +- * to the highest NUMA node in the machine at the end of the +- * reserved space. +- * Memory devices may override proximity set by this entry, +- * providing _PXM method if necessary. +- */ +- build_srat_memory(numamem, end - 1, 1, default_node, +- MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); +- break; +- } +- +- mi = info->value; +- is_nvdimm = (mi->type == MEMORY_DEVICE_INFO_KIND_NVDIMM); +- di = !is_nvdimm ? mi->u.dimm.data : mi->u.nvdimm.data; +- +- if (cur < di->addr) { +- build_srat_memory(numamem, cur, di->addr - cur, default_node, +- MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); +- numamem = acpi_data_push(table_data, sizeof *numamem); +- } +- +- size = di->size; +- +- flags = MEM_AFFINITY_ENABLED; +- if (di->hotpluggable) { +- flags |= MEM_AFFINITY_HOTPLUGGABLE; +- } +- if (is_nvdimm) { +- flags |= MEM_AFFINITY_NON_VOLATILE; +- } +- +- build_srat_memory(numamem, di->addr, size, di->node, flags); +- } +- +- qapi_free_MemoryDeviceInfoList(info_list); +-} +- + static void + build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) + { +@@ -2413,10 +2355,20 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) + build_srat_memory(numamem, 0, 0, 0, MEM_AFFINITY_NOFLAGS); + } + ++ /* ++ * Entry is required for Windows to enable memory hotplug in OS ++ * and for Linux to enable SWIOTLB when booted with less than ++ * 4G of RAM. Windows works better if the entry sets proximity ++ * to the highest NUMA node in the machine at the end of the ++ * reserved space. ++ * Memory devices may override proximity set by this entry, ++ * providing _PXM method if necessary. ++ */ + if (hotplugabble_address_space_size) { +- build_srat_hotpluggable_memory(table_data, pcms->hotplug_memory.base, +- hotplugabble_address_space_size, +- pcms->numa_nodes - 1); ++ numamem = acpi_data_push(table_data, sizeof *numamem); ++ build_srat_memory(numamem, pcms->hotplug_memory.base, ++ hotplugabble_address_space_size, pcms->numa_nodes - 1, ++ MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); + } + + build_header(linker, table_data, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pc-bios-s390-ccw-fix-loadparm-initialization-and-int.patch b/SOURCES/kvm-pc-bios-s390-ccw-fix-loadparm-initialization-and-int.patch new file mode 100644 index 0000000..875f3bc --- /dev/null +++ b/SOURCES/kvm-pc-bios-s390-ccw-fix-loadparm-initialization-and-int.patch @@ -0,0 +1,107 @@ +From eac5906d2b0edecae91115e41d899c6220ff1267 Mon Sep 17 00:00:00 2001 +From: Thomas Huth +Date: Mon, 7 May 2018 07:58:06 +0200 +Subject: [PATCH 10/13] pc-bios/s390-ccw: fix loadparm initialization and int + conversion + +RH-Author: Thomas Huth +Message-id: <1525679888-9234-5-git-send-email-thuth@redhat.com> +Patchwork-id: 80055 +O-Subject: [RHEL-7.6 qemu-kvm-ma PATCH 4/6] pc-bios/s390-ccw: fix loadparm initialization and int conversion +Bugzilla: 1523857 +RH-Acked-by: David Hildenbrand +RH-Acked-by: Cornelia Huck +RH-Acked-by: Laszlo Ersek + +From: Collin Walling + +Rename the loadparm char array in main.c to loadparm_str and +increased the size by one byte to account for a null termination +when converting the loadparm string to an int via atoui. We +also allow the boot menu to be enabled when loadparm is set to +an empty string or a series of spaces. + +Signed-off-by: Collin Walling +Reported-by: Vasily Gorbik +Reviewed-by: Thomas Huth +Reviewed-by: Janosch Frank +Signed-off-by: Thomas Huth +(cherry picked from commit 074afe60d4c8167dcfaee7aca1065c6360449eaa) +Signed-off-by: Miroslav Rezanina +--- + hw/s390x/ipl.c | 4 ++++ + pc-bios/s390-ccw/main.c | 14 +++++++------- + 2 files changed, 11 insertions(+), 7 deletions(-) + +diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c +index fb554ab..150f6c0 100644 +--- a/hw/s390x/ipl.c ++++ b/hw/s390x/ipl.c +@@ -373,6 +373,10 @@ int s390_ipl_set_loadparm(uint8_t *loadparm) + loadparm[i] = ascii2ebcdic[(uint8_t) lp[i]]; + } + ++ if (i < 8) { ++ memset(loadparm + i, 0x40, 8 - i); /* fill with EBCDIC spaces */ ++ } ++ + g_free(lp); + return 0; + } +diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c +index 9d9f8cf..26f9adf 100644 +--- a/pc-bios/s390-ccw/main.c ++++ b/pc-bios/s390-ccw/main.c +@@ -15,11 +15,11 @@ + char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); + static SubChannelId blk_schid = { .one = 1 }; + IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); +-static char loadparm[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; ++static char loadparm_str[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + QemuIplParameters qipl; + + #define LOADPARM_PROMPT "PROMPT " +-#define LOADPARM_EMPTY "........" ++#define LOADPARM_EMPTY " " + #define BOOT_MENU_FLAG_MASK (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL) + + /* +@@ -45,7 +45,7 @@ void panic(const char *string) + + unsigned int get_loadparm_index(void) + { +- return atoui(loadparm); ++ return atoui(loadparm_str); + } + + static bool find_dev(Schib *schib, int dev_no) +@@ -80,13 +80,13 @@ static bool find_dev(Schib *schib, int dev_no) + + static void menu_setup(void) + { +- if (memcmp(loadparm, LOADPARM_PROMPT, 8) == 0) { ++ if (memcmp(loadparm_str, LOADPARM_PROMPT, 8) == 0) { + menu_set_parms(QIPL_FLAG_BM_OPTS_CMD, 0); + return; + } + + /* If loadparm was set to any other value, then do not enable menu */ +- if (memcmp(loadparm, LOADPARM_EMPTY, 8) != 0) { ++ if (memcmp(loadparm_str, LOADPARM_EMPTY, 8) != 0) { + return; + } + +@@ -116,8 +116,8 @@ static void virtio_setup(void) + */ + enable_mss_facility(); + +- sclp_get_loadparm_ascii(loadparm); +- memcpy(ldp + 10, loadparm, 8); ++ sclp_get_loadparm_ascii(loadparm_str); ++ memcpy(ldp + 10, loadparm_str, 8); + sclp_print(ldp); + + memcpy(&qipl, early_qipl, sizeof(QemuIplParameters)); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pc-bios-s390-ccw-fix-non-sequential-boot-entries-eck.patch b/SOURCES/kvm-pc-bios-s390-ccw-fix-non-sequential-boot-entries-eck.patch new file mode 100644 index 0000000..a5be5b2 --- /dev/null +++ b/SOURCES/kvm-pc-bios-s390-ccw-fix-non-sequential-boot-entries-eck.patch @@ -0,0 +1,118 @@ +From 3ded1fcf70af2fa1a3a785b777e7f724bd285fc3 Mon Sep 17 00:00:00 2001 +From: Thomas Huth +Date: Mon, 7 May 2018 07:58:07 +0200 +Subject: [PATCH 11/13] pc-bios/s390-ccw: fix non-sequential boot entries + (eckd) + +RH-Author: Thomas Huth +Message-id: <1525679888-9234-6-git-send-email-thuth@redhat.com> +Patchwork-id: 80053 +O-Subject: [RHEL-7.6 qemu-kvm-ma PATCH 5/6] pc-bios/s390-ccw: fix non-sequential boot entries (eckd) +Bugzilla: 1523857 +RH-Acked-by: David Hildenbrand +RH-Acked-by: Cornelia Huck +RH-Acked-by: Laszlo Ersek + +From: Collin Walling + +zIPL boot menu entries can be non-sequential. Let's account +for this issue for the s390 zIPL boot menu. Since this boot +menu is actually an imitation and is not completely capable +of everything the real zIPL menu can do, let's also print a +different banner to the user. + +Signed-off-by: Collin Walling +Reported-by: Vasily Gorbik +Reviewed-by: Thomas Huth +Reviewed-by: Janosch Frank +Signed-off-by: Thomas Huth +(cherry picked from commit 7385e947fc65a44dd05abb86c874beb915c1989c) +Signed-off-by: Miroslav Rezanina +--- + pc-bios/s390-ccw/menu.c | 29 ++++++++++++++++++++--------- + 1 file changed, 20 insertions(+), 9 deletions(-) + +diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c +index 96eec81..aaf5d61 100644 +--- a/pc-bios/s390-ccw/menu.c ++++ b/pc-bios/s390-ccw/menu.c +@@ -158,7 +158,7 @@ static void boot_menu_prompt(bool retry) + } + } + +-static int get_boot_index(int entries) ++static int get_boot_index(bool *valid_entries) + { + int boot_index; + bool retry = false; +@@ -168,7 +168,8 @@ static int get_boot_index(int entries) + boot_menu_prompt(retry); + boot_index = get_index(); + retry = true; +- } while (boot_index < 0 || boot_index >= entries); ++ } while (boot_index < 0 || boot_index >= MAX_BOOT_ENTRIES || ++ !valid_entries[boot_index]); + + sclp_print("\nBooting entry #"); + sclp_print(uitoa(boot_index, tmp, sizeof(tmp))); +@@ -176,7 +177,8 @@ static int get_boot_index(int entries) + return boot_index; + } + +-static void zipl_println(const char *data, size_t len) ++/* Returns the entry number that was printed */ ++static int zipl_print_entry(const char *data, size_t len) + { + char buf[len + 2]; + +@@ -185,12 +187,15 @@ static void zipl_println(const char *data, size_t len) + buf[len + 1] = '\0'; + + sclp_print(buf); ++ ++ return buf[0] == ' ' ? atoui(buf + 1) : atoui(buf); + } + + int menu_get_zipl_boot_index(const char *menu_data) + { + size_t len; +- int entries; ++ int entry; ++ bool valid_entries[MAX_BOOT_ENTRIES] = {false}; + uint16_t zipl_flag = *(uint16_t *)(menu_data - ZIPL_FLAG_OFFSET); + uint16_t zipl_timeout = *(uint16_t *)(menu_data - ZIPL_TIMEOUT_OFFSET); + +@@ -202,19 +207,25 @@ int menu_get_zipl_boot_index(const char *menu_data) + timeout = zipl_timeout * 1000; + } + +- /* Print and count all menu items, including the banner */ +- for (entries = 0; *menu_data; entries++) { ++ /* Print banner */ ++ sclp_print("s390-ccw zIPL Boot Menu\n\n"); ++ menu_data += strlen(menu_data) + 1; ++ ++ /* Print entries */ ++ while (*menu_data) { + len = strlen(menu_data); +- zipl_println(menu_data, len); ++ entry = zipl_print_entry(menu_data, len); + menu_data += len + 1; + +- if (entries < 2) { ++ valid_entries[entry] = true; ++ ++ if (entry == 0) { + sclp_print("\n"); + } + } + + sclp_print("\n"); +- return get_boot_index(entries - 1); /* subtract 1 to exclude banner */ ++ return get_boot_index(valid_entries); + } + + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pc-bios-s390-ccw-fix-non-sequential-boot-entries-enu.patch b/SOURCES/kvm-pc-bios-s390-ccw-fix-non-sequential-boot-entries-enu.patch new file mode 100644 index 0000000..32d078b --- /dev/null +++ b/SOURCES/kvm-pc-bios-s390-ccw-fix-non-sequential-boot-entries-enu.patch @@ -0,0 +1,148 @@ +From 81a0dae65a07d9323c78c6bc9adc7f8dbbb19145 Mon Sep 17 00:00:00 2001 +From: Thomas Huth +Date: Mon, 7 May 2018 07:58:08 +0200 +Subject: [PATCH 12/13] pc-bios/s390-ccw: fix non-sequential boot entries + (enum) + +RH-Author: Thomas Huth +Message-id: <1525679888-9234-7-git-send-email-thuth@redhat.com> +Patchwork-id: 80054 +O-Subject: [RHEL-7.6 qemu-kvm-ma PATCH 6/6] pc-bios/s390-ccw: fix non-sequential boot entries (enum) +Bugzilla: 1523857 +RH-Acked-by: David Hildenbrand +RH-Acked-by: Cornelia Huck +RH-Acked-by: Laszlo Ersek + +From: Collin Walling + +zIPL boot menu entries can be non-sequential. Let's account +for this issue for the s390 enumerated boot menu. Since we +can no longer print a range of available entries to the +user, we have to present a list of each available entry. + +An example of this menu: + + s390-ccw Enumerated Boot Menu. + + [0] default + + [1] + [2] + [7] + [8] + [9] + [11] + [12] + + Please choose: + +Signed-off-by: Collin Walling +Reported-by: Vasily Gorbik +Reviewed-by: Thomas Huth +Reviewed-by: Janosch Frank +Signed-off-by: Thomas Huth +(cherry picked from commit 622b39178057289a1c8c1b5148f513e658e90ea1) +Signed-off-by: Miroslav Rezanina +--- + pc-bios/s390-ccw/bootmap.c | 12 +++++++----- + pc-bios/s390-ccw/menu.c | 29 ++++++++++++++++++++--------- + pc-bios/s390-ccw/s390-ccw.h | 2 +- + 3 files changed, 28 insertions(+), 15 deletions(-) + +diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c +index b767fa2..e41e715 100644 +--- a/pc-bios/s390-ccw/bootmap.c ++++ b/pc-bios/s390-ccw/bootmap.c +@@ -565,6 +565,8 @@ static void ipl_scsi(void) + int program_table_entries = 0; + BootMapTable *prog_table = (void *)sec; + unsigned int loadparm = get_loadparm_index(); ++ bool valid_entries[MAX_BOOT_ENTRIES] = {false}; ++ size_t i; + + /* Grab the MBR */ + memset(sec, FREE_SPACE_FILLER, sizeof(sec)); +@@ -585,18 +587,18 @@ static void ipl_scsi(void) + read_block(mbr->pt.blockno, sec, "Error reading Program Table"); + IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT"); + +- while (program_table_entries < MAX_BOOT_ENTRIES) { +- if (!prog_table->entry[program_table_entries].scsi.blockno) { +- break; ++ for (i = 0; i < MAX_BOOT_ENTRIES; i++) { ++ if (prog_table->entry[i].scsi.blockno) { ++ valid_entries[i] = true; ++ program_table_entries++; + } +- program_table_entries++; + } + + debug_print_int("program table entries", program_table_entries); + IPL_assert(program_table_entries != 0, "Empty Program Table"); + + if (menu_is_enabled_enum()) { +- loadparm = menu_get_enum_boot_index(program_table_entries); ++ loadparm = menu_get_enum_boot_index(valid_entries); + } + + debug_print_int("loadparm", loadparm); +diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c +index aaf5d61..82a4ae6 100644 +--- a/pc-bios/s390-ccw/menu.c ++++ b/pc-bios/s390-ccw/menu.c +@@ -228,19 +228,30 @@ int menu_get_zipl_boot_index(const char *menu_data) + return get_boot_index(valid_entries); + } + +- +-int menu_get_enum_boot_index(int entries) ++int menu_get_enum_boot_index(bool *valid_entries) + { +- char tmp[4]; ++ char tmp[3]; ++ int i; + +- sclp_print("s390x Enumerated Boot Menu.\n\n"); ++ sclp_print("s390-ccw Enumerated Boot Menu.\n\n"); + +- sclp_print(uitoa(entries, tmp, sizeof(tmp))); +- sclp_print(" entries detected. Select from boot index 0 to "); +- sclp_print(uitoa(entries - 1, tmp, sizeof(tmp))); +- sclp_print(".\n\n"); ++ for (i = 0; i < MAX_BOOT_ENTRIES; i++) { ++ if (valid_entries[i]) { ++ if (i < 10) { ++ sclp_print(" "); ++ } ++ sclp_print("["); ++ sclp_print(uitoa(i, tmp, sizeof(tmp))); ++ sclp_print("]"); ++ if (i == 0) { ++ sclp_print(" default\n"); ++ } ++ sclp_print("\n"); ++ } ++ } + +- return get_boot_index(entries); ++ sclp_print("\n"); ++ return get_boot_index(valid_entries); + } + + void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout) +diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h +index 2c9e601..a1bdb4c 100644 +--- a/pc-bios/s390-ccw/s390-ccw.h ++++ b/pc-bios/s390-ccw/s390-ccw.h +@@ -91,7 +91,7 @@ void zipl_load(void); + void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout); + int menu_get_zipl_boot_index(const char *menu_data); + bool menu_is_enabled_zipl(void); +-int menu_get_enum_boot_index(int entries); ++int menu_get_enum_boot_index(bool *valid_entries); + bool menu_is_enabled_enum(void); + + #define MAX_BOOT_ENTRIES 31 +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pc-bios-s390-ccw-rename-MAX_TABLE_ENTRIES-to-MAX_BOO.patch b/SOURCES/kvm-pc-bios-s390-ccw-rename-MAX_TABLE_ENTRIES-to-MAX_BOO.patch new file mode 100644 index 0000000..d43581a --- /dev/null +++ b/SOURCES/kvm-pc-bios-s390-ccw-rename-MAX_TABLE_ENTRIES-to-MAX_BOO.patch @@ -0,0 +1,95 @@ +From cb2f6e00200df2c15a22f54196879c523fa610da Mon Sep 17 00:00:00 2001 +From: Thomas Huth +Date: Mon, 7 May 2018 07:58:05 +0200 +Subject: [PATCH 09/13] pc-bios/s390-ccw: rename MAX_TABLE_ENTRIES to + MAX_BOOT_ENTRIES + +RH-Author: Thomas Huth +Message-id: <1525679888-9234-4-git-send-email-thuth@redhat.com> +Patchwork-id: 80052 +O-Subject: [RHEL-7.6 qemu-kvm-ma PATCH 3/6] pc-bios/s390-ccw: rename MAX_TABLE_ENTRIES to MAX_BOOT_ENTRIES +Bugzilla: 1523857 +RH-Acked-by: David Hildenbrand +RH-Acked-by: Cornelia Huck +RH-Acked-by: Laszlo Ersek + +From: Collin Walling + +The MAX_TABLE_ENTRIES constant has a name that is too generic. As we +want to declare a limit for boot menu entries, let's rename it to a more +fitting MAX_BOOT_ENTRIES and set its value to 31 (30 boot entries and +1 default entry). Also we move it from bootmap.h to s390-ccw.h to make +it available for menu.c in a later patch. + +Signed-off-by: Collin Walling +Reviewed-by: Thomas Huth +Reviewed-by: Janosch Frank +Signed-off-by: Thomas Huth +(cherry picked from commit 6df2a829dfacfbf10a78199ad4b023a7ea65d9cd) +Signed-off-by: Miroslav Rezanina +--- + pc-bios/s390-ccw/bootmap.c | 6 +++--- + pc-bios/s390-ccw/bootmap.h | 2 -- + pc-bios/s390-ccw/s390-ccw.h | 2 ++ + 3 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c +index 9287b7a..b767fa2 100644 +--- a/pc-bios/s390-ccw/bootmap.c ++++ b/pc-bios/s390-ccw/bootmap.c +@@ -297,7 +297,7 @@ static void run_eckd_boot_script(block_number_t bmt_block_nr, + } + + debug_print_int("loadparm", loadparm); +- IPL_assert(loadparm <= MAX_TABLE_ENTRIES, "loadparm value greater than" ++ IPL_assert(loadparm < MAX_BOOT_ENTRIES, "loadparm value greater than" + " maximum number of boot entries allowed"); + + memset(sec, FREE_SPACE_FILLER, sizeof(sec)); +@@ -585,7 +585,7 @@ static void ipl_scsi(void) + read_block(mbr->pt.blockno, sec, "Error reading Program Table"); + IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT"); + +- while (program_table_entries <= MAX_TABLE_ENTRIES) { ++ while (program_table_entries < MAX_BOOT_ENTRIES) { + if (!prog_table->entry[program_table_entries].scsi.blockno) { + break; + } +@@ -600,7 +600,7 @@ static void ipl_scsi(void) + } + + debug_print_int("loadparm", loadparm); +- IPL_assert(loadparm <= MAX_TABLE_ENTRIES, "loadparm value greater than" ++ IPL_assert(loadparm < MAX_BOOT_ENTRIES, "loadparm value greater than" + " maximum number of boot entries allowed"); + + zipl_run(&prog_table->entry[loadparm].scsi); /* no return */ +diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h +index 07eb600..732c111 100644 +--- a/pc-bios/s390-ccw/bootmap.h ++++ b/pc-bios/s390-ccw/bootmap.h +@@ -57,8 +57,6 @@ typedef union BootMapPointer { + ExtEckdBlockPtr xeckd; + } __attribute__ ((packed)) BootMapPointer; + +-#define MAX_TABLE_ENTRIES 30 +- + /* aka Program Table */ + typedef struct BootMapTable { + uint8_t magic[4]; +diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h +index fd18da2..2c9e601 100644 +--- a/pc-bios/s390-ccw/s390-ccw.h ++++ b/pc-bios/s390-ccw/s390-ccw.h +@@ -94,6 +94,8 @@ bool menu_is_enabled_zipl(void); + int menu_get_enum_boot_index(int entries); + bool menu_is_enabled_enum(void); + ++#define MAX_BOOT_ENTRIES 31 ++ + static inline void fill_hex(char *out, unsigned char val) + { + const char hex[] = "0123456789abcdef"; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pc-bios-s390-ccw-size_t-should-be-unsigned.patch b/SOURCES/kvm-pc-bios-s390-ccw-size_t-should-be-unsigned.patch new file mode 100644 index 0000000..d0e6267 --- /dev/null +++ b/SOURCES/kvm-pc-bios-s390-ccw-size_t-should-be-unsigned.patch @@ -0,0 +1,60 @@ +From e25487ca5ac49460e5a0dcb802866f156485f188 Mon Sep 17 00:00:00 2001 +From: Thomas Huth +Date: Mon, 7 May 2018 07:58:04 +0200 +Subject: [PATCH 08/13] pc-bios/s390-ccw: size_t should be unsigned + +RH-Author: Thomas Huth +Message-id: <1525679888-9234-3-git-send-email-thuth@redhat.com> +Patchwork-id: 80050 +O-Subject: [RHEL-7.6 qemu-kvm-ma PATCH 2/6] pc-bios/s390-ccw: size_t should be unsigned +Bugzilla: 1523857 +RH-Acked-by: David Hildenbrand +RH-Acked-by: Cornelia Huck +RH-Acked-by: Laszlo Ersek + +"size_t" should be an unsigned type according to the C standard. +Thus we should also use this convention in the s390-ccw firmware to avoid +confusion. I checked the sources, and apart from one spot in libc.c, the +code should all be fine with this change. + +Buglink: https://bugs.launchpad.net/qemu/+bug/1753437 +Reviewed-by: Christian Borntraeger +Reviewed-by: Halil Pasic +Reviewed-by: Collin Walling +Signed-off-by: Thomas Huth +(cherry picked from commit e4f869621203955761cf274c87d5595e9facd319) +Signed-off-by: Miroslav Rezanina +--- + pc-bios/s390-ccw/libc.c | 2 +- + pc-bios/s390-ccw/libc.h | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/pc-bios/s390-ccw/libc.c b/pc-bios/s390-ccw/libc.c +index 38ea77d..a786566 100644 +--- a/pc-bios/s390-ccw/libc.c ++++ b/pc-bios/s390-ccw/libc.c +@@ -63,7 +63,7 @@ uint64_t atoui(const char *str) + */ + char *uitoa(uint64_t num, char *str, size_t len) + { +- size_t num_idx = 1; /* account for NUL */ ++ long num_idx = 1; /* account for NUL */ + uint64_t tmp = num; + + IPL_assert(str != NULL, "uitoa: no space allocated to store string"); +diff --git a/pc-bios/s390-ccw/libc.h b/pc-bios/s390-ccw/libc.h +index 63ece70..818517f 100644 +--- a/pc-bios/s390-ccw/libc.h ++++ b/pc-bios/s390-ccw/libc.h +@@ -12,7 +12,7 @@ + #ifndef S390_CCW_LIBC_H + #define S390_CCW_LIBC_H + +-typedef long size_t; ++typedef unsigned long size_t; + typedef int bool; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pc-dimm-turn-alignment-assert-into-check.patch b/SOURCES/kvm-pc-dimm-turn-alignment-assert-into-check.patch new file mode 100644 index 0000000..9b1025f --- /dev/null +++ b/SOURCES/kvm-pc-dimm-turn-alignment-assert-into-check.patch @@ -0,0 +1,76 @@ +From 84d49bc7469905877fc22f6faea1e53c8c0cbe1c Mon Sep 17 00:00:00 2001 +From: David Hildenbrand +Date: Fri, 21 Sep 2018 09:19:39 +0200 +Subject: [PATCH 03/22] pc-dimm: turn alignment assert into check + +RH-Author: David Hildenbrand +Message-id: <20180921091939.4107-1-david@redhat.com> +Patchwork-id: 82227 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH] pc-dimm: turn alignment assert into check +Bugzilla: 1629720 +RH-Acked-by: Pankaj Gupta +RH-Acked-by: Igor Mammedov +RH-Acked-by: Paolo Bonzini + +BZ: https://bugzilla.redhat.com/show_bug.cgi?id=1629720 +Brew: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=18439425 +Upstream: 4d8938a05db15dea2c86c4ab9c5f872f160d2188 + +The start of the address space indicates which maximum alignment is +supported by our machine (e.g. ppc, x86 1GB). This is helpful to +catch fragmenting guest physical memory in strange fashions. + +Right now we can crash QEMU by e.g. (there might be easier examples) + +qemu-system-x86_64 -m 256M,maxmem=20G,slots=2 \ + -object memory-backend-file,id=mem0,size=8192M,mem-path=/dev/zero,align=8192M \ + -device pc-dimm,id=dimm1,memdev=mem0 + +Backport conflicts: + hw/mem/memory-device.c: The memory device refactoring is part of 3.0 + and probably not worth backporting to 7.X. + So fix it in previous pc-dimm code. +Note: The upstream patch missed a "x" (0% .. vs. 0x% ..), will fix that + upstream, too. + +Signed-off-by: David Hildenbrand +Message-Id: <20180607154705.6316-2-david@redhat.com> +Reviewed-by: Michael S. Tsirkin +Reviewed-by: Igor Mammedov +Signed-off-by: Paolo Bonzini +(cherry picked from commit 4d8938a05db15dea2c86c4ab9c5f872f160d2188) +Signed-off-by: David Hildenbrand +Signed-off-by: Miroslav Rezanina +--- + hw/mem/pc-dimm.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c +index 51350d9..a9d7c51 100644 +--- a/hw/mem/pc-dimm.c ++++ b/hw/mem/pc-dimm.c +@@ -298,14 +298,19 @@ uint64_t pc_dimm_get_free_addr(uint64_t address_space_start, + uint64_t new_addr, ret = 0; + uint64_t address_space_end = address_space_start + address_space_size; + +- g_assert(QEMU_ALIGN_UP(address_space_start, align) == address_space_start); +- + if (!address_space_size) { + error_setg(errp, "memory hotplug is not enabled, " + "please add maxmem option"); + goto out; + } + ++ /* address_space_start indicates the maximum alignment we expect */ ++ if (QEMU_ALIGN_UP(address_space_start, align) != address_space_start) { ++ error_setg(errp, "the alignment (0x%" PRIx64 ") is not supported", ++ align); ++ goto out; ++ } ++ + if (hint && QEMU_ALIGN_UP(*hint, align) != *hint) { + error_setg(errp, "address must be aligned to 0x%" PRIx64 " bytes", + align); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pc-pc-rhel75.5.0-compat-code.patch b/SOURCES/kvm-pc-pc-rhel75.5.0-compat-code.patch new file mode 100644 index 0000000..de19af0 --- /dev/null +++ b/SOURCES/kvm-pc-pc-rhel75.5.0-compat-code.patch @@ -0,0 +1,81 @@ +From e1e6a404ee2c4833a05356378da2ffc7bdb5e6fe Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Fri, 25 May 2018 23:38:16 +0200 +Subject: [PATCH 5/8] pc: pc-*-rhel75.5.0 compat code + +RH-Author: Eduardo Habkost +Message-id: <20180525233816.19506-2-ehabkost@redhat.com> +Patchwork-id: 80481 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/1] pc: pc-*-rhel75.5.0 compat code +Bugzilla: 1578068 +RH-Acked-by: Peter Xu +RH-Acked-by: Igor Mammedov +RH-Acked-by: Laurent Vivier + +Based on the pc-*-2.11 and pc-*-2.10 compat code from upstream. + +Signed-off-by: Eduardo Habkost +Signed-off-by: Miroslav Rezanina +--- + hw/i386/pc_piix.c | 2 ++ + hw/i386/pc_q35.c | 4 ++++ + include/hw/i386/pc.h | 13 +++++++++++++ + 3 files changed, 19 insertions(+) + +diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c +index 6794bb7..7b87ef6 100644 +--- a/hw/i386/pc_piix.c ++++ b/hw/i386/pc_piix.c +@@ -1166,6 +1166,8 @@ static void pc_machine_rhel750_options(MachineClass *m) + { + pc_machine_rhel7_options(m); + m->desc = "RHEL 7.5.0 PC (i440FX + PIIX, 1996)"; ++ m->auto_enable_numa_with_memhp = false; ++ SET_MACHINE_COMPAT(m, PC_RHEL7_5_COMPAT); + } + + DEFINE_PC_MACHINE(rhel750, "pc-i440fx-rhel7.5.0", pc_init_rhel750, +diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c +index ecd6255..1805f55 100644 +--- a/hw/i386/pc_q35.c ++++ b/hw/i386/pc_q35.c +@@ -430,8 +430,12 @@ static void pc_q35_init_rhel750(MachineState *machine) + + static void pc_q35_machine_rhel750_options(MachineClass *m) + { ++ PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pc_q35_machine_rhel7_options(m); + m->desc = "RHEL-7.5.0 PC (Q35 + ICH9, 2009)"; ++ m->auto_enable_numa_with_memhp = false; ++ pcmc->default_nic_model = "e1000"; ++ SET_MACHINE_COMPAT(m, PC_RHEL7_5_COMPAT); + } + + DEFINE_PC_MACHINE(q35_rhel750, "pc-q35-rhel7.5.0", pc_q35_init_rhel750, +diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h +index faddeba..e94424f 100644 +--- a/include/hw/i386/pc.h ++++ b/include/hw/i386/pc.h +@@ -969,6 +969,19 @@ extern void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id); + .value = "on",\ + }, + ++/* Similar to PC_COMPAT_2_11 + PC_COMPAT_2_10, but: ++ * - x-hv-max-vps was backported to 7.5 ++ * - x-pci-hole64-fix was backported to 7.5 ++ */ ++#define PC_RHEL7_5_COMPAT \ ++ HW_COMPAT_RHEL7_5 \ ++ { /* PC_RHEL7_5_COMPAT from PC_COMPAT_2_11 */ \ ++ .driver = "Skylake-Server" "-" TYPE_X86_CPU,\ ++ .property = "clflushopt",\ ++ .value = "off",\ ++ }, ++ ++ + #define PC_RHEL7_4_COMPAT \ + HW_COMPAT_RHEL7_4 \ + { /* PC_RHEL7_4_COMPAT from PC_COMPAT_2_9 */ \ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pc_sysfw-Pass-PCMachineState-to-pc_system_firmware_i.patch b/SOURCES/kvm-pc_sysfw-Pass-PCMachineState-to-pc_system_firmware_i.patch new file mode 100644 index 0000000..47c2168 --- /dev/null +++ b/SOURCES/kvm-pc_sysfw-Pass-PCMachineState-to-pc_system_firmware_i.patch @@ -0,0 +1,85 @@ +From 5991cbe99b2faf035522ec4910524421a34d9f14 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:16 +0200 +Subject: [PATCH 49/53] pc_sysfw: Pass PCMachineState to + pc_system_firmware_init() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-28-armbru@redhat.com> +Patchwork-id: 87988 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 27/31] pc_sysfw: Pass PCMachineState to pc_system_firmware_init() +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +From: Philippe Mathieu-Daudé + +pc_system_firmware_init() parameter @isapc_ram_fw is PCMachineState +member pci_enabled negated. The next commit will need more of +PCMachineState. To prepare for that, pass a PCMachineState *, and +drop the now redundant parameter @isapc_ram_fw. + +Signed-off-by: Markus Armbruster +Signed-off-by: Philippe Mathieu-Daudé +Reviewed-by: Laszlo Ersek +Message-Id: <20190308131445.17502-11-armbru@redhat.com> +Reviewed-by: Michael S. Tsirkin +(cherry picked from commit 5e640a9e78ea61c50401a2b11fa144b5f0c217dc) +Signed-off-by: Miroslav Rezanina +--- + hw/i386/pc.c | 2 +- + hw/i386/pc_sysfw.c | 5 ++++- + include/hw/i386/pc.h | 3 +-- + 3 files changed, 6 insertions(+), 4 deletions(-) + +diff --git a/hw/i386/pc.c b/hw/i386/pc.c +index 7160969..86e9d43 100644 +--- a/hw/i386/pc.c ++++ b/hw/i386/pc.c +@@ -1413,7 +1413,7 @@ void pc_memory_init(PCMachineState *pcms, + } + + /* Initialize PC system firmware */ +- pc_system_firmware_init(rom_memory, !pcmc->pci_enabled); ++ pc_system_firmware_init(pcms, rom_memory); + + option_rom_mr = g_malloc(sizeof(*option_rom_mr)); + memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE, +diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c +index d40d727..2a0e18c 100644 +--- a/hw/i386/pc_sysfw.c ++++ b/hw/i386/pc_sysfw.c +@@ -240,8 +240,11 @@ static void old_pc_system_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw) + bios); + } + +-void pc_system_firmware_init(MemoryRegion *rom_memory, bool isapc_ram_fw) ++void pc_system_firmware_init(PCMachineState *pcms, ++ MemoryRegion *rom_memory) + { ++ PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); ++ bool isapc_ram_fw = !pcmc->pci_enabled; + DriveInfo *pflash_drv; + + pflash_drv = drive_get(IF_PFLASH, 0, 0); +diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h +index 1e9f252..611e111 100644 +--- a/include/hw/i386/pc.h ++++ b/include/hw/i386/pc.h +@@ -290,8 +290,7 @@ extern PCIDevice *piix4_dev; + int piix4_init(PCIBus *bus, ISABus **isa_bus, int devfn); + + /* pc_sysfw.c */ +-void pc_system_firmware_init(MemoryRegion *rom_memory, +- bool isapc_ram_fw); ++void pc_system_firmware_init(PCMachineState *pcms, MemoryRegion *rom_memory); + + /* acpi-build.c */ + void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pc_sysfw-Remove-unused-PcSysFwDevice.patch b/SOURCES/kvm-pc_sysfw-Remove-unused-PcSysFwDevice.patch new file mode 100644 index 0000000..e4d8009 --- /dev/null +++ b/SOURCES/kvm-pc_sysfw-Remove-unused-PcSysFwDevice.patch @@ -0,0 +1,51 @@ +From 546c38d9ecd455af7962cbe555c4304bb519f215 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:15 +0200 +Subject: [PATCH 48/53] pc_sysfw: Remove unused PcSysFwDevice +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-27-armbru@redhat.com> +Patchwork-id: 88010 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 26/31] pc_sysfw: Remove unused PcSysFwDevice +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +From: Philippe Mathieu-Daudé + +This structure is not used since commit 6dd2a5c98a. + +Signed-off-by: Philippe Mathieu-Daudé +Reviewed-by: Laszlo Ersek +Signed-off-by: Markus Armbruster +Message-Id: <20190308131445.17502-10-armbru@redhat.com> +Reviewed-by: Michael S. Tsirkin +(cherry picked from commit d6edbe91b98e3e6e0d0185a9c4c3d2e6d6bf0a6b) +Signed-off-by: Miroslav Rezanina +--- + hw/i386/pc_sysfw.c | 5 ----- + 1 file changed, 5 deletions(-) + +diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c +index 20eed89..d40d727 100644 +--- a/hw/i386/pc_sysfw.c ++++ b/hw/i386/pc_sysfw.c +@@ -39,11 +39,6 @@ + + #define BIOS_FILENAME "bios.bin" + +-typedef struct PcSysFwDevice { +- SysBusDevice busdev; +- uint8_t isapc_ram_fw; +-} PcSysFwDevice; +- + static void pc_isa_bios_init(MemoryRegion *rom_memory, + MemoryRegion *flash_mem, + int ram_size) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pflash-Clean-up-after-commit-368a354f02b-part-1.patch b/SOURCES/kvm-pflash-Clean-up-after-commit-368a354f02b-part-1.patch new file mode 100644 index 0000000..ad5f352 --- /dev/null +++ b/SOURCES/kvm-pflash-Clean-up-after-commit-368a354f02b-part-1.patch @@ -0,0 +1,426 @@ +From 267bac031b2b6e5faf270f4af195b18820162ddb Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:05 +0200 +Subject: [PATCH 38/53] pflash: Clean up after commit 368a354f02b, part 1 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-17-armbru@redhat.com> +Patchwork-id: 88000 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 16/31] pflash: Clean up after commit 368a354f02b, part 1 +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +QOMification left parameter @qdev unused in pflash_cfi01_register() +and pflash_cfi02_register(). All callers pass NULL. Remove. + +Signed-off-by: Markus Armbruster +Reviewed-by: Laszlo Ersek +Reviewed-by: Philippe Mathieu-Daudé +Reviewed-by: Alex Bennée +Tested-by: Philippe Mathieu-Daudé +Message-Id: <20190308094610.21210-15-armbru@redhat.com> +(cherry picked from commit 940d5b132fab085bd8ec2bcfa5c1dd119785b217) +Signed-off-by: Miroslav Rezanina +--- + hw/arm/collie.c | 4 ++-- + hw/arm/digic_boards.c | 2 +- + hw/arm/gumstix.c | 4 ++-- + hw/arm/mainstone.c | 2 +- + hw/arm/musicpal.c | 4 ++-- + hw/arm/omap_sx1.c | 4 ++-- + hw/arm/versatilepb.c | 2 +- + hw/arm/xilinx_zynq.c | 2 +- + hw/arm/z2.c | 3 +-- + hw/block/pflash_cfi01.c | 2 +- + hw/block/pflash_cfi02.c | 2 +- + hw/i386/pc_sysfw.c | 2 +- + hw/lm32/lm32_boards.c | 4 ++-- + hw/lm32/milkymist.c | 2 +- + hw/microblaze/petalogix_ml605_mmu.c | 3 +-- + hw/microblaze/petalogix_s3adsp1800_mmu.c | 2 +- + hw/mips/mips_malta.c | 2 +- + hw/mips/mips_r4k.c | 2 +- + hw/ppc/ppc405_boards.c | 6 +++--- + hw/ppc/sam460ex.c | 2 +- + hw/ppc/virtex_ml507.c | 2 +- + hw/sh4/r2d.c | 2 +- + include/hw/block/flash.h | 4 ++-- + 23 files changed, 31 insertions(+), 33 deletions(-) + +diff --git a/hw/arm/collie.c b/hw/arm/collie.c +index f8c566e..9cf4538 100644 +--- a/hw/arm/collie.c ++++ b/hw/arm/collie.c +@@ -37,12 +37,12 @@ static void collie_init(MachineState *machine) + s = sa1110_init(sysmem, collie_binfo.ram_size, machine->cpu_type); + + dinfo = drive_get(IF_PFLASH, 0, 0); +- pflash_cfi01_register(SA_CS0, NULL, "collie.fl1", 0x02000000, ++ pflash_cfi01_register(SA_CS0, "collie.fl1", 0x02000000, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + (64 * 1024), 512, 4, 0x00, 0x00, 0x00, 0x00, 0); + + dinfo = drive_get(IF_PFLASH, 0, 1); +- pflash_cfi01_register(SA_CS1, NULL, "collie.fl2", 0x02000000, ++ pflash_cfi01_register(SA_CS1, "collie.fl2", 0x02000000, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + (64 * 1024), 512, 4, 0x00, 0x00, 0x00, 0x00, 0); + +diff --git a/hw/arm/digic_boards.c b/hw/arm/digic_boards.c +index 9f11dcd..15a00a1 100644 +--- a/hw/arm/digic_boards.c ++++ b/hw/arm/digic_boards.c +@@ -129,7 +129,7 @@ static void digic4_add_k8p3215uqb_rom(DigicBoardState *s, hwaddr addr, + #define FLASH_K8P3215UQB_SIZE (4 * 1024 * 1024) + #define FLASH_K8P3215UQB_SECTOR_SIZE (64 * 1024) + +- pflash_cfi02_register(addr, NULL, "pflash", FLASH_K8P3215UQB_SIZE, ++ pflash_cfi02_register(addr, "pflash", FLASH_K8P3215UQB_SIZE, + NULL, FLASH_K8P3215UQB_SECTOR_SIZE, + FLASH_K8P3215UQB_SIZE / FLASH_K8P3215UQB_SECTOR_SIZE, + DIGIC4_ROM_MAX_SIZE / FLASH_K8P3215UQB_SIZE, +diff --git a/hw/arm/gumstix.c b/hw/arm/gumstix.c +index ea2a3c5..68a0376 100644 +--- a/hw/arm/gumstix.c ++++ b/hw/arm/gumstix.c +@@ -73,7 +73,7 @@ static void connex_init(MachineState *machine) + #else + be = 0; + #endif +- if (!pflash_cfi01_register(0x00000000, NULL, "connext.rom", connex_rom, ++ if (!pflash_cfi01_register(0x00000000, "connext.rom", connex_rom, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + sector_len, connex_rom / sector_len, + 2, 0, 0, 0, 0, be)) { +@@ -110,7 +110,7 @@ static void verdex_init(MachineState *machine) + #else + be = 0; + #endif +- if (!pflash_cfi01_register(0x00000000, NULL, "verdex.rom", verdex_rom, ++ if (!pflash_cfi01_register(0x00000000, "verdex.rom", verdex_rom, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + sector_len, verdex_rom / sector_len, + 2, 0, 0, 0, 0, be)) { +diff --git a/hw/arm/mainstone.c b/hw/arm/mainstone.c +index 4215c02..cb1116b 100644 +--- a/hw/arm/mainstone.c ++++ b/hw/arm/mainstone.c +@@ -149,7 +149,7 @@ static void mainstone_common_init(MemoryRegion *address_space_mem, + exit(1); + } + +- if (!pflash_cfi01_register(mainstone_flash_base[i], NULL, ++ if (!pflash_cfi01_register(mainstone_flash_base[i], + i ? "mainstone.flash1" : "mainstone.flash0", + MAINSTONE_FLASH, + blk_by_legacy_dinfo(dinfo), +diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c +index 38d7322..fd7a7a9 100644 +--- a/hw/arm/musicpal.c ++++ b/hw/arm/musicpal.c +@@ -1637,14 +1637,14 @@ static void musicpal_init(MachineState *machine) + * image is smaller than 32 MB. + */ + #ifdef TARGET_WORDS_BIGENDIAN +- pflash_cfi02_register(0x100000000ULL-MP_FLASH_SIZE_MAX, NULL, ++ pflash_cfi02_register(0x100000000ULL - MP_FLASH_SIZE_MAX, + "musicpal.flash", flash_size, + blk, 0x10000, (flash_size + 0xffff) >> 16, + MP_FLASH_SIZE_MAX / flash_size, + 2, 0x00BF, 0x236D, 0x0000, 0x0000, + 0x5555, 0x2AAA, 1); + #else +- pflash_cfi02_register(0x100000000ULL-MP_FLASH_SIZE_MAX, NULL, ++ pflash_cfi02_register(0x100000000ULL - MP_FLASH_SIZE_MAX, + "musicpal.flash", flash_size, + blk, 0x10000, (flash_size + 0xffff) >> 16, + MP_FLASH_SIZE_MAX / flash_size, +diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c +index eccc19c..a7a3e7d 100644 +--- a/hw/arm/omap_sx1.c ++++ b/hw/arm/omap_sx1.c +@@ -153,7 +153,7 @@ static void sx1_init(MachineState *machine, const int version) + #endif + + if ((dinfo = drive_get(IF_PFLASH, 0, fl_idx)) != NULL) { +- if (!pflash_cfi01_register(OMAP_CS0_BASE, NULL, ++ if (!pflash_cfi01_register(OMAP_CS0_BASE, + "omap_sx1.flash0-1", flash_size, + blk_by_legacy_dinfo(dinfo), + sector_size, flash_size / sector_size, +@@ -177,7 +177,7 @@ static void sx1_init(MachineState *machine, const int version) + memory_region_add_subregion(address_space, + OMAP_CS1_BASE + flash1_size, &cs[1]); + +- if (!pflash_cfi01_register(OMAP_CS1_BASE, NULL, ++ if (!pflash_cfi01_register(OMAP_CS1_BASE, + "omap_sx1.flash1-1", flash1_size, + blk_by_legacy_dinfo(dinfo), + sector_size, flash1_size / sector_size, +diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c +index 418792c..99d8ff9 100644 +--- a/hw/arm/versatilepb.c ++++ b/hw/arm/versatilepb.c +@@ -358,7 +358,7 @@ static void versatile_init(MachineState *machine, int board_id) + /* 0x34000000 NOR Flash */ + + dinfo = drive_get(IF_PFLASH, 0, 0); +- if (!pflash_cfi01_register(VERSATILE_FLASH_ADDR, NULL, "versatile.flash", ++ if (!pflash_cfi01_register(VERSATILE_FLASH_ADDR, "versatile.flash", + VERSATILE_FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + VERSATILE_FLASH_SECT_SIZE, +diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c +index 0f76333..4a05e1c 100644 +--- a/hw/arm/xilinx_zynq.c ++++ b/hw/arm/xilinx_zynq.c +@@ -206,7 +206,7 @@ static void zynq_init(MachineState *machine) + DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0); + + /* AMD */ +- pflash_cfi02_register(0xe2000000, NULL, "zynq.pflash", FLASH_SIZE, ++ pflash_cfi02_register(0xe2000000, "zynq.pflash", FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + FLASH_SECTOR_SIZE, + FLASH_SIZE/FLASH_SECTOR_SIZE, 1, +diff --git a/hw/arm/z2.c b/hw/arm/z2.c +index 730a539..f5f1903 100644 +--- a/hw/arm/z2.c ++++ b/hw/arm/z2.c +@@ -325,8 +325,7 @@ static void z2_init(MachineState *machine) + exit(1); + } + +- if (!pflash_cfi01_register(Z2_FLASH_BASE, +- NULL, "z2.flash0", Z2_FLASH_SIZE, ++ if (!pflash_cfi01_register(Z2_FLASH_BASE, "z2.flash0", Z2_FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + sector_len, Z2_FLASH_SIZE / sector_len, + 4, 0, 0, 0, 0, be)) { +diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c +index dbd3b9d..31926e2 100644 +--- a/hw/block/pflash_cfi01.c ++++ b/hw/block/pflash_cfi01.c +@@ -950,7 +950,7 @@ static void pflash_cfi01_register_types(void) + type_init(pflash_cfi01_register_types) + + PFlashCFI01 *pflash_cfi01_register(hwaddr base, +- DeviceState *qdev, const char *name, ++ const char *name, + hwaddr size, + BlockBackend *blk, + uint32_t sector_len, int nb_blocs, +diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c +index b9e0448..8c6c6e1 100644 +--- a/hw/block/pflash_cfi02.c ++++ b/hw/block/pflash_cfi02.c +@@ -786,7 +786,7 @@ static void pflash_cfi02_register_types(void) + type_init(pflash_cfi02_register_types) + + PFlashCFI02 *pflash_cfi02_register(hwaddr base, +- DeviceState *qdev, const char *name, ++ const char *name, + hwaddr size, + BlockBackend *blk, + uint32_t sector_len, int nb_blocs, +diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c +index d8a98f3..721a867 100644 +--- a/hw/i386/pc_sysfw.c ++++ b/hw/i386/pc_sysfw.c +@@ -159,7 +159,7 @@ static void pc_system_flash_init(MemoryRegion *rom_memory) + + /* pflash_cfi01_register() creates a deep copy of the name */ + snprintf(name, sizeof name, "system.flash%d", unit); +- system_flash = pflash_cfi01_register(phys_addr, NULL /* qdev */, name, ++ system_flash = pflash_cfi01_register(phys_addr, name, + size, blk, sector_size, + size >> sector_bits, + 1 /* width */, +diff --git a/hw/lm32/lm32_boards.c b/hw/lm32/lm32_boards.c +index 527bcc2..0a1b6e9 100644 +--- a/hw/lm32/lm32_boards.c ++++ b/hw/lm32/lm32_boards.c +@@ -114,7 +114,7 @@ static void lm32_evr_init(MachineState *machine) + + dinfo = drive_get(IF_PFLASH, 0, 0); + /* Spansion S29NS128P */ +- pflash_cfi02_register(flash_base, NULL, "lm32_evr.flash", flash_size, ++ pflash_cfi02_register(flash_base, "lm32_evr.flash", flash_size, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + flash_sector_size, flash_size / flash_sector_size, + 1, 2, 0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1); +@@ -206,7 +206,7 @@ static void lm32_uclinux_init(MachineState *machine) + + dinfo = drive_get(IF_PFLASH, 0, 0); + /* Spansion S29NS128P */ +- pflash_cfi02_register(flash_base, NULL, "lm32_uclinux.flash", flash_size, ++ pflash_cfi02_register(flash_base, "lm32_uclinux.flash", flash_size, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + flash_sector_size, flash_size / flash_sector_size, + 1, 2, 0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1); +diff --git a/hw/lm32/milkymist.c b/hw/lm32/milkymist.c +index 85d64fe..3e38313 100644 +--- a/hw/lm32/milkymist.c ++++ b/hw/lm32/milkymist.c +@@ -121,7 +121,7 @@ milkymist_init(MachineState *machine) + + dinfo = drive_get(IF_PFLASH, 0, 0); + /* Numonyx JS28F256J3F105 */ +- pflash_cfi01_register(flash_base, NULL, "milkymist.flash", flash_size, ++ pflash_cfi01_register(flash_base, "milkymist.flash", flash_size, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + flash_sector_size, flash_size / flash_sector_size, + 2, 0x00, 0x89, 0x00, 0x1d, 1); +diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c +index b664dc0..a2bc9fc 100644 +--- a/hw/microblaze/petalogix_ml605_mmu.c ++++ b/hw/microblaze/petalogix_ml605_mmu.c +@@ -107,8 +107,7 @@ petalogix_ml605_init(MachineState *machine) + dinfo = drive_get(IF_PFLASH, 0, 0); + /* 5th parameter 2 means bank-width + * 10th paremeter 0 means little-endian */ +- pflash_cfi01_register(FLASH_BASEADDR, +- NULL, "petalogix_ml605.flash", FLASH_SIZE, ++ pflash_cfi01_register(FLASH_BASEADDR, "petalogix_ml605.flash", FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + (64 * 1024), FLASH_SIZE >> 16, + 2, 0x89, 0x18, 0x0000, 0x0, 0); +diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c +index 5cb4deb..37090c5 100644 +--- a/hw/microblaze/petalogix_s3adsp1800_mmu.c ++++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c +@@ -86,7 +86,7 @@ petalogix_s3adsp1800_init(MachineState *machine) + + dinfo = drive_get(IF_PFLASH, 0, 0); + pflash_cfi01_register(FLASH_BASEADDR, +- NULL, "petalogix_s3adsp1800.flash", FLASH_SIZE, ++ "petalogix_s3adsp1800.flash", FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + (64 * 1024), FLASH_SIZE >> 16, + 1, 0x89, 0x18, 0x0000, 0x0, 1); +diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c +index e46a252..6b09258 100644 +--- a/hw/mips/mips_malta.c ++++ b/hw/mips/mips_malta.c +@@ -1062,7 +1062,7 @@ void mips_malta_init(MachineState *machine) + + /* Load firmware in flash / BIOS. */ + dinfo = drive_get(IF_PFLASH, 0, fl_idx); +- fl = pflash_cfi01_register(FLASH_ADDRESS, NULL, "mips_malta.bios", ++ fl = pflash_cfi01_register(FLASH_ADDRESS, "mips_malta.bios", + FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + 65536, FLASH_SIZE >> 16, +diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c +index aeadc4a..4ac5471 100644 +--- a/hw/mips/mips_r4k.c ++++ b/hw/mips/mips_r4k.c +@@ -234,7 +234,7 @@ void mips_r4k_init(MachineState *machine) + load_image_targphys(filename, 0x1fc00000, BIOS_SIZE); + } else if ((dinfo = drive_get(IF_PFLASH, 0, 0)) != NULL) { + uint32_t mips_rom = 0x00400000; +- if (!pflash_cfi01_register(0x1fc00000, NULL, "mips_r4k.bios", mips_rom, ++ if (!pflash_cfi01_register(0x1fc00000, "mips_r4k.bios", mips_rom, + blk_by_legacy_dinfo(dinfo), + sector_len, mips_rom / sector_len, + 4, 0, 0, 0, 0, be)) { +diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c +index bf32672..d2ad8cd 100644 +--- a/hw/ppc/ppc405_boards.c ++++ b/hw/ppc/ppc405_boards.c +@@ -229,7 +229,7 @@ static void ref405ep_init(MachineState *machine) + if (dinfo) { + bios_size = 8 * MiB; + pflash_cfi02_register((uint32_t)(-bios_size), +- NULL, "ef405ep.bios", bios_size, ++ "ef405ep.bios", bios_size, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + 64 * KiB, bios_size / (64 * KiB), 1, + 2, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, +@@ -500,7 +500,7 @@ static void taihu_405ep_init(MachineState *machine) + if (dinfo) { + bios_size = 2 * MiB; + pflash_cfi02_register(0xFFE00000, +- NULL, "taihu_405ep.bios", bios_size, ++ "taihu_405ep.bios", bios_size, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + 64 * KiB, bios_size / (64 * KiB), 1, + 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, +@@ -534,7 +534,7 @@ static void taihu_405ep_init(MachineState *machine) + dinfo = drive_get(IF_PFLASH, 0, fl_idx); + if (dinfo) { + bios_size = 32 * MiB; +- pflash_cfi02_register(0xfc000000, NULL, "taihu_405ep.flash", bios_size, ++ pflash_cfi02_register(0xfc000000, "taihu_405ep.flash", bios_size, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + 64 * KiB, bios_size / (64 * KiB), 1, + 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, +diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c +index 83c6e90..0c1790f 100644 +--- a/hw/ppc/sam460ex.c ++++ b/hw/ppc/sam460ex.c +@@ -234,7 +234,7 @@ static int sam460ex_load_uboot(void) + + dinfo = drive_get(IF_PFLASH, 0, 0); + if (!pflash_cfi01_register(FLASH_BASE | ((hwaddr)FLASH_BASE_H << 32), +- NULL, "sam460ex.flash", FLASH_SIZE, ++ "sam460ex.flash", FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + 64 * KiB, FLASH_SIZE / (64 * KiB), + 1, 0x89, 0x18, 0x0000, 0x0, 1)) { +diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c +index 77a1778..66fa766 100644 +--- a/hw/ppc/virtex_ml507.c ++++ b/hw/ppc/virtex_ml507.c +@@ -235,7 +235,7 @@ static void virtex_init(MachineState *machine) + memory_region_add_subregion(address_space_mem, ram_base, phys_ram); + + dinfo = drive_get(IF_PFLASH, 0, 0); +- pflash_cfi01_register(PFLASH_BASEADDR, NULL, "virtex.flash", FLASH_SIZE, ++ pflash_cfi01_register(PFLASH_BASEADDR, "virtex.flash", FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + (64 * 1024), FLASH_SIZE >> 16, + 1, 0x89, 0x18, 0x0000, 0x0, 1); +diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c +index befb582..7016d6f 100644 +--- a/hw/sh4/r2d.c ++++ b/hw/sh4/r2d.c +@@ -298,7 +298,7 @@ static void r2d_init(MachineState *machine) + * addressable in words of 16bit. + */ + dinfo = drive_get(IF_PFLASH, 0, 0); +- pflash_cfi02_register(0x0, NULL, "r2d.flash", FLASH_SIZE, ++ pflash_cfi02_register(0x0, "r2d.flash", FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + 64 * KiB, FLASH_SIZE >> 16, + 1, 2, 0x0001, 0x227e, 0x2220, 0x2200, +diff --git a/include/hw/block/flash.h b/include/hw/block/flash.h +index aeea3ca..3e48901 100644 +--- a/include/hw/block/flash.h ++++ b/include/hw/block/flash.h +@@ -14,7 +14,7 @@ + typedef struct PFlashCFI01 PFlashCFI01; + + PFlashCFI01 *pflash_cfi01_register(hwaddr base, +- DeviceState *qdev, const char *name, ++ const char *name, + hwaddr size, + BlockBackend *blk, + uint32_t sector_len, int nb_blocs, +@@ -33,7 +33,7 @@ MemoryRegion *pflash_cfi01_get_memory(PFlashCFI01 *fl); + typedef struct PFlashCFI02 PFlashCFI02; + + PFlashCFI02 *pflash_cfi02_register(hwaddr base, +- DeviceState *qdev, const char *name, ++ const char *name, + hwaddr size, + BlockBackend *blk, + uint32_t sector_len, int nb_blocs, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pflash-Clean-up-after-commit-368a354f02b-part-2.patch b/SOURCES/kvm-pflash-Clean-up-after-commit-368a354f02b-part-2.patch new file mode 100644 index 0000000..f0f646a --- /dev/null +++ b/SOURCES/kvm-pflash-Clean-up-after-commit-368a354f02b-part-2.patch @@ -0,0 +1,504 @@ +From b64a99d86f7f10387f207b3fc52d04b6f1f0a818 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:06 +0200 +Subject: [PATCH 39/53] pflash: Clean up after commit 368a354f02b, part 2 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-18-armbru@redhat.com> +Patchwork-id: 88006 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 17/31] pflash: Clean up after commit 368a354f02b, part 2 +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +Our pflash devices are simplistically modelled has having +"num-blocks" sectors of equal size "sector-length". Real hardware +commonly has sectors of different sizes. How our "sector-length" +property is related to the physical device's multiple sector sizes +is unclear. + +Helper functions pflash_cfi01_register() and pflash_cfi02_register() +create a pflash device, set properties including "sector-length" and +"num-blocks", and realize. They take parameters @size, @sector_len +and @nb_blocs. + +QOMification left parameter @size unused. Obviously, @size should +match @sector_len and @nb_blocs, i.e. size == sector_len * nb_blocs. +All callers satisfy this. + +Remove @nb_blocs and compute it from @size and @sector_len. + +Signed-off-by: Markus Armbruster +Reviewed-by: Laszlo Ersek +Reviewed-by: Alex Bennée +Message-Id: <20190308094610.21210-16-armbru@redhat.com> +Reviewed-by: Philippe Mathieu-Daudé +(cherry picked from commit ce14710f4fdfca32123d7efd3ddcbee984ef0ae5) +[Trivial conflicts in hw/microblaze/petalogix_ml605_mmu.c, +hw/microblaze/petalogix_s3adsp1800_mmu.c, and hw/ppc/virtex_ml507.c +due to lack of commit ab3dd749241. Trivial conflict in +hw/ppc/sam460ex.c due to lack of commit 371b74e2215.] + +Signed-off-by: Miroslav Rezanina +--- + hw/arm/collie.c | 5 +++-- + hw/arm/digic_boards.c | 1 - + hw/arm/gumstix.c | 6 ++---- + hw/arm/mainstone.c | 3 +-- + hw/arm/musicpal.c | 4 ++-- + hw/arm/omap_sx1.c | 6 ++---- + hw/arm/versatilepb.c | 1 - + hw/arm/xilinx_zynq.c | 5 ++--- + hw/arm/z2.c | 3 +-- + hw/block/pflash_cfi01.c | 5 +++-- + hw/block/pflash_cfi02.c | 5 +++-- + hw/i386/pc_sysfw.c | 6 +----- + hw/lm32/lm32_boards.c | 4 ++-- + hw/lm32/milkymist.c | 3 +-- + hw/microblaze/petalogix_ml605_mmu.c | 3 +-- + hw/microblaze/petalogix_s3adsp1800_mmu.c | 3 +-- + hw/mips/mips_malta.c | 2 +- + hw/mips/mips_r4k.c | 3 +-- + hw/ppc/ppc405_boards.c | 6 +++--- + hw/ppc/sam460ex.c | 5 ++--- + hw/ppc/virtex_ml507.c | 3 +-- + hw/sh4/r2d.c | 3 +-- + include/hw/block/flash.h | 4 ++-- + 23 files changed, 36 insertions(+), 53 deletions(-) + +diff --git a/hw/arm/collie.c b/hw/arm/collie.c +index 9cf4538..29cacd8 100644 +--- a/hw/arm/collie.c ++++ b/hw/arm/collie.c +@@ -9,6 +9,7 @@ + * GNU GPL, version 2 or (at your option) any later version. + */ + #include "qemu/osdep.h" ++#include "qemu/units.h" + #include "hw/hw.h" + #include "hw/sysbus.h" + #include "hw/boards.h" +@@ -39,12 +40,12 @@ static void collie_init(MachineState *machine) + dinfo = drive_get(IF_PFLASH, 0, 0); + pflash_cfi01_register(SA_CS0, "collie.fl1", 0x02000000, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +- (64 * 1024), 512, 4, 0x00, 0x00, 0x00, 0x00, 0); ++ 64 * KiB, 4, 0x00, 0x00, 0x00, 0x00, 0); + + dinfo = drive_get(IF_PFLASH, 0, 1); + pflash_cfi01_register(SA_CS1, "collie.fl2", 0x02000000, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +- (64 * 1024), 512, 4, 0x00, 0x00, 0x00, 0x00, 0); ++ 64 * KiB, 4, 0x00, 0x00, 0x00, 0x00, 0); + + sysbus_create_simple("scoop", 0x40800000, NULL); + +diff --git a/hw/arm/digic_boards.c b/hw/arm/digic_boards.c +index 15a00a1..304e4d1 100644 +--- a/hw/arm/digic_boards.c ++++ b/hw/arm/digic_boards.c +@@ -131,7 +131,6 @@ static void digic4_add_k8p3215uqb_rom(DigicBoardState *s, hwaddr addr, + + pflash_cfi02_register(addr, "pflash", FLASH_K8P3215UQB_SIZE, + NULL, FLASH_K8P3215UQB_SECTOR_SIZE, +- FLASH_K8P3215UQB_SIZE / FLASH_K8P3215UQB_SECTOR_SIZE, + DIGIC4_ROM_MAX_SIZE / FLASH_K8P3215UQB_SIZE, + 4, + 0x00EC, 0x007E, 0x0003, 0x0001, +diff --git a/hw/arm/gumstix.c b/hw/arm/gumstix.c +index 68a0376..03f6ef0 100644 +--- a/hw/arm/gumstix.c ++++ b/hw/arm/gumstix.c +@@ -75,8 +75,7 @@ static void connex_init(MachineState *machine) + #endif + if (!pflash_cfi01_register(0x00000000, "connext.rom", connex_rom, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +- sector_len, connex_rom / sector_len, +- 2, 0, 0, 0, 0, be)) { ++ sector_len, 2, 0, 0, 0, 0, be)) { + error_report("Error registering flash memory"); + exit(1); + } +@@ -112,8 +111,7 @@ static void verdex_init(MachineState *machine) + #endif + if (!pflash_cfi01_register(0x00000000, "verdex.rom", verdex_rom, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +- sector_len, verdex_rom / sector_len, +- 2, 0, 0, 0, 0, be)) { ++ sector_len, 2, 0, 0, 0, 0, be)) { + error_report("Error registering flash memory"); + exit(1); + } +diff --git a/hw/arm/mainstone.c b/hw/arm/mainstone.c +index cb1116b..62cca6a 100644 +--- a/hw/arm/mainstone.c ++++ b/hw/arm/mainstone.c +@@ -153,8 +153,7 @@ static void mainstone_common_init(MemoryRegion *address_space_mem, + i ? "mainstone.flash1" : "mainstone.flash0", + MAINSTONE_FLASH, + blk_by_legacy_dinfo(dinfo), +- sector_len, MAINSTONE_FLASH / sector_len, +- 4, 0, 0, 0, 0, be)) { ++ sector_len, 4, 0, 0, 0, 0, be)) { + error_report("Error registering flash memory"); + exit(1); + } +diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c +index fd7a7a9..bf8062b 100644 +--- a/hw/arm/musicpal.c ++++ b/hw/arm/musicpal.c +@@ -1639,14 +1639,14 @@ static void musicpal_init(MachineState *machine) + #ifdef TARGET_WORDS_BIGENDIAN + pflash_cfi02_register(0x100000000ULL - MP_FLASH_SIZE_MAX, + "musicpal.flash", flash_size, +- blk, 0x10000, (flash_size + 0xffff) >> 16, ++ blk, 0x10000, + MP_FLASH_SIZE_MAX / flash_size, + 2, 0x00BF, 0x236D, 0x0000, 0x0000, + 0x5555, 0x2AAA, 1); + #else + pflash_cfi02_register(0x100000000ULL - MP_FLASH_SIZE_MAX, + "musicpal.flash", flash_size, +- blk, 0x10000, (flash_size + 0xffff) >> 16, ++ blk, 0x10000, + MP_FLASH_SIZE_MAX / flash_size, + 2, 0x00BF, 0x236D, 0x0000, 0x0000, + 0x5555, 0x2AAA, 0); +diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c +index a7a3e7d..082d811 100644 +--- a/hw/arm/omap_sx1.c ++++ b/hw/arm/omap_sx1.c +@@ -156,8 +156,7 @@ static void sx1_init(MachineState *machine, const int version) + if (!pflash_cfi01_register(OMAP_CS0_BASE, + "omap_sx1.flash0-1", flash_size, + blk_by_legacy_dinfo(dinfo), +- sector_size, flash_size / sector_size, +- 4, 0, 0, 0, 0, be)) { ++ sector_size, 4, 0, 0, 0, 0, be)) { + fprintf(stderr, "qemu: Error registering flash memory %d.\n", + fl_idx); + } +@@ -180,8 +179,7 @@ static void sx1_init(MachineState *machine, const int version) + if (!pflash_cfi01_register(OMAP_CS1_BASE, + "omap_sx1.flash1-1", flash1_size, + blk_by_legacy_dinfo(dinfo), +- sector_size, flash1_size / sector_size, +- 4, 0, 0, 0, 0, be)) { ++ sector_size, 4, 0, 0, 0, 0, be)) { + fprintf(stderr, "qemu: Error registering flash memory %d.\n", + fl_idx); + } +diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c +index 99d8ff9..acb408d 100644 +--- a/hw/arm/versatilepb.c ++++ b/hw/arm/versatilepb.c +@@ -362,7 +362,6 @@ static void versatile_init(MachineState *machine, int board_id) + VERSATILE_FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + VERSATILE_FLASH_SECT_SIZE, +- VERSATILE_FLASH_SIZE / VERSATILE_FLASH_SECT_SIZE, + 4, 0x0089, 0x0018, 0x0000, 0x0, 0)) { + fprintf(stderr, "qemu: Error registering flash memory.\n"); + } +diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c +index 4a05e1c..3234cf6 100644 +--- a/hw/arm/xilinx_zynq.c ++++ b/hw/arm/xilinx_zynq.c +@@ -208,10 +208,9 @@ static void zynq_init(MachineState *machine) + /* AMD */ + pflash_cfi02_register(0xe2000000, "zynq.pflash", FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +- FLASH_SECTOR_SIZE, +- FLASH_SIZE/FLASH_SECTOR_SIZE, 1, ++ FLASH_SECTOR_SIZE, 1, + 1, 0x0066, 0x0022, 0x0000, 0x0000, 0x0555, 0x2aa, +- 0); ++ 0); + + dev = qdev_create(NULL, "xilinx,zynq_slcr"); + qdev_init_nofail(dev); +diff --git a/hw/arm/z2.c b/hw/arm/z2.c +index f5f1903..394e7a8 100644 +--- a/hw/arm/z2.c ++++ b/hw/arm/z2.c +@@ -327,8 +327,7 @@ static void z2_init(MachineState *machine) + + if (!pflash_cfi01_register(Z2_FLASH_BASE, "z2.flash0", Z2_FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +- sector_len, Z2_FLASH_SIZE / sector_len, +- 4, 0, 0, 0, 0, be)) { ++ sector_len, 4, 0, 0, 0, 0, be)) { + error_report("Error registering flash memory"); + exit(1); + } +diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c +index 31926e2..0101127 100644 +--- a/hw/block/pflash_cfi01.c ++++ b/hw/block/pflash_cfi01.c +@@ -953,7 +953,7 @@ PFlashCFI01 *pflash_cfi01_register(hwaddr base, + const char *name, + hwaddr size, + BlockBackend *blk, +- uint32_t sector_len, int nb_blocs, ++ uint32_t sector_len, + int bank_width, + uint16_t id0, uint16_t id1, + uint16_t id2, uint16_t id3, +@@ -964,7 +964,8 @@ PFlashCFI01 *pflash_cfi01_register(hwaddr base, + if (blk) { + qdev_prop_set_drive(dev, "drive", blk, &error_abort); + } +- qdev_prop_set_uint32(dev, "num-blocks", nb_blocs); ++ assert(size % sector_len == 0); ++ qdev_prop_set_uint32(dev, "num-blocks", size / sector_len); + qdev_prop_set_uint64(dev, "sector-length", sector_len); + qdev_prop_set_uint8(dev, "width", bank_width); + qdev_prop_set_bit(dev, "big-endian", !!be); +diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c +index 8c6c6e1..31b2e6c 100644 +--- a/hw/block/pflash_cfi02.c ++++ b/hw/block/pflash_cfi02.c +@@ -789,7 +789,7 @@ PFlashCFI02 *pflash_cfi02_register(hwaddr base, + const char *name, + hwaddr size, + BlockBackend *blk, +- uint32_t sector_len, int nb_blocs, ++ uint32_t sector_len, + int nb_mappings, int width, + uint16_t id0, uint16_t id1, + uint16_t id2, uint16_t id3, +@@ -802,7 +802,8 @@ PFlashCFI02 *pflash_cfi02_register(hwaddr base, + if (blk) { + qdev_prop_set_drive(dev, "drive", blk, &error_abort); + } +- qdev_prop_set_uint32(dev, "num-blocks", nb_blocs); ++ assert(size % sector_len == 0); ++ qdev_prop_set_uint32(dev, "num-blocks", size / sector_len); + qdev_prop_set_uint32(dev, "sector-length", sector_len); + qdev_prop_set_uint8(dev, "width", width); + qdev_prop_set_uint8(dev, "mappings", nb_mappings); +diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c +index 721a867..20eed89 100644 +--- a/hw/i386/pc_sysfw.c ++++ b/hw/i386/pc_sysfw.c +@@ -109,16 +109,13 @@ static void pc_system_flash_init(MemoryRegion *rom_memory) + int64_t size; + char *fatal_errmsg = NULL; + hwaddr phys_addr = 0x100000000ULL; +- int sector_bits, sector_size; ++ uint32_t sector_size = 4096; + PFlashCFI01 *system_flash; + MemoryRegion *flash_mem; + char name[64]; + void *flash_ptr; + int ret, flash_size; + +- sector_bits = 12; +- sector_size = 1 << sector_bits; +- + for (unit = 0; + (unit < FLASH_MAP_UNIT_MAX && + (pflash_drv = drive_get(IF_PFLASH, 0, unit)) != NULL); +@@ -161,7 +158,6 @@ static void pc_system_flash_init(MemoryRegion *rom_memory) + snprintf(name, sizeof name, "system.flash%d", unit); + system_flash = pflash_cfi01_register(phys_addr, name, + size, blk, sector_size, +- size >> sector_bits, + 1 /* width */, + 0x0000 /* id0 */, + 0x0000 /* id1 */, +diff --git a/hw/lm32/lm32_boards.c b/hw/lm32/lm32_boards.c +index 0a1b6e9..92fe920 100644 +--- a/hw/lm32/lm32_boards.c ++++ b/hw/lm32/lm32_boards.c +@@ -116,7 +116,7 @@ static void lm32_evr_init(MachineState *machine) + /* Spansion S29NS128P */ + pflash_cfi02_register(flash_base, "lm32_evr.flash", flash_size, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +- flash_sector_size, flash_size / flash_sector_size, ++ flash_sector_size, + 1, 2, 0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1); + + /* create irq lines */ +@@ -208,7 +208,7 @@ static void lm32_uclinux_init(MachineState *machine) + /* Spansion S29NS128P */ + pflash_cfi02_register(flash_base, "lm32_uclinux.flash", flash_size, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +- flash_sector_size, flash_size / flash_sector_size, ++ flash_sector_size, + 1, 2, 0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1); + + /* create irq lines */ +diff --git a/hw/lm32/milkymist.c b/hw/lm32/milkymist.c +index 3e38313..ac62a63 100644 +--- a/hw/lm32/milkymist.c ++++ b/hw/lm32/milkymist.c +@@ -123,8 +123,7 @@ milkymist_init(MachineState *machine) + /* Numonyx JS28F256J3F105 */ + pflash_cfi01_register(flash_base, "milkymist.flash", flash_size, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +- flash_sector_size, flash_size / flash_sector_size, +- 2, 0x00, 0x89, 0x00, 0x1d, 1); ++ flash_sector_size, 2, 0x00, 0x89, 0x00, 0x1d, 1); + + /* create irq lines */ + env->pic_state = lm32_pic_init(qemu_allocate_irq(cpu_irq_handler, cpu, 0)); +diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c +index a2bc9fc..2a2c772 100644 +--- a/hw/microblaze/petalogix_ml605_mmu.c ++++ b/hw/microblaze/petalogix_ml605_mmu.c +@@ -109,8 +109,7 @@ petalogix_ml605_init(MachineState *machine) + * 10th paremeter 0 means little-endian */ + pflash_cfi01_register(FLASH_BASEADDR, "petalogix_ml605.flash", FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +- (64 * 1024), FLASH_SIZE >> 16, +- 2, 0x89, 0x18, 0x0000, 0x0, 0); ++ 64 * KiB, 2, 0x89, 0x18, 0x0000, 0x0, 0); + + + dev = qdev_create(NULL, "xlnx.xps-intc"); +diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c +index 37090c5..eab6eb9 100644 +--- a/hw/microblaze/petalogix_s3adsp1800_mmu.c ++++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c +@@ -88,8 +88,7 @@ petalogix_s3adsp1800_init(MachineState *machine) + pflash_cfi01_register(FLASH_BASEADDR, + "petalogix_s3adsp1800.flash", FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +- (64 * 1024), FLASH_SIZE >> 16, +- 1, 0x89, 0x18, 0x0000, 0x0, 1); ++ 64 * KiB, 1, 0x89, 0x18, 0x0000, 0x0, 1); + + dev = qdev_create(NULL, "xlnx.xps-intc"); + qdev_prop_set_uint32(dev, "kind-of-intr", +diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c +index 6b09258..921eb22 100644 +--- a/hw/mips/mips_malta.c ++++ b/hw/mips/mips_malta.c +@@ -1065,7 +1065,7 @@ void mips_malta_init(MachineState *machine) + fl = pflash_cfi01_register(FLASH_ADDRESS, "mips_malta.bios", + FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +- 65536, FLASH_SIZE >> 16, ++ 65536, + 4, 0x0000, 0x0000, 0x0000, 0x0000, be); + bios = pflash_cfi01_get_memory(fl); + fl_idx++; +diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c +index 4ac5471..0aca7f9 100644 +--- a/hw/mips/mips_r4k.c ++++ b/hw/mips/mips_r4k.c +@@ -236,8 +236,7 @@ void mips_r4k_init(MachineState *machine) + uint32_t mips_rom = 0x00400000; + if (!pflash_cfi01_register(0x1fc00000, "mips_r4k.bios", mips_rom, + blk_by_legacy_dinfo(dinfo), +- sector_len, mips_rom / sector_len, +- 4, 0, 0, 0, 0, be)) { ++ sector_len, 4, 0, 0, 0, 0, be)) { + fprintf(stderr, "qemu: Error registering flash memory.\n"); + } + } else if (!qtest_enabled()) { +diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c +index d2ad8cd..4575043 100644 +--- a/hw/ppc/ppc405_boards.c ++++ b/hw/ppc/ppc405_boards.c +@@ -231,7 +231,7 @@ static void ref405ep_init(MachineState *machine) + pflash_cfi02_register((uint32_t)(-bios_size), + "ef405ep.bios", bios_size, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +- 64 * KiB, bios_size / (64 * KiB), 1, ++ 64 * KiB, 1, + 2, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, + 1); + } else +@@ -502,7 +502,7 @@ static void taihu_405ep_init(MachineState *machine) + pflash_cfi02_register(0xFFE00000, + "taihu_405ep.bios", bios_size, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +- 64 * KiB, bios_size / (64 * KiB), 1, ++ 64 * KiB, 1, + 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, + 1); + fl_idx++; +@@ -536,7 +536,7 @@ static void taihu_405ep_init(MachineState *machine) + bios_size = 32 * MiB; + pflash_cfi02_register(0xfc000000, "taihu_405ep.flash", bios_size, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +- 64 * KiB, bios_size / (64 * KiB), 1, ++ 64 * KiB, 1, + 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, + 1); + fl_idx++; +diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c +index 0c1790f..4ec934f 100644 +--- a/hw/ppc/sam460ex.c ++++ b/hw/ppc/sam460ex.c +@@ -236,9 +236,8 @@ static int sam460ex_load_uboot(void) + if (!pflash_cfi01_register(FLASH_BASE | ((hwaddr)FLASH_BASE_H << 32), + "sam460ex.flash", FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +- 64 * KiB, FLASH_SIZE / (64 * KiB), +- 1, 0x89, 0x18, 0x0000, 0x0, 1)) { +- error_report("qemu: Error registering flash memory."); ++ 64 * KiB, 1, 0x89, 0x18, 0x0000, 0x0, 1)) { ++ error_report("Error registering flash memory"); + /* XXX: return an error instead? */ + exit(1); + } +diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c +index 66fa766..2fd8efe 100644 +--- a/hw/ppc/virtex_ml507.c ++++ b/hw/ppc/virtex_ml507.c +@@ -237,8 +237,7 @@ static void virtex_init(MachineState *machine) + dinfo = drive_get(IF_PFLASH, 0, 0); + pflash_cfi01_register(PFLASH_BASEADDR, "virtex.flash", FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +- (64 * 1024), FLASH_SIZE >> 16, +- 1, 0x89, 0x18, 0x0000, 0x0, 1); ++ 64 * KiB, 1, 0x89, 0x18, 0x0000, 0x0, 1); + + cpu_irq = (qemu_irq *) &env->irq_inputs[PPC40x_INPUT_INT]; + dev = qdev_create(NULL, "xlnx.xps-intc"); +diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c +index 7016d6f..d6e6515 100644 +--- a/hw/sh4/r2d.c ++++ b/hw/sh4/r2d.c +@@ -300,8 +300,7 @@ static void r2d_init(MachineState *machine) + dinfo = drive_get(IF_PFLASH, 0, 0); + pflash_cfi02_register(0x0, "r2d.flash", FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +- 64 * KiB, FLASH_SIZE >> 16, +- 1, 2, 0x0001, 0x227e, 0x2220, 0x2200, ++ 64 * KiB, 1, 2, 0x0001, 0x227e, 0x2220, 0x2200, + 0x555, 0x2aa, 0); + + /* NIC: rtl8139 on-board, and 2 slots. */ +diff --git a/include/hw/block/flash.h b/include/hw/block/flash.h +index 3e48901..914932e 100644 +--- a/include/hw/block/flash.h ++++ b/include/hw/block/flash.h +@@ -17,7 +17,7 @@ PFlashCFI01 *pflash_cfi01_register(hwaddr base, + const char *name, + hwaddr size, + BlockBackend *blk, +- uint32_t sector_len, int nb_blocs, ++ uint32_t sector_len, + int width, + uint16_t id0, uint16_t id1, + uint16_t id2, uint16_t id3, +@@ -36,7 +36,7 @@ PFlashCFI02 *pflash_cfi02_register(hwaddr base, + const char *name, + hwaddr size, + BlockBackend *blk, +- uint32_t sector_len, int nb_blocs, ++ uint32_t sector_len, + int nb_mappings, + int width, + uint16_t id0, uint16_t id1, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pflash-Rename-CFI_PFLASH-to-PFLASH_CFI.patch b/SOURCES/kvm-pflash-Rename-CFI_PFLASH-to-PFLASH_CFI.patch new file mode 100644 index 0000000..be8dfac --- /dev/null +++ b/SOURCES/kvm-pflash-Rename-CFI_PFLASH-to-PFLASH_CFI.patch @@ -0,0 +1,169 @@ +From 23f2d8fa204a3aa29a69e2e9d1efa5b7d22f3288 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:50:55 +0200 +Subject: [PATCH 28/53] pflash: Rename *CFI_PFLASH* to *PFLASH_CFI* +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-7-armbru@redhat.com> +Patchwork-id: 87993 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 06/31] pflash: Rename *CFI_PFLASH* to *PFLASH_CFI* +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +pflash_cfi01.c and pflash_cfi02.c start their identifiers with +pflash_cfi01_ and pflash_cfi02_ respectively, except for +CFI_PFLASH01(), TYPE_CFI_PFLASH01, CFI_PFLASH02(), TYPE_CFI_PFLASH02. +Rename for consistency. + +Suggested-by: Philippe Mathieu-Daudé +Signed-off-by: Markus Armbruster +Reviewed-by: Philippe Mathieu-Daudé +Reviewed-by: Alex Bennée +Message-Id: <20190308094610.21210-5-armbru@redhat.com> +(cherry picked from commit e7b6274197c5f096860014ca750544d6aca0b9b9) +Signed-off-by: Miroslav Rezanina +--- + hw/block/pflash_cfi01.c | 12 ++++++------ + hw/block/pflash_cfi02.c | 14 +++++++------- + include/hw/block/flash.h | 4 ++-- + 3 files changed, 15 insertions(+), 15 deletions(-) + +diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c +index 3a6bd10..1ff4d25 100644 +--- a/hw/block/pflash_cfi01.c ++++ b/hw/block/pflash_cfi01.c +@@ -59,8 +59,8 @@ do { \ + #define DPRINTF(fmt, ...) do { } while (0) + #endif + +-#define CFI_PFLASH01(obj) \ +- OBJECT_CHECK(PFlashCFI01, (obj), TYPE_CFI_PFLASH01) ++#define PFLASH_CFI01(obj) \ ++ OBJECT_CHECK(PFlashCFI01, (obj), TYPE_PFLASH_CFI01) + + #define PFLASH_BE 0 + #define PFLASH_SECURE 1 +@@ -711,7 +711,7 @@ static const MemoryRegionOps pflash_cfi01_ops = { + + static void pflash_cfi01_realize(DeviceState *dev, Error **errp) + { +- PFlashCFI01 *pfl = CFI_PFLASH01(dev); ++ PFlashCFI01 *pfl = PFLASH_CFI01(dev); + uint64_t total_len; + int ret; + uint64_t blocks_per_device, sector_len_per_device, device_len; +@@ -939,7 +939,7 @@ static void pflash_cfi01_class_init(ObjectClass *klass, void *data) + + + static const TypeInfo pflash_cfi01_info = { +- .name = TYPE_CFI_PFLASH01, ++ .name = TYPE_PFLASH_CFI01, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PFlashCFI01), + .class_init = pflash_cfi01_class_init, +@@ -962,7 +962,7 @@ PFlashCFI01 *pflash_cfi01_register(hwaddr base, + uint16_t id2, uint16_t id3, + int be) + { +- DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH01); ++ DeviceState *dev = qdev_create(NULL, TYPE_PFLASH_CFI01); + + if (blk) { + qdev_prop_set_drive(dev, "drive", blk, &error_abort); +@@ -979,7 +979,7 @@ PFlashCFI01 *pflash_cfi01_register(hwaddr base, + qdev_init_nofail(dev); + + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); +- return CFI_PFLASH01(dev); ++ return PFLASH_CFI01(dev); + } + + MemoryRegion *pflash_cfi01_get_memory(PFlashCFI01 *fl) +diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c +index 7fa02d2..2c0cbde 100644 +--- a/hw/block/pflash_cfi02.c ++++ b/hw/block/pflash_cfi02.c +@@ -57,8 +57,8 @@ do { \ + + #define PFLASH_LAZY_ROMD_THRESHOLD 42 + +-#define CFI_PFLASH02(obj) \ +- OBJECT_CHECK(PFlashCFI02, (obj), TYPE_CFI_PFLASH02) ++#define PFLASH_CFI02(obj) \ ++ OBJECT_CHECK(PFlashCFI02, (obj), TYPE_PFLASH_CFI02) + + struct PFlashCFI02 { + /*< private >*/ +@@ -596,7 +596,7 @@ static const MemoryRegionOps pflash_cfi02_ops_le = { + + static void pflash_cfi02_realize(DeviceState *dev, Error **errp) + { +- PFlashCFI02 *pfl = CFI_PFLASH02(dev); ++ PFlashCFI02 *pfl = PFLASH_CFI02(dev); + uint32_t chip_len; + int ret; + Error *local_err = NULL; +@@ -760,7 +760,7 @@ static Property pflash_cfi02_properties[] = { + + static void pflash_cfi02_unrealize(DeviceState *dev, Error **errp) + { +- PFlashCFI02 *pfl = CFI_PFLASH02(dev); ++ PFlashCFI02 *pfl = PFLASH_CFI02(dev); + timer_del(&pfl->timer); + } + +@@ -775,7 +775,7 @@ static void pflash_cfi02_class_init(ObjectClass *klass, void *data) + } + + static const TypeInfo pflash_cfi02_info = { +- .name = TYPE_CFI_PFLASH02, ++ .name = TYPE_PFLASH_CFI02, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PFlashCFI02), + .class_init = pflash_cfi02_class_init, +@@ -800,7 +800,7 @@ PFlashCFI02 *pflash_cfi02_register(hwaddr base, + uint16_t unlock_addr1, + int be) + { +- DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH02); ++ DeviceState *dev = qdev_create(NULL, TYPE_PFLASH_CFI02); + + if (blk) { + qdev_prop_set_drive(dev, "drive", blk, &error_abort); +@@ -820,5 +820,5 @@ PFlashCFI02 *pflash_cfi02_register(hwaddr base, + qdev_init_nofail(dev); + + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); +- return CFI_PFLASH02(dev); ++ return PFLASH_CFI02(dev); + } +diff --git a/include/hw/block/flash.h b/include/hw/block/flash.h +index 51d8f60..333005d 100644 +--- a/include/hw/block/flash.h ++++ b/include/hw/block/flash.h +@@ -7,7 +7,7 @@ + + /* pflash_cfi01.c */ + +-#define TYPE_CFI_PFLASH01 "cfi.pflash01" ++#define TYPE_PFLASH_CFI01 "cfi.pflash01" + + typedef struct PFlashCFI01 PFlashCFI01; + +@@ -24,7 +24,7 @@ MemoryRegion *pflash_cfi01_get_memory(PFlashCFI01 *fl); + + /* pflash_cfi02.c */ + +-#define TYPE_CFI_PFLASH02 "cfi.pflash02" ++#define TYPE_PFLASH_CFI02 "cfi.pflash02" + + typedef struct PFlashCFI02 PFlashCFI02; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pflash-Rename-pflash_t-to-PFlashCFI01-PFlashCFI02.patch b/SOURCES/kvm-pflash-Rename-pflash_t-to-PFlashCFI01-PFlashCFI02.patch new file mode 100644 index 0000000..ac2a111 --- /dev/null +++ b/SOURCES/kvm-pflash-Rename-pflash_t-to-PFlashCFI01-PFlashCFI02.patch @@ -0,0 +1,607 @@ +From d8578dd73598ffaa42a9fc7aeaeec8bcba11df42 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:50:52 +0200 +Subject: [PATCH 25/53] pflash: Rename pflash_t to PFlashCFI01, PFlashCFI02 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-4-armbru@redhat.com> +Patchwork-id: 87982 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 03/31] pflash: Rename pflash_t to PFlashCFI01, PFlashCFI02 +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +flash.h's incomplete struct pflash_t is completed both in +pflash_cfi01.c and in pflash_cfi02.c. The complete types are +incompatible. This can hide type errors, such as passing a pflash_t +created with pflash_cfi02_register() to pflash_cfi01_get_memory(). + +Furthermore, POSIX reserves typedef names ending with _t. + +Rename the two structs to PFlashCFI01 and PFlashCFI02. + +Signed-off-by: Markus Armbruster +Reviewed-by: Alex Bennée +Reviewed-by: Philippe Mathieu-Daudé +Tested-by: Philippe Mathieu-Daudé +Message-Id: <20190308094610.21210-2-armbru@redhat.com> +(cherry picked from commit 1643406520f8ff6f4cc11062950f5f898b03b573) +Signed-off-by: Miroslav Rezanina +--- + hw/arm/vexpress.c | 8 ++--- + hw/block/pflash_cfi01.c | 89 +++++++++++++++++++++++++----------------------- + hw/block/pflash_cfi02.c | 73 ++++++++++++++++++++------------------- + hw/i386/pc_sysfw.c | 2 +- + hw/mips/mips_malta.c | 2 +- + hw/xtensa/xtfpga.c | 10 +++--- + include/hw/block/flash.h | 51 +++++++++++++++------------ + 7 files changed, 125 insertions(+), 110 deletions(-) + +diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c +index 9fad791..5cca371 100644 +--- a/hw/arm/vexpress.c ++++ b/hw/arm/vexpress.c +@@ -501,8 +501,8 @@ static void vexpress_modify_dtb(const struct arm_boot_info *info, void *fdt) + /* Open code a private version of pflash registration since we + * need to set non-default device width for VExpress platform. + */ +-static pflash_t *ve_pflash_cfi01_register(hwaddr base, const char *name, +- DriveInfo *di) ++static PFlashCFI01 *ve_pflash_cfi01_register(hwaddr base, const char *name, ++ DriveInfo *di) + { + DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); + +@@ -525,7 +525,7 @@ static pflash_t *ve_pflash_cfi01_register(hwaddr base, const char *name, + qdev_init_nofail(dev); + + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); +- return OBJECT_CHECK(pflash_t, (dev), "cfi.pflash01"); ++ return OBJECT_CHECK(PFlashCFI01, (dev), "cfi.pflash01"); + } + + static void vexpress_common_init(MachineState *machine) +@@ -537,7 +537,7 @@ static void vexpress_common_init(MachineState *machine) + qemu_irq pic[64]; + uint32_t sys_id; + DriveInfo *dinfo; +- pflash_t *pflash0; ++ PFlashCFI01 *pflash0; + I2CBus *i2c; + ram_addr_t vram_size, sram_size; + MemoryRegion *sysmem = get_system_memory(); +diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c +index 2e82840..389bc60 100644 +--- a/hw/block/pflash_cfi01.c ++++ b/hw/block/pflash_cfi01.c +@@ -65,12 +65,13 @@ do { \ + #define DPRINTF(fmt, ...) do { } while (0) + #endif + +-#define CFI_PFLASH01(obj) OBJECT_CHECK(pflash_t, (obj), TYPE_CFI_PFLASH01) ++#define CFI_PFLASH01(obj) \ ++ OBJECT_CHECK(PFlashCFI01, (obj), TYPE_CFI_PFLASH01) + + #define PFLASH_BE 0 + #define PFLASH_SECURE 1 + +-struct pflash_t { ++struct PFlashCFI01 { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ +@@ -109,17 +110,17 @@ static const VMStateDescription vmstate_pflash = { + .minimum_version_id = 1, + .post_load = pflash_post_load, + .fields = (VMStateField[]) { +- VMSTATE_UINT8(wcycle, pflash_t), +- VMSTATE_UINT8(cmd, pflash_t), +- VMSTATE_UINT8(status, pflash_t), +- VMSTATE_UINT64(counter, pflash_t), ++ VMSTATE_UINT8(wcycle, PFlashCFI01), ++ VMSTATE_UINT8(cmd, PFlashCFI01), ++ VMSTATE_UINT8(status, PFlashCFI01), ++ VMSTATE_UINT64(counter, PFlashCFI01), + VMSTATE_END_OF_LIST() + } + }; + + static void pflash_timer (void *opaque) + { +- pflash_t *pfl = opaque; ++ PFlashCFI01 *pfl = opaque; + + DPRINTF("%s: command %02x done\n", __func__, pfl->cmd); + /* Reset flash */ +@@ -133,7 +134,7 @@ static void pflash_timer (void *opaque) + * If this code is called we know we have a device_width set for + * this flash. + */ +-static uint32_t pflash_cfi_query(pflash_t *pfl, hwaddr offset) ++static uint32_t pflash_cfi_query(PFlashCFI01 *pfl, hwaddr offset) + { + int i; + uint32_t resp = 0; +@@ -193,7 +194,7 @@ static uint32_t pflash_cfi_query(pflash_t *pfl, hwaddr offset) + + + /* Perform a device id query based on the bank width of the flash. */ +-static uint32_t pflash_devid_query(pflash_t *pfl, hwaddr offset) ++static uint32_t pflash_devid_query(PFlashCFI01 *pfl, hwaddr offset) + { + int i; + uint32_t resp; +@@ -242,7 +243,7 @@ static uint32_t pflash_devid_query(pflash_t *pfl, hwaddr offset) + return resp; + } + +-static uint32_t pflash_data_read(pflash_t *pfl, hwaddr offset, ++static uint32_t pflash_data_read(PFlashCFI01 *pfl, hwaddr offset, + int width, int be) + { + uint8_t *p; +@@ -288,8 +289,8 @@ static uint32_t pflash_data_read(pflash_t *pfl, hwaddr offset, + return ret; + } + +-static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, +- int width, int be) ++static uint32_t pflash_read(PFlashCFI01 *pfl, hwaddr offset, ++ int width, int be) + { + hwaddr boff; + uint32_t ret; +@@ -407,7 +408,7 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, + } + + /* update flash content on disk */ +-static void pflash_update(pflash_t *pfl, int offset, ++static void pflash_update(PFlashCFI01 *pfl, int offset, + int size) + { + int offset_end; +@@ -421,7 +422,7 @@ static void pflash_update(pflash_t *pfl, int offset, + } + } + +-static inline void pflash_data_write(pflash_t *pfl, hwaddr offset, ++static inline void pflash_data_write(PFlashCFI01 *pfl, hwaddr offset, + uint32_t value, int width, int be) + { + uint8_t *p = pfl->storage; +@@ -459,7 +460,7 @@ static inline void pflash_data_write(pflash_t *pfl, hwaddr offset, + + } + +-static void pflash_write(pflash_t *pfl, hwaddr offset, ++static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, + uint32_t value, int width, int be) + { + uint8_t *p; +@@ -667,7 +668,7 @@ static void pflash_write(pflash_t *pfl, hwaddr offset, + static MemTxResult pflash_mem_read_with_attrs(void *opaque, hwaddr addr, uint64_t *value, + unsigned len, MemTxAttrs attrs) + { +- pflash_t *pfl = opaque; ++ PFlashCFI01 *pfl = opaque; + bool be = !!(pfl->features & (1 << PFLASH_BE)); + + if ((pfl->features & (1 << PFLASH_SECURE)) && !attrs.secure) { +@@ -681,7 +682,7 @@ static MemTxResult pflash_mem_read_with_attrs(void *opaque, hwaddr addr, uint64_ + static MemTxResult pflash_mem_write_with_attrs(void *opaque, hwaddr addr, uint64_t value, + unsigned len, MemTxAttrs attrs) + { +- pflash_t *pfl = opaque; ++ PFlashCFI01 *pfl = opaque; + bool be = !!(pfl->features & (1 << PFLASH_BE)); + + if ((pfl->features & (1 << PFLASH_SECURE)) && !attrs.secure) { +@@ -700,7 +701,7 @@ static const MemoryRegionOps pflash_cfi01_ops = { + + static void pflash_cfi01_realize(DeviceState *dev, Error **errp) + { +- pflash_t *pfl = CFI_PFLASH01(dev); ++ PFlashCFI01 *pfl = CFI_PFLASH01(dev); + uint64_t total_len; + int ret; + uint64_t blocks_per_device, sector_len_per_device, device_len; +@@ -877,14 +878,14 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) + } + + static Property pflash_cfi01_properties[] = { +- DEFINE_PROP_DRIVE("drive", struct pflash_t, blk), ++ DEFINE_PROP_DRIVE("drive", PFlashCFI01, blk), + /* num-blocks is the number of blocks actually visible to the guest, + * ie the total size of the device divided by the sector length. + * If we're emulating flash devices wired in parallel the actual + * number of blocks per indvidual device will differ. + */ +- DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0), +- DEFINE_PROP_UINT64("sector-length", struct pflash_t, sector_len, 0), ++ DEFINE_PROP_UINT32("num-blocks", PFlashCFI01, nb_blocs, 0), ++ DEFINE_PROP_UINT64("sector-length", PFlashCFI01, sector_len, 0), + /* width here is the overall width of this QEMU device in bytes. + * The QEMU device may be emulating a number of flash devices + * wired up in parallel; the width of each individual flash +@@ -901,17 +902,17 @@ static Property pflash_cfi01_properties[] = { + * 16 bit devices making up a 32 bit wide QEMU device. This + * is deprecated for new uses of this device. + */ +- DEFINE_PROP_UINT8("width", struct pflash_t, bank_width, 0), +- DEFINE_PROP_UINT8("device-width", struct pflash_t, device_width, 0), +- DEFINE_PROP_UINT8("max-device-width", struct pflash_t, max_device_width, 0), +- DEFINE_PROP_BIT("big-endian", struct pflash_t, features, PFLASH_BE, 0), +- DEFINE_PROP_BIT("secure", struct pflash_t, features, PFLASH_SECURE, 0), +- DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0), +- DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0), +- DEFINE_PROP_UINT16("id2", struct pflash_t, ident2, 0), +- DEFINE_PROP_UINT16("id3", struct pflash_t, ident3, 0), +- DEFINE_PROP_STRING("name", struct pflash_t, name), +- DEFINE_PROP_BOOL("old-multiple-chip-handling", struct pflash_t, ++ DEFINE_PROP_UINT8("width", PFlashCFI01, bank_width, 0), ++ DEFINE_PROP_UINT8("device-width", PFlashCFI01, device_width, 0), ++ DEFINE_PROP_UINT8("max-device-width", PFlashCFI01, max_device_width, 0), ++ DEFINE_PROP_BIT("big-endian", PFlashCFI01, features, PFLASH_BE, 0), ++ DEFINE_PROP_BIT("secure", PFlashCFI01, features, PFLASH_SECURE, 0), ++ DEFINE_PROP_UINT16("id0", PFlashCFI01, ident0, 0), ++ DEFINE_PROP_UINT16("id1", PFlashCFI01, ident1, 0), ++ DEFINE_PROP_UINT16("id2", PFlashCFI01, ident2, 0), ++ DEFINE_PROP_UINT16("id3", PFlashCFI01, ident3, 0), ++ DEFINE_PROP_STRING("name", PFlashCFI01, name), ++ DEFINE_PROP_BOOL("old-multiple-chip-handling", PFlashCFI01, + old_multiple_chip_handling, false), + DEFINE_PROP_END_OF_LIST(), + }; +@@ -930,7 +931,7 @@ static void pflash_cfi01_class_init(ObjectClass *klass, void *data) + static const TypeInfo pflash_cfi01_info = { + .name = TYPE_CFI_PFLASH01, + .parent = TYPE_SYS_BUS_DEVICE, +- .instance_size = sizeof(struct pflash_t), ++ .instance_size = sizeof(PFlashCFI01), + .class_init = pflash_cfi01_class_init, + }; + +@@ -941,13 +942,15 @@ static void pflash_cfi01_register_types(void) + + type_init(pflash_cfi01_register_types) + +-pflash_t *pflash_cfi01_register(hwaddr base, +- DeviceState *qdev, const char *name, +- hwaddr size, +- BlockBackend *blk, +- uint32_t sector_len, int nb_blocs, +- int bank_width, uint16_t id0, uint16_t id1, +- uint16_t id2, uint16_t id3, int be) ++PFlashCFI01 *pflash_cfi01_register(hwaddr base, ++ DeviceState *qdev, const char *name, ++ hwaddr size, ++ BlockBackend *blk, ++ uint32_t sector_len, int nb_blocs, ++ int bank_width, ++ uint16_t id0, uint16_t id1, ++ uint16_t id2, uint16_t id3, ++ int be) + { + DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH01); + +@@ -969,14 +972,14 @@ pflash_t *pflash_cfi01_register(hwaddr base, + return CFI_PFLASH01(dev); + } + +-MemoryRegion *pflash_cfi01_get_memory(pflash_t *fl) ++MemoryRegion *pflash_cfi01_get_memory(PFlashCFI01 *fl) + { + return &fl->mem; + } + + static void postload_update_cb(void *opaque, int running, RunState state) + { +- pflash_t *pfl = opaque; ++ PFlashCFI01 *pfl = opaque; + + /* This is called after bdrv_invalidate_cache_all. */ + qemu_del_vm_change_state_handler(pfl->vmstate); +@@ -988,7 +991,7 @@ static void postload_update_cb(void *opaque, int running, RunState state) + + static int pflash_post_load(void *opaque, int version_id) + { +- pflash_t *pfl = opaque; ++ PFlashCFI01 *pfl = opaque; + + if (!pfl->ro) { + pfl->vmstate = qemu_add_vm_change_state_handler(postload_update_cb, +diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c +index cbc3d4d..7fa02d2 100644 +--- a/hw/block/pflash_cfi02.c ++++ b/hw/block/pflash_cfi02.c +@@ -57,9 +57,10 @@ do { \ + + #define PFLASH_LAZY_ROMD_THRESHOLD 42 + +-#define CFI_PFLASH02(obj) OBJECT_CHECK(pflash_t, (obj), TYPE_CFI_PFLASH02) ++#define CFI_PFLASH02(obj) \ ++ OBJECT_CHECK(PFlashCFI02, (obj), TYPE_CFI_PFLASH02) + +-struct pflash_t { ++struct PFlashCFI02 { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ +@@ -101,7 +102,7 @@ struct pflash_t { + /* + * Set up replicated mappings of the same region. + */ +-static void pflash_setup_mappings(pflash_t *pfl) ++static void pflash_setup_mappings(PFlashCFI02 *pfl) + { + unsigned i; + hwaddr size = memory_region_size(&pfl->orig_mem); +@@ -115,7 +116,7 @@ static void pflash_setup_mappings(pflash_t *pfl) + } + } + +-static void pflash_register_memory(pflash_t *pfl, int rom_mode) ++static void pflash_register_memory(PFlashCFI02 *pfl, int rom_mode) + { + memory_region_rom_device_set_romd(&pfl->orig_mem, rom_mode); + pfl->rom_mode = rom_mode; +@@ -123,7 +124,7 @@ static void pflash_register_memory(pflash_t *pfl, int rom_mode) + + static void pflash_timer (void *opaque) + { +- pflash_t *pfl = opaque; ++ PFlashCFI02 *pfl = opaque; + + DPRINTF("%s: command %02x done\n", __func__, pfl->cmd); + /* Reset flash */ +@@ -137,8 +138,8 @@ static void pflash_timer (void *opaque) + pfl->cmd = 0; + } + +-static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, +- int width, int be) ++static uint32_t pflash_read(PFlashCFI02 *pfl, hwaddr offset, ++ int width, int be) + { + hwaddr boff; + uint32_t ret; +@@ -246,7 +247,7 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, + } + + /* update flash content on disk */ +-static void pflash_update(pflash_t *pfl, int offset, ++static void pflash_update(PFlashCFI02 *pfl, int offset, + int size) + { + int offset_end; +@@ -260,8 +261,8 @@ static void pflash_update(pflash_t *pfl, int offset, + } + } + +-static void pflash_write (pflash_t *pfl, hwaddr offset, +- uint32_t value, int width, int be) ++static void pflash_write(PFlashCFI02 *pfl, hwaddr offset, ++ uint32_t value, int width, int be) + { + hwaddr boff; + uint8_t *p; +@@ -595,7 +596,7 @@ static const MemoryRegionOps pflash_cfi02_ops_le = { + + static void pflash_cfi02_realize(DeviceState *dev, Error **errp) + { +- pflash_t *pfl = CFI_PFLASH02(dev); ++ PFlashCFI02 *pfl = CFI_PFLASH02(dev); + uint32_t chip_len; + int ret; + Error *local_err = NULL; +@@ -741,25 +742,25 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp) + } + + static Property pflash_cfi02_properties[] = { +- DEFINE_PROP_DRIVE("drive", struct pflash_t, blk), +- DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0), +- DEFINE_PROP_UINT32("sector-length", struct pflash_t, sector_len, 0), +- DEFINE_PROP_UINT8("width", struct pflash_t, width, 0), +- DEFINE_PROP_UINT8("mappings", struct pflash_t, mappings, 0), +- DEFINE_PROP_UINT8("big-endian", struct pflash_t, be, 0), +- DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0), +- DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0), +- DEFINE_PROP_UINT16("id2", struct pflash_t, ident2, 0), +- DEFINE_PROP_UINT16("id3", struct pflash_t, ident3, 0), +- DEFINE_PROP_UINT16("unlock-addr0", struct pflash_t, unlock_addr0, 0), +- DEFINE_PROP_UINT16("unlock-addr1", struct pflash_t, unlock_addr1, 0), +- DEFINE_PROP_STRING("name", struct pflash_t, name), ++ DEFINE_PROP_DRIVE("drive", PFlashCFI02, blk), ++ DEFINE_PROP_UINT32("num-blocks", PFlashCFI02, nb_blocs, 0), ++ DEFINE_PROP_UINT32("sector-length", PFlashCFI02, sector_len, 0), ++ DEFINE_PROP_UINT8("width", PFlashCFI02, width, 0), ++ DEFINE_PROP_UINT8("mappings", PFlashCFI02, mappings, 0), ++ DEFINE_PROP_UINT8("big-endian", PFlashCFI02, be, 0), ++ DEFINE_PROP_UINT16("id0", PFlashCFI02, ident0, 0), ++ DEFINE_PROP_UINT16("id1", PFlashCFI02, ident1, 0), ++ DEFINE_PROP_UINT16("id2", PFlashCFI02, ident2, 0), ++ DEFINE_PROP_UINT16("id3", PFlashCFI02, ident3, 0), ++ DEFINE_PROP_UINT16("unlock-addr0", PFlashCFI02, unlock_addr0, 0), ++ DEFINE_PROP_UINT16("unlock-addr1", PFlashCFI02, unlock_addr1, 0), ++ DEFINE_PROP_STRING("name", PFlashCFI02, name), + DEFINE_PROP_END_OF_LIST(), + }; + + static void pflash_cfi02_unrealize(DeviceState *dev, Error **errp) + { +- pflash_t *pfl = CFI_PFLASH02(dev); ++ PFlashCFI02 *pfl = CFI_PFLASH02(dev); + timer_del(&pfl->timer); + } + +@@ -776,7 +777,7 @@ static void pflash_cfi02_class_init(ObjectClass *klass, void *data) + static const TypeInfo pflash_cfi02_info = { + .name = TYPE_CFI_PFLASH02, + .parent = TYPE_SYS_BUS_DEVICE, +- .instance_size = sizeof(struct pflash_t), ++ .instance_size = sizeof(PFlashCFI02), + .class_init = pflash_cfi02_class_init, + }; + +@@ -787,15 +788,17 @@ static void pflash_cfi02_register_types(void) + + type_init(pflash_cfi02_register_types) + +-pflash_t *pflash_cfi02_register(hwaddr base, +- DeviceState *qdev, const char *name, +- hwaddr size, +- BlockBackend *blk, uint32_t sector_len, +- int nb_blocs, int nb_mappings, int width, +- uint16_t id0, uint16_t id1, +- uint16_t id2, uint16_t id3, +- uint16_t unlock_addr0, uint16_t unlock_addr1, +- int be) ++PFlashCFI02 *pflash_cfi02_register(hwaddr base, ++ DeviceState *qdev, const char *name, ++ hwaddr size, ++ BlockBackend *blk, ++ uint32_t sector_len, int nb_blocs, ++ int nb_mappings, int width, ++ uint16_t id0, uint16_t id1, ++ uint16_t id2, uint16_t id3, ++ uint16_t unlock_addr0, ++ uint16_t unlock_addr1, ++ int be) + { + DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH02); + +diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c +index 2a6de35..d8a98f3 100644 +--- a/hw/i386/pc_sysfw.c ++++ b/hw/i386/pc_sysfw.c +@@ -110,7 +110,7 @@ static void pc_system_flash_init(MemoryRegion *rom_memory) + char *fatal_errmsg = NULL; + hwaddr phys_addr = 0x100000000ULL; + int sector_bits, sector_size; +- pflash_t *system_flash; ++ PFlashCFI01 *system_flash; + MemoryRegion *flash_mem; + char name[64]; + void *flash_ptr; +diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c +index f6513a4..88fd7d6 100644 +--- a/hw/mips/mips_malta.c ++++ b/hw/mips/mips_malta.c +@@ -986,7 +986,7 @@ void mips_malta_init(MachineState *machine) + const char *kernel_cmdline = machine->kernel_cmdline; + const char *initrd_filename = machine->initrd_filename; + char *filename; +- pflash_t *fl; ++ PFlashCFI01 *fl; + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *ram_high = g_new(MemoryRegion, 1); + MemoryRegion *ram_low_preio = g_new(MemoryRegion, 1); +diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c +index 70686a2..8f8c0b9 100644 +--- a/hw/xtensa/xtfpga.c ++++ b/hw/xtensa/xtfpga.c +@@ -159,9 +159,9 @@ static void xtfpga_net_init(MemoryRegion *address_space, + memory_region_add_subregion(address_space, buffers, ram); + } + +-static pflash_t *xtfpga_flash_init(MemoryRegion *address_space, +- const XtfpgaBoardDesc *board, +- DriveInfo *dinfo, int be) ++static PFlashCFI01 *xtfpga_flash_init(MemoryRegion *address_space, ++ const XtfpgaBoardDesc *board, ++ DriveInfo *dinfo, int be) + { + SysBusDevice *s; + DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); +@@ -178,7 +178,7 @@ static pflash_t *xtfpga_flash_init(MemoryRegion *address_space, + s = SYS_BUS_DEVICE(dev); + memory_region_add_subregion(address_space, board->flash->base, + sysbus_mmio_get_region(s, 0)); +- return OBJECT_CHECK(pflash_t, (dev), "cfi.pflash01"); ++ return OBJECT_CHECK(PFlashCFI01, (dev), "cfi.pflash01"); + } + + static uint64_t translate_phys_addr(void *opaque, uint64_t addr) +@@ -224,7 +224,7 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) + CPUXtensaState *env = NULL; + MemoryRegion *system_io; + DriveInfo *dinfo; +- pflash_t *flash = NULL; ++ PFlashCFI01 *flash = NULL; + QemuOpts *machine_opts = qemu_get_machine_opts(); + const char *kernel_filename = qemu_opt_get(machine_opts, "kernel"); + const char *kernel_cmdline = qemu_opt_get(machine_opts, "append"); +diff --git a/include/hw/block/flash.h b/include/hw/block/flash.h +index 67c3aa3..51d8f60 100644 +--- a/include/hw/block/flash.h ++++ b/include/hw/block/flash.h +@@ -5,32 +5,41 @@ + + #include "exec/memory.h" + ++/* pflash_cfi01.c */ ++ + #define TYPE_CFI_PFLASH01 "cfi.pflash01" +-#define TYPE_CFI_PFLASH02 "cfi.pflash02" + +-typedef struct pflash_t pflash_t; ++typedef struct PFlashCFI01 PFlashCFI01; + +-/* pflash_cfi01.c */ +-pflash_t *pflash_cfi01_register(hwaddr base, +- DeviceState *qdev, const char *name, +- hwaddr size, +- BlockBackend *blk, +- uint32_t sector_len, int nb_blocs, int width, +- uint16_t id0, uint16_t id1, +- uint16_t id2, uint16_t id3, int be); ++PFlashCFI01 *pflash_cfi01_register(hwaddr base, ++ DeviceState *qdev, const char *name, ++ hwaddr size, ++ BlockBackend *blk, ++ uint32_t sector_len, int nb_blocs, ++ int width, ++ uint16_t id0, uint16_t id1, ++ uint16_t id2, uint16_t id3, ++ int be); ++MemoryRegion *pflash_cfi01_get_memory(PFlashCFI01 *fl); + + /* pflash_cfi02.c */ +-pflash_t *pflash_cfi02_register(hwaddr base, +- DeviceState *qdev, const char *name, +- hwaddr size, +- BlockBackend *blk, uint32_t sector_len, +- int nb_blocs, int nb_mappings, int width, +- uint16_t id0, uint16_t id1, +- uint16_t id2, uint16_t id3, +- uint16_t unlock_addr0, uint16_t unlock_addr1, +- int be); +- +-MemoryRegion *pflash_cfi01_get_memory(pflash_t *fl); ++ ++#define TYPE_CFI_PFLASH02 "cfi.pflash02" ++ ++typedef struct PFlashCFI02 PFlashCFI02; ++ ++PFlashCFI02 *pflash_cfi02_register(hwaddr base, ++ DeviceState *qdev, const char *name, ++ hwaddr size, ++ BlockBackend *blk, ++ uint32_t sector_len, int nb_blocs, ++ int nb_mappings, ++ int width, ++ uint16_t id0, uint16_t id1, ++ uint16_t id2, uint16_t id3, ++ uint16_t unlock_addr0, ++ uint16_t unlock_addr1, ++ int be); + + /* nand.c */ + DeviceState *nand_init(BlockBackend *blk, int manf_id, int chip_id); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pflash_cfi01-Add-pflash_cfi01_get_blk-helper.patch b/SOURCES/kvm-pflash_cfi01-Add-pflash_cfi01_get_blk-helper.patch new file mode 100644 index 0000000..8ff67be --- /dev/null +++ b/SOURCES/kvm-pflash_cfi01-Add-pflash_cfi01_get_blk-helper.patch @@ -0,0 +1,64 @@ +From 8486c0843637f5822dbfcc1eed561d640ff14fe9 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:14 +0200 +Subject: [PATCH 47/53] pflash_cfi01: Add pflash_cfi01_get_blk() helper +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-26-armbru@redhat.com> +Patchwork-id: 87983 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 25/31] pflash_cfi01: Add pflash_cfi01_get_blk() helper +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +From: Philippe Mathieu-Daudé + +Add an helper to access the opaque struct PFlashCFI01. + +Signed-off-by: Markus Armbruster +Signed-off-by: Philippe Mathieu-Daudé +Reviewed-by: Laszlo Ersek +Message-Id: <20190308131445.17502-9-armbru@redhat.com> +Reviewed-by: Michael S. Tsirkin +(cherry picked from commit e60cf76549a628d63f865fb6faeb1c7c0f390d0b) +Signed-off-by: Miroslav Rezanina +--- + hw/block/pflash_cfi01.c | 5 +++++ + include/hw/block/flash.h | 1 + + 2 files changed, 6 insertions(+) + +diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c +index 0101127..7a25051 100644 +--- a/hw/block/pflash_cfi01.c ++++ b/hw/block/pflash_cfi01.c +@@ -980,6 +980,11 @@ PFlashCFI01 *pflash_cfi01_register(hwaddr base, + return PFLASH_CFI01(dev); + } + ++BlockBackend *pflash_cfi01_get_blk(PFlashCFI01 *fl) ++{ ++ return fl->blk; ++} ++ + MemoryRegion *pflash_cfi01_get_memory(PFlashCFI01 *fl) + { + return &fl->mem; +diff --git a/include/hw/block/flash.h b/include/hw/block/flash.h +index 914932e..a0f4887 100644 +--- a/include/hw/block/flash.h ++++ b/include/hw/block/flash.h +@@ -22,6 +22,7 @@ PFlashCFI01 *pflash_cfi01_register(hwaddr base, + uint16_t id0, uint16_t id1, + uint16_t id2, uint16_t id3, + int be); ++BlockBackend *pflash_cfi01_get_blk(PFlashCFI01 *fl); + MemoryRegion *pflash_cfi01_get_memory(PFlashCFI01 *fl); + + /* pflash_cfi02.c */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pflash_cfi01-Do-not-exit-on-guest-aborting-write-to-.patch b/SOURCES/kvm-pflash_cfi01-Do-not-exit-on-guest-aborting-write-to-.patch new file mode 100644 index 0000000..87d4bc9 --- /dev/null +++ b/SOURCES/kvm-pflash_cfi01-Do-not-exit-on-guest-aborting-write-to-.patch @@ -0,0 +1,70 @@ +From f72f8a75f6b29f02a2226324901e35dbb07ae932 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:50:53 +0200 +Subject: [PATCH 26/53] pflash_cfi01: Do not exit() on guest aborting "write to + buffer" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-5-armbru@redhat.com> +Patchwork-id: 87986 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 04/31] pflash_cfi01: Do not exit() on guest aborting "write to buffer" +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +When a guest tries to abort "write to buffer" (command 0xE8), we print +"PFLASH: Possible BUG - Write block confirm", then exit(1). Letting +the guest terminate QEMU is not a good idea. Instead, LOG_UNIMP we +screwed up, then reset the device. + +Macro PFLASH_BUG() is now unused; delete it. + +Suggested-by: Philippe Mathieu-Daudé +Signed-off-by: Markus Armbruster +Reviewed-by: Philippe Mathieu-Daudé +Reviewed-by: Alex Bennée +Message-Id: <20190308094610.21210-3-armbru@redhat.com> +(cherry picked from commit 2d93bebf81520ccebeb9a3ea9bd051ce088854ea) +Signed-off-by: Miroslav Rezanina +--- + hw/block/pflash_cfi01.c | 13 +++++-------- + 1 file changed, 5 insertions(+), 8 deletions(-) + +diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c +index 389bc60..c565db1 100644 +--- a/hw/block/pflash_cfi01.c ++++ b/hw/block/pflash_cfi01.c +@@ -49,12 +49,6 @@ + #include "hw/sysbus.h" + #include "sysemu/sysemu.h" + +-#define PFLASH_BUG(fmt, ...) \ +-do { \ +- fprintf(stderr, "PFLASH: Possible BUG - " fmt, ## __VA_ARGS__); \ +- exit(1); \ +-} while(0) +- + /* #define PFLASH_DEBUG */ + #ifdef PFLASH_DEBUG + #define DPRINTF(fmt, ...) \ +@@ -636,8 +630,11 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, + pfl->wcycle = 0; + pfl->status |= 0x80; + } else { +- DPRINTF("%s: unknown command for \"write block\"\n", __func__); +- PFLASH_BUG("Write block confirm"); ++ qemu_log_mask(LOG_UNIMP, ++ "%s: Aborting write to buffer not implemented," ++ " the data is already written to storage!\n" ++ "Flash device reset into READ mode.\n", ++ __func__); + goto reset_flash; + } + break; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pflash_cfi01-Log-use-of-flawed-write-to-buffer.patch b/SOURCES/kvm-pflash_cfi01-Log-use-of-flawed-write-to-buffer.patch new file mode 100644 index 0000000..35c3aeb --- /dev/null +++ b/SOURCES/kvm-pflash_cfi01-Log-use-of-flawed-write-to-buffer.patch @@ -0,0 +1,86 @@ +From d35903b0e772c0cdcd3f685dbee81ffbd110a24a Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:50:54 +0200 +Subject: [PATCH 27/53] pflash_cfi01: Log use of flawed "write to buffer" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-6-armbru@redhat.com> +Patchwork-id: 87990 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 05/31] pflash_cfi01: Log use of flawed "write to buffer" +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +Our implementation of "write to buffer" (command 0xE8) is flawed. +LOG_UNIMP its use, and add some FIXME comments. + +Signed-off-by: Markus Armbruster +Reviewed-by: Philippe Mathieu-Daudé +Reviewed-by: Alex Bennée +Message-Id: <20190308094610.21210-4-armbru@redhat.com> +(cherry picked from commit 4dbda935e054600667f9e57095fa97e2ce5936f9) +Signed-off-by: Miroslav Rezanina +--- + hw/block/pflash_cfi01.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c +index c565db1..3a6bd10 100644 +--- a/hw/block/pflash_cfi01.c ++++ b/hw/block/pflash_cfi01.c +@@ -515,6 +515,10 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, + break; + case 0xe8: /* Write to buffer */ + DPRINTF("%s: Write to buffer\n", __func__); ++ /* FIXME should save @offset, @width for case 1+ */ ++ qemu_log_mask(LOG_UNIMP, ++ "%s: Write to buffer emulation is flawed\n", ++ __func__); + pfl->status |= 0x80; /* Ready! */ + break; + case 0xf0: /* Probe for AMD flash */ +@@ -558,6 +562,7 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, + /* Mask writeblock size based on device width, or bank width if + * device width not specified. + */ ++ /* FIXME check @offset, @width */ + if (pfl->device_width) { + value = extract32(value, 0, pfl->device_width * 8); + } else { +@@ -595,7 +600,13 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, + case 2: + switch (pfl->cmd) { + case 0xe8: /* Block write */ ++ /* FIXME check @offset, @width */ + if (!pfl->ro) { ++ /* ++ * FIXME writing straight to memory is *wrong*. We ++ * should write to a buffer, and flush it to memory ++ * only on confirm command (see below). ++ */ + pflash_data_write(pfl, offset, value, width, be); + } else { + pfl->status |= 0x10; /* Programming error */ +@@ -611,6 +622,7 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, + pfl->wcycle++; + if (!pfl->ro) { + /* Flush the entire write buffer onto backing storage. */ ++ /* FIXME premature! */ + pflash_update(pfl, offset & mask, pfl->writeblock_size); + } else { + pfl->status |= 0x10; /* Programming error */ +@@ -627,6 +639,7 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, + switch (pfl->cmd) { + case 0xe8: /* Block write */ + if (cmd == 0xd0) { ++ /* FIXME this is where we should write out the buffer */ + pfl->wcycle = 0; + pfl->status |= 0x80; + } else { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-postcopy-Synchronize-usage-of-the-balloon-inhibitor.patch b/SOURCES/kvm-postcopy-Synchronize-usage-of-the-balloon-inhibitor.patch new file mode 100644 index 0000000..2519940 --- /dev/null +++ b/SOURCES/kvm-postcopy-Synchronize-usage-of-the-balloon-inhibitor.patch @@ -0,0 +1,81 @@ +From 4c1361e2dfac092c94077a20610087871c7878f4 Mon Sep 17 00:00:00 2001 +From: Alex Williamson +Date: Mon, 3 Dec 2018 21:54:25 +0100 +Subject: [PATCH 24/34] postcopy: Synchronize usage of the balloon inhibitor + +RH-Author: Alex Williamson +Message-id: <154387406568.26945.10032109321234466443.stgit@gimli.home> +Patchwork-id: 83233 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 7/7] postcopy: Synchronize usage of the balloon inhibitor +Bugzilla: 1619778 +RH-Acked-by: Peter Xu +RH-Acked-by: Cornelia Huck +RH-Acked-by: Auger Eric +RH-Acked-by: David Hildenbrand + +Bugzilla: 1619778 + +While the qemu_balloon_inhibit() interface appears rather general purpose, +postcopy uses it in a last-caller-wins approach with no guarantee of balanced +inhibits and de-inhibits. Wrap postcopy's usage of the inhibitor to give it +one vote overall, using the same last-caller-wins approach as previously +implemented at the balloon level. + +Fixes: 01ccbec7bdf6 ("balloon: Allow multiple inhibit users") +Reported-by: Christian Borntraeger +Tested-by: Christian Borntraeger +Reviewed-by: Cornelia Huck +Reviewed-by: Juan Quintela +Signed-off-by: Alex Williamson +(cherry picked from commit 154304cd6e99e4222ed762976f9d9aca33c094d3) +Signed-off-by: Miroslav Rezanina +--- + migration/postcopy-ram.c | 18 ++++++++++++++++-- + 1 file changed, 16 insertions(+), 2 deletions(-) + +diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c +index 001b041..4b65ff9 100644 +--- a/migration/postcopy-ram.c ++++ b/migration/postcopy-ram.c +@@ -400,6 +400,20 @@ int postcopy_ram_incoming_init(MigrationIncomingState *mis, size_t ram_pages) + } + + /* ++ * Manage a single vote to the QEMU balloon inhibitor for all postcopy usage, ++ * last caller wins. ++ */ ++static void postcopy_balloon_inhibit(bool state) ++{ ++ static bool cur_state = false; ++ ++ if (state != cur_state) { ++ qemu_balloon_inhibit(state); ++ cur_state = state; ++ } ++} ++ ++/* + * At the end of a migration where postcopy_ram_incoming_init was called. + */ + int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) +@@ -429,7 +443,7 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) + mis->have_fault_thread = false; + } + +- qemu_balloon_inhibit(false); ++ postcopy_balloon_inhibit(false); + + if (enable_mlock) { + if (os_mlock() < 0) { +@@ -801,7 +815,7 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis) + * Ballooning can mark pages as absent while we're postcopying + * that would cause false userfaults. + */ +- qemu_balloon_inhibit(true); ++ postcopy_balloon_inhibit(true); + + trace_postcopy_ram_enable_notify(); + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-ppc-spapr_caps-Don-t-disable-cap_cfpc-on-POWER8-by-d.patch b/SOURCES/kvm-ppc-spapr_caps-Don-t-disable-cap_cfpc-on-POWER8-by-d.patch new file mode 100644 index 0000000..9ae3b8a --- /dev/null +++ b/SOURCES/kvm-ppc-spapr_caps-Don-t-disable-cap_cfpc-on-POWER8-by-d.patch @@ -0,0 +1,60 @@ +From 003ad494e12c03291a61039302eb766372929130 Mon Sep 17 00:00:00 2001 +From: Suraj Jitindar Singh +Date: Thu, 21 Jun 2018 06:56:49 +0200 +Subject: [PATCH 54/54] ppc/spapr_caps: Don't disable cap_cfpc on POWER8 by + default + +RH-Author: Suraj Jitindar Singh +Message-id: <1529564209-30369-4-git-send-email-sursingh@redhat.com> +Patchwork-id: 80929 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 3/3] ppc/spapr_caps: Don't disable cap_cfpc on POWER8 by default +Bugzilla: 1560847 +RH-Acked-by: Laurent Vivier +RH-Acked-by: David Gibson +RH-Acked-by: Miroslav Rezanina + +From: Suraj Jitindar Singh + +In default_caps_with_cpu() we set spapr_cap_cfpc to broken for POWER8 +processors and before. + +Since we no longer require private l1d cache on POWER8 for this cap to +be set to workaround change this to default to broken for POWER7 +processors and before. + +Signed-off-by: Suraj Jitindar Singh +Reviewed-by: David Gibson +Signed-off-by: David Gibson +(cherry picked from commit b2540203bdf4a390c3489146eae82ce237303653) + +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1560847 + +Signed-off-by: Suraj Jitindar Singh +Signed-off-by: Miroslav Rezanina +--- + hw/ppc/spapr_caps.c | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c +index 531e145..00e43a9 100644 +--- a/hw/ppc/spapr_caps.c ++++ b/hw/ppc/spapr_caps.c +@@ -335,14 +335,10 @@ static sPAPRCapabilities default_caps_with_cpu(sPAPRMachineState *spapr, + + caps = smc->default_caps; + +- if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_00, +- 0, spapr->max_compat_pvr)) { +- caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_BROKEN; +- } +- + if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_07, + 0, spapr->max_compat_pvr)) { + caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_OFF; ++ caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_BROKEN; + } + + if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_06_PLUS, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-ppc405_boards-Delete-stale-disabled-DEBUG_BOARD_INIT.patch b/SOURCES/kvm-ppc405_boards-Delete-stale-disabled-DEBUG_BOARD_INIT.patch new file mode 100644 index 0000000..7443f66 --- /dev/null +++ b/SOURCES/kvm-ppc405_boards-Delete-stale-disabled-DEBUG_BOARD_INIT.patch @@ -0,0 +1,208 @@ +From 9844bbde0f1586645b1d8b148452270984ae85e5 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:50:58 +0200 +Subject: [PATCH 31/53] ppc405_boards: Delete stale, disabled DEBUG_BOARD_INIT + code +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-10-armbru@redhat.com> +Patchwork-id: 88005 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 09/31] ppc405_boards: Delete stale, disabled DEBUG_BOARD_INIT code +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +The disabled DEBUG_BOARD_INIT code goes back to the initial commit +1a6c0886203, and has since seen only mechanical updates. It sure +feels like useless clutter now. Delete it. + +Suggested-by: Alex Bennée +Signed-off-by: Markus Armbruster +Message-Id: <20190308094610.21210-8-armbru@redhat.com> +Reviewed-by: Alex Bennée +(cherry picked from commit 886db7c55c72246e3810628d735f5e25eceb8c45) +[Trivial conflict in hw/ppc/ppc405_boards.c due to lack of +commit ab3dd749241] + +Signed-off-by: Miroslav Rezanina +--- + hw/ppc/ppc405_boards.c | 62 +------------------------------------------------- + 1 file changed, 1 insertion(+), 61 deletions(-) + +diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c +index 0b65893..2c23e8b 100644 +--- a/hw/ppc/ppc405_boards.c ++++ b/hw/ppc/ppc405_boards.c +@@ -48,8 +48,6 @@ + + #define USE_FLASH_BIOS + +-//#define DEBUG_BOARD_INIT +- + /*****************************************************************************/ + /* PPC405EP reference board (IBM) */ + /* Standalone board with: +@@ -217,10 +215,7 @@ static void ref405ep_init(MachineState *machine) + memory_region_init(&ram_memories[1], NULL, "ef405ep.ram1", 0); + ram_bases[1] = 0x00000000; + ram_sizes[1] = 0x00000000; +- ram_size = 128 * 1024 * 1024; +-#ifdef DEBUG_BOARD_INIT +- printf("%s: register cpu\n", __func__); +-#endif ++ ram_size = 128 * MiB; + env = ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes, + 33333333, &pic, kernel_filename == NULL ? 0 : 1); + /* allocate SRAM */ +@@ -229,9 +224,6 @@ static void ref405ep_init(MachineState *machine) + &error_fatal); + memory_region_add_subregion(sysmem, 0xFFF00000, sram); + /* allocate and load BIOS */ +-#ifdef DEBUG_BOARD_INIT +- printf("%s: register BIOS\n", __func__); +-#endif + fl_idx = 0; + #ifdef USE_FLASH_BIOS + dinfo = drive_get(IF_PFLASH, 0, fl_idx); +@@ -240,12 +232,6 @@ static void ref405ep_init(MachineState *machine) + + bios_size = blk_getlength(blk); + fl_sectors = (bios_size + 65535) >> 16; +-#ifdef DEBUG_BOARD_INIT +- printf("Register parallel flash %d size %lx" +- " at addr %lx '%s' %d\n", +- fl_idx, bios_size, -bios_size, +- blk_name(blk), fl_sectors); +-#endif + pflash_cfi02_register((uint32_t)(-bios_size), + NULL, "ef405ep.bios", bios_size, + blk, 65536, fl_sectors, 1, +@@ -255,9 +241,6 @@ static void ref405ep_init(MachineState *machine) + } else + #endif + { +-#ifdef DEBUG_BOARD_INIT +- printf("Load BIOS from file\n"); +-#endif + bios = g_new(MemoryRegion, 1); + memory_region_init_ram(bios, NULL, "ef405ep.bios", BIOS_SIZE, + &error_fatal); +@@ -284,21 +267,12 @@ static void ref405ep_init(MachineState *machine) + memory_region_set_readonly(bios, true); + } + /* Register FPGA */ +-#ifdef DEBUG_BOARD_INIT +- printf("%s: register FPGA\n", __func__); +-#endif + ref405ep_fpga_init(sysmem, 0xF0300000); + /* Register NVRAM */ +-#ifdef DEBUG_BOARD_INIT +- printf("%s: register NVRAM\n", __func__); +-#endif + m48t59_init(NULL, 0xF0000000, 0, 8192, 1968, 8); + /* Load kernel */ + linux_boot = (kernel_filename != NULL); + if (linux_boot) { +-#ifdef DEBUG_BOARD_INIT +- printf("%s: load kernel\n", __func__); +-#endif + memset(&bd, 0, sizeof(bd)); + bd.bi_memstart = 0x00000000; + bd.bi_memsize = ram_size; +@@ -370,10 +344,6 @@ static void ref405ep_init(MachineState *machine) + initrd_size = 0; + bdloc = 0; + } +-#ifdef DEBUG_BOARD_INIT +- printf("bdloc " RAM_ADDR_FMT "\n", bdloc); +- printf("%s: Done\n", __func__); +-#endif + } + + static void ref405ep_class_init(ObjectClass *oc, void *data) +@@ -525,15 +495,9 @@ static void taihu_405ep_init(MachineState *machine) + memory_region_init_alias(&ram_memories[1], NULL, + "taihu_405ep.ram-1", ram, ram_bases[1], + ram_sizes[1]); +-#ifdef DEBUG_BOARD_INIT +- printf("%s: register cpu\n", __func__); +-#endif + ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes, + 33333333, &pic, kernel_filename == NULL ? 0 : 1); + /* allocate and load BIOS */ +-#ifdef DEBUG_BOARD_INIT +- printf("%s: register BIOS\n", __func__); +-#endif + fl_idx = 0; + #if defined(USE_FLASH_BIOS) + dinfo = drive_get(IF_PFLASH, 0, fl_idx); +@@ -544,12 +508,6 @@ static void taihu_405ep_init(MachineState *machine) + /* XXX: should check that size is 2MB */ + // bios_size = 2 * 1024 * 1024; + fl_sectors = (bios_size + 65535) >> 16; +-#ifdef DEBUG_BOARD_INIT +- printf("Register parallel flash %d size %lx" +- " at addr %lx '%s' %d\n", +- fl_idx, bios_size, -bios_size, +- blk_name(blk), fl_sectors); +-#endif + pflash_cfi02_register((uint32_t)(-bios_size), + NULL, "taihu_405ep.bios", bios_size, + blk, 65536, fl_sectors, 1, +@@ -559,9 +517,6 @@ static void taihu_405ep_init(MachineState *machine) + } else + #endif + { +-#ifdef DEBUG_BOARD_INIT +- printf("Load BIOS from file\n"); +-#endif + if (bios_name == NULL) + bios_name = BIOS_FILENAME; + bios = g_new(MemoryRegion, 1); +@@ -592,12 +547,6 @@ static void taihu_405ep_init(MachineState *machine) + /* XXX: should check that size is 32MB */ + bios_size = 32 * 1024 * 1024; + fl_sectors = (bios_size + 65535) >> 16; +-#ifdef DEBUG_BOARD_INIT +- printf("Register parallel flash %d size %lx" +- " at addr " TARGET_FMT_lx " '%s'\n", +- fl_idx, bios_size, (target_ulong)0xfc000000, +- blk_name(blk)); +-#endif + pflash_cfi02_register(0xfc000000, NULL, "taihu_405ep.flash", bios_size, + blk, 65536, fl_sectors, 1, + 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, +@@ -605,16 +554,10 @@ static void taihu_405ep_init(MachineState *machine) + fl_idx++; + } + /* Register CLPD & LCD display */ +-#ifdef DEBUG_BOARD_INIT +- printf("%s: register CPLD\n", __func__); +-#endif + taihu_cpld_init(sysmem, 0x50100000); + /* Load kernel */ + linux_boot = (kernel_filename != NULL); + if (linux_boot) { +-#ifdef DEBUG_BOARD_INIT +- printf("%s: load kernel\n", __func__); +-#endif + kernel_base = KERNEL_LOAD_ADDR; + /* now we can load the kernel */ + kernel_size = load_image_targphys(kernel_filename, kernel_base, +@@ -643,9 +586,6 @@ static void taihu_405ep_init(MachineState *machine) + initrd_base = 0; + initrd_size = 0; + } +-#ifdef DEBUG_BOARD_INIT +- printf("%s: Done\n", __func__); +-#endif + } + + static void taihu_class_init(ObjectClass *oc, void *data) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-ppc405_boards-Don-t-size-flash-memory-to-match-backi.patch b/SOURCES/kvm-ppc405_boards-Don-t-size-flash-memory-to-match-backi.patch new file mode 100644 index 0000000..842d7b9 --- /dev/null +++ b/SOURCES/kvm-ppc405_boards-Don-t-size-flash-memory-to-match-backi.patch @@ -0,0 +1,145 @@ +From fc257e52fc251e4db6cd7ad604391941a2592556 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:50:59 +0200 +Subject: [PATCH 32/53] ppc405_boards: Don't size flash memory to match backing + image +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-11-armbru@redhat.com> +Patchwork-id: 88004 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 10/31] ppc405_boards: Don't size flash memory to match backing image +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +Machine "ref405ep" maps its flash memory at address 2^32 - image size. +Image size is rounded up to the next multiple of 64KiB. Useless, +because pflash_cfi02_realize() fails with "failed to read the initial +flash content" unless the rounding is a no-op. + +If the image size exceeds 0x80000 Bytes, we overlap first SRAM, then +other stuff. No idea how that would play out, but useful outcomes +seem unlikely. + +Map the flash memory at fixed address 0xFFF80000 with size 512KiB, +regardless of image size, to match the physical hardware. + +Machine "taihu" maps its boot flash memory similarly. The code even +has a comment /* XXX: should check that size is 2MB */, followed by +disabled code to adjust the size to 2MiB regardless of image size. + +Its code to map its application flash memory looks the same, except +there the XXX comment asks for 32MiB, and the code to adjust the size +isn't disabled. Note that pflash_cfi02_realize() fails with "failed +to read the initial flash content" for images smaller than 32MiB. + +Map the boot flash memory at fixed address 0xFFE00000 with size 2MiB, +to match the physical hardware. Delete dead code from application +flash mapping, and simplify some. + +Cc: David Gibson +Signed-off-by: Markus Armbruster +Acked-by: David Gibson +Reviewed-by: Alex Bennée +Message-Id: <20190308094610.21210-9-armbru@redhat.com> +(cherry picked from commit dd59bcae7687df4b2ba8e5292607724996e00892) +[Trivial conflict in hw/ppc/ppc405_boards.c due to lack of +commit ab3dd749241] + +Signed-off-by: Miroslav Rezanina +--- + hw/ppc/ppc405_boards.c | 38 +++++++++++++------------------------- + 1 file changed, 13 insertions(+), 25 deletions(-) + +diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c +index 2c23e8b..bf32672 100644 +--- a/hw/ppc/ppc405_boards.c ++++ b/hw/ppc/ppc405_boards.c +@@ -196,7 +196,7 @@ static void ref405ep_init(MachineState *machine) + target_ulong kernel_base, initrd_base; + long kernel_size, initrd_size; + int linux_boot; +- int fl_idx, fl_sectors, len; ++ int len; + DriveInfo *dinfo; + MemoryRegion *sysmem = get_system_memory(); + +@@ -224,20 +224,16 @@ static void ref405ep_init(MachineState *machine) + &error_fatal); + memory_region_add_subregion(sysmem, 0xFFF00000, sram); + /* allocate and load BIOS */ +- fl_idx = 0; + #ifdef USE_FLASH_BIOS +- dinfo = drive_get(IF_PFLASH, 0, fl_idx); ++ dinfo = drive_get(IF_PFLASH, 0, 0); + if (dinfo) { +- BlockBackend *blk = blk_by_legacy_dinfo(dinfo); +- +- bios_size = blk_getlength(blk); +- fl_sectors = (bios_size + 65535) >> 16; ++ bios_size = 8 * MiB; + pflash_cfi02_register((uint32_t)(-bios_size), + NULL, "ef405ep.bios", bios_size, +- blk, 65536, fl_sectors, 1, ++ dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, ++ 64 * KiB, bios_size / (64 * KiB), 1, + 2, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, + 1); +- fl_idx++; + } else + #endif + { +@@ -470,7 +466,7 @@ static void taihu_405ep_init(MachineState *machine) + target_ulong kernel_base, initrd_base; + long kernel_size, initrd_size; + int linux_boot; +- int fl_idx, fl_sectors; ++ int fl_idx; + DriveInfo *dinfo; + + #ifdef TARGET_PPCEMB +@@ -502,15 +498,11 @@ static void taihu_405ep_init(MachineState *machine) + #if defined(USE_FLASH_BIOS) + dinfo = drive_get(IF_PFLASH, 0, fl_idx); + if (dinfo) { +- BlockBackend *blk = blk_by_legacy_dinfo(dinfo); +- +- bios_size = blk_getlength(blk); +- /* XXX: should check that size is 2MB */ +- // bios_size = 2 * 1024 * 1024; +- fl_sectors = (bios_size + 65535) >> 16; +- pflash_cfi02_register((uint32_t)(-bios_size), ++ bios_size = 2 * MiB; ++ pflash_cfi02_register(0xFFE00000, + NULL, "taihu_405ep.bios", bios_size, +- blk, 65536, fl_sectors, 1, ++ dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, ++ 64 * KiB, bios_size / (64 * KiB), 1, + 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, + 1); + fl_idx++; +@@ -541,14 +533,10 @@ static void taihu_405ep_init(MachineState *machine) + /* Register Linux flash */ + dinfo = drive_get(IF_PFLASH, 0, fl_idx); + if (dinfo) { +- BlockBackend *blk = blk_by_legacy_dinfo(dinfo); +- +- bios_size = blk_getlength(blk); +- /* XXX: should check that size is 32MB */ +- bios_size = 32 * 1024 * 1024; +- fl_sectors = (bios_size + 65535) >> 16; ++ bios_size = 32 * MiB; + pflash_cfi02_register(0xfc000000, NULL, "taihu_405ep.flash", bios_size, +- blk, 65536, fl_sectors, 1, ++ dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, ++ 64 * KiB, bios_size / (64 * KiB), 1, + 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, + 1); + fl_idx++; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pr-helper-Rework-socket-path-handling.patch b/SOURCES/kvm-pr-helper-Rework-socket-path-handling.patch new file mode 100644 index 0000000..0245a99 --- /dev/null +++ b/SOURCES/kvm-pr-helper-Rework-socket-path-handling.patch @@ -0,0 +1,141 @@ +From 5b8df1e97b5b90c9d41fd10796217bfe06951856 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Fri, 6 Jul 2018 17:56:58 +0200 +Subject: [PATCH 24/89] pr-helper: Rework socket path handling + +RH-Author: Paolo Bonzini +Message-id: <20180706175659.30615-9-pbonzini@redhat.com> +Patchwork-id: 81252 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 8/9] pr-helper: Rework socket path handling +Bugzilla: 1533158 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Michal Privoznik + +From: Michal Privoznik + +When reviewing Paolo's pr-helper patches I've noticed couple of +problems: + +1) socket_path needs to be calculated at two different places +(one for printing out help, the other if socket activation is NOT +used), + +2) even though the default socket_path is allocated in +compute_default_paths() it is the only default path the function +handles. For instance, pidfile is allocated outside of this +function. And yet again, at different places than 1) + +Signed-off-by: Michal Privoznik +Message-Id: +Signed-off-by: Paolo Bonzini +(cherry picked from commit 2729d79d4993099782002c9a218de1fc12c32c69) +Signed-off-by: Miroslav Rezanina +--- + scsi/qemu-pr-helper.c | 36 ++++++++++-------------------------- + 1 file changed, 10 insertions(+), 26 deletions(-) + +diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c +index 4d843bd..bae3e55 100644 +--- a/scsi/qemu-pr-helper.c ++++ b/scsi/qemu-pr-helper.c +@@ -76,14 +76,12 @@ static int gid = -1; + + static void compute_default_paths(void) + { +- if (!socket_path) { +- socket_path = qemu_get_local_state_pathname("run/qemu-pr-helper.sock"); +- } ++ socket_path = qemu_get_local_state_pathname("run/qemu-pr-helper.sock"); ++ pidfile = qemu_get_local_state_pathname("run/qemu-pr-helper.pid"); + } + + static void usage(const char *name) + { +- compute_default_paths(); + (printf) ( + "Usage: %s [OPTIONS] FILE\n" + "Persistent Reservation helper program for QEMU\n" +@@ -830,19 +828,6 @@ static gboolean accept_client(QIOChannel *ioc, GIOCondition cond, gpointer opaqu + return TRUE; + } + +- +-/* +- * Check socket parameters compatibility when socket activation is used. +- */ +-static const char *socket_activation_validate_opts(void) +-{ +- if (socket_path != NULL) { +- return "Unix socket can't be set when using socket activation"; +- } +- +- return NULL; +-} +- + static void termsig_handler(int signum) + { + atomic_cmpxchg(&state, RUNNING, TERMINATE); +@@ -916,6 +901,7 @@ int main(int argc, char **argv) + char *trace_file = NULL; + bool daemonize = false; + bool pidfile_specified = false; ++ bool socket_path_specified = false; + unsigned socket_activation; + + struct sigaction sa_sigterm; +@@ -932,12 +918,14 @@ int main(int argc, char **argv) + qemu_add_opts(&qemu_trace_opts); + qemu_init_exec_dir(argv[0]); + +- pidfile = qemu_get_local_state_pathname("run/qemu-pr-helper.pid"); ++ compute_default_paths(); + + while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { + switch (ch) { + case 'k': +- socket_path = optarg; ++ g_free(socket_path); ++ socket_path = g_strdup(optarg); ++ socket_path_specified = true; + if (socket_path[0] != '/') { + error_report("socket path must be absolute"); + exit(EXIT_FAILURE); +@@ -1028,10 +1016,9 @@ int main(int argc, char **argv) + socket_activation = check_socket_activation(); + if (socket_activation == 0) { + SocketAddress saddr; +- compute_default_paths(); + saddr = (SocketAddress){ + .type = SOCKET_ADDRESS_TYPE_UNIX, +- .u.q_unix.path = g_strdup(socket_path) ++ .u.q_unix.path = socket_path, + }; + server_ioc = qio_channel_socket_new(); + if (qio_channel_socket_listen_sync(server_ioc, &saddr, &local_err) < 0) { +@@ -1039,12 +1026,10 @@ int main(int argc, char **argv) + error_report_err(local_err); + return 1; + } +- g_free(saddr.u.q_unix.path); + } else { + /* Using socket activation - check user didn't use -p etc. */ +- const char *err_msg = socket_activation_validate_opts(); +- if (err_msg != NULL) { +- error_report("%s", err_msg); ++ if (socket_path_specified) { ++ error_report("Unix socket can't be set when using socket activation"); + exit(EXIT_FAILURE); + } + +@@ -1061,7 +1046,6 @@ int main(int argc, char **argv) + error_get_pretty(local_err)); + exit(EXIT_FAILURE); + } +- socket_path = NULL; + } + + if (qemu_init_main_loop(&local_err)) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pr-helper-avoid-error-on-PR-IN-command-with-zero-req.patch b/SOURCES/kvm-pr-helper-avoid-error-on-PR-IN-command-with-zero-req.patch new file mode 100644 index 0000000..261f115 --- /dev/null +++ b/SOURCES/kvm-pr-helper-avoid-error-on-PR-IN-command-with-zero-req.patch @@ -0,0 +1,139 @@ +From 502aa0cb17507c7af0cca61aeb5ff17b65c7afc7 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Fri, 6 Jul 2018 17:56:57 +0200 +Subject: [PATCH 23/89] pr-helper: avoid error on PR IN command with zero + request size + +RH-Author: Paolo Bonzini +Message-id: <20180706175659.30615-8-pbonzini@redhat.com> +Patchwork-id: 81250 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 7/9] pr-helper: avoid error on PR IN command with zero request size +Bugzilla: 1533158 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Michal Privoznik + +After reading a PR IN command with zero request size in prh_read_request, +the resp->result field will be uninitialized and the resp.sz field will +be also uninitialized when returning to prh_co_entry. + +If resp->result == GOOD (from a previous successful reply or just luck), +then the assert in prh_write_response might not be triggered and +uninitialized response will be sent. + +The fix is to remove the whole handling of sz == 0 in prh_co_entry. +Those errors apply only to PR OUT commands and it's perfectly okay to +catch them later in do_pr_out and multipath_pr_out; the check for +too-short parameters in fact doesn't apply in the easy SG_IO case, as +it can be left to the target firmware even. + +The result is that prh_read_request does not fail requests anymore and +prh_co_entry becomes simpler. + +Reported-by: Dima Stepanov +Signed-off-by: Paolo Bonzini +(cherry picked from commit ee8c13b81474e002db083e9692b11c0e106a9c7f) +Signed-off-by: Miroslav Rezanina +--- + scsi/qemu-pr-helper.c | 63 ++++++++++++++++++++++++--------------------------- + 1 file changed, 30 insertions(+), 33 deletions(-) + +diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c +index 3961023..4d843bd 100644 +--- a/scsi/qemu-pr-helper.c ++++ b/scsi/qemu-pr-helper.c +@@ -444,6 +444,14 @@ static int multipath_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, + char transportids[PR_HELPER_DATA_SIZE]; + int r; + ++ if (sz < PR_OUT_FIXED_PARAM_SIZE) { ++ /* Illegal request, Parameter list length error. This isn't fatal; ++ * we have read the data, send an error without closing the socket. ++ */ ++ scsi_build_sense(sense, SENSE_CODE(INVALID_PARAM_LEN)); ++ return CHECK_CONDITION; ++ } ++ + switch (rq_servact) { + case MPATH_PROUT_REG_SA: + case MPATH_PROUT_RES_SA: +@@ -563,6 +571,12 @@ static int do_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, + const uint8_t *param, int sz) + { + int resp_sz; ++ ++ if ((fcntl(fd, F_GETFL) & O_ACCMODE) == O_RDONLY) { ++ scsi_build_sense(sense, SENSE_CODE(INVALID_OPCODE)); ++ return CHECK_CONDITION; ++ } ++ + #ifdef CONFIG_MPATH + if (is_mpath(fd)) { + return multipath_pr_out(fd, cdb, sense, param, sz); +@@ -679,21 +693,6 @@ static int coroutine_fn prh_read_request(PRHelperClient *client, + errp) < 0) { + goto out_close; + } +- if ((fcntl(client->fd, F_GETFL) & O_ACCMODE) == O_RDONLY) { +- scsi_build_sense(resp->sense, SENSE_CODE(INVALID_OPCODE)); +- sz = 0; +- } else if (sz < PR_OUT_FIXED_PARAM_SIZE) { +- /* Illegal request, Parameter list length error. This isn't fatal; +- * we have read the data, send an error without closing the socket. +- */ +- scsi_build_sense(resp->sense, SENSE_CODE(INVALID_PARAM_LEN)); +- sz = 0; +- } +- if (sz == 0) { +- resp->result = CHECK_CONDITION; +- close(client->fd); +- client->fd = -1; +- } + } + + req->fd = client->fd; +@@ -774,25 +773,23 @@ static void coroutine_fn prh_co_entry(void *opaque) + break; + } + +- if (sz > 0) { +- num_active_sockets++; +- if (req.cdb[0] == PERSISTENT_RESERVE_OUT) { +- r = do_pr_out(req.fd, req.cdb, resp.sense, +- client->data, sz); +- resp.sz = 0; +- } else { +- resp.sz = sizeof(client->data); +- r = do_pr_in(req.fd, req.cdb, resp.sense, +- client->data, &resp.sz); +- resp.sz = MIN(resp.sz, sz); +- } +- num_active_sockets--; +- close(req.fd); +- if (r == -1) { +- break; +- } +- resp.result = r; ++ num_active_sockets++; ++ if (req.cdb[0] == PERSISTENT_RESERVE_OUT) { ++ r = do_pr_out(req.fd, req.cdb, resp.sense, ++ client->data, sz); ++ resp.sz = 0; ++ } else { ++ resp.sz = sizeof(client->data); ++ r = do_pr_in(req.fd, req.cdb, resp.sense, ++ client->data, &resp.sz); ++ resp.sz = MIN(resp.sz, sz); ++ } ++ num_active_sockets--; ++ close(req.fd); ++ if (r == -1) { ++ break; + } ++ resp.result = r; + + if (prh_write_response(client, &req, &resp, &local_err) < 0) { + break; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pr-helper-fix-assertion-failure-on-failed-multipath-.patch b/SOURCES/kvm-pr-helper-fix-assertion-failure-on-failed-multipath-.patch new file mode 100644 index 0000000..c0888ab --- /dev/null +++ b/SOURCES/kvm-pr-helper-fix-assertion-failure-on-failed-multipath-.patch @@ -0,0 +1,69 @@ +From ffe4cdcc9b82668410476d565961821515dde5b1 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Fri, 6 Jul 2018 17:56:52 +0200 +Subject: [PATCH 18/89] pr-helper: fix assertion failure on failed multipath + PERSISTENT RESERVE IN +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Paolo Bonzini +Message-id: <20180706175659.30615-3-pbonzini@redhat.com> +Patchwork-id: 81246 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 2/9] pr-helper: fix assertion failure on failed multipath PERSISTENT RESERVE IN +Bugzilla: 1533158 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Michal Privoznik + +The response size is expected to be zero if the SCSI status is not +"GOOD", but nothing was resetting it. + +This can be reproduced simply by "sg_persist -s /dev/sdb" where /dev/sdb +in the guest is a scsi-block device corresponding to a multipath device +on the host. + +Before: + + PR in (Read full status): Aborted command + +and on the host: + + prh_write_response: Assertion `resp->sz == 0' failed. + +After: + + PR in (Read full status): bad field in cdb or parameter list + (perhaps unsupported service action) + +Reported-by: Jiri Belka +Reviewed-by: Michal Privoznik +Signed-off-by: Paolo Bonzini +Reviewed-by: Paolo Bonzini +Reviewed-by: Philippe Mathieu-Daudé +(cherry picked from commit 86933b4e7879e427e03365bf352c0964640cb37b) +Signed-off-by: Miroslav Rezanina +--- + scsi/qemu-pr-helper.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c +index 19887f5..3961023 100644 +--- a/scsi/qemu-pr-helper.c ++++ b/scsi/qemu-pr-helper.c +@@ -547,7 +547,11 @@ static int do_pr_in(int fd, const uint8_t *cdb, uint8_t *sense, + #ifdef CONFIG_MPATH + if (is_mpath(fd)) { + /* multipath_pr_in fills the whole input buffer. */ +- return multipath_pr_in(fd, cdb, sense, data, *resp_sz); ++ int r = multipath_pr_in(fd, cdb, sense, data, *resp_sz); ++ if (r != GOOD) { ++ *resp_sz = 0; ++ } ++ return r; + } + #endif + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pr-helper-fix-socket-path-default-in-help.patch b/SOURCES/kvm-pr-helper-fix-socket-path-default-in-help.patch new file mode 100644 index 0000000..dca2948 --- /dev/null +++ b/SOURCES/kvm-pr-helper-fix-socket-path-default-in-help.patch @@ -0,0 +1,66 @@ +From ba4e622bc5d07daba8a63589124c944d6105e054 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Fri, 6 Jul 2018 17:56:51 +0200 +Subject: [PATCH 17/89] pr-helper: fix --socket-path default in help +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Paolo Bonzini +Message-id: <20180706175659.30615-2-pbonzini@redhat.com> +Patchwork-id: 81244 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 1/9] pr-helper: fix --socket-path default in help +Bugzilla: 1533158 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Michal Privoznik + +Currently --help shows "(default '(null)')" for the -k/--socket-path +option. Fix it by getting the default path in /var/run. + +Signed-off-by: Paolo Bonzini +Reviewed-by: Philippe Mathieu-Daudé +(cherry picked from commit 50fa332516d5e42695811f43396b749185e21b9c) +Signed-off-by: Miroslav Rezanina +--- + scsi/qemu-pr-helper.c | 15 ++++++++------- + 1 file changed, 8 insertions(+), 7 deletions(-) + +diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c +index 7a29e64..19887f5 100644 +--- a/scsi/qemu-pr-helper.c ++++ b/scsi/qemu-pr-helper.c +@@ -74,8 +74,16 @@ static int uid = -1; + static int gid = -1; + #endif + ++static void compute_default_paths(void) ++{ ++ if (!socket_path) { ++ socket_path = qemu_get_local_state_pathname("run/qemu-pr-helper.sock"); ++ } ++} ++ + static void usage(const char *name) + { ++ compute_default_paths(); + (printf) ( + "Usage: %s [OPTIONS] FILE\n" + "Persistent Reservation helper program for QEMU\n" +@@ -834,13 +842,6 @@ static const char *socket_activation_validate_opts(void) + return NULL; + } + +-static void compute_default_paths(void) +-{ +- if (!socket_path) { +- socket_path = qemu_get_local_state_pathname("run/qemu-pr-helper.sock"); +- } +-} +- + static void termsig_handler(int signum) + { + atomic_cmpxchg(&state, RUNNING, TERMINATE); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pr-manager-add-query-pr-managers-QMP-command.patch b/SOURCES/kvm-pr-manager-add-query-pr-managers-QMP-command.patch new file mode 100644 index 0000000..3f06e19 --- /dev/null +++ b/SOURCES/kvm-pr-manager-add-query-pr-managers-QMP-command.patch @@ -0,0 +1,201 @@ +From 91fa5e18d2ac6e376901a71f3f44343761fde012 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Fri, 6 Jul 2018 17:56:55 +0200 +Subject: [PATCH 21/89] pr-manager: add query-pr-managers QMP command + +RH-Author: Paolo Bonzini +Message-id: <20180706175659.30615-6-pbonzini@redhat.com> +Patchwork-id: 81251 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 5/9] pr-manager: add query-pr-managers QMP command +Bugzilla: 1533158 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Michal Privoznik + +This command lets you query the connection status of each pr-manager-helper +object. + +Signed-off-by: Paolo Bonzini +(cherry picked from commit 5f64089416f0d77c87683401838f064c51a292ed) + +[RHEL: no allow-preconfig yet, so don't specify it] + +Signed-off-by: Miroslav Rezanina +--- + include/scsi/pr-manager.h | 2 ++ + qapi/block.json | 27 +++++++++++++++++++++++++++ + scsi/pr-manager-helper.c | 13 +++++++++++++ + scsi/pr-manager-stub.c | 6 ++++++ + scsi/pr-manager.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ + 5 files changed, 93 insertions(+) + +diff --git a/include/scsi/pr-manager.h b/include/scsi/pr-manager.h +index 71971ae..50a77b0 100644 +--- a/include/scsi/pr-manager.h ++++ b/include/scsi/pr-manager.h +@@ -33,8 +33,10 @@ typedef struct PRManagerClass { + + /* */ + int (*run)(PRManager *pr_mgr, int fd, struct sg_io_hdr *hdr); ++ bool (*is_connected)(PRManager *pr_mgr); + } PRManagerClass; + ++bool pr_manager_is_connected(PRManager *pr_mgr); + BlockAIOCB *pr_manager_execute(PRManager *pr_mgr, + AioContext *ctx, int fd, + struct sg_io_hdr *hdr, +diff --git a/qapi/block.json b/qapi/block.json +index 48c732f..c636354 100644 +--- a/qapi/block.json ++++ b/qapi/block.json +@@ -78,6 +78,33 @@ + 'data': { 'device': 'str', 'name': 'str' } } + + ## ++# @PRManagerInfo: ++# ++# Information about a persistent reservation manager ++# ++# @id: the identifier of the persistent reservation manager ++# ++# @connected: true if the persistent reservation manager is connected to ++# the underlying storage or helper ++# ++# Since: 3.0 ++## ++{ 'struct': 'PRManagerInfo', ++ 'data': {'id': 'str', 'connected': 'bool'} } ++ ++## ++# @query-pr-managers: ++# ++# Returns a list of information about each persistent reservation manager. ++# ++# Returns: a list of @PRManagerInfo for each persistent reservation manager ++# ++# Since: 3.0 ++## ++{ 'command': 'query-pr-managers', 'returns': ['PRManagerInfo'] } ++ ++ ++## + # @blockdev-snapshot-internal-sync: + # + # Synchronously take an internal snapshot of a block device, when the +diff --git a/scsi/pr-manager-helper.c b/scsi/pr-manager-helper.c +index 0c0fe38..b11481b 100644 +--- a/scsi/pr-manager-helper.c ++++ b/scsi/pr-manager-helper.c +@@ -235,6 +235,18 @@ out: + return ret; + } + ++static bool pr_manager_helper_is_connected(PRManager *p) ++{ ++ PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(p); ++ bool result; ++ ++ qemu_mutex_lock(&pr_mgr->lock); ++ result = (pr_mgr->ioc != NULL); ++ qemu_mutex_unlock(&pr_mgr->lock); ++ ++ return result; ++} ++ + static void pr_manager_helper_complete(UserCreatable *uc, Error **errp) + { + PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(uc); +@@ -284,6 +296,7 @@ static void pr_manager_helper_class_init(ObjectClass *klass, + &error_abort); + uc_klass->complete = pr_manager_helper_complete; + prmgr_klass->run = pr_manager_helper_run; ++ prmgr_klass->is_connected = pr_manager_helper_is_connected; + } + + static const TypeInfo pr_manager_helper_info = { +diff --git a/scsi/pr-manager-stub.c b/scsi/pr-manager-stub.c +index 632f17c..738b6d7 100644 +--- a/scsi/pr-manager-stub.c ++++ b/scsi/pr-manager-stub.c +@@ -22,3 +22,9 @@ PRManager *pr_manager_lookup(const char *id, Error **errp) + error_setg(errp, "No persistent reservation manager with id '%s'", id); + return NULL; + } ++ ++ ++PRManagerInfoList *qmp_query_pr_managers(Error **errp) ++{ ++ return NULL; ++} +diff --git a/scsi/pr-manager.c b/scsi/pr-manager.c +index 87c45db..2a8f300 100644 +--- a/scsi/pr-manager.c ++++ b/scsi/pr-manager.c +@@ -17,6 +17,10 @@ + #include "block/thread-pool.h" + #include "scsi/pr-manager.h" + #include "trace.h" ++#include "qapi/qapi-types-block.h" ++#include "qapi/qapi-commands-block.h" ++ ++#define PR_MANAGER_PATH "/objects" + + typedef struct PRManagerData { + PRManager *pr_mgr; +@@ -64,6 +68,14 @@ BlockAIOCB *pr_manager_execute(PRManager *pr_mgr, + data, complete, opaque); + } + ++bool pr_manager_is_connected(PRManager *pr_mgr) ++{ ++ PRManagerClass *pr_mgr_class = ++ PR_MANAGER_GET_CLASS(pr_mgr); ++ ++ return !pr_mgr_class->is_connected || pr_mgr_class->is_connected(pr_mgr); ++} ++ + static const TypeInfo pr_manager_info = { + .parent = TYPE_OBJECT, + .name = TYPE_PR_MANAGER, +@@ -105,5 +117,38 @@ pr_manager_register_types(void) + type_register_static(&pr_manager_info); + } + ++static int query_one_pr_manager(Object *object, void *opaque) ++{ ++ PRManagerInfoList ***prev = opaque; ++ PRManagerInfoList *elem; ++ PRManagerInfo *info; ++ PRManager *pr_mgr; ++ ++ pr_mgr = (PRManager *)object_dynamic_cast(object, TYPE_PR_MANAGER); ++ if (!pr_mgr) { ++ return 0; ++ } ++ ++ elem = g_new0(PRManagerInfoList, 1); ++ info = g_new0(PRManagerInfo, 1); ++ info->id = object_get_canonical_path_component(object); ++ info->connected = pr_manager_is_connected(pr_mgr); ++ elem->value = info; ++ elem->next = NULL; ++ ++ **prev = elem; ++ *prev = &elem->next; ++ return 0; ++} ++ ++PRManagerInfoList *qmp_query_pr_managers(Error **errp) ++{ ++ PRManagerInfoList *head = NULL; ++ PRManagerInfoList **prev = &head; ++ Object *container = container_get(object_get_root(), PR_MANAGER_PATH); ++ ++ object_child_foreach(container, query_one_pr_manager, &prev); ++ return head; ++} + + type_init(pr_manager_register_types); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pr-manager-helper-avoid-SIGSEGV-when-writing-to-the-.patch b/SOURCES/kvm-pr-manager-helper-avoid-SIGSEGV-when-writing-to-the-.patch new file mode 100644 index 0000000..d16482f --- /dev/null +++ b/SOURCES/kvm-pr-manager-helper-avoid-SIGSEGV-when-writing-to-the-.patch @@ -0,0 +1,47 @@ +From 6bd996ee8e67d32215526016b84631147d065b7b Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Fri, 6 Jul 2018 17:56:53 +0200 +Subject: [PATCH 19/89] pr-manager-helper: avoid SIGSEGV when writing to the + socket fail + +RH-Author: Paolo Bonzini +Message-id: <20180706175659.30615-4-pbonzini@redhat.com> +Patchwork-id: 81245 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 3/9] pr-manager-helper: avoid SIGSEGV when writing to the socket fail +Bugzilla: 1533158 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Michal Privoznik + +When writing to the qemu-pr-helper socket failed, the persistent +reservation manager was correctly disconnecting the socket, but it +did not clear pr_mgr->ioc. So the rest of the code did not know +that the socket had been disconnected, accessed pr_mgr->ioc and +happily caused a crash. + +To reproduce, it is enough to stop qemu-pr-helper between QEMU +startup and executing e.g. sg_persist -k /dev/sdb. + +Reviewed-by: Michal Privoznik +Signed-off-by: Paolo Bonzini +(cherry picked from commit aad10040d411d21542dc9ae58a2854c89ccedd78) +Signed-off-by: Miroslav Rezanina +--- + scsi/pr-manager-helper.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/scsi/pr-manager-helper.c b/scsi/pr-manager-helper.c +index 82ff6b6..0c0fe38 100644 +--- a/scsi/pr-manager-helper.c ++++ b/scsi/pr-manager-helper.c +@@ -71,6 +71,7 @@ static int pr_manager_helper_write(PRManagerHelper *pr_mgr, + if (n_written <= 0) { + assert(n_written != QIO_CHANNEL_ERR_BLOCK); + object_unref(OBJECT(pr_mgr->ioc)); ++ pr_mgr->ioc = NULL; + return n_written < 0 ? -EINVAL : 0; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pr-manager-helper-fix-memory-leak-on-event.patch b/SOURCES/kvm-pr-manager-helper-fix-memory-leak-on-event.patch new file mode 100644 index 0000000..856ed72 --- /dev/null +++ b/SOURCES/kvm-pr-manager-helper-fix-memory-leak-on-event.patch @@ -0,0 +1,38 @@ +From 7d3c970b0033d3bdb99e901676228bdb205e96c0 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Fri, 6 Jul 2018 17:56:59 +0200 +Subject: [PATCH 25/89] pr-manager-helper: fix memory leak on event + +RH-Author: Paolo Bonzini +Message-id: <20180706175659.30615-10-pbonzini@redhat.com> +Patchwork-id: 81249 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 9/9] pr-manager-helper: fix memory leak on event +Bugzilla: 1533158 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Michal Privoznik + +Reported by Coverity. + +Signed-off-by: Paolo Bonzini +(cherry picked from commit ea3d77c889cfa8c450da8a716c2bfd6aaea0adb2) +Signed-off-by: Miroslav Rezanina +--- + scsi/pr-manager-helper.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/scsi/pr-manager-helper.c b/scsi/pr-manager-helper.c +index 519a296..3027dde 100644 +--- a/scsi/pr-manager-helper.c ++++ b/scsi/pr-manager-helper.c +@@ -46,6 +46,7 @@ static void pr_manager_send_status_changed_event(PRManagerHelper *pr_mgr) + if (id) { + qapi_event_send_pr_manager_status_changed(id, !!pr_mgr->ioc, + &error_abort); ++ g_free(id); + } + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pr-manager-helper-report-event-on-connection-disconn.patch b/SOURCES/kvm-pr-manager-helper-report-event-on-connection-disconn.patch new file mode 100644 index 0000000..5cd17ad --- /dev/null +++ b/SOURCES/kvm-pr-manager-helper-report-event-on-connection-disconn.patch @@ -0,0 +1,118 @@ +From d6d700d511612f621c0b6dcbd3d7996126f3da80 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Fri, 6 Jul 2018 17:56:56 +0200 +Subject: [PATCH 22/89] pr-manager-helper: report event on + connection/disconnection + +RH-Author: Paolo Bonzini +Message-id: <20180706175659.30615-7-pbonzini@redhat.com> +Patchwork-id: 81247 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 6/9] pr-manager-helper: report event on connection/disconnection +Bugzilla: 1533158 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Michal Privoznik + +Let management know if there were any problems communicating with +qemu-pr-helper. The event is edge-triggered, and is sent every time +the connection status of the pr-manager-helper object changes. + +Signed-off-by: Paolo Bonzini +(cherry picked from commit e2c81a45101fdddfd47477a1805806f2c76639bf) +Signed-off-by: Miroslav Rezanina +--- + qapi/block.json | 24 ++++++++++++++++++++++++ + scsi/pr-manager-helper.c | 14 ++++++++++++++ + 2 files changed, 38 insertions(+) + +diff --git a/qapi/block.json b/qapi/block.json +index c636354..f05b91a 100644 +--- a/qapi/block.json ++++ b/qapi/block.json +@@ -403,6 +403,30 @@ + 'data': { 'device': 'str', 'id': 'str', 'tray-open': 'bool' } } + + ## ++# @PR_MANAGER_STATUS_CHANGED: ++# ++# Emitted whenever the connected status of a persistent reservation ++# manager changes. ++# ++# @id: The id of the PR manager object ++# ++# @connected: true if the PR manager is connected to a backend ++# ++# Since: 3.0 ++# ++# Example: ++# ++# <- { "event": "PR_MANAGER_STATUS_CHANGED", ++# "data": { "id": "pr-helper0", ++# "connected": true ++# }, ++# "timestamp": { "seconds": 1519840375, "microseconds": 450486 } } ++# ++## ++{ 'event': 'PR_MANAGER_STATUS_CHANGED', ++ 'data': { 'id': 'str', 'connected': 'bool' } } ++ ++## + # @QuorumOpType: + # + # An enumeration of the quorum operation types +diff --git a/scsi/pr-manager-helper.c b/scsi/pr-manager-helper.c +index b11481b..519a296 100644 +--- a/scsi/pr-manager-helper.c ++++ b/scsi/pr-manager-helper.c +@@ -17,6 +17,7 @@ + #include "io/channel.h" + #include "io/channel-socket.h" + #include "pr-helper.h" ++#include "qapi/qapi-events-block.h" + + #include + +@@ -38,6 +39,16 @@ typedef struct PRManagerHelper { + QIOChannel *ioc; + } PRManagerHelper; + ++static void pr_manager_send_status_changed_event(PRManagerHelper *pr_mgr) ++{ ++ char *id = object_get_canonical_path_component(OBJECT(pr_mgr)); ++ ++ if (id) { ++ qapi_event_send_pr_manager_status_changed(id, !!pr_mgr->ioc, ++ &error_abort); ++ } ++} ++ + /* Called with lock held. */ + static int pr_manager_helper_read(PRManagerHelper *pr_mgr, + void *buf, int sz, Error **errp) +@@ -47,6 +58,7 @@ static int pr_manager_helper_read(PRManagerHelper *pr_mgr, + if (r < 0) { + object_unref(OBJECT(pr_mgr->ioc)); + pr_mgr->ioc = NULL; ++ pr_manager_send_status_changed_event(pr_mgr); + return -EINVAL; + } + +@@ -72,6 +84,7 @@ static int pr_manager_helper_write(PRManagerHelper *pr_mgr, + assert(n_written != QIO_CHANNEL_ERR_BLOCK); + object_unref(OBJECT(pr_mgr->ioc)); + pr_mgr->ioc = NULL; ++ pr_manager_send_status_changed_event(pr_mgr); + return n_written < 0 ? -EINVAL : 0; + } + +@@ -127,6 +140,7 @@ static int pr_manager_helper_initialize(PRManagerHelper *pr_mgr, + goto out_close; + } + ++ pr_manager_send_status_changed_event(pr_mgr); + return 0; + + out_close: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-pr-manager-put-stubs-in-.c-file.patch b/SOURCES/kvm-pr-manager-put-stubs-in-.c-file.patch new file mode 100644 index 0000000..39e5b36 --- /dev/null +++ b/SOURCES/kvm-pr-manager-put-stubs-in-.c-file.patch @@ -0,0 +1,86 @@ +From 16baef3f994e29063e8f7c48c9f03a8d07744932 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Fri, 6 Jul 2018 17:56:54 +0200 +Subject: [PATCH 20/89] pr-manager: put stubs in .c file + +RH-Author: Paolo Bonzini +Message-id: <20180706175659.30615-5-pbonzini@redhat.com> +Patchwork-id: 81253 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH 4/9] pr-manager: put stubs in .c file +Bugzilla: 1533158 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Michal Privoznik + +Signed-off-by: Paolo Bonzini +(cherry picked from commit 58b3017f7fba15e8c440115dfd5d380f490d0b61) +Signed-off-by: Miroslav Rezanina +--- + include/scsi/pr-manager.h | 9 --------- + scsi/Makefile.objs | 1 + + scsi/pr-manager-stub.c | 24 ++++++++++++++++++++++++ + 3 files changed, 25 insertions(+), 9 deletions(-) + create mode 100644 scsi/pr-manager-stub.c + +diff --git a/include/scsi/pr-manager.h b/include/scsi/pr-manager.h +index 5d2f13a..71971ae 100644 +--- a/include/scsi/pr-manager.h ++++ b/include/scsi/pr-manager.h +@@ -41,15 +41,6 @@ BlockAIOCB *pr_manager_execute(PRManager *pr_mgr, + BlockCompletionFunc *complete, + void *opaque); + +-#ifdef CONFIG_LINUX + PRManager *pr_manager_lookup(const char *id, Error **errp); +-#else +-static inline PRManager *pr_manager_lookup(const char *id, Error **errp) +-{ +- /* The classes do not exist at all! */ +- error_setg(errp, "No persistent reservation manager with id '%s'", id); +- return NULL; +-} +-#endif + + #endif +diff --git a/scsi/Makefile.objs b/scsi/Makefile.objs +index 4d25e47..bb8789c 100644 +--- a/scsi/Makefile.objs ++++ b/scsi/Makefile.objs +@@ -1,3 +1,4 @@ + block-obj-y += utils.o + + block-obj-$(CONFIG_LINUX) += pr-manager.o pr-manager-helper.o ++block-obj-$(call lnot,$(CONFIG_LINUX)) += pr-manager-stub.o +diff --git a/scsi/pr-manager-stub.c b/scsi/pr-manager-stub.c +new file mode 100644 +index 0000000..632f17c +--- /dev/null ++++ b/scsi/pr-manager-stub.c +@@ -0,0 +1,24 @@ ++/* ++ * Persistent reservation manager - stub for non-Linux platforms ++ * ++ * Copyright (c) 2018 Red Hat, Inc. ++ * ++ * Author: Paolo Bonzini ++ * ++ * This code is licensed under the LGPL. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "scsi/pr-manager.h" ++#include "trace.h" ++#include "qapi/qapi-types-block.h" ++#include "qapi/qapi-commands-block.h" ++ ++PRManager *pr_manager_lookup(const char *id, Error **errp) ++{ ++ /* The classes do not exist at all! */ ++ error_setg(errp, "No persistent reservation manager with id '%s'", id); ++ return NULL; ++} +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qapi-Add-rendernode-display-option-for-egl-headless.patch b/SOURCES/kvm-qapi-Add-rendernode-display-option-for-egl-headless.patch new file mode 100644 index 0000000..893a3d4 --- /dev/null +++ b/SOURCES/kvm-qapi-Add-rendernode-display-option-for-egl-headless.patch @@ -0,0 +1,69 @@ +From c6a95857cb076ed085b2620009da4c0e108cd8ed Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 5 Mar 2019 08:26:14 +0100 +Subject: [PATCH 6/9] qapi: Add "rendernode" display option for egl-headless +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Gerd Hoffmann +Message-id: <20190305082617.14614-2-kraxel@redhat.com> +Patchwork-id: 84796 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/4] qapi: Add "rendernode" display option for egl-headless +Bugzilla: 1648236 +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Marc-André Lureau +RH-Acked-by: John Snow +RH-Acked-by: Erik Skultety + +From: Erik Skultety + +Unlike SPICE, egl-headless doesn't offer a way of specifying the DRM +node used for OpenGL, hence QEMU always selecting the first one that is +available. Thus, add the 'rendernode' option for egl-headless to QAPI. + +Signed-off-by: Erik Skultety +Message-id: 7658e15eca72d520e7a5fb1c2e724702d83d4f7f.1542362949.git.eskultet@redhat.com +Signed-off-by: Gerd Hoffmann +(cherry picked from commit d4dc4ab133b5d7b066aa14036f297ed20398dd32) +Signed-off-by: Miroslav Rezanina +--- + qapi/ui.json | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +diff --git a/qapi/ui.json b/qapi/ui.json +index 5d01ad4..3e8aeee 100644 +--- a/qapi/ui.json ++++ b/qapi/ui.json +@@ -1020,6 +1020,20 @@ + 'data' : { '*grab-on-hover' : 'bool' } } + + ## ++# @DisplayEGLHeadless: ++# ++# EGL headless display options. ++# ++# @rendernode: Which DRM render node should be used. Default is the first ++# available node on the host. ++# ++# Since: 3.1 ++# ++## ++{ 'struct' : 'DisplayEGLHeadless', ++ 'data' : { '*rendernode' : 'str' } } ++ ++## + # @DisplayType: + # + # Display (user interface) type. +@@ -1054,6 +1068,6 @@ + 'none' : 'DisplayNoOpts', + 'gtk' : 'DisplayGTK', + 'sdl' : 'DisplayNoOpts', +- 'egl-headless' : 'DisplayNoOpts', ++ 'egl-headless' : 'DisplayEGLHeadless', + 'curses' : 'DisplayNoOpts', + 'cocoa' : 'DisplayNoOpts' } } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qapi-add-SysEmuTarget-to-common.json.patch b/SOURCES/kvm-qapi-add-SysEmuTarget-to-common.json.patch new file mode 100644 index 0000000..dd02be1 --- /dev/null +++ b/SOURCES/kvm-qapi-add-SysEmuTarget-to-common.json.patch @@ -0,0 +1,95 @@ +From f156a5a4d6e4afbe88e0485771f87db2a17aa75e Mon Sep 17 00:00:00 2001 +From: Laszlo Ersek +Date: Tue, 13 Nov 2018 18:16:35 +0100 +Subject: [PATCH 16/22] qapi: add SysEmuTarget to "common.json" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Laszlo Ersek +Message-id: <20181113181639.4999-3-lersek@redhat.com> +Patchwork-id: 83008 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 2/6] qapi: add SysEmuTarget to "common.json" +Bugzilla: 1607406 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Marc-André Lureau +RH-Acked-by: Markus Armbruster + +We'll soon need an enumeration type that lists all the softmmu targets +that QEMU (the project) supports. Introduce @SysEmuTarget to +"common.json". + +The enum constant @x86_64 doesn't match the QAPI convention of preferring +hyphen ("-") over underscore ("_"). This is intentional; the @SysEmuTarget +constants are supposed to produce QEMU executable names when stringified +and appended to the "qemu-system-" prefix. Put differently, the +replacement text of the TARGET_NAME preprocessor macro must be possible to +look up in the list of (stringified) enum constants. + +Like other enum types, @SysEmuTarget too can be used for discriminator +fields in unions. For the @i386 constant, a C-language union member called +"i386" would be generated. On mingw build hosts, "i386" is a macro +however. Add "i386" to "polluted_words" at once. + +Cc: "Daniel P. Berrange" +Cc: Eric Blake +Cc: Markus Armbruster +Signed-off-by: Laszlo Ersek +Message-Id: <20180427192852.15013-3-lersek@redhat.com> +Reviewed-by: Eric Blake +Reviewed-by: Markus Armbruster +Signed-off-by: Markus Armbruster +(cherry picked from commit 9a801c7d6c8fd927df915f2ac3eacd306e8fc334) +Signed-off-by: Miroslav Rezanina +--- + qapi/common.json | 23 +++++++++++++++++++++++ + scripts/qapi/common.py | 2 +- + 2 files changed, 24 insertions(+), 1 deletion(-) + +diff --git a/qapi/common.json b/qapi/common.json +index d9b14dd..c811d04 100644 +--- a/qapi/common.json ++++ b/qapi/common.json +@@ -126,3 +126,26 @@ + ## + { 'enum': 'OffAutoPCIBAR', + 'data': [ 'off', 'auto', 'bar0', 'bar1', 'bar2', 'bar3', 'bar4', 'bar5' ] } ++ ++## ++# @SysEmuTarget: ++# ++# The comprehensive enumeration of QEMU system emulation ("softmmu") ++# targets. Run "./configure --help" in the project root directory, and ++# look for the *-softmmu targets near the "--target-list" option. The ++# individual target constants are not documented here, for the time ++# being. ++# ++# Notes: The resulting QMP strings can be appended to the "qemu-system-" ++# prefix to produce the corresponding QEMU executable name. This ++# is true even for "qemu-system-x86_64". ++# ++# Since: 2.13 ++## ++{ 'enum' : 'SysEmuTarget', ++ 'data' : [ 'aarch64', 'alpha', 'arm', 'cris', 'hppa', 'i386', 'lm32', ++ 'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64', ++ 'mips64el', 'mipsel', 'moxie', 'nios2', 'or1k', 'ppc', ++ 'ppc64', 'ppcemb', 'riscv32', 'riscv64', 's390x', 'sh4', ++ 'sh4eb', 'sparc', 'sparc64', 'tricore', 'unicore32', ++ 'x86_64', 'xtensa', 'xtensaeb' ] } +diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py +index 3e14bc4..a032cec 100644 +--- a/scripts/qapi/common.py ++++ b/scripts/qapi/common.py +@@ -1822,7 +1822,7 @@ def c_name(name, protect=True): + 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', + 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq']) + # namespace pollution: +- polluted_words = set(['unix', 'errno', 'mips', 'sparc']) ++ polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386']) + name = name.translate(c_name_trans) + if protect and (name in c89_words | c99_words | c11_words | gcc_words + | cpp_words | polluted_words): +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qapi-add-disabled-parameter-to-block-dirty-bitmap-ad.patch b/SOURCES/kvm-qapi-add-disabled-parameter-to-block-dirty-bitmap-ad.patch new file mode 100644 index 0000000..766d30d --- /dev/null +++ b/SOURCES/kvm-qapi-add-disabled-parameter-to-block-dirty-bitmap-ad.patch @@ -0,0 +1,100 @@ +From ecbd9793ceb09c1dbbe2e37fd132aa428eb2b4fb Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:54:41 +0200 +Subject: [PATCH 56/89] qapi: add disabled parameter to block-dirty-bitmap-add + +RH-Author: John Snow +Message-id: <20180718225511.14878-6-jsnow@redhat.com> +Patchwork-id: 81404 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 05/35] qapi: add disabled parameter to block-dirty-bitmap-add +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +This is needed, for example, to create a new bitmap and merge several +disabled bitmaps into a new one. Without this flag we will have to +put block-dirty-bitmap-add and block-dirty-bitmap-disable into one +transaction. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Signed-off-by: John Snow +Reviewed-by: Jeff Cody +Message-id: 20180606182449.1607-6-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit a6e2ca5f6521553681ae136578ec1cb67e1a7973) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 10 ++++++++++ + qapi/block-core.json | 6 +++++- + 2 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/blockdev.c b/blockdev.c +index 3728082..9beef10 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -2115,6 +2115,7 @@ static void block_dirty_bitmap_add_prepare(BlkActionState *common, + action->has_granularity, action->granularity, + action->has_persistent, action->persistent, + action->has_autoload, action->autoload, ++ action->has_x_disabled, action->x_disabled, + &local_err); + + if (!local_err) { +@@ -2922,6 +2923,7 @@ 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_autoload, bool autoload, ++ bool has_disabled, bool disabled, + Error **errp) + { + BlockDriverState *bs; +@@ -2956,6 +2958,10 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, + warn_report("Autoload option is deprecated and its value is ignored"); + } + ++ if (!has_disabled) { ++ disabled = false; ++ } ++ + if (persistent && + !bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) + { +@@ -2967,6 +2973,10 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, + return; + } + ++ if (disabled) { ++ bdrv_disable_dirty_bitmap(bitmap); ++ } ++ + bdrv_dirty_bitmap_set_persistance(bitmap, persistent); + } + +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 4e1fda3..27ed91b 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -1734,11 +1734,15 @@ + # Currently, all dirty tracking bitmaps are loaded from Qcow2 on + # open. + # ++# @x-disabled: the bitmap is created in the disabled state, which means that ++# it will not track drive changes. The bitmap may be enabled with ++# x-block-dirty-bitmap-enable. Default is false. (Since: 3.0) ++# + # Since: 2.4 + ## + { 'struct': 'BlockDirtyBitmapAdd', + 'data': { 'node': 'str', 'name': 'str', '*granularity': 'uint32', +- '*persistent': 'bool', '*autoload': 'bool' } } ++ '*persistent': 'bool', '*autoload': 'bool', '*x-disabled': 'bool' } } + + ## + # @BlockDirtyBitmapMerge: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qapi-add-query-display-options-command.patch b/SOURCES/kvm-qapi-add-query-display-options-command.patch new file mode 100644 index 0000000..b5101c6 --- /dev/null +++ b/SOURCES/kvm-qapi-add-query-display-options-command.patch @@ -0,0 +1,95 @@ +From 8c923259c0880336125c05adb5b7e8001776cd79 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 5 Mar 2019 08:26:16 +0100 +Subject: [PATCH 8/9] qapi: add query-display-options command +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Gerd Hoffmann +Message-id: <20190305082617.14614-4-kraxel@redhat.com> +Patchwork-id: 84795 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 3/4] qapi: add query-display-options command +Bugzilla: 1648236 +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Marc-André Lureau +RH-Acked-by: John Snow +RH-Acked-by: Erik Skultety + +Add query-display-options command, which allows querying the qemu +display configuration. This isn't particularly useful, except it +exposes QAPI type DisplayOptions in query-qmp-schema, so that libvirt +can discover recently added -display parameter rendernode (commit +d4dc4ab133b). Works around lack of sufficiently powerful command line +introspection. + +Signed-off-by: Gerd Hoffmann +Reviewed-by: Eric Blake +Tested-by: Eric Blake +Tested-by: Erik Skultety +Message-id: 20181122071613.2889-1-kraxel@redhat.com + +[ kraxel: reworded commit message as suggested by armbru ] + +(cherry picked from commit e1ca8f7e1915496148f6e0ce1f7c2309af013312) + +[ kraxel: No QAPI_CLONE() in rhel-7. So do a manual clone of the + one essential field: DisplayOptions->type ] + +Signed-off-by: Miroslav Rezanina +--- + qapi/ui.json | 13 +++++++++++++ + vl.c | 9 +++++++++ + 2 files changed, 22 insertions(+) + +diff --git a/qapi/ui.json b/qapi/ui.json +index 3e8aeee..1475867 100644 +--- a/qapi/ui.json ++++ b/qapi/ui.json +@@ -1071,3 +1071,16 @@ + 'egl-headless' : 'DisplayEGLHeadless', + 'curses' : 'DisplayNoOpts', + 'cocoa' : 'DisplayNoOpts' } } ++ ++## ++# @query-display-options: ++# ++# Returns information about display configuration ++# ++# Returns: @DisplayOptions ++# ++# Since: 3.1 ++# ++## ++{ 'command': 'query-display-options', ++ 'returns': 'DisplayOptions' } +diff --git a/vl.c b/vl.c +index 713f899..8b79eb9 100644 +--- a/vl.c ++++ b/vl.c +@@ -129,6 +129,7 @@ int main(int argc, char **argv) + #include "qapi/qapi-commands-block-core.h" + #include "qapi/qapi-commands-misc.h" + #include "qapi/qapi-commands-run-state.h" ++#include "qapi/qapi-commands-ui.h" + #include "qapi/qmp/qerror.h" + #include "sysemu/iothread.h" + +@@ -2119,6 +2120,14 @@ static void select_vgahw(const char *p) + } + } + ++DisplayOptions *qmp_query_display_options(Error **errp) ++{ ++ DisplayOptions *r = g_new0(DisplayOptions, 1); ++ ++ r->type = dpy.type; ++ return r; ++} ++ + static void parse_display(const char *p) + { + const char *opts; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qapi-add-transaction-support-for-x-block-dirty-bitma.patch b/SOURCES/kvm-qapi-add-transaction-support-for-x-block-dirty-bitma.patch new file mode 100644 index 0000000..7965bbd --- /dev/null +++ b/SOURCES/kvm-qapi-add-transaction-support-for-x-block-dirty-bitma.patch @@ -0,0 +1,107 @@ +From f678429fca4701bda6a4631722e99c57a977846f Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:30 +0100 +Subject: [PATCH 20/33] qapi: add transaction support for + x-block-dirty-bitmap-merge + +RH-Author: John Snow +Message-id: <20190206221243.7407-11-jsnow@redhat.com> +Patchwork-id: 84270 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 10/23] qapi: add transaction support for x-block-dirty-bitmap-merge +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +From: Vladimir Sementsov-Ogievskiy + +New action is like clean action: do the whole thing in .prepare and +undo in .abort. This behavior for bitmap-changing actions is needed +because backup job actions use bitmap in .prepare. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: John Snow +(cherry picked from commit 6fd2e40789ef7389b17c5fff93b0bf82d4352cb3) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 35 +++++++++++++++++++++++++++++++++++ + qapi/transaction.json | 2 ++ + 2 files changed, 37 insertions(+) + +diff --git a/blockdev.c b/blockdev.c +index 853dd0e..df0cbe2 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -2264,6 +2264,35 @@ static void block_dirty_bitmap_disable_abort(BlkActionState *common) + } + } + ++static void block_dirty_bitmap_merge_prepare(BlkActionState *common, ++ Error **errp) ++{ ++ BlockDirtyBitmapMerge *action; ++ BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, ++ common, common); ++ BdrvDirtyBitmap *merge_source; ++ ++ if (action_check_completion_mode(common, errp) < 0) { ++ return; ++ } ++ ++ action = common->action->u.x_block_dirty_bitmap_merge.data; ++ state->bitmap = block_dirty_bitmap_lookup(action->node, ++ action->dst_name, ++ &state->bs, ++ errp); ++ if (!state->bitmap) { ++ return; ++ } ++ ++ merge_source = bdrv_find_dirty_bitmap(state->bs, action->src_name); ++ if (!merge_source) { ++ return; ++ } ++ ++ bdrv_merge_dirty_bitmap(state->bitmap, merge_source, &state->backup, errp); ++} ++ + static void abort_prepare(BlkActionState *common, Error **errp) + { + error_setg(errp, "Transaction aborted using Abort action"); +@@ -2335,6 +2364,12 @@ static const BlkActionOps actions[] = { + .prepare = block_dirty_bitmap_disable_prepare, + .abort = block_dirty_bitmap_disable_abort, + }, ++ [TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_MERGE] = { ++ .instance_size = sizeof(BlockDirtyBitmapState), ++ .prepare = block_dirty_bitmap_merge_prepare, ++ .commit = block_dirty_bitmap_free_backup, ++ .abort = block_dirty_bitmap_restore, ++ }, + /* 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. +diff --git a/qapi/transaction.json b/qapi/transaction.json +index d7e4274..5875cdb 100644 +--- a/qapi/transaction.json ++++ b/qapi/transaction.json +@@ -48,6 +48,7 @@ + # - @block-dirty-bitmap-clear: since 2.5 + # - @x-block-dirty-bitmap-enable: since 3.0 + # - @x-block-dirty-bitmap-disable: since 3.0 ++# - @x-block-dirty-bitmap-merge: since 3.1 + # - @blockdev-backup: since 2.3 + # - @blockdev-snapshot: since 2.5 + # - @blockdev-snapshot-internal-sync: since 1.7 +@@ -63,6 +64,7 @@ + 'block-dirty-bitmap-clear': 'BlockDirtyBitmap', + 'x-block-dirty-bitmap-enable': 'BlockDirtyBitmap', + 'x-block-dirty-bitmap-disable': 'BlockDirtyBitmap', ++ 'x-block-dirty-bitmap-merge': 'BlockDirtyBitmapMerge', + 'blockdev-backup': 'BlockdevBackup', + 'blockdev-snapshot': 'BlockdevSnapshot', + 'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal', +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qapi-add-x-block-dirty-bitmap-enable-disable.patch b/SOURCES/kvm-qapi-add-x-block-dirty-bitmap-enable-disable.patch new file mode 100644 index 0000000..c4674e8 --- /dev/null +++ b/SOURCES/kvm-qapi-add-x-block-dirty-bitmap-enable-disable.patch @@ -0,0 +1,142 @@ +From 65fa69814f994a95b1574dd16ea81aef4774fec5 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:54:38 +0200 +Subject: [PATCH 53/89] qapi: add x-block-dirty-bitmap-enable/disable + +RH-Author: John Snow +Message-id: <20180718225511.14878-3-jsnow@redhat.com> +Patchwork-id: 81406 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 02/35] qapi: add x-block-dirty-bitmap-enable/disable +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +Expose the ability to turn bitmaps "on" or "off". This is experimental +and principally for the sake of the Libvirt Checkpoints API, and it may +or may not be committed for 3.0. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Signed-off-by: John Snow +Reviewed-by: Jeff Cody +Message-id: 20180606182449.1607-3-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit 5c5d2e50e5ac85234d793f0127a20ea3424a1229) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 42 ++++++++++++++++++++++++++++++++++++++++++ + qapi/block-core.json | 42 ++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 84 insertions(+) + +diff --git a/blockdev.c b/blockdev.c +index d8b6520..ca2ffff 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -2965,6 +2965,48 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name, + bdrv_clear_dirty_bitmap(bitmap, NULL); + } + ++void qmp_x_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_frozen(bitmap)) { ++ error_setg(errp, ++ "Bitmap '%s' is currently frozen and cannot be enabled", ++ name); ++ return; ++ } ++ ++ bdrv_enable_dirty_bitmap(bitmap); ++} ++ ++void qmp_x_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_frozen(bitmap)) { ++ error_setg(errp, ++ "Bitmap '%s' is currently frozen and cannot be disabled", ++ name); ++ return; ++ } ++ ++ bdrv_disable_dirty_bitmap(bitmap); ++} ++ + BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, + const char *name, + Error **errp) +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 0b07e41..5c8cfa3 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -1809,6 +1809,48 @@ + 'data': 'BlockDirtyBitmap' } + + ## ++# @x-block-dirty-bitmap-enable: ++# ++# Enables a dirty bitmap so that it will begin tracking disk changes. ++# ++# Returns: nothing on success ++# If @node is not a valid block device, DeviceNotFound ++# If @name is not found, GenericError with an explanation ++# ++# Since: 3.0 ++# ++# Example: ++# ++# -> { "execute": "x-block-dirty-bitmap-enable", ++# "arguments": { "node": "drive0", "name": "bitmap0" } } ++# <- { "return": {} } ++# ++## ++ { 'command': 'x-block-dirty-bitmap-enable', ++ 'data': 'BlockDirtyBitmap' } ++ ++## ++# @x-block-dirty-bitmap-disable: ++# ++# Disables a dirty bitmap so that it will stop tracking disk changes. ++# ++# Returns: nothing on success ++# If @node is not a valid block device, DeviceNotFound ++# If @name is not found, GenericError with an explanation ++# ++# Since: 3.0 ++# ++# Example: ++# ++# -> { "execute": "x-block-dirty-bitmap-disable", ++# "arguments": { "node": "drive0", "name": "bitmap0" } } ++# <- { "return": {} } ++# ++## ++ { 'command': 'x-block-dirty-bitmap-disable', ++ 'data': 'BlockDirtyBitmap' } ++ ++## + # @BlockDirtyBitmapSha256: + # + # SHA256 hash of dirty bitmap data +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qapi-add-x-block-dirty-bitmap-merge.patch b/SOURCES/kvm-qapi-add-x-block-dirty-bitmap-merge.patch new file mode 100644 index 0000000..36c3b20 --- /dev/null +++ b/SOURCES/kvm-qapi-add-x-block-dirty-bitmap-merge.patch @@ -0,0 +1,171 @@ +From 56f208617b142524b79ddd060db62c045561c418 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:54:40 +0200 +Subject: [PATCH 55/89] qapi: add x-block-dirty-bitmap-merge + +RH-Author: John Snow +Message-id: <20180718225511.14878-5-jsnow@redhat.com> +Patchwork-id: 81397 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 04/35] qapi: add x-block-dirty-bitmap-merge +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Signed-off-by: John Snow +Reviewed-by: Jeff Cody +Message-id: 20180606182449.1607-5-jsnow@redhat.com +Signed-off-by: John Snow +(cherry picked from commit b598e531f1123d2fb72615f1161c66093be751ea) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/dirty-bitmap.c | 18 ++++++++++++++++++ + blockdev.c | 30 ++++++++++++++++++++++++++++++ + include/block/dirty-bitmap.h | 3 ++- + qapi/block-core.json | 38 ++++++++++++++++++++++++++++++++++++++ + 4 files changed, 88 insertions(+), 1 deletion(-) + +diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c +index 5623425..4159d39 100644 +--- a/block/dirty-bitmap.c ++++ b/block/dirty-bitmap.c +@@ -757,3 +757,21 @@ int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset) + { + return hbitmap_next_zero(bitmap->bitmap, offset); + } ++ ++void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, ++ Error **errp) ++{ ++ /* only bitmaps from one bds are supported */ ++ assert(dest->mutex == src->mutex); ++ ++ qemu_mutex_lock(dest->mutex); ++ ++ assert(bdrv_dirty_bitmap_enabled(dest)); ++ assert(!bdrv_dirty_bitmap_readonly(dest)); ++ ++ if (!hbitmap_merge(dest->bitmap, src->bitmap)) { ++ error_setg(errp, "Bitmaps are incompatible and can't be merged"); ++ } ++ ++ qemu_mutex_unlock(dest->mutex); ++} +diff --git a/blockdev.c b/blockdev.c +index a88d792..3728082 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3086,6 +3086,36 @@ void qmp_x_block_dirty_bitmap_disable(const char *node, const char *name, + bdrv_disable_dirty_bitmap(bitmap); + } + ++void qmp_x_block_dirty_bitmap_merge(const char *node, const char *dst_name, ++ const char *src_name, Error **errp) ++{ ++ BlockDriverState *bs; ++ BdrvDirtyBitmap *dst, *src; ++ ++ dst = block_dirty_bitmap_lookup(node, dst_name, &bs, errp); ++ if (!dst) { ++ return; ++ } ++ ++ if (bdrv_dirty_bitmap_frozen(dst)) { ++ error_setg(errp, "Bitmap '%s' is frozen and cannot be modified", ++ dst_name); ++ return; ++ } else if (bdrv_dirty_bitmap_readonly(dst)) { ++ error_setg(errp, "Bitmap '%s' is readonly and cannot be modified", ++ dst_name); ++ return; ++ } ++ ++ src = bdrv_find_dirty_bitmap(bs, src_name); ++ if (!src) { ++ error_setg(errp, "Dirty bitmap '%s' not found", src_name); ++ return; ++ } ++ ++ bdrv_merge_dirty_bitmap(dst, src, errp); ++} ++ + BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, + const char *name, + Error **errp) +diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h +index 1ff8949..1e14743 100644 +--- a/include/block/dirty-bitmap.h ++++ b/include/block/dirty-bitmap.h +@@ -70,7 +70,8 @@ void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value); + void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, + bool persistent); + void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked); +- ++void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, ++ Error **errp); + + /* Functions that require manual locking. */ + void bdrv_dirty_bitmap_lock(BdrvDirtyBitmap *bitmap); +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 5c8cfa3..4e1fda3 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -1741,6 +1741,20 @@ + '*persistent': 'bool', '*autoload': 'bool' } } + + ## ++# @BlockDirtyBitmapMerge: ++# ++# @node: name of device/node which the bitmap is tracking ++# ++# @dst_name: name of the destination dirty bitmap ++# ++# @src_name: name of the source dirty bitmap ++# ++# Since: 3.0 ++## ++{ 'struct': 'BlockDirtyBitmapMerge', ++ 'data': { 'node': 'str', 'dst_name': 'str', 'src_name': 'str' } } ++ ++## + # @block-dirty-bitmap-add: + # + # Create a dirty bitmap with a name on the node, and start tracking the writes. +@@ -1851,6 +1865,30 @@ + 'data': 'BlockDirtyBitmap' } + + ## ++# @x-block-dirty-bitmap-merge: ++# ++# Merge @src_name dirty bitmap to @dst_name dirty bitmap. @src_name dirty ++# bitmap is unchanged. On error, @dst_name is unchanged. ++# ++# Returns: nothing on success ++# If @node is not a valid block device, DeviceNotFound ++# If @dst_name or @src_name is not found, GenericError ++# If bitmaps has different sizes or granularities, GenericError ++# ++# Since: 3.0 ++# ++# Example: ++# ++# -> { "execute": "x-block-dirty-bitmap-merge", ++# "arguments": { "node": "drive0", "dst_name": "bitmap0", ++# "src_name": "bitmap1" } } ++# <- { "return": {} } ++# ++## ++ { 'command': 'x-block-dirty-bitmap-merge', ++ 'data': 'BlockDirtyBitmapMerge' } ++ ++## + # @BlockDirtyBitmapSha256: + # + # SHA256 hash of dirty bitmap data +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qapi-bitmap-merge-document-name-change.patch b/SOURCES/kvm-qapi-bitmap-merge-document-name-change.patch new file mode 100644 index 0000000..9be3350 --- /dev/null +++ b/SOURCES/kvm-qapi-bitmap-merge-document-name-change.patch @@ -0,0 +1,52 @@ +From 31ed4da2c4cecaeff9ebdf7bfe8c911b8411b624 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 16:16:13 +0100 +Subject: [PATCH 015/163] qapi: bitmap-merge: document name change + +RH-Author: John Snow +Message-id: <20190320161631.14841-2-jsnow@redhat.com> +Patchwork-id: 84947 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 01/19] qapi: bitmap-merge: document name change +Bugzilla: 1668956 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +We named these using underscores instead of the preferred dash, +document this nearby so we cannot possibly forget to rectify this +when we remove the 'x-' prefixes when the feature becomes stable. + +We do not implement the change ahead of time to avoid more work +for libvirt to do in order to figure out how to use the beta version +of the API needlessly. + +Reported-by: Eric Blake +Signed-off-by: John Snow +Message-Id: <20180919190934.16284-1-jsnow@redhat.com> +Reviewed-by: Eric Blake +[eblake: typo fix] +Signed-off-by: Eric Blake +(cherry picked from commit cb9f871e80fe630422e0962751a6f9fb1795fe02) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + qapi/block-core.json | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 9c8c9ff..0960449 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -1923,6 +1923,8 @@ + ## + # @x-block-dirty-bitmap-merge: + # ++# FIXME: Rename @src_name and @dst_name to src-name and dst-name. ++# + # Merge @src_name dirty bitmap to @dst_name dirty bitmap. @src_name dirty + # bitmap is unchanged. On error, @dst_name is unchanged. + # +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qapi-block-commit-expose-new-job-properties.patch b/SOURCES/kvm-qapi-block-commit-expose-new-job-properties.patch new file mode 100644 index 0000000..2c7aa78 --- /dev/null +++ b/SOURCES/kvm-qapi-block-commit-expose-new-job-properties.patch @@ -0,0 +1,90 @@ +From 64f2d8be8a97b47baa7c51cceaa29b60d6b969a5 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:59 +0200 +Subject: [PATCH 21/25] qapi/block-commit: expose new job properties + +RH-Author: John Snow +Message-id: <20180910181803.11781-22-jsnow@redhat.com> +Patchwork-id: 82100 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 21/25] qapi/block-commit: expose new job properties +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Signed-off-by: John Snow +Reviewed-by: Max Reitz +Message-id: 20180906130225.5118-13-jsnow@redhat.com +Reviewed-by: Jeff Cody +Signed-off-by: Max Reitz +(cherry picked from commit 7c91d440488214e953f8cc76e5d5fa477dc0376f) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 8 ++++++++ + qapi/block-core.json | 16 +++++++++++++++- + 2 files changed, 23 insertions(+), 1 deletion(-) + +diff --git a/blockdev.c b/blockdev.c +index c4a1801..1d0cbb1 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3361,6 +3361,8 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, + bool has_backing_file, const char *backing_file, + bool has_speed, int64_t speed, + bool has_filter_node_name, const char *filter_node_name, ++ bool has_auto_finalize, bool auto_finalize, ++ bool has_auto_dismiss, bool auto_dismiss, + Error **errp) + { + BlockDriverState *bs; +@@ -3380,6 +3382,12 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, + if (!has_filter_node_name) { + filter_node_name = NULL; + } ++ if (has_auto_finalize && !auto_finalize) { ++ job_flags |= JOB_MANUAL_FINALIZE; ++ } ++ if (has_auto_dismiss && !auto_dismiss) { ++ job_flags |= JOB_MANUAL_DISMISS; ++ } + + /* Important Note: + * libvirt relies on the DeviceNotFound error class in order to probe for +diff --git a/qapi/block-core.json b/qapi/block-core.json +index c6b42eb..af453c5 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -1481,6 +1481,19 @@ + # above @top. If this option is not given, a node name is + # autogenerated. (Since: 2.9) + # ++# @auto-finalize: When false, this job will wait in a PENDING state after it has ++# finished its work, waiting for @block-job-finalize before ++# making any block graph changes. ++# When true, this job will automatically ++# perform its abort or commit actions. ++# Defaults to true. (Since 3.1) ++# ++# @auto-dismiss: When false, this job will wait in a CONCLUDED state after it ++# has completely ceased all work, and awaits @block-job-dismiss. ++# When true, this job will automatically disappear from the query ++# list without user intervention. ++# Defaults to true. (Since 3.1) ++# + # Returns: Nothing on success + # If commit or stream is already active on this device, DeviceInUse + # If @device does not exist, DeviceNotFound +@@ -1501,7 +1514,8 @@ + { 'command': 'block-commit', + 'data': { '*job-id': 'str', 'device': 'str', '*base': 'str', '*top': 'str', + '*backing-file': 'str', '*speed': 'int', +- '*filter-node-name': 'str' } } ++ '*filter-node-name': 'str', ++ '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } + + ## + # @drive-backup: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qapi-block-mirror-expose-new-job-properties.patch b/SOURCES/kvm-qapi-block-mirror-expose-new-job-properties.patch new file mode 100644 index 0000000..20068a7 --- /dev/null +++ b/SOURCES/kvm-qapi-block-mirror-expose-new-job-properties.patch @@ -0,0 +1,150 @@ +From 4fd9e923e840ff8a9618e018192bad6ed000cc40 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:18:00 +0200 +Subject: [PATCH 22/25] qapi/block-mirror: expose new job properties + +RH-Author: John Snow +Message-id: <20180910181803.11781-23-jsnow@redhat.com> +Patchwork-id: 82096 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 22/25] qapi/block-mirror: expose new job properties +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Signed-off-by: John Snow +Reviewed-by: Max Reitz +Message-id: 20180906130225.5118-14-jsnow@redhat.com +Reviewed-by: Jeff Cody +Signed-off-by: Max Reitz +(cherry picked from commit 61bd6718ebceae42f97a99d2ffd315b05b86f1b3) +Signed-off-by: John Snow + +Conflicts: downstream does not have has_copy_mode. + -blockdev.c + -qapi/block-core.json + +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 14 ++++++++++++++ + qapi/block-core.json | 30 ++++++++++++++++++++++++++++-- + 2 files changed, 42 insertions(+), 2 deletions(-) + +diff --git a/blockdev.c b/blockdev.c +index 1d0cbb1..a655387 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3753,6 +3753,8 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, + bool has_unmap, bool unmap, + bool has_filter_node_name, + const char *filter_node_name, ++ bool has_auto_finalize, bool auto_finalize, ++ bool has_auto_dismiss, bool auto_dismiss, + Error **errp) + { + int job_flags = JOB_DEFAULT; +@@ -3778,6 +3780,12 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, + if (!has_filter_node_name) { + filter_node_name = NULL; + } ++ 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", +@@ -3954,6 +3962,8 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) + arg->has_on_target_error, arg->on_target_error, + arg->has_unmap, arg->unmap, + false, NULL, ++ arg->has_auto_finalize, arg->auto_finalize, ++ arg->has_auto_dismiss, arg->auto_dismiss, + &local_err); + bdrv_unref(target_bs); + error_propagate(errp, local_err); +@@ -3974,6 +3984,8 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id, + BlockdevOnError on_target_error, + bool has_filter_node_name, + const char *filter_node_name, ++ bool has_auto_finalize, bool auto_finalize, ++ bool has_auto_dismiss, bool auto_dismiss, + Error **errp) + { + BlockDriverState *bs; +@@ -4006,6 +4018,8 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id, + has_on_target_error, on_target_error, + true, true, + has_filter_node_name, filter_node_name, ++ has_auto_finalize, auto_finalize, ++ has_auto_dismiss, auto_dismiss, + &local_err); + error_propagate(errp, local_err); + +diff --git a/qapi/block-core.json b/qapi/block-core.json +index af453c5..6a5e357 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -1712,6 +1712,18 @@ + # written. Both will result in identical contents. + # Default is true. (Since 2.4) + # ++# @auto-finalize: When false, this job will wait in a PENDING state after it has ++# finished its work, waiting for @block-job-finalize before ++# making any block graph changes. ++# When true, this job will automatically ++# perform its abort or commit actions. ++# Defaults to true. (Since 3.1) ++# ++# @auto-dismiss: When false, this job will wait in a CONCLUDED state after it ++# has completely ceased all work, and awaits @block-job-dismiss. ++# When true, this job will automatically disappear from the query ++# list without user intervention. ++# Defaults to true. (Since 3.1) + # Since: 1.3 + ## + { 'struct': 'DriveMirror', +@@ -1721,7 +1733,8 @@ + '*speed': 'int', '*granularity': 'uint32', + '*buf-size': 'int', '*on-source-error': 'BlockdevOnError', + '*on-target-error': 'BlockdevOnError', +- '*unmap': 'bool' } } ++ '*unmap': 'bool', ++ '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } + + ## + # @BlockDirtyBitmap: +@@ -1984,6 +1997,18 @@ + # above @device. If this option is not given, a node name is + # autogenerated. (Since: 2.9) + # ++# @auto-finalize: When false, this job will wait in a PENDING state after it has ++# finished its work, waiting for @block-job-finalize before ++# making any block graph changes. ++# When true, this job will automatically ++# perform its abort or commit actions. ++# Defaults to true. (Since 3.1) ++# ++# @auto-dismiss: When false, this job will wait in a CONCLUDED state after it ++# has completely ceased all work, and awaits @block-job-dismiss. ++# When true, this job will automatically disappear from the query ++# list without user intervention. ++# Defaults to true. (Since 3.1) + # Returns: nothing on success. + # + # Since: 2.6 +@@ -2004,7 +2029,8 @@ + '*speed': 'int', '*granularity': 'uint32', + '*buf-size': 'int', '*on-source-error': 'BlockdevOnError', + '*on-target-error': 'BlockdevOnError', +- '*filter-node-name': 'str' } } ++ '*filter-node-name': 'str', ++ '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } + + ## + # @block_set_io_throttle: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qapi-block-stream-expose-new-job-properties.patch b/SOURCES/kvm-qapi-block-stream-expose-new-job-properties.patch new file mode 100644 index 0000000..0470c4d --- /dev/null +++ b/SOURCES/kvm-qapi-block-stream-expose-new-job-properties.patch @@ -0,0 +1,108 @@ +From 0ac47b4655421a41af7f14aed2949643849635e5 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:18:01 +0200 +Subject: [PATCH 23/25] qapi/block-stream: expose new job properties + +RH-Author: John Snow +Message-id: <20180910181803.11781-24-jsnow@redhat.com> +Patchwork-id: 82099 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 23/25] qapi/block-stream: expose new job properties +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Signed-off-by: John Snow +Reviewed-by: Max Reitz +Message-id: 20180906130225.5118-15-jsnow@redhat.com +Reviewed-by: Jeff Cody +Signed-off-by: Max Reitz +(cherry picked from commit fd4bbe3c2caa01a2be5b311ef0685c7f92789592) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 9 +++++++++ + hmp.c | 5 +++-- + qapi/block-core.json | 16 +++++++++++++++- + 3 files changed, 27 insertions(+), 3 deletions(-) + +diff --git a/blockdev.c b/blockdev.c +index a655387..6325471 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -3273,6 +3273,8 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, + bool has_backing_file, const char *backing_file, + bool has_speed, int64_t speed, + bool has_on_error, BlockdevOnError on_error, ++ bool has_auto_finalize, bool auto_finalize, ++ bool has_auto_dismiss, bool auto_dismiss, + Error **errp) + { + BlockDriverState *bs, *iter; +@@ -3342,6 +3344,13 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, + /* backing_file string overrides base bs filename */ + base_name = has_backing_file ? backing_file : base_name; + ++ if (has_auto_finalize && !auto_finalize) { ++ job_flags |= JOB_MANUAL_FINALIZE; ++ } ++ if (has_auto_dismiss && !auto_dismiss) { ++ job_flags |= JOB_MANUAL_DISMISS; ++ } ++ + stream_start(has_job_id ? job_id : NULL, bs, base_bs, base_name, + job_flags, has_speed ? speed : 0, on_error, &local_err); + if (local_err) { +diff --git a/hmp.c b/hmp.c +index 7a53e63..cc088da 100644 +--- a/hmp.c ++++ b/hmp.c +@@ -1807,8 +1807,9 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict) + int64_t speed = qdict_get_try_int(qdict, "speed", 0); + + qmp_block_stream(true, device, device, base != NULL, base, false, NULL, +- false, NULL, qdict_haskey(qdict, "speed"), speed, +- true, BLOCKDEV_ON_ERROR_REPORT, &error); ++ false, NULL, qdict_haskey(qdict, "speed"), speed, true, ++ BLOCKDEV_ON_ERROR_REPORT, false, false, false, false, ++ &error); + + hmp_handle_error(mon, &error); + } +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 6a5e357..5526cbc 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -2296,6 +2296,19 @@ + # 'stop' and 'enospc' can only be used if the block device + # supports io-status (see BlockInfo). Since 1.3. + # ++# @auto-finalize: When false, this job will wait in a PENDING state after it has ++# finished its work, waiting for @block-job-finalize before ++# making any block graph changes. ++# When true, this job will automatically ++# perform its abort or commit actions. ++# Defaults to true. (Since 3.1) ++# ++# @auto-dismiss: When false, this job will wait in a CONCLUDED state after it ++# has completely ceased all work, and awaits @block-job-dismiss. ++# When true, this job will automatically disappear from the query ++# list without user intervention. ++# Defaults to true. (Since 3.1) ++# + # Returns: Nothing on success. If @device does not exist, DeviceNotFound. + # + # Since: 1.1 +@@ -2311,7 +2324,8 @@ + { 'command': 'block-stream', + 'data': { '*job-id': 'str', 'device': 'str', '*base': 'str', + '*base-node': 'str', '*backing-file': 'str', '*speed': 'int', +- '*on-error': 'BlockdevOnError' } } ++ '*on-error': 'BlockdevOnError', ++ '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } + + ## + # @block-job-set-speed: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qapi-change-the-type-of-TargetInfo.arch-from-string-.patch b/SOURCES/kvm-qapi-change-the-type-of-TargetInfo.arch-from-string-.patch new file mode 100644 index 0000000..6eadf4b --- /dev/null +++ b/SOURCES/kvm-qapi-change-the-type-of-TargetInfo.arch-from-string-.patch @@ -0,0 +1,89 @@ +From 27de7538bc01f788b4b2b652e96817b3c80e760c Mon Sep 17 00:00:00 2001 +From: Laszlo Ersek +Date: Tue, 13 Nov 2018 18:16:36 +0100 +Subject: [PATCH 17/22] qapi: change the type of TargetInfo.arch from string to + enum SysEmuTarget +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Laszlo Ersek +Message-id: <20181113181639.4999-4-lersek@redhat.com> +Patchwork-id: 83004 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 3/6] qapi: change the type of TargetInfo.arch from string to enum SysEmuTarget +Bugzilla: 1607406 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Marc-André Lureau +RH-Acked-by: Markus Armbruster + +Now that we have @SysEmuTarget, it makes sense to restrict +@TargetInfo.@arch to valid sysemu targets at the schema level. + +Cc: "Daniel P. Berrange" +Cc: Eric Blake +Cc: Markus Armbruster +Signed-off-by: Laszlo Ersek +Reviewed-by: Markus Armbruster +Reviewed-by: Eric Blake +Message-Id: <20180427192852.15013-4-lersek@redhat.com> +Signed-off-by: Markus Armbruster +(cherry picked from commit b47aa7b3d44dcedf7bf541563f70704f82f500d0) +Signed-off-by: Miroslav Rezanina +--- + arch_init.c | 4 +++- + qapi/misc.json | 6 ++++-- + 2 files changed, 7 insertions(+), 3 deletions(-) + +diff --git a/arch_init.c b/arch_init.c +index 6ee0747..9597218 100644 +--- a/arch_init.c ++++ b/arch_init.c +@@ -29,6 +29,7 @@ + #include "hw/pci/pci.h" + #include "hw/audio/soundhw.h" + #include "qapi/qapi-commands-misc.h" ++#include "qapi/error.h" + #include "qemu/config-file.h" + #include "qemu/error-report.h" + #include "hw/acpi/acpi.h" +@@ -112,7 +113,8 @@ TargetInfo *qmp_query_target(Error **errp) + { + TargetInfo *info = g_malloc0(sizeof(*info)); + +- info->arch = g_strdup(TARGET_NAME); ++ info->arch = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, -1, ++ &error_abort); + + return info; + } +diff --git a/qapi/misc.json b/qapi/misc.json +index 8b28270..7cf4fbc 100644 +--- a/qapi/misc.json ++++ b/qapi/misc.json +@@ -5,6 +5,8 @@ + # = Miscellanea + ## + ++{ 'include': 'common.json' } ++ + ## + # @qmp_capabilities: + # +@@ -2471,12 +2473,12 @@ + # + # Information describing the QEMU target. + # +-# @arch: the target architecture (eg "x86_64", "i386", etc) ++# @arch: the target architecture + # + # Since: 1.2.0 + ## + { 'struct': 'TargetInfo', +- 'data': { 'arch': 'str' } } ++ 'data': { 'arch': 'SysEmuTarget' } } + + ## + # @query-target: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qapi-deprecate-CpuInfoFast.arch.patch b/SOURCES/kvm-qapi-deprecate-CpuInfoFast.arch.patch new file mode 100644 index 0000000..211cd34 --- /dev/null +++ b/SOURCES/kvm-qapi-deprecate-CpuInfoFast.arch.patch @@ -0,0 +1,84 @@ +From 1d06e4e316610a2ffc805d8cc122dab5494bb8e5 Mon Sep 17 00:00:00 2001 +From: Laszlo Ersek +Date: Tue, 13 Nov 2018 18:16:38 +0100 +Subject: [PATCH 19/22] qapi: deprecate CpuInfoFast.arch +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Laszlo Ersek +Message-id: <20181113181639.4999-6-lersek@redhat.com> +Patchwork-id: 83006 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 5/6] qapi: deprecate CpuInfoFast.arch +Bugzilla: 1607406 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Marc-André Lureau +RH-Acked-by: Markus Armbruster + +The TARGET_BASE_ARCH values from "configure" don't all map to the +@CpuInfoArch enum constants; in particular "s390x" from the former does +not match @s390 in the latter. Clients are known to rely on the @s390 +constant specifically, so we can't change it silently. Instead, deprecate +the @CpuInfoFast.@arch member (in favor of @CpuInfoFast.@target) using the +regular deprecation process. + +(No deprecation reminder is added to sysemu_target_to_cpuinfo_arch(): once +@CpuInfoFast.@arch is removed, the assignment expression that calls +sysemu_target_to_cpuinfo_arch() from qmp_query_cpus_fast() will have to +disappear; in turn the static function left without callers will also +break the build, thus it'll have to go.) + +Cc: "Daniel P. Berrange" +Cc: Eric Blake +Cc: Markus Armbruster +Signed-off-by: Laszlo Ersek +Message-Id: <20180427192852.15013-6-lersek@redhat.com> +Reviewed-by: Eric Blake +Reviewed-by: Markus Armbruster +Signed-off-by: Markus Armbruster +(cherry picked from commit 6ffa3ab453b431ec047ff1fc87120059b5266014) +Signed-off-by: Miroslav Rezanina +--- + qapi/misc.json | 8 ++++---- + qemu-doc.texi | 5 +++++ + 2 files changed, 9 insertions(+), 4 deletions(-) + +diff --git a/qapi/misc.json b/qapi/misc.json +index d7fd8bd..e6291fd 100644 +--- a/qapi/misc.json ++++ b/qapi/misc.json +@@ -558,11 +558,11 @@ + # @props: properties describing to which node/socket/core/thread + # virtual CPU belongs to, provided if supported by board + # +-# @arch: base architecture of the cpu ++# @arch: base architecture of the cpu; deprecated since 2.13.0 in favor ++# of @target + # +-# @target: the QEMU system emulation target, which is more specific than +-# @arch and determines which additional fields will be listed +-# (since 2.13) ++# @target: the QEMU system emulation target, which determines which ++# additional fields will be listed (since 2.13) + # + # Since: 2.12 + # +diff --git a/qemu-doc.texi b/qemu-doc.texi +index 985e0f2..88358be 100644 +--- a/qemu-doc.texi ++++ b/qemu-doc.texi +@@ -2955,6 +2955,11 @@ from qcow2 images. + + The ``query-cpus'' command is replaced by the ``query-cpus-fast'' command. + ++@subsection query-cpus-fast "arch" output member (since 2.13.0) ++ ++The ``arch'' output member of the ``query-cpus-fast'' command is ++replaced by the ``target'' output member. ++ + @section System emulator devices + + @subsection ivshmem (since 2.6.0) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qapi-discriminate-CpuInfoFast-on-SysEmuTarget-not-Cp.patch b/SOURCES/kvm-qapi-discriminate-CpuInfoFast-on-SysEmuTarget-not-Cp.patch new file mode 100644 index 0000000..7501e53 --- /dev/null +++ b/SOURCES/kvm-qapi-discriminate-CpuInfoFast-on-SysEmuTarget-not-Cp.patch @@ -0,0 +1,246 @@ +From 17a1d383393e879c43e2aa8f2b264cb001cbca78 Mon Sep 17 00:00:00 2001 +From: Laszlo Ersek +Date: Tue, 13 Nov 2018 18:16:37 +0100 +Subject: [PATCH 18/22] qapi: discriminate CpuInfoFast on SysEmuTarget, not + CpuInfoArch +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Laszlo Ersek +Message-id: <20181113181639.4999-5-lersek@redhat.com> +Patchwork-id: 83005 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 4/6] qapi: discriminate CpuInfoFast on SysEmuTarget, not CpuInfoArch +Bugzilla: 1607406 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Marc-André Lureau +RH-Acked-by: Markus Armbruster + +Add a new field @target (of type @SysEmuTarget) to the output of the +@query-cpus-fast command, which provides more information about the +emulation target than the field @arch (of type @CpuInfoArch). Make @target +the new discriminator for the @CpuInfoFast return structure. Keep @arch +for compatibility. + +Cc: "Daniel P. Berrange" +Cc: Eric Blake +Cc: Markus Armbruster +Signed-off-by: Laszlo Ersek +Reviewed-by: Eric Blake +Message-Id: <20180427192852.15013-5-lersek@redhat.com> +Reviewed-by: Markus Armbruster +Signed-off-by: Markus Armbruster +(cherry picked from commit daa9d2bc6d73618bc230787ddfa821a6a6560fc2) +Signed-off-by: Miroslav Rezanina +--- + cpus.c | 87 ++++++++++++++++++++++++++++++++++++++++++---------------- + qapi/misc.json | 62 +++++++++++++++++++++++++++++++---------- + 2 files changed, 110 insertions(+), 39 deletions(-) + +diff --git a/cpus.c b/cpus.c +index 4f83f16..be3a4eb 100644 +--- a/cpus.c ++++ b/cpus.c +@@ -2187,6 +2187,59 @@ CpuInfoList *qmp_query_cpus(Error **errp) + return head; + } + ++static CpuInfoArch sysemu_target_to_cpuinfo_arch(SysEmuTarget target) ++{ ++ /* ++ * The @SysEmuTarget -> @CpuInfoArch mapping below is based on the ++ * TARGET_ARCH -> TARGET_BASE_ARCH mapping in the "configure" script. ++ */ ++ switch (target) { ++ case SYS_EMU_TARGET_I386: ++ case SYS_EMU_TARGET_X86_64: ++ return CPU_INFO_ARCH_X86; ++ ++ case SYS_EMU_TARGET_PPC: ++ case SYS_EMU_TARGET_PPCEMB: ++ case SYS_EMU_TARGET_PPC64: ++ return CPU_INFO_ARCH_PPC; ++ ++ case SYS_EMU_TARGET_SPARC: ++ case SYS_EMU_TARGET_SPARC64: ++ return CPU_INFO_ARCH_SPARC; ++ ++ case SYS_EMU_TARGET_MIPS: ++ case SYS_EMU_TARGET_MIPSEL: ++ case SYS_EMU_TARGET_MIPS64: ++ case SYS_EMU_TARGET_MIPS64EL: ++ return CPU_INFO_ARCH_MIPS; ++ ++ case SYS_EMU_TARGET_TRICORE: ++ return CPU_INFO_ARCH_TRICORE; ++ ++ case SYS_EMU_TARGET_S390X: ++ return CPU_INFO_ARCH_S390; ++ ++ case SYS_EMU_TARGET_RISCV32: ++ case SYS_EMU_TARGET_RISCV64: ++ return CPU_INFO_ARCH_RISCV; ++ ++ default: ++ return CPU_INFO_ARCH_OTHER; ++ } ++} ++ ++static void cpustate_to_cpuinfo_s390(CpuInfoS390 *info, const CPUState *cpu) ++{ ++#ifdef TARGET_S390X ++ S390CPU *s390_cpu = S390_CPU(cpu); ++ CPUS390XState *env = &s390_cpu->env; ++ ++ info->cpu_state = env->cpu_state; ++#else ++ abort(); ++#endif ++} ++ + /* + * fast means: we NEVER interrupt vCPU threads to retrieve + * information from KVM. +@@ -2196,11 +2249,9 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp) + MachineState *ms = MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(ms); + CpuInfoFastList *head = NULL, *cur_item = NULL; ++ SysEmuTarget target = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, ++ -1, &error_abort); + CPUState *cpu; +-#if defined(TARGET_S390X) +- S390CPU *s390_cpu; +- CPUS390XState *env; +-#endif + + CPU_FOREACH(cpu) { + CpuInfoFastList *info = g_malloc0(sizeof(*info)); +@@ -2218,26 +2269,14 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp) + info->value->props = props; + } + +-#if defined(TARGET_I386) +- info->value->arch = CPU_INFO_ARCH_X86; +-#elif defined(TARGET_PPC) +- info->value->arch = CPU_INFO_ARCH_PPC; +-#elif defined(TARGET_SPARC) +- info->value->arch = CPU_INFO_ARCH_SPARC; +-#elif defined(TARGET_MIPS) +- info->value->arch = CPU_INFO_ARCH_MIPS; +-#elif defined(TARGET_TRICORE) +- info->value->arch = CPU_INFO_ARCH_TRICORE; +-#elif defined(TARGET_S390X) +- s390_cpu = S390_CPU(cpu); +- env = &s390_cpu->env; +- info->value->arch = CPU_INFO_ARCH_S390; +- info->value->u.s390.cpu_state = env->cpu_state; +-#elif defined(TARGET_RISCV) +- info->value->arch = CPU_INFO_ARCH_RISCV; +-#else +- info->value->arch = CPU_INFO_ARCH_OTHER; +-#endif ++ info->value->arch = sysemu_target_to_cpuinfo_arch(target); ++ info->value->target = target; ++ if (target == SYS_EMU_TARGET_S390X) { ++ cpustate_to_cpuinfo_s390(&info->value->u.s390x, cpu); ++ } else { ++ /* do nothing for @CpuInfoOther */ ++ } ++ + if (!cur_item) { + head = cur_item = info; + } else { +diff --git a/qapi/misc.json b/qapi/misc.json +index 7cf4fbc..d7fd8bd 100644 +--- a/qapi/misc.json ++++ b/qapi/misc.json +@@ -558,25 +558,55 @@ + # @props: properties describing to which node/socket/core/thread + # virtual CPU belongs to, provided if supported by board + # +-# @arch: architecture of the cpu, which determines which additional fields +-# will be listed ++# @arch: base architecture of the cpu ++# ++# @target: the QEMU system emulation target, which is more specific than ++# @arch and determines which additional fields will be listed ++# (since 2.13) + # + # Since: 2.12 + # + ## +-{ 'union': 'CpuInfoFast', +- 'base': {'cpu-index': 'int', 'qom-path': 'str', +- 'thread-id': 'int', '*props': 'CpuInstanceProperties', +- 'arch': 'CpuInfoArch' }, +- 'discriminator': 'arch', +- 'data': { 'x86': 'CpuInfoOther', +- 'sparc': 'CpuInfoOther', +- 'ppc': 'CpuInfoOther', +- 'mips': 'CpuInfoOther', +- 'tricore': 'CpuInfoOther', +- 's390': 'CpuInfoS390', +- 'riscv': 'CpuInfoOther', +- 'other': 'CpuInfoOther' } } ++{ 'union' : 'CpuInfoFast', ++ 'base' : { 'cpu-index' : 'int', ++ 'qom-path' : 'str', ++ 'thread-id' : 'int', ++ '*props' : 'CpuInstanceProperties', ++ 'arch' : 'CpuInfoArch', ++ 'target' : 'SysEmuTarget' }, ++ 'discriminator' : 'target', ++ 'data' : { 'aarch64' : 'CpuInfoOther', ++ 'alpha' : 'CpuInfoOther', ++ 'arm' : 'CpuInfoOther', ++ 'cris' : 'CpuInfoOther', ++ 'hppa' : 'CpuInfoOther', ++ 'i386' : 'CpuInfoOther', ++ 'lm32' : 'CpuInfoOther', ++ 'm68k' : 'CpuInfoOther', ++ 'microblaze' : 'CpuInfoOther', ++ 'microblazeel' : 'CpuInfoOther', ++ 'mips' : 'CpuInfoOther', ++ 'mips64' : 'CpuInfoOther', ++ 'mips64el' : 'CpuInfoOther', ++ 'mipsel' : 'CpuInfoOther', ++ 'moxie' : 'CpuInfoOther', ++ 'nios2' : 'CpuInfoOther', ++ 'or1k' : 'CpuInfoOther', ++ 'ppc' : 'CpuInfoOther', ++ 'ppc64' : 'CpuInfoOther', ++ 'ppcemb' : 'CpuInfoOther', ++ 'riscv32' : 'CpuInfoOther', ++ 'riscv64' : 'CpuInfoOther', ++ 's390x' : 'CpuInfoS390', ++ 'sh4' : 'CpuInfoOther', ++ 'sh4eb' : 'CpuInfoOther', ++ 'sparc' : 'CpuInfoOther', ++ 'sparc64' : 'CpuInfoOther', ++ 'tricore' : 'CpuInfoOther', ++ 'unicore32' : 'CpuInfoOther', ++ 'x86_64' : 'CpuInfoOther', ++ 'xtensa' : 'CpuInfoOther', ++ 'xtensaeb' : 'CpuInfoOther' } } + + ## + # @query-cpus-fast: +@@ -602,6 +632,7 @@ + # }, + # "qom-path": "/machine/unattached/device[0]", + # "arch":"x86", ++# "target":"x86_64", + # "cpu-index": 0 + # }, + # { +@@ -613,6 +644,7 @@ + # }, + # "qom-path": "/machine/unattached/device[2]", + # "arch":"x86", ++# "target":"x86_64", + # "cpu-index": 1 + # } + # ] +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qapi-fill-in-CpuInfoFast.arch-in-query-cpus-fast.patch b/SOURCES/kvm-qapi-fill-in-CpuInfoFast.arch-in-query-cpus-fast.patch new file mode 100644 index 0000000..6acbe7a --- /dev/null +++ b/SOURCES/kvm-qapi-fill-in-CpuInfoFast.arch-in-query-cpus-fast.patch @@ -0,0 +1,116 @@ +From 06441913a6b0832d545bb40b1c4969cca99059a7 Mon Sep 17 00:00:00 2001 +From: Laszlo Ersek +Date: Tue, 13 Nov 2018 18:16:34 +0100 +Subject: [PATCH 15/22] qapi: fill in CpuInfoFast.arch in query-cpus-fast +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Laszlo Ersek +Message-id: <20181113181639.4999-2-lersek@redhat.com> +Patchwork-id: 83003 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/6] qapi: fill in CpuInfoFast.arch in query-cpus-fast +Bugzilla: 1607406 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Marc-André Lureau +RH-Acked-by: Markus Armbruster + +* Commit ca230ff33f89 added the @arch field to @CpuInfoFast, but it failed + to set the new field in qmp_query_cpus_fast(), when TARGET_S390X was not + defined. The updated @query-cpus-fast example in "qapi-schema.json" + showed "arch":"x86" only because qmp_query_cpus_fast() calls g_malloc0() + to allocate @CpuInfoFast, and the CPU_INFO_ARCH_X86 enum constant is + generated with value 0. + + All @arch values other than @s390 implied the @CpuInfoOther sub-struct + for @CpuInfoFast -- at the time of writing the patch --, thus no fields + other than @arch needed to be set when TARGET_S390X was not defined. Set + @arch now, by copying the corresponding assignments from + qmp_query_cpus(). + +* Commit 25fa194b7b11 added the @riscv enum constant to @CpuInfoArch (used + in both @CpuInfo and @CpuInfoFast -- the return types of the @query-cpus + and @query-cpus-fast commands, respectively), and assigned, in both + return structures, the @CpuInfoRISCV sub-structure to the new enum + value. + + However, qmp_query_cpus_fast() would not populate either the @arch field + or the @CpuInfoRISCV sub-structure, when TARGET_RISCV was defined; only + qmp_query_cpus() would. + + Assign @CpuInfoOther to the @riscv enum constant in @CpuInfoFast, and + populate only the @arch field in qmp_query_cpus_fast(). Getting CPU + state without interrupting KVM is an exceptional thing that only S390X + does currently. Quoting Cornelia Huck , "s390x is + exceptional in that it has state in QEMU that is actually interesting + for upper layers and can be retrieved without performance penalty". See + also + . + +Cc: Cornelia Huck +Cc: Eric Blake +Cc: Markus Armbruster +Cc: Viktor VM Mihajlovski +Cc: qemu-stable@nongnu.org +Fixes: ca230ff33f89bf7102cbfbc2328716da6750aaed +Fixes: 25fa194b7b11901561532e435beb83d046899f7a +Signed-off-by: Laszlo Ersek +Reviewed-by: Eric Blake +Reviewed-by: Cornelia Huck +Reviewed-by: Markus Armbruster +Message-Id: <20180427192852.15013-2-lersek@redhat.com> +Signed-off-by: Markus Armbruster +(cherry picked from commit 96054f56396eaa0b9b5c681fc3e42a0004b17ade) +Signed-off-by: Miroslav Rezanina +--- + cpus.c | 16 +++++++++++++++- + qapi/misc.json | 2 +- + 2 files changed, 16 insertions(+), 2 deletions(-) + +diff --git a/cpus.c b/cpus.c +index 398392b..4f83f16 100644 +--- a/cpus.c ++++ b/cpus.c +@@ -2218,11 +2218,25 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp) + info->value->props = props; + } + +-#if defined(TARGET_S390X) ++#if defined(TARGET_I386) ++ info->value->arch = CPU_INFO_ARCH_X86; ++#elif defined(TARGET_PPC) ++ info->value->arch = CPU_INFO_ARCH_PPC; ++#elif defined(TARGET_SPARC) ++ info->value->arch = CPU_INFO_ARCH_SPARC; ++#elif defined(TARGET_MIPS) ++ info->value->arch = CPU_INFO_ARCH_MIPS; ++#elif defined(TARGET_TRICORE) ++ info->value->arch = CPU_INFO_ARCH_TRICORE; ++#elif defined(TARGET_S390X) + s390_cpu = S390_CPU(cpu); + env = &s390_cpu->env; + info->value->arch = CPU_INFO_ARCH_S390; + info->value->u.s390.cpu_state = env->cpu_state; ++#elif defined(TARGET_RISCV) ++ info->value->arch = CPU_INFO_ARCH_RISCV; ++#else ++ info->value->arch = CPU_INFO_ARCH_OTHER; + #endif + if (!cur_item) { + head = cur_item = info; +diff --git a/qapi/misc.json b/qapi/misc.json +index 045eb7c..8b28270 100644 +--- a/qapi/misc.json ++++ b/qapi/misc.json +@@ -573,7 +573,7 @@ + 'mips': 'CpuInfoOther', + 'tricore': 'CpuInfoOther', + 's390': 'CpuInfoS390', +- 'riscv': 'CpuInfoRISCV', ++ 'riscv': 'CpuInfoOther', + 'other': 'CpuInfoOther' } } + + ## +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qapi-new-qmp-command-nbd-server-add-bitmap.patch b/SOURCES/kvm-qapi-new-qmp-command-nbd-server-add-bitmap.patch new file mode 100644 index 0000000..70f30e5 --- /dev/null +++ b/SOURCES/kvm-qapi-new-qmp-command-nbd-server-add-bitmap.patch @@ -0,0 +1,104 @@ +From 9949f65ad44a273fabbf98591ef98b4742e6882f Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:55:04 +0200 +Subject: [PATCH 79/89] qapi: new qmp command nbd-server-add-bitmap + +RH-Author: John Snow +Message-id: <20180718225511.14878-29-jsnow@redhat.com> +Patchwork-id: 81408 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 28/35] qapi: new qmp command nbd-server-add-bitmap +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +For now, the actual command ix x-nbd-server-add-bitmap, reflecting +the fact that we are still working on libvirt code that proves the +command works as needed, and also the fact that we may remove +bitmap-export-name (and just require that the exported name be the +bitmap name). + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20180609151758.17343-6-vsementsov@virtuozzo.com> +Reviewed-by: Eric Blake +[eblake: make the command experimental by adding x- prefix] +Signed-off-by: Eric Blake +(cherry picked from commit 767f0c7d6cddedbc97ad700bd1e0229cc2ce5eb5) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + blockdev-nbd.c | 23 +++++++++++++++++++++++ + qapi/block.json | 23 +++++++++++++++++++++++ + 2 files changed, 46 insertions(+) + +diff --git a/blockdev-nbd.c b/blockdev-nbd.c +index 65a8473..1ef1104 100644 +--- a/blockdev-nbd.c ++++ b/blockdev-nbd.c +@@ -220,3 +220,26 @@ void qmp_nbd_server_stop(Error **errp) + nbd_server_free(nbd_server); + nbd_server = NULL; + } ++ ++void qmp_x_nbd_server_add_bitmap(const char *name, const char *bitmap, ++ bool has_bitmap_export_name, ++ const char *bitmap_export_name, ++ Error **errp) ++{ ++ NBDExport *exp; ++ ++ if (!nbd_server) { ++ error_setg(errp, "NBD server not running"); ++ return; ++ } ++ ++ exp = nbd_export_find(name); ++ if (exp == NULL) { ++ error_setg(errp, "Export '%s' is not found", name); ++ return; ++ } ++ ++ nbd_export_bitmap(exp, bitmap, ++ has_bitmap_export_name ? bitmap_export_name : bitmap, ++ errp); ++} +diff --git a/qapi/block.json b/qapi/block.json +index f05b91a..ba85ceb 100644 +--- a/qapi/block.json ++++ b/qapi/block.json +@@ -364,6 +364,29 @@ + 'data': {'name': 'str', '*mode': 'NbdServerRemoveMode'} } + + ## ++# @x-nbd-server-add-bitmap: ++# ++# Expose a dirty bitmap associated with the selected export. The bitmap search ++# starts at the device attached to the export, and includes all backing files. ++# The exported bitmap is then locked until the NBD export is removed. ++# ++# @name: Export name. ++# ++# @bitmap: Bitmap name to search for. ++# ++# @bitmap-export-name: How the bitmap will be seen by nbd clients ++# (default @bitmap) ++# ++# Note: the client must use NBD_OPT_SET_META_CONTEXT with a query of ++# "qemu:dirty-bitmap:NAME" (where NAME matches @bitmap-export-name) to access ++# the exposed bitmap. ++# ++# Since: 3.0 ++## ++ { 'command': 'x-nbd-server-add-bitmap', ++ 'data': {'name': 'str', 'bitmap': 'str', '*bitmap-export-name': 'str'} } ++ ++## + # @nbd-server-stop: + # + # Stop QEMU's embedded NBD server, and unregister all devices previously +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-Add-list-of-bitmaps-to-ImageInfoSpecificQCow2.patch b/SOURCES/kvm-qcow2-Add-list-of-bitmaps-to-ImageInfoSpecificQCow2.patch new file mode 100644 index 0000000..d5df57c --- /dev/null +++ b/SOURCES/kvm-qcow2-Add-list-of-bitmaps-to-ImageInfoSpecificQCow2.patch @@ -0,0 +1,264 @@ +From bc648c67692348f9fa8cb091f5949c608dbc7565 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 21:48:37 +0100 +Subject: [PATCH 044/163] qcow2: Add list of bitmaps to ImageInfoSpecificQCow2 + +RH-Author: John Snow +Message-id: <20190320214838.22027-10-jsnow@redhat.com> +Patchwork-id: 85000 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 09/10] qcow2: Add list of bitmaps to ImageInfoSpecificQCow2 +Bugzilla: 1691048 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Andrey Shinkevich + +In the 'Format specific information' section of the 'qemu-img info' +command output, the supplemental information about existing QCOW2 +bitmaps will be shown, such as a bitmap name, flags and granularity: + +image: /vz/vmprivate/VM1/harddisk.hdd +file format: qcow2 +virtual size: 64G (68719476736 bytes) +disk size: 3.0M +cluster_size: 1048576 +Format specific information: + compat: 1.1 + lazy refcounts: true + bitmaps: + [0]: + flags: + [0]: in-use + [1]: auto + name: back-up1 + granularity: 65536 + [1]: + flags: + [0]: in-use + [1]: auto + name: back-up2 + granularity: 65536 + refcount bits: 16 + corrupt: false + +Signed-off-by: Andrey Shinkevich +Reviewed-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <1549638368-530182-3-git-send-email-andrey.shinkevich@virtuozzo.com> +Signed-off-by: Eric Blake +(cherry picked from commit b8968c875f4030dd6924d6971bb3d92dfa3d2f65) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/qcow2-bitmap.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + block/qcow2.c | 11 +++++++- + block/qcow2.h | 2 ++ + qapi/block-core.json | 41 +++++++++++++++++++++++++++- + 4 files changed, 128 insertions(+), 2 deletions(-) + +diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c +index a36773c..4899719 100644 +--- a/block/qcow2-bitmap.c ++++ b/block/qcow2-bitmap.c +@@ -1008,6 +1008,82 @@ fail: + return false; + } + ++ ++static Qcow2BitmapInfoFlagsList *get_bitmap_info_flags(uint32_t flags) ++{ ++ Qcow2BitmapInfoFlagsList *list = NULL; ++ Qcow2BitmapInfoFlagsList **plist = &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) { ++ Qcow2BitmapInfoFlagsList *entry = ++ g_new0(Qcow2BitmapInfoFlagsList, 1); ++ entry->value = map[i].info; ++ *plist = entry; ++ plist = &entry->next; ++ 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. ++ * In case of no bitmaps, the function returns NULL and ++ * the @errp parameter is not set. ++ * When bitmap information can not be obtained, the function returns ++ * NULL and the @errp parameter is set. ++ */ ++Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2BitmapList *bm_list; ++ Qcow2Bitmap *bm; ++ Qcow2BitmapInfoList *list = NULL; ++ Qcow2BitmapInfoList **plist = &list; ++ ++ if (s->nb_bitmaps == 0) { ++ return NULL; ++ } ++ ++ bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, ++ s->bitmap_directory_size, errp); ++ if (bm_list == NULL) { ++ return NULL; ++ } ++ ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ Qcow2BitmapInfo *info = g_new0(Qcow2BitmapInfo, 1); ++ Qcow2BitmapInfoList *obj = g_new0(Qcow2BitmapInfoList, 1); ++ info->granularity = 1U << bm->granularity_bits; ++ info->name = g_strdup(bm->name); ++ info->flags = get_bitmap_info_flags(bm->flags & ~BME_RESERVED_FLAGS); ++ obj->value = info; ++ *plist = obj; ++ plist = &obj->next; ++ } ++ ++ bitmap_list_free(bm_list); ++ ++ return list; ++} ++ + int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated, + Error **errp) + { +diff --git a/block/qcow2.c b/block/qcow2.c +index de9872f..21f7556 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -4185,7 +4185,7 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, + spec_info = g_new(ImageInfoSpecific, 1); + *spec_info = (ImageInfoSpecific){ + .type = IMAGE_INFO_SPECIFIC_KIND_QCOW2, +- .u.qcow2.data = g_new(ImageInfoSpecificQCow2, 1), ++ .u.qcow2.data = g_new0(ImageInfoSpecificQCow2, 1), + }; + if (s->qcow_version == 2) { + *spec_info->u.qcow2.data = (ImageInfoSpecificQCow2){ +@@ -4193,6 +4193,13 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, + .refcount_bits = s->refcount_bits, + }; + } else if (s->qcow_version == 3) { ++ Qcow2BitmapInfoList *bitmaps; ++ bitmaps = qcow2_get_bitmap_info_list(bs, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ qapi_free_ImageInfoSpecific(spec_info); ++ return NULL; ++ } + *spec_info->u.qcow2.data = (ImageInfoSpecificQCow2){ + .compat = g_strdup("1.1"), + .lazy_refcounts = s->compatible_features & +@@ -4202,6 +4209,8 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, + QCOW2_INCOMPAT_CORRUPT, + .has_corrupt = true, + .refcount_bits = s->refcount_bits, ++ .has_bitmaps = !!bitmaps, ++ .bitmaps = bitmaps, + }; + } else { + /* if this assertion fails, this probably means a new version was +diff --git a/block/qcow2.h b/block/qcow2.h +index 29b041c..2633e33 100644 +--- a/block/qcow2.h ++++ b/block/qcow2.h +@@ -676,6 +676,8 @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, + void **refcount_table, + int64_t *refcount_table_size); + bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp); ++Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs, ++ Error **errp); + int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated, + Error **errp); + int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp); +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 176c04e..5d6bb14 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -70,6 +70,8 @@ + # @encrypt: details about encryption parameters; only set if image + # is encrypted (since 2.10) + # ++# @bitmaps: A list of qcow2 bitmap details (since 4.0) ++# + # Since: 1.7 + ## + { 'struct': 'ImageInfoSpecificQCow2', +@@ -78,7 +80,8 @@ + '*lazy-refcounts': 'bool', + '*corrupt': 'bool', + 'refcount-bits': 'int', +- '*encrypt': 'ImageInfoSpecificQCow2Encryption' ++ '*encrypt': 'ImageInfoSpecificQCow2Encryption', ++ '*bitmaps': ['Qcow2BitmapInfo'] + } } + + ## +@@ -455,6 +458,42 @@ + 'status': 'DirtyBitmapStatus'} } + + ## ++# @Qcow2BitmapInfoFlags: ++# ++# An enumeration of flags that a bitmap can report to the user. ++# ++# @in-use: This flag is set by any process actively modifying the qcow2 file, ++# and cleared when the updated bitmap is flushed to the qcow2 image. ++# The presence of this flag in an offline image means that the bitmap ++# was not saved correctly after its last usage, and may contain ++# inconsistent data. ++# ++# @auto: The bitmap must reflect all changes of the virtual disk by any ++# application that would write to this qcow2 file. ++# ++# Since: 4.0 ++## ++{ 'enum': 'Qcow2BitmapInfoFlags', ++ 'data': ['in-use', 'auto'] } ++ ++## ++# @Qcow2BitmapInfo: ++# ++# Qcow2 bitmap information. ++# ++# @name: the name of the bitmap ++# ++# @granularity: granularity of the bitmap in bytes ++# ++# @flags: flags of the bitmap ++# ++# Since: 4.0 ++## ++{ 'struct': 'Qcow2BitmapInfo', ++ 'data': {'name': 'str', 'granularity': 'uint32', ++ 'flags': ['Qcow2BitmapInfoFlags'] } } ++ ++## + # @BlockLatencyHistogramInfo: + # + # Block latency histogram. +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-Assign-the-L2-cache-relatively-to-the-image-si.patch b/SOURCES/kvm-qcow2-Assign-the-L2-cache-relatively-to-the-image-si.patch new file mode 100644 index 0000000..c03d9d2 --- /dev/null +++ b/SOURCES/kvm-qcow2-Assign-the-L2-cache-relatively-to-the-image-si.patch @@ -0,0 +1,231 @@ +From 2790e1d1df870d455bbd493dc7c342e34df6e4dd Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 19 Feb 2019 17:00:18 +0100 +Subject: [PATCH 17/23] qcow2: Assign the L2 cache relatively to the image size + +RH-Author: Kevin Wolf +Message-id: <20190219170023.27826-9-kwolf@redhat.com> +Patchwork-id: 84548 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 08/13] qcow2: Assign the L2 cache relatively to the image size +Bugzilla: 1656913 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Leonid Bloch + +Sufficient L2 cache can noticeably improve the performance when using +large images with frequent I/O. + +Previously, unless 'cache-size' was specified and was large enough, the +L2 cache was set to a certain size without taking the virtual image size +into account. + +Now, the L2 cache assignment is aware of the virtual size of the image, +and will cover the entire image, unless the cache size needed for that is +larger than a certain maximum. This maximum is set to 1 MB by default +(enough to cover an 8 GB image with the default cluster size) but can +be increased or decreased using the 'l2-cache-size' option. This option +was previously documented as the *maximum* L2 cache size, and this patch +makes it behave as such, instead of as a constant size. Also, the +existing option 'cache-size' can limit the sum of both L2 and refcount +caches, as previously. + +Signed-off-by: Leonid Bloch +Reviewed-by: Alberto Garcia +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit b749562d9822d14ef69c9eaa5f85903010b86c30) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/qcow2.c | 21 +++++++++------------ + block/qcow2.h | 4 +--- + docs/qcow2-cache.txt | 15 ++++++++++----- + qemu-options.hx | 6 +++--- + tests/qemu-iotests/137 | 8 +++++++- + tests/qemu-iotests/137.out | 4 +++- + 6 files changed, 33 insertions(+), 25 deletions(-) + +diff --git a/block/qcow2.c b/block/qcow2.c +index f3b2860..fc6bddd 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -773,29 +773,35 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, + uint64_t *refcount_cache_size, Error **errp) + { + BDRVQcow2State *s = bs->opaque; +- uint64_t combined_cache_size; ++ uint64_t combined_cache_size, l2_cache_max_setting; + bool l2_cache_size_set, refcount_cache_size_set, combined_cache_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_cache = virtual_disk_size / (s->cluster_size / 8); + + 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); + + combined_cache_size = qemu_opt_get_size(opts, QCOW2_OPT_CACHE_SIZE, 0); +- *l2_cache_size = qemu_opt_get_size(opts, QCOW2_OPT_L2_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 " + "the same time"); + return; +- } else if (*l2_cache_size > combined_cache_size) { ++ } 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; +@@ -810,9 +816,6 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, + } else if (refcount_cache_size_set) { + *l2_cache_size = combined_cache_size - *refcount_cache_size; + } else { +- uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; +- uint64_t max_l2_cache = virtual_disk_size / (s->cluster_size / 8); +- + /* 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) { +@@ -824,12 +827,6 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, + *l2_cache_size = combined_cache_size - *refcount_cache_size; + } + } +- } else { +- if (!l2_cache_size_set) { +- *l2_cache_size = MAX(DEFAULT_L2_CACHE_SIZE, +- (uint64_t)DEFAULT_L2_CACHE_CLUSTERS +- * s->cluster_size); +- } + } + /* l2_cache_size and refcount_cache_size are ensured to have at least + * their minimum values in qcow2_update_options_prepare() */ +diff --git a/block/qcow2.h b/block/qcow2.h +index f73a48a..d0dd4a2 100644 +--- a/block/qcow2.h ++++ b/block/qcow2.h +@@ -74,9 +74,7 @@ + /* Must be at least 4 to cover all cases of refcount table growth */ + #define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */ + +-/* Whichever is more */ +-#define DEFAULT_L2_CACHE_CLUSTERS 8 /* clusters */ +-#define DEFAULT_L2_CACHE_SIZE S_1MiB ++#define DEFAULT_L2_CACHE_MAX_SIZE S_1MiB + + #define DEFAULT_CLUSTER_SIZE S_64KiB + +diff --git a/docs/qcow2-cache.txt b/docs/qcow2-cache.txt +index 7e28b41..750447e 100644 +--- a/docs/qcow2-cache.txt ++++ b/docs/qcow2-cache.txt +@@ -125,8 +125,12 @@ There are a few things that need to be taken into account: + - Both caches must have a size that is a multiple of the cluster size + (or the cache entry size: see "Using smaller cache sizes" below). + +- - The default L2 cache size is 8 clusters or 1MB (whichever is more), +- and the minimum is 2 clusters (or 2 cache entries, see below). ++ - The maximum L2 cache size is 1 MB by default (enough for full coverage ++ of 8 GB images, with the default cluster size). This value can be ++ modified using the "l2-cache-size" option. QEMU will not use more memory ++ than needed to hold all of the image's L2 tables, regardless of this max. ++ value. The minimal L2 cache size is 2 clusters (or 2 cache entries, see ++ below). + + - The default (and minimum) refcount cache size is 4 clusters. + +@@ -184,9 +188,10 @@ Some things to take into account: + always uses the cluster size as the entry size. + + - If the L2 cache is big enough to hold all of the image's L2 tables +- (as explained in the "Choosing the right cache sizes" section +- earlier in this document) then none of this is necessary and you +- can omit the "l2-cache-entry-size" parameter altogether. ++ (as explained in the "Choosing the right cache sizes" and "How to ++ configure the cache sizes" sections in this document) then none of ++ this is necessary and you can omit the "l2-cache-entry-size" ++ parameter altogether. + + + Reducing the memory usage +diff --git a/qemu-options.hx b/qemu-options.hx +index 3308b94..e1fbc5b 100644 +--- a/qemu-options.hx ++++ b/qemu-options.hx +@@ -756,9 +756,9 @@ The maximum total size of the L2 table and refcount block caches in bytes + + @item l2-cache-size + The maximum size of the L2 table cache in bytes +-(default: if cache-size is not defined - 1048576 bytes or 8 clusters, whichever +-is larger; otherwise, as large as possible or needed within the cache-size, +-while permitting the requested or the minimal refcount cache size) ++(default: if cache-size is not specified - 1M; otherwise, as large as possible ++within the cache-size, while permitting the requested or the minimal refcount ++cache size) + + @item refcount-cache-size + The maximum size of the refcount block cache in bytes +diff --git a/tests/qemu-iotests/137 b/tests/qemu-iotests/137 +index 8796562..19e8597 100755 +--- a/tests/qemu-iotests/137 ++++ b/tests/qemu-iotests/137 +@@ -109,7 +109,6 @@ $QEMU_IO \ + -c "reopen -o cache-size=1M,l2-cache-size=64k,refcount-cache-size=64k" \ + -c "reopen -o cache-size=1M,l2-cache-size=2M" \ + -c "reopen -o cache-size=1M,refcount-cache-size=2M" \ +- -c "reopen -o l2-cache-size=256T" \ + -c "reopen -o l2-cache-entry-size=33k" \ + -c "reopen -o l2-cache-entry-size=128k" \ + -c "reopen -o refcount-cache-size=256T" \ +@@ -119,6 +118,13 @@ $QEMU_IO \ + -c "reopen -o cache-clean-interval=-1" \ + "$TEST_IMG" | _filter_qemu_io + ++IMGOPTS="cluster_size=256k" _make_test_img 32P ++$QEMU_IO \ ++ -c "reopen -o l2-cache-entry-size=512,l2-cache-size=1T" \ ++ "$TEST_IMG" | _filter_qemu_io ++ ++_make_test_img 64M ++ + echo + echo === Test transaction semantics === + echo +diff --git a/tests/qemu-iotests/137.out b/tests/qemu-iotests/137.out +index 96724a6..afcc000 100644 +--- a/tests/qemu-iotests/137.out ++++ b/tests/qemu-iotests/137.out +@@ -19,7 +19,6 @@ Parameter 'lazy-refcounts' expects 'on' or 'off' + cache-size, l2-cache-size and refcount-cache-size may not be set the same time + l2-cache-size may not exceed cache-size + refcount-cache-size may not exceed cache-size +-L2 cache size too big + L2 cache entry size must be a power of two between 512 and the cluster size (65536) + L2 cache entry size must be a power of two between 512 and the cluster size (65536) + Refcount cache size too big +@@ -27,6 +26,9 @@ Conflicting values for qcow2 options 'overlap-check' ('constant') and 'overlap-c + Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all + Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all + Cache clean interval too big ++Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=36028797018963968 ++L2 cache size too big ++Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + + === Test transaction semantics === + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-Avoid-duplication-in-setting-the-refcount-cach.patch b/SOURCES/kvm-qcow2-Avoid-duplication-in-setting-the-refcount-cach.patch new file mode 100644 index 0000000..8736442 --- /dev/null +++ b/SOURCES/kvm-qcow2-Avoid-duplication-in-setting-the-refcount-cach.patch @@ -0,0 +1,52 @@ +From 7eca8ae696c2f3be102eef0a0e1bd0e6f24129cb Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 19 Feb 2019 17:00:17 +0100 +Subject: [PATCH 16/23] qcow2: Avoid duplication in setting the refcount cache + size + +RH-Author: Kevin Wolf +Message-id: <20190219170023.27826-8-kwolf@redhat.com> +Patchwork-id: 84547 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 07/13] qcow2: Avoid duplication in setting the refcount cache size +Bugzilla: 1656913 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Leonid Bloch + +The refcount cache size does not need to be set to its minimum value in +read_cache_sizes(), as it is set to at least its minimum value in +qcow2_update_options_prepare(). + +Signed-off-by: Leonid Bloch +Reviewed-by: Alberto Garcia +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit 657ada52abb85140e56949f522ecec527b256450) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/qcow2.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/block/qcow2.c b/block/qcow2.c +index 3859112..f3b2860 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -830,10 +830,9 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, + (uint64_t)DEFAULT_L2_CACHE_CLUSTERS + * s->cluster_size); + } +- if (!refcount_cache_size_set) { +- *refcount_cache_size = min_refcount_cache; +- } + } ++ /* 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 || +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-Do-not-mark-inactive-images-corrupt.patch b/SOURCES/kvm-qcow2-Do-not-mark-inactive-images-corrupt.patch new file mode 100644 index 0000000..8f5eb86 --- /dev/null +++ b/SOURCES/kvm-qcow2-Do-not-mark-inactive-images-corrupt.patch @@ -0,0 +1,57 @@ +From f853102082f547314221cdd5846493252826086f Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 18:00:54 +0200 +Subject: [PATCH 48/54] qcow2: Do not mark inactive images corrupt + +RH-Author: Max Reitz +Message-id: <20180618180055.22739-3-mreitz@redhat.com> +Patchwork-id: 80793 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 2/3] qcow2: Do not mark inactive images corrupt +Bugzilla: 1588039 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf +RH-Acked-by: John Snow + +When signaling a corruption on a read-only image, qcow2 already makes +fatal events non-fatal (i.e., they will not result in the image being +closed, and the image header's corrupt flag will not be set). This is +necessary because we cannot set the corrupt flag on read-only images, +and it is possible because further corruption of read-only images is +impossible. + +Inactive images are effectively read-only, too, so we should do the same +for them. bdrv_is_writable() can tell us whether an image can actually +be written to, so use its result instead of !bs->read_only. + +(Otherwise, the assert(!(bs->open_flags & BDRV_O_INACTIVE)) in +bdrv_co_pwritev() will fail, crashing qemu.) + +Cc: qemu-stable@nongnu.org +Signed-off-by: Max Reitz +Message-id: 20180606193702.7113-3-mreitz@redhat.com +Reviewed-by: John Snow +Reviewed-by: Jeff Cody +Signed-off-by: Max Reitz +(cherry picked from commit ddf3b47ef4b5ed0bf6558d4c2c8ae130b8d8a580) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/qcow2.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/block/qcow2.c b/block/qcow2.c +index 35842c5..26a6a7f 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -4383,7 +4383,7 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, + char *message; + va_list ap; + +- fatal = fatal && !bs->read_only; ++ fatal = fatal && bdrv_is_writable(bs); + + if (s->signaled_corruption && + (!fatal || (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT))) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-Explicit-number-replaced-by-a-constant.patch b/SOURCES/kvm-qcow2-Explicit-number-replaced-by-a-constant.patch new file mode 100644 index 0000000..288affa --- /dev/null +++ b/SOURCES/kvm-qcow2-Explicit-number-replaced-by-a-constant.patch @@ -0,0 +1,52 @@ +From 01cb24169e6f707daea28e19dbb9cbc59180023c Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 19 Feb 2019 17:00:22 +0100 +Subject: [PATCH 21/23] qcow2: Explicit number replaced by a constant + +RH-Author: Kevin Wolf +Message-id: <20190219170023.27826-13-kwolf@redhat.com> +Patchwork-id: 84551 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 12/13] qcow2: Explicit number replaced by a constant +Bugzilla: 1656913 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Leonid Bloch + +Signed-off-by: Leonid Bloch +Reviewed-by: Alberto Garcia +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit bd016b912cc68c6f6c68cd5acb2e13126bd9e05c) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/qcow2.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/block/qcow2.c b/block/qcow2.c +index acd076c..114dcdd 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -1321,7 +1321,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, + /* 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 / 512; ++ 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; +@@ -3494,7 +3494,7 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, + goto fail; + } + +- old_length = bs->total_sectors * 512; ++ old_length = bs->total_sectors * BDRV_SECTOR_SIZE; + new_l1_size = size_to_l1(s, offset); + + if (offset < old_length) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-Fix-Coverity-warning-when-calculating-the-refc.patch b/SOURCES/kvm-qcow2-Fix-Coverity-warning-when-calculating-the-refc.patch new file mode 100644 index 0000000..9cd8c8f --- /dev/null +++ b/SOURCES/kvm-qcow2-Fix-Coverity-warning-when-calculating-the-refc.patch @@ -0,0 +1,75 @@ +From 5a43aa56f4a0435e9e36884088673513444b8129 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 19 Feb 2019 17:00:13 +0100 +Subject: [PATCH 12/23] qcow2: Fix Coverity warning when calculating the + refcount cache size + +RH-Author: Kevin Wolf +Message-id: <20190219170023.27826-4-kwolf@redhat.com> +Patchwork-id: 84543 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 03/13] qcow2: Fix Coverity warning when calculating the refcount cache size +Bugzilla: 1656913 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Alberto Garcia + +MIN_REFCOUNT_CACHE_SIZE is 4 and the cluster size is guaranteed to be +at most 2MB, so the minimum refcount cache size (in bytes) is always +going to fit in a 32-bit integer. + +Coverity doesn't know that, and since we're storing the result in a +uint64_t (*refcount_cache_size) it thinks that we need the 64 bits and +that we probably want to do a 64-bit multiplication to prevent the +result from being truncated. + +This is a false positive in this case, but it's a fair warning. +We could do a 64-bit multiplication to get rid of it, but since we +know that a 32-bit variable is enough to store this value let's simply +reuse min_refcount_cache, make it a normal int and stop doing casts. + +Reported-by: Peter Maydell +Signed-off-by: Alberto Garcia +Reviewed-by: Eric Blake +Signed-off-by: Kevin Wolf +(cherry picked from commit 7af5eea9b34ffb7a9a9fc25ba71998a02b76e159) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/qcow2.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/block/qcow2.c b/block/qcow2.c +index 4b65e4c..a0f7234 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -775,6 +775,7 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, + BDRVQcow2State *s = bs->opaque; + uint64_t combined_cache_size; + bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set; ++ int min_refcount_cache = MIN_REFCOUNT_CACHE_SIZE * 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); +@@ -811,8 +812,6 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, + } else { + uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; + uint64_t max_l2_cache = virtual_disk_size / (s->cluster_size / 8); +- uint64_t min_refcount_cache = +- (uint64_t) MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; + + /* Assign as much memory as possible to the L2 cache, and + * use the remainder for the refcount cache */ +@@ -832,7 +831,7 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, + * s->cluster_size); + } + if (!refcount_cache_size_set) { +- *refcount_cache_size = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; ++ *refcount_cache_size = min_refcount_cache; + } + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-Fix-cache-clean-interval-documentation.patch b/SOURCES/kvm-qcow2-Fix-cache-clean-interval-documentation.patch new file mode 100644 index 0000000..4e31c2c --- /dev/null +++ b/SOURCES/kvm-qcow2-Fix-cache-clean-interval-documentation.patch @@ -0,0 +1,94 @@ +From c51af90d479b6fb65a64be78c94b38726f4d466b Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 19 Feb 2019 17:00:23 +0100 +Subject: [PATCH 22/23] qcow2: Fix cache-clean-interval documentation + +RH-Author: Kevin Wolf +Message-id: <20190219170023.27826-14-kwolf@redhat.com> +Patchwork-id: 84552 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 13/13] qcow2: Fix cache-clean-interval documentation +Bugzilla: 1656913 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Leonid Bloch + +Fixing cache-clean-interval documentation following the recent change to +a default of 600 seconds on supported plarforms (only Linux currently). + +Signed-off-by: Leonid Bloch +Reviewed-by: Eric Blake +Signed-off-by: Kevin Wolf +(cherry picked from commit e3a7b4556ee33feba2b396769a9c8354be06b024) +Signed-off-by: Miroslav Rezanina +--- + docs/qcow2-cache.txt | 20 ++++++++++---------- + qapi/block-core.json | 3 ++- + qemu-options.hx | 3 ++- + 3 files changed, 14 insertions(+), 12 deletions(-) + +diff --git a/docs/qcow2-cache.txt b/docs/qcow2-cache.txt +index 59358b8..c459bf5 100644 +--- a/docs/qcow2-cache.txt ++++ b/docs/qcow2-cache.txt +@@ -202,18 +202,18 @@ Reducing the memory usage + It is possible to clean unused cache entries in order to reduce the + memory usage during periods of low I/O activity. + +-The parameter "cache-clean-interval" defines an interval (in seconds). +-All cache entries that haven't been accessed during that interval are +-removed from memory. ++The parameter "cache-clean-interval" defines an interval (in seconds), ++after which all the cache entries that haven't been accessed during the ++interval are removed from memory. Setting this parameter to 0 disables this ++feature. + +-This example removes all unused cache entries every 15 minutes: ++The following example removes all unused cache entries every 15 minutes: + + -drive file=hd.qcow2,cache-clean-interval=900 + +-If unset, the default value for this parameter is 600. Setting it to 0 +-disables this feature. ++If unset, the default value for this parameter is 600 on platforms which ++support this functionality, and is 0 (disabled) on other platforms. + +-Note that this functionality currently relies on the MADV_DONTNEED +-argument for madvise() to actually free the memory. This is a +-Linux-specific feature, so cache-clean-interval is not supported in +-other systems. ++This functionality currently relies on the MADV_DONTNEED argument for ++madvise() to actually free the memory. This is a Linux-specific feature, ++so cache-clean-interval is not supported on other systems. +diff --git a/qapi/block-core.json b/qapi/block-core.json +index f43124e..9c8c9ff 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -2873,7 +2873,8 @@ + # + # @cache-clean-interval: clean unused entries in the L2 and refcount + # caches. The interval is in seconds. The default value +-# is 600, and 0 disables this feature. (since 2.5) ++# is 600 on supporting platforms, and 0 on other ++# platforms. 0 disables this feature. (since 2.5) + # + # @encrypt: Image decryption options. Mandatory for + # encrypted images, except when doing a metadata-only +diff --git a/qemu-options.hx b/qemu-options.hx +index f7c9e23..e1ac8ae 100644 +--- a/qemu-options.hx ++++ b/qemu-options.hx +@@ -767,7 +767,8 @@ it which is not used for the L2 cache) + + @item cache-clean-interval + Clean unused entries in the L2 and refcount caches. The interval is in seconds. +-The default value is 600. Setting it to 0 disables this feature. ++The default value is 600 on supporting platforms, and 0 on other platforms. ++Setting it to 0 disables this feature. + + @item pass-discard-request + Whether discard requests to the qcow2 device should be forwarded to the data +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-Fix-qcow2_truncate-error-return-value.patch b/SOURCES/kvm-qcow2-Fix-qcow2_truncate-error-return-value.patch new file mode 100644 index 0000000..70a5a29 --- /dev/null +++ b/SOURCES/kvm-qcow2-Fix-qcow2_truncate-error-return-value.patch @@ -0,0 +1,44 @@ +From 2854a11fc02a664688ddc3ccbd7786f0aec219bd Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Thu, 12 Jul 2018 14:42:53 +0200 +Subject: [PATCH 34/89] qcow2: Fix qcow2_truncate() error return value + +RH-Author: Kevin Wolf +Message-id: <20180712144258.17303-2-kwolf@redhat.com> +Patchwork-id: 81325 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/6] qcow2: Fix qcow2_truncate() error return value +Bugzilla: 1595173 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow + +If qcow2_alloc_clusters_at() returns an error, we do need to negate it +to get back the positive errno code for error_setg_errno(), but we still +need to return the negative error code. + +Fixes: 772d1f973f87269f6a4a4ea4b880680f3779bbdf +Signed-off-by: Kevin Wolf +Reviewed-by: Stefan Hajnoczi +(cherry picked from commit ae5475e82fd1ebb24f4f77cf28f59ca6548c6136) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/qcow2.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/block/qcow2.c b/block/qcow2.c +index da74e2a..dbd448c 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -3594,7 +3594,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, + if (clusters_allocated < 0) { + error_setg_errno(errp, -clusters_allocated, + "Failed to allocate data clusters"); +- return -clusters_allocated; ++ return clusters_allocated; + } + + assert(clusters_allocated == nb_new_data_clusters); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-Fix-src_offset-in-copy-offloading.patch b/SOURCES/kvm-qcow2-Fix-src_offset-in-copy-offloading.patch new file mode 100644 index 0000000..c2b2623 --- /dev/null +++ b/SOURCES/kvm-qcow2-Fix-src_offset-in-copy-offloading.patch @@ -0,0 +1,82 @@ +From 9cc4ecc4169dc8a8636a1ddc8dbe454a274e1432 Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Fri, 29 Jun 2018 06:11:51 +0200 +Subject: [PATCH 47/57] qcow2: Fix src_offset in copy offloading + +RH-Author: Fam Zheng +Message-id: <20180629061153.12687-12-famz@redhat.com> +Patchwork-id: 81163 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 11/13] qcow2: Fix src_offset in copy offloading +Bugzilla: 1482537 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Kevin Wolf + +Not updating src_offset will result in wrong data being written to dst +image. + +Reported-by: Max Reitz +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + block/qcow2.c | 1 + + tests/qemu-iotests/063 | 9 +++++++++ + tests/qemu-iotests/063.out | 12 ++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/block/qcow2.c b/block/qcow2.c +index c85ebcb..4404dc7 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -3416,6 +3416,7 @@ qcow2_co_copy_range_to(BlockDriverState *bs, + } + + bytes -= cur_bytes; ++ src_offset += cur_bytes; + dst_offset += cur_bytes; + } + ret = 0; +diff --git a/tests/qemu-iotests/063 b/tests/qemu-iotests/063 +index e4f6ea9..adc037c 100755 +--- a/tests/qemu-iotests/063 ++++ b/tests/qemu-iotests/063 +@@ -91,6 +91,15 @@ if $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n "$TEST_IMG.orig" "$TEST_IMG" >/dev + exit 1 + fi + ++echo "== Regression testing for copy offloading bug ==" ++ ++_make_test_img 1M ++TEST_IMG="$TEST_IMG.target" _make_test_img 1M ++$QEMU_IO -c 'write -P 1 0 512k' -c 'write -P 2 512k 512k' "$TEST_IMG" | _filter_qemu_io ++$QEMU_IO -c 'write -P 4 512k 512k' -c 'write -P 3 0 512k' "$TEST_IMG.target" | _filter_qemu_io ++$QEMU_IMG convert -n -O $IMGFMT "$TEST_IMG" "$TEST_IMG.target" ++$QEMU_IMG compare "$TEST_IMG" "$TEST_IMG.target" ++ + echo "*** done" + rm -f $seq.full + status=0 +diff --git a/tests/qemu-iotests/063.out b/tests/qemu-iotests/063.out +index de1c99a..7b691b2 100644 +--- a/tests/qemu-iotests/063.out ++++ b/tests/qemu-iotests/063.out +@@ -7,4 +7,16 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 + No errors were found on the image. + == Testing conversion to a smaller file fails == + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2097152 ++== Regression testing for copy offloading bug == ++Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 ++Formatting 'TEST_DIR/t.IMGFMT.target', fmt=IMGFMT size=1048576 ++wrote 524288/524288 bytes at offset 0 ++512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++wrote 524288/524288 bytes at offset 524288 ++512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++wrote 524288/524288 bytes at offset 524288 ++512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++wrote 524288/524288 bytes at offset 0 ++512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++Images are identical. + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-Free-allocated-clusters-on-write-error.patch b/SOURCES/kvm-qcow2-Free-allocated-clusters-on-write-error.patch new file mode 100644 index 0000000..faca4b4 --- /dev/null +++ b/SOURCES/kvm-qcow2-Free-allocated-clusters-on-write-error.patch @@ -0,0 +1,83 @@ +From d53f85c0fbaf3b7936e7a4b0f019be986a39fe7a Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Mon, 2 Jul 2018 15:40:07 +0200 +Subject: [PATCH 54/57] qcow2: Free allocated clusters on write error + +RH-Author: Kevin Wolf +Message-id: <20180702154008.15533-3-kwolf@redhat.com> +Patchwork-id: 81185 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 2/3] qcow2: Free allocated clusters on write error +Bugzilla: 1528541 +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng +RH-Acked-by: Stefan Hajnoczi + +If we managed to allocate the clusters, but then failed to write the +data, there's a good chance that we'll still be able to free the +clusters again in order to avoid cluster leaks (the refcounts are +cached, so even if we can't write them out right now, we may be able to +do so when the VM is resumed after a werror=stop/enospc pause). + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: Eric Blake +Tested-by: Eric Blake +(cherry picked from commit 8b24cd141549b5b264baeddd4e72902cfb5de23b) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/qcow2-cluster.c | 11 +++++++++++ + block/qcow2.c | 2 ++ + block/qcow2.h | 1 + + 3 files changed, 14 insertions(+) + +diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c +index 1aee726..c90e2ec 100644 +--- a/block/qcow2-cluster.c ++++ b/block/qcow2-cluster.c +@@ -994,6 +994,17 @@ err: + return ret; + } + ++/** ++ * Frees the allocated clusters because the request failed and they won't ++ * actually be linked. ++ */ ++void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ qcow2_free_clusters(bs, m->alloc_offset, m->nb_clusters << s->cluster_bits, ++ QCOW2_DISCARD_NEVER); ++} ++ + /* + * Returns the number of contiguous clusters that can be used for an allocating + * write, but require COW to be performed (this includes yet unallocated space, +diff --git a/block/qcow2.c b/block/qcow2.c +index 4404dc7..da74e2a 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -1771,6 +1771,8 @@ static coroutine_fn int qcow2_handle_l2meta(BlockDriverState *bs, + if (ret) { + goto out; + } ++ } else { ++ qcow2_alloc_cluster_abort(bs, l2meta); + } + + /* Take the request off the list of running requests */ +diff --git a/block/qcow2.h b/block/qcow2.h +index adf5c39..b5e2aa3 100644 +--- a/block/qcow2.h ++++ b/block/qcow2.h +@@ -618,6 +618,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, + int compressed_size); + + int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); ++void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m); + int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, enum qcow2_discard_type type, + bool full_discard); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-Give-the-refcount-cache-the-minimum-possible-s.patch b/SOURCES/kvm-qcow2-Give-the-refcount-cache-the-minimum-possible-s.patch new file mode 100644 index 0000000..9a04caf --- /dev/null +++ b/SOURCES/kvm-qcow2-Give-the-refcount-cache-the-minimum-possible-s.patch @@ -0,0 +1,152 @@ +From f212cada55593c16cbfdfce2eee75ccd7b375e38 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 19 Feb 2019 17:00:11 +0100 +Subject: [PATCH 10/23] qcow2: Give the refcount cache the minimum possible + size by default + +RH-Author: Kevin Wolf +Message-id: <20190219170023.27826-2-kwolf@redhat.com> +Patchwork-id: 84541 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 01/13] qcow2: Give the refcount cache the minimum possible size by default +Bugzilla: 1656913 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Alberto Garcia + +The L2 and refcount caches have default sizes that can be overridden +using the l2-cache-size and refcount-cache-size (an additional +parameter named cache-size sets the combined size of both caches). + +Unless forced by one of the aforementioned parameters, QEMU will set +the unspecified sizes so that the L2 cache is 4 times larger than the +refcount cache. + +This is based on the premise that the refcount metadata needs to be +only a fourth of the L2 metadata to cover the same amount of disk +space. This is incorrect for two reasons: + + a) The amount of disk covered by an L2 table depends solely on the + cluster size, but in the case of a refcount block it depends on + the cluster size *and* the width of each refcount entry. + The 4/1 ratio is only valid with 16-bit entries (the default). + + b) When we talk about disk space and L2 tables we are talking about + guest space (L2 tables map guest clusters to host clusters), + whereas refcount blocks are used for host clusters (including + L1/L2 tables and the refcount blocks themselves). On a fully + populated (and uncompressed) qcow2 file, image size > virtual size + so there are more refcount entries than L2 entries. + +Problem (a) could be fixed by adjusting the algorithm to take into +account the refcount entry width. Problem (b) could be fixed by +increasing a bit the refcount cache size to account for the clusters +used for qcow2 metadata. + +However this patch takes a completely different approach and instead +of keeping a ratio between both cache sizes it assigns as much as +possible to the L2 cache and the remainder to the refcount cache. + +The reason is that L2 tables are used for every single I/O request +from the guest and the effect of increasing the cache is significant +and clearly measurable. Refcount blocks are however only used for +cluster allocation and internal snapshots and in practice are accessed +sequentially in most cases, so the effect of increasing the cache is +negligible (even when doing random writes from the guest). + +So, make the refcount cache as small as possible unless the user +explicitly asks for a larger one. + +Signed-off-by: Alberto Garcia +Reviewed-by: Eric Blake +Reviewed-by: Max Reitz +Message-id: 9695182c2eb11b77cb319689a1ebaa4e7c9d6591.1523968389.git.berto@igalia.com +Signed-off-by: Max Reitz +(cherry picked from commit 52253998ec3e523c9e20ae81e2a6431d8ff733ba) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/qcow2.c | 31 +++++++++++++++++++------------ + block/qcow2.h | 4 ---- + tests/qemu-iotests/137.out | 2 +- + 3 files changed, 20 insertions(+), 17 deletions(-) + +diff --git a/block/qcow2.c b/block/qcow2.c +index 36d1152..4b65e4c 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -809,23 +809,30 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, + } else if (refcount_cache_size_set) { + *l2_cache_size = combined_cache_size - *refcount_cache_size; + } else { +- *refcount_cache_size = combined_cache_size +- / (DEFAULT_L2_REFCOUNT_SIZE_RATIO + 1); +- *l2_cache_size = combined_cache_size - *refcount_cache_size; ++ uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; ++ uint64_t max_l2_cache = virtual_disk_size / (s->cluster_size / 8); ++ uint64_t min_refcount_cache = ++ (uint64_t) MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; ++ ++ /* 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; ++ } + } + } else { +- if (!l2_cache_size_set && !refcount_cache_size_set) { ++ if (!l2_cache_size_set) { + *l2_cache_size = MAX(DEFAULT_L2_CACHE_BYTE_SIZE, + (uint64_t)DEFAULT_L2_CACHE_CLUSTERS + * s->cluster_size); +- *refcount_cache_size = *l2_cache_size +- / DEFAULT_L2_REFCOUNT_SIZE_RATIO; +- } else if (!l2_cache_size_set) { +- *l2_cache_size = *refcount_cache_size +- * DEFAULT_L2_REFCOUNT_SIZE_RATIO; +- } else if (!refcount_cache_size_set) { +- *refcount_cache_size = *l2_cache_size +- / DEFAULT_L2_REFCOUNT_SIZE_RATIO; ++ } ++ if (!refcount_cache_size_set) { ++ *refcount_cache_size = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; + } + } + +diff --git a/block/qcow2.h b/block/qcow2.h +index 43163b2..3d92cdb 100644 +--- a/block/qcow2.h ++++ b/block/qcow2.h +@@ -77,10 +77,6 @@ + #define DEFAULT_L2_CACHE_CLUSTERS 8 /* clusters */ + #define DEFAULT_L2_CACHE_BYTE_SIZE 1048576 /* bytes */ + +-/* The refblock cache needs only a fourth of the L2 cache size to cover as many +- * clusters */ +-#define DEFAULT_L2_REFCOUNT_SIZE_RATIO 4 +- + #define DEFAULT_CLUSTER_SIZE 65536 + + +diff --git a/tests/qemu-iotests/137.out b/tests/qemu-iotests/137.out +index e28e1ea..96724a6 100644 +--- a/tests/qemu-iotests/137.out ++++ b/tests/qemu-iotests/137.out +@@ -22,7 +22,7 @@ refcount-cache-size may not exceed cache-size + L2 cache size too big + L2 cache entry size must be a power of two between 512 and the cluster size (65536) + L2 cache entry size must be a power of two between 512 and the cluster size (65536) +-L2 cache size too big ++Refcount cache size too big + Conflicting values for qcow2 options 'overlap-check' ('constant') and 'overlap-check.template' ('all') + Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all + Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-Implement-copy-offloading.patch b/SOURCES/kvm-qcow2-Implement-copy-offloading.patch new file mode 100644 index 0000000..a8bc9a3 --- /dev/null +++ b/SOURCES/kvm-qcow2-Implement-copy-offloading.patch @@ -0,0 +1,301 @@ +From 3650b202ee9499c9c234eb05d296eb4d35f52f34 Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Fri, 29 Jun 2018 06:11:44 +0200 +Subject: [PATCH 40/57] qcow2: Implement copy offloading + +RH-Author: Fam Zheng +Message-id: <20180629061153.12687-5-famz@redhat.com> +Patchwork-id: 81154 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 04/13] qcow2: Implement copy offloading +Bugzilla: 1482537 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Kevin Wolf + +The two callbacks are implemented quite similarly to the read/write +functions: bdrv_co_copy_range_from maps for read and calls into bs->file +or bs->backing depending on the allocation status; bdrv_co_copy_range_to +maps for write and calls into bs->file. + +Reviewed-by: Stefan Hajnoczi +Signed-off-by: Fam Zheng +Message-id: 20180601092648.24614-5-famz@redhat.com +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit fd9fcd37a8645efe322956d94f76e90135522a16) +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + block/qcow2.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++++++-------- + 1 file changed, 199 insertions(+), 30 deletions(-) + +diff --git a/block/qcow2.c b/block/qcow2.c +index 092db81..c85ebcb 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -1756,6 +1756,39 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs, + return status; + } + ++static coroutine_fn int qcow2_handle_l2meta(BlockDriverState *bs, ++ QCowL2Meta **pl2meta, ++ bool link_l2) ++{ ++ int ret = 0; ++ QCowL2Meta *l2meta = *pl2meta; ++ ++ while (l2meta != NULL) { ++ QCowL2Meta *next; ++ ++ if (!ret && link_l2) { ++ ret = qcow2_alloc_cluster_link_l2(bs, l2meta); ++ if (ret) { ++ goto out; ++ } ++ } ++ ++ /* Take the request off the list of running requests */ ++ if (l2meta->nb_clusters != 0) { ++ 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 coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, QEMUIOVector *qiov, + int flags) +@@ -2042,24 +2075,9 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset, + } + } + +- while (l2meta != NULL) { +- QCowL2Meta *next; +- +- ret = qcow2_alloc_cluster_link_l2(bs, l2meta); +- if (ret < 0) { +- goto fail; +- } +- +- /* Take the request off the list of running requests */ +- if (l2meta->nb_clusters != 0) { +- QLIST_REMOVE(l2meta, next_in_flight); +- } +- +- qemu_co_queue_restart_all(&l2meta->dependent_requests); +- +- next = l2meta->next; +- g_free(l2meta); +- l2meta = next; ++ ret = qcow2_handle_l2meta(bs, &l2meta, true); ++ if (ret) { ++ goto fail; + } + + bytes -= cur_bytes; +@@ -2070,18 +2088,7 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset, + ret = 0; + + fail: +- while (l2meta != NULL) { +- QCowL2Meta *next; +- +- if (l2meta->nb_clusters != 0) { +- QLIST_REMOVE(l2meta, next_in_flight); +- } +- qemu_co_queue_restart_all(&l2meta->dependent_requests); +- +- next = l2meta->next; +- g_free(l2meta); +- l2meta = next; +- } ++ qcow2_handle_l2meta(bs, &l2meta, false); + + qemu_co_mutex_unlock(&s->lock); + +@@ -3264,6 +3271,166 @@ static coroutine_fn int qcow2_co_pdiscard(BlockDriverState *bs, + return ret; + } + ++static int coroutine_fn ++qcow2_co_copy_range_from(BlockDriverState *bs, ++ BdrvChild *src, uint64_t src_offset, ++ BdrvChild *dst, uint64_t dst_offset, ++ uint64_t bytes, BdrvRequestFlags flags) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ unsigned int cur_bytes; /* number of bytes in current iteration */ ++ BdrvChild *child = NULL; ++ BdrvRequestFlags cur_flags; ++ ++ assert(!bs->encrypted); ++ qemu_co_mutex_lock(&s->lock); ++ ++ while (bytes != 0) { ++ uint64_t copy_offset = 0; ++ /* prepare next request */ ++ cur_bytes = MIN(bytes, INT_MAX); ++ cur_flags = flags; ++ ++ ret = qcow2_get_cluster_offset(bs, src_offset, &cur_bytes, ©_offset); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ switch (ret) { ++ case QCOW2_CLUSTER_UNALLOCATED: ++ if (bs->backing && bs->backing->bs) { ++ int64_t backing_length = bdrv_getlength(bs->backing->bs); ++ if (src_offset >= backing_length) { ++ cur_flags |= BDRV_REQ_ZERO_WRITE; ++ } else { ++ child = bs->backing; ++ cur_bytes = MIN(cur_bytes, backing_length - src_offset); ++ copy_offset = src_offset; ++ } ++ } else { ++ cur_flags |= BDRV_REQ_ZERO_WRITE; ++ } ++ break; ++ ++ case QCOW2_CLUSTER_ZERO_PLAIN: ++ case QCOW2_CLUSTER_ZERO_ALLOC: ++ cur_flags |= BDRV_REQ_ZERO_WRITE; ++ break; ++ ++ case QCOW2_CLUSTER_COMPRESSED: ++ ret = -ENOTSUP; ++ goto out; ++ break; ++ ++ case QCOW2_CLUSTER_NORMAL: ++ child = bs->file; ++ copy_offset += offset_into_cluster(s, src_offset); ++ if ((copy_offset & 511) != 0) { ++ ret = -EIO; ++ goto out; ++ } ++ break; ++ ++ default: ++ abort(); ++ } ++ qemu_co_mutex_unlock(&s->lock); ++ ret = bdrv_co_copy_range_from(child, ++ copy_offset, ++ dst, dst_offset, ++ cur_bytes, cur_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 ++qcow2_co_copy_range_to(BlockDriverState *bs, ++ BdrvChild *src, uint64_t src_offset, ++ BdrvChild *dst, uint64_t dst_offset, ++ uint64_t bytes, BdrvRequestFlags flags) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int offset_in_cluster; ++ int ret; ++ unsigned int cur_bytes; /* number of sectors in current iteration */ ++ uint64_t cluster_offset; ++ uint8_t *cluster_data = NULL; ++ QCowL2Meta *l2meta = NULL; ++ ++ assert(!bs->encrypted); ++ s->cluster_cache_offset = -1; /* disable compressed cache */ ++ ++ qemu_co_mutex_lock(&s->lock); ++ ++ while (bytes != 0) { ++ ++ l2meta = NULL; ++ ++ offset_in_cluster = offset_into_cluster(s, dst_offset); ++ 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_cluster_offset(bs, dst_offset, &cur_bytes, ++ &cluster_offset, &l2meta); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ assert((cluster_offset & 511) == 0); ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, ++ cluster_offset + offset_in_cluster, cur_bytes); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ qemu_co_mutex_unlock(&s->lock); ++ ret = bdrv_co_copy_range_to(src, src_offset, ++ bs->file, ++ cluster_offset + offset_in_cluster, ++ cur_bytes, 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; ++ dst_offset += cur_bytes; ++ } ++ ret = 0; ++ ++fail: ++ qcow2_handle_l2meta(bs, &l2meta, false); ++ ++ qemu_co_mutex_unlock(&s->lock); ++ ++ qemu_vfree(cluster_data); ++ trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); ++ ++ return ret; ++} ++ + static int qcow2_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) + { +@@ -4522,6 +4689,8 @@ BlockDriver bdrv_qcow2 = { + + .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_truncate = qcow2_truncate, + .bdrv_co_pwritev_compressed = qcow2_co_pwritev_compressed, + .bdrv_make_empty = qcow2_make_empty, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-Increase-the-default-upper-limit-on-the-L2-cac.patch b/SOURCES/kvm-qcow2-Increase-the-default-upper-limit-on-the-L2-cac.patch new file mode 100644 index 0000000..d6d5db8 --- /dev/null +++ b/SOURCES/kvm-qcow2-Increase-the-default-upper-limit-on-the-L2-cac.patch @@ -0,0 +1,104 @@ +From fac45a82432d342bf8cf86fab57af88ba54fb0f4 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 19 Feb 2019 17:00:19 +0100 +Subject: [PATCH 18/23] qcow2: Increase the default upper limit on the L2 cache + size + +RH-Author: Kevin Wolf +Message-id: <20190219170023.27826-10-kwolf@redhat.com> +Patchwork-id: 84549 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 09/13] qcow2: Increase the default upper limit on the L2 cache size +Bugzilla: 1656913 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Leonid Bloch + +The upper limit on the L2 cache size is increased from 1 MB to 32 MB +on Linux platforms, and to 8 MB on other platforms (this difference is +caused by the ability to set intervals for cache cleaning on Linux +platforms only). + +This is done in order to allow default full coverage with the L2 cache +for images of up to 256 GB in size (was 8 GB). Note, that only the +needed amount to cover the full image is allocated. The value which is +changed here is just the upper limit on the L2 cache size, beyond which +it will not grow, even if the size of the image will require it to. + +Signed-off-by: Leonid Bloch +Reviewed-by: Alberto Garcia +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit 80668d0fb735f0839a46278a7d42116089b82816) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/qcow2.h | 6 +++++- + docs/qcow2-cache.txt | 15 +++++++++------ + qemu-options.hx | 6 +++--- + 3 files changed, 17 insertions(+), 10 deletions(-) + +diff --git a/block/qcow2.h b/block/qcow2.h +index d0dd4a2..6c6c742 100644 +--- a/block/qcow2.h ++++ b/block/qcow2.h +@@ -74,7 +74,11 @@ + /* Must be at least 4 to cover all cases of refcount table growth */ + #define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */ + +-#define DEFAULT_L2_CACHE_MAX_SIZE S_1MiB ++#ifdef CONFIG_LINUX ++#define DEFAULT_L2_CACHE_MAX_SIZE S_32MiB ++#else ++#define DEFAULT_L2_CACHE_MAX_SIZE S_8MiB ++#endif + + #define DEFAULT_CLUSTER_SIZE S_64KiB + +diff --git a/docs/qcow2-cache.txt b/docs/qcow2-cache.txt +index 750447e..1fcc065 100644 +--- a/docs/qcow2-cache.txt ++++ b/docs/qcow2-cache.txt +@@ -125,12 +125,15 @@ There are a few things that need to be taken into account: + - Both caches must have a size that is a multiple of the cluster size + (or the cache entry size: see "Using smaller cache sizes" below). + +- - The maximum L2 cache size is 1 MB by default (enough for full coverage +- of 8 GB images, with the default cluster size). This value can be +- modified using the "l2-cache-size" option. QEMU will not use more memory +- than needed to hold all of the image's L2 tables, regardless of this max. +- value. The minimal L2 cache size is 2 clusters (or 2 cache entries, see +- below). ++ - The maximum L2 cache size is 32 MB by default on Linux platforms (enough ++ for full coverage of 256 GB images, with the default cluster size). This ++ value can be modified using the "l2-cache-size" option. QEMU will not use ++ more memory than needed to hold all of the image's L2 tables, regardless ++ of this max. value. ++ On non-Linux platforms the maximal value is smaller by default (8 MB) and ++ this difference stems from the fact that on Linux the cache can be cleared ++ periodically if needed, using the "cache-clean-interval" option (see below). ++ The minimal L2 cache size is 2 clusters (or 2 cache entries, see below). + + - The default (and minimum) refcount cache size is 4 clusters. + +diff --git a/qemu-options.hx b/qemu-options.hx +index e1fbc5b..3580d16 100644 +--- a/qemu-options.hx ++++ b/qemu-options.hx +@@ -756,9 +756,9 @@ The maximum total size of the L2 table and refcount block caches in bytes + + @item l2-cache-size + The maximum size of the L2 table cache in bytes +-(default: if cache-size is not specified - 1M; otherwise, as large as possible +-within the cache-size, while permitting the requested or the minimal refcount +-cache size) ++(default: if cache-size is not specified - 32M on Linux platforms, and 8M on ++non-Linux platforms; otherwise, as large as possible within the cache-size, ++while permitting the requested or the minimal refcount cache size) + + @item refcount-cache-size + The maximum size of the refcount block cache in bytes +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-Make-sizes-more-humanly-readable.patch b/SOURCES/kvm-qcow2-Make-sizes-more-humanly-readable.patch new file mode 100644 index 0000000..dc7b37b --- /dev/null +++ b/SOURCES/kvm-qcow2-Make-sizes-more-humanly-readable.patch @@ -0,0 +1,82 @@ +From 320903cac23bb449df8c2e9cf737e16b3f94d684 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 19 Feb 2019 17:00:16 +0100 +Subject: [PATCH 15/23] qcow2: Make sizes more humanly readable + +RH-Author: Kevin Wolf +Message-id: <20190219170023.27826-7-kwolf@redhat.com> +Patchwork-id: 84546 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 06/13] qcow2: Make sizes more humanly readable +Bugzilla: 1656913 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Leonid Bloch + +Signed-off-by: Leonid Bloch +Reviewed-by: Alberto Garcia +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit b6a95c6d10075bb540ce50198bbe22fc0a4392c7) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/qcow2.c | 2 +- + block/qcow2.h | 9 +++++---- + 2 files changed, 6 insertions(+), 5 deletions(-) + +diff --git a/block/qcow2.c b/block/qcow2.c +index a0f7234..3859112 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -826,7 +826,7 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, + } + } else { + if (!l2_cache_size_set) { +- *l2_cache_size = MAX(DEFAULT_L2_CACHE_BYTE_SIZE, ++ *l2_cache_size = MAX(DEFAULT_L2_CACHE_SIZE, + (uint64_t)DEFAULT_L2_CACHE_CLUSTERS + * s->cluster_size); + } +diff --git a/block/qcow2.h b/block/qcow2.h +index 3d92cdb..f73a48a 100644 +--- a/block/qcow2.h ++++ b/block/qcow2.h +@@ -27,6 +27,7 @@ + + #include "crypto/block.h" + #include "qemu/coroutine.h" ++#include "qemu/units.h" + + //#define DEBUG_ALLOC + //#define DEBUG_ALLOC2 +@@ -43,11 +44,11 @@ + + /* 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 0x800000 ++#define QCOW_MAX_REFTABLE_SIZE S_8MiB + + /* 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 0x2000000 ++#define QCOW_MAX_L1_SIZE S_32MiB + + /* Allow for an average of 1k per snapshot table entry, should be plenty of + * space for snapshot names and IDs */ +@@ -75,9 +76,9 @@ + + /* Whichever is more */ + #define DEFAULT_L2_CACHE_CLUSTERS 8 /* clusters */ +-#define DEFAULT_L2_CACHE_BYTE_SIZE 1048576 /* bytes */ ++#define DEFAULT_L2_CACHE_SIZE S_1MiB + +-#define DEFAULT_CLUSTER_SIZE 65536 ++#define DEFAULT_CLUSTER_SIZE S_64KiB + + + #define QCOW2_OPT_LAZY_REFCOUNTS "lazy-refcounts" +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-Options-documentation-fixes.patch b/SOURCES/kvm-qcow2-Options-documentation-fixes.patch new file mode 100644 index 0000000..ab7ddfe --- /dev/null +++ b/SOURCES/kvm-qcow2-Options-documentation-fixes.patch @@ -0,0 +1,110 @@ +From 4ae8e237a43188fd01c057a336d06942a53bc04f Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 19 Feb 2019 17:00:14 +0100 +Subject: [PATCH 13/23] qcow2: Options' documentation fixes + +RH-Author: Kevin Wolf +Message-id: <20190219170023.27826-5-kwolf@redhat.com> +Patchwork-id: 84544 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 04/13] qcow2: Options' documentation fixes +Bugzilla: 1656913 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Leonid Bloch + +Signed-off-by: Leonid Bloch +Reviewed-by: Alberto Garcia +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit 40fb215d483ce510e211b843352288894eb13285) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + docs/qcow2-cache.txt | 21 ++++++++++++++------- + qemu-options.hx | 9 ++++++--- + 2 files changed, 20 insertions(+), 10 deletions(-) + +diff --git a/docs/qcow2-cache.txt b/docs/qcow2-cache.txt +index 8a09a5c..7e28b41 100644 +--- a/docs/qcow2-cache.txt ++++ b/docs/qcow2-cache.txt +@@ -79,14 +79,14 @@ Choosing the right cache sizes + In order to choose the cache sizes we need to know how they relate to + the amount of allocated space. + +-The amount of virtual disk that can be mapped by the L2 and refcount ++The part of the virtual disk that can be mapped by the L2 and refcount + caches (in bytes) is: + + disk_size = l2_cache_size * cluster_size / 8 + disk_size = refcount_cache_size * cluster_size * 8 / refcount_bits + + With the default values for cluster_size (64KB) and refcount_bits +-(16), that is ++(16), this becomes: + + disk_size = l2_cache_size * 8192 + disk_size = refcount_cache_size * 32768 +@@ -97,12 +97,16 @@ need: + l2_cache_size = disk_size_GB * 131072 + refcount_cache_size = disk_size_GB * 32768 + +-QEMU has a default L2 cache of 1MB (1048576 bytes) and a refcount +-cache of 256KB (262144 bytes), so using the formulas we've just seen +-we have ++For example, 1MB of L2 cache is needed to cover every 8 GB of the virtual ++image size (given that the default cluster size is used): + +- 1048576 / 131072 = 8 GB of virtual disk covered by that cache +- 262144 / 32768 = 8 GB ++ 8 GB / 8192 = 1 MB ++ ++The refcount cache is 4 times the cluster size by default. With the default ++cluster size of 64 KB, it is 256 KB (262144 bytes). This is sufficient for ++8 GB of image size: ++ ++ 262144 * 32768 = 8 GB + + + How to configure the cache sizes +@@ -130,6 +134,9 @@ There are a few things that need to be taken into account: + memory as possible to the L2 cache before increasing the refcount + cache size. + ++ - At most two of "l2-cache-size", "refcount-cache-size", and "cache-size" ++ can be set simultaneously. ++ + Unlike L2 tables, refcount blocks are not used during normal I/O but + only during allocations and internal snapshots. In most cases they are + accessed sequentially (even during random guest I/O) so increasing the +diff --git a/qemu-options.hx b/qemu-options.hx +index 5c58760..3308b94 100644 +--- a/qemu-options.hx ++++ b/qemu-options.hx +@@ -752,15 +752,18 @@ image file) + + @item cache-size + The maximum total size of the L2 table and refcount block caches in bytes +-(default: 1048576 bytes or 8 clusters, whichever is larger) ++(default: the sum of l2-cache-size and refcount-cache-size) + + @item l2-cache-size + The maximum size of the L2 table cache in bytes +-(default: 4/5 of the total cache size) ++(default: if cache-size is not defined - 1048576 bytes or 8 clusters, whichever ++is larger; otherwise, as large as possible or needed within the cache-size, ++while permitting the requested or the minimal refcount cache size) + + @item refcount-cache-size + The maximum size of the refcount block cache in bytes +-(default: 1/5 of the total cache size) ++(default: 4 times the cluster size; or if cache-size is specified, the part of ++it which is not used for the L2 cache) + + @item cache-clean-interval + Clean unused entries in the L2 and refcount caches. The interval is in seconds. +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-Remove-coroutine-trampoline-for-preallocate_co.patch b/SOURCES/kvm-qcow2-Remove-coroutine-trampoline-for-preallocate_co.patch new file mode 100644 index 0000000..8c2d3d0 --- /dev/null +++ b/SOURCES/kvm-qcow2-Remove-coroutine-trampoline-for-preallocate_co.patch @@ -0,0 +1,137 @@ +From 17d9cdafd0e6859af0b44b26870f8d61bc6f7c2d Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Thu, 12 Jul 2018 14:42:55 +0200 +Subject: [PATCH 36/89] qcow2: Remove coroutine trampoline for preallocate_co() + +RH-Author: Kevin Wolf +Message-id: <20180712144258.17303-4-kwolf@redhat.com> +Patchwork-id: 81329 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 3/6] qcow2: Remove coroutine trampoline for preallocate_co() +Bugzilla: 1595173 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow + +All callers are coroutine_fns now, so we can just directly call +preallocate_co(). + +Signed-off-by: Kevin Wolf +Reviewed-by: Stefan Hajnoczi +(cherry picked from commit 47e86b868d57ffe13162ca44e5f6a51c15c4a769) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/qcow2.c | 51 ++++++++------------------------------------------- + 1 file changed, 8 insertions(+), 43 deletions(-) + +diff --git a/block/qcow2.c b/block/qcow2.c +index c5c6ae9..71fbfcd 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -2517,15 +2517,6 @@ static int qcow2_set_up_encryption(BlockDriverState *bs, + return ret; + } + +- +-typedef struct PreallocCo { +- BlockDriverState *bs; +- uint64_t offset; +- uint64_t new_length; +- +- int ret; +-} PreallocCo; +- + /** + * Preallocates metadata structures for data clusters between @offset (in the + * guest disk) and @new_length (which is thus generally the new guest disk +@@ -2533,12 +2524,9 @@ typedef struct PreallocCo { + * + * Returns: 0 on success, -errno on failure. + */ +-static void coroutine_fn preallocate_co(void *opaque) ++static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset, ++ uint64_t new_length) + { +- PreallocCo *params = opaque; +- BlockDriverState *bs = params->bs; +- uint64_t offset = params->offset; +- uint64_t new_length = params->new_length; + uint64_t bytes; + uint64_t host_offset = 0; + unsigned int cur_bytes; +@@ -2553,7 +2541,7 @@ static void coroutine_fn preallocate_co(void *opaque) + ret = qcow2_alloc_cluster_offset(bs, offset, &cur_bytes, + &host_offset, &meta); + if (ret < 0) { +- goto done; ++ return ret; + } + + while (meta) { +@@ -2563,7 +2551,7 @@ static void coroutine_fn preallocate_co(void *opaque) + if (ret < 0) { + qcow2_free_any_clusters(bs, meta->alloc_offset, + meta->nb_clusters, QCOW2_DISCARD_NEVER); +- goto done; ++ return ret; + } + + /* There are no dependent requests, but we need to remove our +@@ -2590,34 +2578,11 @@ static void coroutine_fn preallocate_co(void *opaque) + ret = bdrv_pwrite(bs->file, (host_offset + cur_bytes) - 1, + &data, 1); + if (ret < 0) { +- goto done; ++ return ret; + } + } + +- ret = 0; +- +-done: +- params->ret = ret; +-} +- +-static int preallocate(BlockDriverState *bs, +- uint64_t offset, uint64_t new_length) +-{ +- PreallocCo params = { +- .bs = bs, +- .offset = offset, +- .new_length = new_length, +- .ret = -EINPROGRESS, +- }; +- +- if (qemu_in_coroutine()) { +- preallocate_co(¶ms); +- } else { +- Coroutine *co = qemu_coroutine_create(preallocate_co, ¶ms); +- bdrv_coroutine_enter(bs, co); +- BDRV_POLL_WHILE(bs, params.ret == -EINPROGRESS); +- } +- return params.ret; ++ return 0; + } + + /* qcow2_refcount_metadata_size: +@@ -3035,7 +3000,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) + if (qcow2_opts->preallocation != PREALLOC_MODE_OFF) { + BDRVQcow2State *s = blk_bs(blk)->opaque; + qemu_co_mutex_lock(&s->lock); +- ret = preallocate(blk_bs(blk), 0, qcow2_opts->size); ++ ret = preallocate_co(blk_bs(blk), 0, qcow2_opts->size); + qemu_co_mutex_unlock(&s->lock); + + if (ret < 0) { +@@ -3544,7 +3509,7 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, + break; + + case PREALLOC_MODE_METADATA: +- ret = preallocate(bs, old_length, offset); ++ ret = preallocate_co(bs, old_length, offset); + if (ret < 0) { + error_setg_errno(errp, -ret, "Preallocation failed"); + goto fail; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-Remove-dead-check-on-ret.patch b/SOURCES/kvm-qcow2-Remove-dead-check-on-ret.patch new file mode 100644 index 0000000..688c982 --- /dev/null +++ b/SOURCES/kvm-qcow2-Remove-dead-check-on-ret.patch @@ -0,0 +1,51 @@ +From 3e96785b4f89d97ca8297e2ea4ed2458409422ec Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:54:48 +0200 +Subject: [PATCH 63/89] qcow2: Remove dead check on !ret +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20180718225511.14878-13-jsnow@redhat.com> +Patchwork-id: 81409 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 12/35] qcow2: Remove dead check on !ret +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Fam Zheng + +In the beginning of the function, we initialize the local variable to 0, +and in the body of the function, we check the assigned values and exit +the loop immediately. So here it can never be non-zero. + +Reported-by: Kevin Wolf +Signed-off-by: Fam Zheng +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Kevin Wolf +(cherry picked from commit 354d930dc6e90e97599459b79c071ff1b93e433b) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/qcow2.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/block/qcow2.c b/block/qcow2.c +index c5341a4..e171a99 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -1772,7 +1772,7 @@ static coroutine_fn int qcow2_handle_l2meta(BlockDriverState *bs, + while (l2meta != NULL) { + QCowL2Meta *next; + +- if (!ret && link_l2) { ++ if (link_l2) { + ret = qcow2_alloc_cluster_link_l2(bs, l2meta); + if (ret) { + goto out; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-Repair-OFLAG_COPIED-when-fixing-leaks.patch b/SOURCES/kvm-qcow2-Repair-OFLAG_COPIED-when-fixing-leaks.patch new file mode 100644 index 0000000..68cc8a7 --- /dev/null +++ b/SOURCES/kvm-qcow2-Repair-OFLAG_COPIED-when-fixing-leaks.patch @@ -0,0 +1,90 @@ +From dfcb9147d46e9e0da762349fd62a4ac620100cb4 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 17:16:54 +0200 +Subject: [PATCH 45/54] qcow2: Repair OFLAG_COPIED when fixing leaks + +RH-Author: Max Reitz +Message-id: <20180618171655.25987-2-mreitz@redhat.com> +Patchwork-id: 80782 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/2] qcow2: Repair OFLAG_COPIED when fixing leaks +Bugzilla: 1527085 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Miroslav Rezanina + +Repairing OFLAG_COPIED is usually safe because it is done after the +refcounts have been repaired. Therefore, it we did not find anyone else +referencing a data or L2 cluster, it makes no sense to not set +OFLAG_COPIED -- and the other direction (clearing OFLAG_COPIED) is +always safe, anyway, it may just induce leaks. + +Furthermore, if OFLAG_COPIED is actually consistent with a wrong (leaky) +refcount, we will decrement the refcount with -r leaks, but OFLAG_COPIED +will then be wrong. qemu-img check should not produce images that are +more corrupted afterwards then they were before. + +Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1527085 +Signed-off-by: Max Reitz +Reviewed-by: Eric Blake +Message-id: 20180509200059.31125-2-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit 3cce51c919c7b4028cf6676dfcb80a45741b5117) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + block/qcow2-refcount.c | 25 +++++++++++++++++-------- + 1 file changed, 17 insertions(+), 8 deletions(-) + +diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c +index 6b8b635..4e14c0a 100644 +--- a/block/qcow2-refcount.c ++++ b/block/qcow2-refcount.c +@@ -1799,6 +1799,19 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, + 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]; +@@ -1818,10 +1831,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, + if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) { + fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d " + "l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n", +- fix & BDRV_FIX_ERRORS ? "Repairing" : +- "ERROR", +- i, l1_entry, refcount); +- if (fix & BDRV_FIX_ERRORS) { ++ 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; +@@ -1862,10 +1873,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, + if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { + fprintf(stderr, "%s OFLAG_COPIED data cluster: " + "l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n", +- fix & BDRV_FIX_ERRORS ? "Repairing" : +- "ERROR", +- l2_entry, refcount); +- if (fix & BDRV_FIX_ERRORS) { ++ repair ? "Repairing" : "ERROR", l2_entry, refcount); ++ if (repair) { + l2_table[j] = cpu_to_be64(refcount == 1 + ? l2_entry | QCOW_OFLAG_COPIED + : l2_entry & ~QCOW_OFLAG_COPIED); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-Resize-the-cache-upon-image-resizing.patch b/SOURCES/kvm-qcow2-Resize-the-cache-upon-image-resizing.patch new file mode 100644 index 0000000..7ddbdc3 --- /dev/null +++ b/SOURCES/kvm-qcow2-Resize-the-cache-upon-image-resizing.patch @@ -0,0 +1,70 @@ +From 25829686076eac2d3d6216145377ecca8f92d6bd Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 19 Feb 2019 17:00:20 +0100 +Subject: [PATCH 19/23] qcow2: Resize the cache upon image resizing + +RH-Author: Kevin Wolf +Message-id: <20190219170023.27826-11-kwolf@redhat.com> +Patchwork-id: 84553 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 10/13] qcow2: Resize the cache upon image resizing +Bugzilla: 1656913 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Leonid Bloch + +The caches are now recalculated upon image resizing. This is done +because the new default behavior of assigning L2 cache relatively to +the image size, implies that the cache will be adapted accordingly +after an image resize. + +Signed-off-by: Leonid Bloch +Reviewed-by: Alberto Garcia +Signed-off-by: Kevin Wolf +(cherry picked from commit 45b4949c7bcdcd998cb42f5c517e80a2657cfd33) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/qcow2.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/block/qcow2.c b/block/qcow2.c +index fc6bddd..72f1ea8 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -3462,6 +3462,7 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, + 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) +@@ -3686,6 +3687,8 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, + } + } + ++ bs->total_sectors = offset / BDRV_SECTOR_SIZE; ++ + /* write updated header.size */ + offset = cpu_to_be64(offset); + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size), +@@ -3696,6 +3699,14 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, + } + + 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); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-Set-the-default-cache-clean-interval-to-10-min.patch b/SOURCES/kvm-qcow2-Set-the-default-cache-clean-interval-to-10-min.patch new file mode 100644 index 0000000..49a136c --- /dev/null +++ b/SOURCES/kvm-qcow2-Set-the-default-cache-clean-interval-to-10-min.patch @@ -0,0 +1,118 @@ +From 098468977d64b1c8f63ae6fb312e24698d368c74 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 19 Feb 2019 17:00:21 +0100 +Subject: [PATCH 20/23] qcow2: Set the default cache-clean-interval to 10 + minutes + +RH-Author: Kevin Wolf +Message-id: <20190219170023.27826-12-kwolf@redhat.com> +Patchwork-id: 84550 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 11/13] qcow2: Set the default cache-clean-interval to 10 minutes +Bugzilla: 1656913 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Leonid Bloch + +The default cache-clean-interval is set to 10 minutes, in order to lower +the overhead of the qcow2 caches (before the default was 0, i.e. +disabled). + +* For non-Linux platforms the default is kept at 0, because + cache-clean-interval is not supported there yet. + +Signed-off-by: Leonid Bloch +Reviewed-by: Alberto Garcia +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit e957b50b8daecfc39a1ac09855b0eacb6edfd328) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/qcow2.c | 2 +- + block/qcow2.h | 4 +++- + docs/qcow2-cache.txt | 4 ++-- + qapi/block-core.json | 3 ++- + qemu-options.hx | 2 +- + 5 files changed, 9 insertions(+), 6 deletions(-) + +diff --git a/block/qcow2.c b/block/qcow2.c +index 72f1ea8..acd076c 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -940,7 +940,7 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, + /* New interval for cache cleanup timer */ + r->cache_clean_interval = + qemu_opt_get_number(opts, QCOW2_OPT_CACHE_CLEAN_INTERVAL, +- s->cache_clean_interval); ++ DEFAULT_CACHE_CLEAN_INTERVAL); + #ifndef CONFIG_LINUX + if (r->cache_clean_interval != 0) { + error_setg(errp, QCOW2_OPT_CACHE_CLEAN_INTERVAL +diff --git a/block/qcow2.h b/block/qcow2.h +index 6c6c742..29b041c 100644 +--- a/block/qcow2.h ++++ b/block/qcow2.h +@@ -76,13 +76,15 @@ + + #ifdef CONFIG_LINUX + #define DEFAULT_L2_CACHE_MAX_SIZE S_32MiB ++#define DEFAULT_CACHE_CLEAN_INTERVAL 600 /* seconds */ + #else + #define DEFAULT_L2_CACHE_MAX_SIZE S_8MiB ++/* Cache clean interval is currently available only on Linux, so must be 0 */ ++#define DEFAULT_CACHE_CLEAN_INTERVAL 0 + #endif + + #define DEFAULT_CLUSTER_SIZE S_64KiB + +- + #define QCOW2_OPT_LAZY_REFCOUNTS "lazy-refcounts" + #define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request" + #define QCOW2_OPT_DISCARD_SNAPSHOT "pass-discard-snapshot" +diff --git a/docs/qcow2-cache.txt b/docs/qcow2-cache.txt +index 1fcc065..59358b8 100644 +--- a/docs/qcow2-cache.txt ++++ b/docs/qcow2-cache.txt +@@ -210,8 +210,8 @@ This example removes all unused cache entries every 15 minutes: + + -drive file=hd.qcow2,cache-clean-interval=900 + +-If unset, the default value for this parameter is 0 and it disables +-this feature. ++If unset, the default value for this parameter is 600. Setting it to 0 ++disables this feature. + + Note that this functionality currently relies on the MADV_DONTNEED + argument for madvise() to actually free the memory. This is a +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 9741555..f43124e 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -2873,7 +2873,8 @@ + # + # @cache-clean-interval: clean unused entries in the L2 and refcount + # caches. The interval is in seconds. The default value +-# is 0 and it disables this feature (since 2.5) ++# is 600, and 0 disables this feature. (since 2.5) ++# + # @encrypt: Image decryption options. Mandatory for + # encrypted images, except when doing a metadata-only + # probe of the image. (since 2.10) +diff --git a/qemu-options.hx b/qemu-options.hx +index 3580d16..f7c9e23 100644 +--- a/qemu-options.hx ++++ b/qemu-options.hx +@@ -767,7 +767,7 @@ it which is not used for the L2 cache) + + @item cache-clean-interval + Clean unused entries in the L2 and refcount caches. The interval is in seconds. +-The default value is 0 and it disables this feature. ++The default value is 600. Setting it to 0 disables this feature. + + @item pass-discard-request + Whether discard requests to the qcow2 device should be forwarded to the data +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-add-overlap-check-for-bitmap-directory.patch b/SOURCES/kvm-qcow2-add-overlap-check-for-bitmap-directory.patch new file mode 100644 index 0000000..e18d53b --- /dev/null +++ b/SOURCES/kvm-qcow2-add-overlap-check-for-bitmap-directory.patch @@ -0,0 +1,217 @@ +From 1b36ea8bc4840907b78867ae040231fe91f12851 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:54:45 +0200 +Subject: [PATCH 60/89] qcow2: add overlap check for bitmap directory + +RH-Author: John Snow +Message-id: <20180718225511.14878-10-jsnow@redhat.com> +Patchwork-id: 81394 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 09/35] qcow2: add overlap check for bitmap directory +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-id: 20180705151515.779173-1-vsementsov@virtuozzo.com +Signed-off-by: Max Reitz +(cherry picked from commit 0e4e4318eaa56c831001bdf617094807ec6d451c) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + block/qcow2-bitmap.c | 7 ++++++- + block/qcow2-refcount.c | 10 ++++++++++ + block/qcow2.c | 22 ++++++++++++++-------- + block/qcow2.h | 45 ++++++++++++++++++++++++--------------------- + qapi/block-core.json | 21 ++++++++++++--------- + 5 files changed, 66 insertions(+), 39 deletions(-) + +diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c +index 3e4e4e4..14050eb 100644 +--- a/block/qcow2-bitmap.c ++++ b/block/qcow2-bitmap.c +@@ -775,7 +775,12 @@ static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list, + } + } + +- ret = qcow2_pre_write_overlap_check(bs, 0, dir_offset, dir_size); ++ /* Actually, even in 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); + if (ret < 0) { + goto fail; + } +diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c +index 4e14c0a..1307069 100644 +--- a/block/qcow2-refcount.c ++++ b/block/qcow2-refcount.c +@@ -2705,6 +2705,16 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, + } + } + ++ 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; + } + +diff --git a/block/qcow2.c b/block/qcow2.c +index 71fbfcd..c5341a4 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -676,6 +676,11 @@ static QemuOptsList qcow2_runtime_opts = { + .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) " +@@ -708,14 +713,15 @@ static QemuOptsList qcow2_runtime_opts = { + }; + + 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_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) +diff --git a/block/qcow2.h b/block/qcow2.h +index b5e2aa3..d2c63e4 100644 +--- a/block/qcow2.h ++++ b/block/qcow2.h +@@ -98,6 +98,7 @@ + #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" +@@ -401,34 +402,36 @@ typedef enum QCow2ClusterType { + } QCow2ClusterType; + + 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_MAX_BITNR = 8, +- +- 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), ++ 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_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_SNAPSHOT_TABLE | QCOW2_OL_BITMAP_DIRECTORY) + + /* Perform all overlap checks which don't require disk access */ + #define QCOW2_OL_CACHED \ +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 27ed91b..69e0cf8 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -2666,18 +2666,21 @@ + # @template: Specifies a template mode which can be adjusted using the other + # flags, defaults to 'cached' + # ++# @bitmap-directory: since 3.0 ++# + # Since: 2.9 + ## + { 'struct': 'Qcow2OverlapCheckFlags', +- 'data': { '*template': 'Qcow2OverlapCheckMode', +- '*main-header': 'bool', +- '*active-l1': 'bool', +- '*active-l2': 'bool', +- '*refcount-table': 'bool', +- '*refcount-block': 'bool', +- '*snapshot-table': 'bool', +- '*inactive-l1': 'bool', +- '*inactive-l2': 'bool' } } ++ 'data': { '*template': 'Qcow2OverlapCheckMode', ++ '*main-header': 'bool', ++ '*active-l1': 'bool', ++ '*active-l2': 'bool', ++ '*refcount-table': 'bool', ++ '*refcount-block': 'bool', ++ '*snapshot-table': 'bool', ++ '*inactive-l1': 'bool', ++ '*inactive-l2': 'bool', ++ '*bitmap-directory': 'bool' } } + + ## + # @Qcow2OverlapChecks: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qcow2-list-of-bitmaps-new-test-242.patch b/SOURCES/kvm-qcow2-list-of-bitmaps-new-test-242.patch new file mode 100644 index 0000000..d00bdb9 --- /dev/null +++ b/SOURCES/kvm-qcow2-list-of-bitmaps-new-test-242.patch @@ -0,0 +1,332 @@ +From 171b297adccba187047045e533213ff1adad7173 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 21:48:38 +0100 +Subject: [PATCH 045/163] qcow2: list of bitmaps new test 242 + +RH-Author: John Snow +Message-id: <20190320214838.22027-11-jsnow@redhat.com> +Patchwork-id: 85001 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 10/10] qcow2: list of bitmaps new test 242 +Bugzilla: 1691048 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Andrey Shinkevich + +A new test file 242 added to the qemu-iotests set. It checks +the format of qcow2 specific information for the new added +section that lists details of bitmaps. + +Signed-off-by: Andrey Shinkevich +Message-Id: <1549638368-530182-4-git-send-email-andrey.shinkevich@virtuozzo.com> +Reviewed-by: Eric Blake +[eblake: pep8 compliance, avoid trailing blank line] +Reviewed-by: Vladimir Sementsov-Ogievskiy +Signed-off-by: Eric Blake +(cherry picked from commit ddd113beedd22908e676d53803843d2f85bf91ab) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/242 | 104 ++++++++++++++++++++++++++++ + tests/qemu-iotests/242.out | 166 +++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/group | 1 + + 3 files changed, 271 insertions(+) + create mode 100755 tests/qemu-iotests/242 + create mode 100644 tests/qemu-iotests/242.out + +diff --git a/tests/qemu-iotests/242 b/tests/qemu-iotests/242 +new file mode 100755 +index 0000000..16c65ed +--- /dev/null ++++ b/tests/qemu-iotests/242 +@@ -0,0 +1,104 @@ ++#!/usr/bin/env python ++# ++# Test for qcow2 bitmap printed information ++# ++# Copyright (c) 2019 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 . ++# ++ ++import iotests ++import json ++from iotests import qemu_img_create, qemu_io, qemu_img_pipe, \ ++ file_path, img_info_log, log, filter_qemu_io ++ ++iotests.verify_image_format(supported_fmts=['qcow2']) ++ ++disk = file_path('disk') ++chunk = 256 * 1024 ++bitmap_flag_unknown = 1 << 2 ++# flag_offset = 5*cluster_size + flag_offset_in_bitmap_directory_entry ++flag_offset = 0x5000f ++ ++ ++def print_bitmap(extra_args): ++ log('qemu-img info dump:\n') ++ img_info_log(disk, extra_args=extra_args) ++ result = json.loads(qemu_img_pipe('info', '--force-share', ++ '--output=json', disk)) ++ if 'bitmaps' in result['format-specific']['data']: ++ bitmaps = result['format-specific']['data']['bitmaps'] ++ log('The same bitmaps in JSON format:') ++ log(bitmaps, indent=2) ++ else: ++ log('No bitmap in JSON format output') ++ ++ ++def add_bitmap(bitmap_number, persistent, disabled): ++ granularity = 1 << (13 + bitmap_number) ++ bitmap_name = 'bitmap-' + str(bitmap_number-1) ++ vm = iotests.VM().add_drive(disk) ++ vm.launch() ++ vm.qmp_log('block-dirty-bitmap-add', node='drive0', name=bitmap_name, ++ granularity=granularity, persistent=persistent, ++ disabled=disabled) ++ vm.shutdown() ++ ++ ++def write_to_disk(offset, size): ++ write = 'write {} {}'.format(offset, size) ++ log(qemu_io('-c', write, disk), filters=[filter_qemu_io]) ++ ++ ++def toggle_flag(offset): ++ with open(disk, "r+b") as f: ++ f.seek(offset, 0) ++ c = f.read(1) ++ toggled = chr(ord(c) ^ bitmap_flag_unknown) ++ f.seek(-1, 1) ++ f.write(toggled) ++ ++ ++qemu_img_create('-f', iotests.imgfmt, disk, '1M') ++ ++for num in range(1, 4): ++ disabled = False ++ if num == 2: ++ disabled = True ++ log('Test {}'.format(num)) ++ add_bitmap(num, num > 1, disabled) ++ write_to_disk((num-1) * chunk, chunk) ++ print_bitmap([]) ++ log('') ++ ++vm = iotests.VM().add_drive(disk) ++vm.launch() ++num += 1 ++log('Test {}\nChecking "in-use" flag...'.format(num)) ++print_bitmap(['--force-share']) ++vm.shutdown() ++ ++num += 1 ++log('\nTest {}'.format(num)) ++qemu_img_create('-f', iotests.imgfmt, disk, '1M') ++add_bitmap(1, True, False) ++log('Write an unknown bitmap flag \'{}\' into a new QCOW2 image at offset {}' ++ .format(hex(bitmap_flag_unknown), flag_offset)) ++toggle_flag(flag_offset) ++img_info_log(disk) ++toggle_flag(flag_offset) ++log('Unset the unknown bitmap flag \'{}\' in the bitmap directory entry:\n' ++ .format(hex(bitmap_flag_unknown))) ++img_info_log(disk) ++log('Test complete') +diff --git a/tests/qemu-iotests/242.out b/tests/qemu-iotests/242.out +new file mode 100644 +index 0000000..fbe05d7 +--- /dev/null ++++ b/tests/qemu-iotests/242.out +@@ -0,0 +1,166 @@ ++Test 1 ++{"execute": "block-dirty-bitmap-add", "arguments": {"disabled": false, "granularity": 16384, "name": "bitmap-0", "node": "drive0", "persistent": false}} ++{"return": {}} ++wrote 262144/262144 bytes at offset 0 ++256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++ ++qemu-img info dump: ++ ++image: TEST_IMG ++file format: IMGFMT ++virtual size: 1.0M (1048576 bytes) ++cluster_size: 65536 ++Format specific information: ++ compat: 1.1 ++ lazy refcounts: false ++ refcount bits: 16 ++ corrupt: false ++ ++No bitmap in JSON format output ++ ++Test 2 ++{"execute": "block-dirty-bitmap-add", "arguments": {"disabled": true, "granularity": 32768, "name": "bitmap-1", "node": "drive0", "persistent": true}} ++{"return": {}} ++wrote 262144/262144 bytes at offset 262144 ++256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++ ++qemu-img info dump: ++ ++image: TEST_IMG ++file format: IMGFMT ++virtual size: 1.0M (1048576 bytes) ++cluster_size: 65536 ++Format specific information: ++ compat: 1.1 ++ lazy refcounts: false ++ bitmaps: ++ [0]: ++ flags: ++ name: bitmap-1 ++ granularity: 32768 ++ refcount bits: 16 ++ corrupt: false ++ ++The same bitmaps in JSON format: ++[ ++ { ++ "flags": [], ++ "granularity": 32768, ++ "name": "bitmap-1" ++ } ++] ++ ++Test 3 ++{"execute": "block-dirty-bitmap-add", "arguments": {"disabled": false, "granularity": 65536, "name": "bitmap-2", "node": "drive0", "persistent": true}} ++{"return": {}} ++wrote 262144/262144 bytes at offset 524288 ++256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++ ++qemu-img info dump: ++ ++image: TEST_IMG ++file format: IMGFMT ++virtual size: 1.0M (1048576 bytes) ++cluster_size: 65536 ++Format specific information: ++ compat: 1.1 ++ lazy refcounts: false ++ bitmaps: ++ [0]: ++ flags: ++ name: bitmap-1 ++ granularity: 32768 ++ [1]: ++ flags: ++ [0]: auto ++ name: bitmap-2 ++ granularity: 65536 ++ refcount bits: 16 ++ corrupt: false ++ ++The same bitmaps in JSON format: ++[ ++ { ++ "flags": [], ++ "granularity": 32768, ++ "name": "bitmap-1" ++ }, ++ { ++ "flags": [ ++ "auto" ++ ], ++ "granularity": 65536, ++ "name": "bitmap-2" ++ } ++] ++ ++Test 4 ++Checking "in-use" flag... ++qemu-img info dump: ++ ++image: TEST_IMG ++file format: IMGFMT ++virtual size: 1.0M (1048576 bytes) ++cluster_size: 65536 ++Format specific information: ++ compat: 1.1 ++ lazy refcounts: false ++ bitmaps: ++ [0]: ++ flags: ++ [0]: in-use ++ name: bitmap-1 ++ granularity: 32768 ++ [1]: ++ flags: ++ [0]: in-use ++ [1]: auto ++ name: bitmap-2 ++ granularity: 65536 ++ refcount bits: 16 ++ corrupt: false ++ ++The same bitmaps in JSON format: ++[ ++ { ++ "flags": [ ++ "in-use" ++ ], ++ "granularity": 32768, ++ "name": "bitmap-1" ++ }, ++ { ++ "flags": [ ++ "in-use", ++ "auto" ++ ], ++ "granularity": 65536, ++ "name": "bitmap-2" ++ } ++] ++ ++Test 5 ++{"execute": "block-dirty-bitmap-add", "arguments": {"disabled": false, "granularity": 16384, "name": "bitmap-0", "node": "drive0", "persistent": true}} ++{"return": {}} ++Write an unknown bitmap flag '0x4' into a new QCOW2 image at offset 327695 ++qemu-img: Could not open 'TEST_IMG': Bitmap 'bitmap-0' doesn't satisfy the constraints ++ ++Unset the unknown bitmap flag '0x4' in the bitmap directory entry: ++ ++image: TEST_IMG ++file format: IMGFMT ++virtual size: 1.0M (1048576 bytes) ++cluster_size: 65536 ++Format specific information: ++ compat: 1.1 ++ lazy refcounts: false ++ bitmaps: ++ [0]: ++ flags: ++ [0]: auto ++ name: bitmap-0 ++ granularity: 16384 ++ refcount bits: 16 ++ corrupt: false ++ ++Test complete +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index 8662839..bee2855 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -228,3 +228,4 @@ + 232 auto quick + 234 auto quick migration + 236 auto quick ++242 rw auto quick +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qdev-Loosen-coupling-between-compat-and-other-global.patch b/SOURCES/kvm-qdev-Loosen-coupling-between-compat-and-other-global.patch new file mode 100644 index 0000000..2667323 --- /dev/null +++ b/SOURCES/kvm-qdev-Loosen-coupling-between-compat-and-other-global.patch @@ -0,0 +1,158 @@ +From a3a904263d8b2c2cb91be8f9dfb048239fc2b1e2 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:07 +0200 +Subject: [PATCH 40/53] qdev: Loosen coupling between compat and other global + properties +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-19-armbru@redhat.com> +Patchwork-id: 88002 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 18/31] qdev: Loosen coupling between compat and other global properties +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +The (upstream) patch after next creates global properties before +compat properties rather than after. That's fine upstream, because +compat properties have been divorced from global properties there, in +merge commit 31ed41889e6. Downstream, changing creation order that +way would break overriding compat properties with -global. + +Since backporting the upstream work is rather invasive, this commit +loosens the coupling between the two just enough to permit the +reordering: collect accelerator and machine compat properties in +separate lists @accel_compat_props and @machine_compat_props rather +than stuffing them all into @global_props, and apply the three lists +order @accel_compat_props, @machine_compat_props, @global_props. This +matches the order before the patch, thus no functional change. + +Signed-off-by: Markus Armbruster +Signed-off-by: Miroslav Rezanina +--- + accel/accel.c | 2 +- + hw/core/machine.c | 2 +- + hw/core/qdev-properties.c | 34 +++++++++++++++++++++++++++------- + include/hw/qdev-properties.h | 9 +++------ + 4 files changed, 32 insertions(+), 15 deletions(-) + +diff --git a/accel/accel.c b/accel/accel.c +index 5f3d73f..124f957 100644 +--- a/accel/accel.c ++++ b/accel/accel.c +@@ -123,7 +123,7 @@ void configure_accelerator(MachineState *ms) + void accel_register_compat_props(AccelState *accel) + { + AccelClass *class = ACCEL_GET_CLASS(accel); +- register_compat_props_array(class->global_props); ++ register_accel_compat_props(class->global_props); + } + + static void register_accel_types(void) +diff --git a/hw/core/machine.c b/hw/core/machine.c +index 2040177..b4804e9 100644 +--- a/hw/core/machine.c ++++ b/hw/core/machine.c +@@ -853,7 +853,7 @@ void machine_register_compat_props(MachineState *machine) + p = g_array_index(mc->compat_props, GlobalProperty *, i); + /* Machine compat_props must never cause errors: */ + p->errp = &error_abort; +- qdev_prop_register_global(p); ++ register_machine_compat_prop(p); + } + } + +diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c +index f3a83a3..5fd6d11 100644 +--- a/hw/core/qdev-properties.c ++++ b/hw/core/qdev-properties.c +@@ -1174,6 +1174,14 @@ void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value) + *ptr = value; + } + ++static GList *machine_compat_props; ++static GList *accel_compat_props; ++ ++void register_machine_compat_prop(GlobalProperty *prop) ++{ ++ machine_compat_props = g_list_append(machine_compat_props, prop); ++} ++ + static GList *global_props; + + void qdev_prop_register_global(GlobalProperty *prop) +@@ -1195,10 +1203,17 @@ void register_compat_prop(const char *driver, + qdev_prop_register_global(p); + } + +-void register_compat_props_array(GlobalProperty *prop) ++void register_accel_compat_props(GlobalProperty *props) + { +- for (; prop && prop->driver; prop++) { +- register_compat_prop(prop->driver, prop->property, prop->value); ++ GlobalProperty *p, *prop; ++ ++ for (p = props; p && p->driver; p++) { ++ prop = g_new0(GlobalProperty, 1); ++ prop->errp = &error_abort; ++ prop->driver = p->driver; ++ prop->property = p->property; ++ prop->value = p->value; ++ accel_compat_props = g_list_append(accel_compat_props, prop); + } + } + +@@ -1245,11 +1260,9 @@ int qdev_prop_check_globals(void) + return ret; + } + +-void qdev_prop_set_globals(DeviceState *dev) ++static void qdev_prop_set_globals_1(DeviceState *dev, GList *l) + { +- GList *l; +- +- for (l = global_props; l; l = l->next) { ++ for (; l; l = l->next) { + GlobalProperty *prop = l->data; + Error *err = NULL; + +@@ -1271,6 +1284,13 @@ void qdev_prop_set_globals(DeviceState *dev) + } + } + ++void qdev_prop_set_globals(DeviceState *dev) ++{ ++ qdev_prop_set_globals_1(dev, accel_compat_props); ++ qdev_prop_set_globals_1(dev, machine_compat_props); ++ qdev_prop_set_globals_1(dev, global_props); ++} ++ + /* --- 64bit unsigned int 'size' type --- */ + + static void get_size(Object *obj, Visitor *v, const char *name, void *opaque, +diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h +index b2ad8e9..19ad0ae 100644 +--- a/include/hw/qdev-properties.h ++++ b/include/hw/qdev-properties.h +@@ -280,12 +280,9 @@ void error_set_from_qdev_prop_error(Error **errp, int ret, DeviceState *dev, + */ + void register_compat_prop(const char *driver, const char *property, + const char *value); +-/* +- * register_compat_props_array(): using register_compat_prop(), which +- * only registers internal global properties (which has lower priority +- * than user-provided global properties) +- */ +-void register_compat_props_array(GlobalProperty *prop); ++ ++void register_accel_compat_props(GlobalProperty *props); ++void register_machine_compat_prop(GlobalProperty *prop); + + /** + * qdev_property_add_static: +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qdev-add-HotplugHandler-post_plug-callback.patch b/SOURCES/kvm-qdev-add-HotplugHandler-post_plug-callback.patch new file mode 100644 index 0000000..2609722 --- /dev/null +++ b/SOURCES/kvm-qdev-add-HotplugHandler-post_plug-callback.patch @@ -0,0 +1,109 @@ +From 5cdd4dfafc5280d312001371c6917ccaf2ba06c3 Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +Date: Tue, 24 Jul 2018 15:13:07 +0200 +Subject: [PATCH 08/15] qdev: add HotplugHandler->post_plug() callback + +RH-Author: Stefan Hajnoczi +Message-id: <20180724151308.20500-2-stefanha@redhat.com> +Patchwork-id: 81485 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/2] qdev: add HotplugHandler->post_plug() callback +Bugzilla: 1607891 +RH-Acked-by: Igor Mammedov +RH-Acked-by: Pankaj Gupta +RH-Acked-by: Cornelia Huck + +The ->pre_plug() callback is invoked before the device is realized. The +->plug() callback is invoked when the device is being realized but +before it is reset. + +This patch adds a ->post_plug() callback which is invoked after the +device has been reset. This callback is needed by HotplugHandlers that +need to wait until after ->reset(). + +Signed-off-by: Stefan Hajnoczi +Message-Id: <20180716083732.3347-2-stefanha@redhat.com> +Signed-off-by: Paolo Bonzini +(cherry picked from commit 25e8978817a54745c44d956d8303e6be6f2c4047) +Signed-off-by: Stefan Hajnoczi +Signed-off-by: Miroslav Rezanina +--- + hw/core/hotplug.c | 10 ++++++++++ + hw/core/qdev.c | 4 ++++ + include/hw/hotplug.h | 11 +++++++++++ + 3 files changed, 25 insertions(+) + +diff --git a/hw/core/hotplug.c b/hw/core/hotplug.c +index 17ac986..2253072 100644 +--- a/hw/core/hotplug.c ++++ b/hw/core/hotplug.c +@@ -35,6 +35,16 @@ void hotplug_handler_plug(HotplugHandler *plug_handler, + } + } + ++void hotplug_handler_post_plug(HotplugHandler *plug_handler, ++ DeviceState *plugged_dev) ++{ ++ HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); ++ ++ if (hdc->post_plug) { ++ hdc->post_plug(plug_handler, plugged_dev); ++ } ++} ++ + void hotplug_handler_unplug_request(HotplugHandler *plug_handler, + DeviceState *plugged_dev, + Error **errp) +diff --git a/hw/core/qdev.c b/hw/core/qdev.c +index ce7c316..24f1ae7 100644 +--- a/hw/core/qdev.c ++++ b/hw/core/qdev.c +@@ -893,6 +893,10 @@ static void device_set_realized(Object *obj, bool value, Error **errp) + device_reset(dev); + } + dev->pending_deleted_event = false; ++ ++ if (hotplug_ctrl) { ++ hotplug_handler_post_plug(hotplug_ctrl, dev); ++ } + } else if (!value && dev->realized) { + Error **local_errp = NULL; + QLIST_FOREACH(bus, &dev->child_bus, sibling) { +diff --git a/include/hw/hotplug.h b/include/hw/hotplug.h +index 1a0516a..51541d6 100644 +--- a/include/hw/hotplug.h ++++ b/include/hw/hotplug.h +@@ -47,6 +47,8 @@ typedef void (*hotplug_fn)(HotplugHandler *plug_handler, + * @parent: Opaque parent interface. + * @pre_plug: pre plug callback called at start of device.realize(true) + * @plug: plug callback called at end of device.realize(true). ++ * @post_plug: post plug callback called after device.realize(true) and device ++ * reset + * @unplug_request: unplug request callback. + * Used as a means to initiate device unplug for devices that + * require asynchronous unplug handling. +@@ -61,6 +63,7 @@ typedef struct HotplugHandlerClass { + /* */ + hotplug_fn pre_plug; + hotplug_fn plug; ++ void (*post_plug)(HotplugHandler *plug_handler, DeviceState *plugged_dev); + hotplug_fn unplug_request; + hotplug_fn unplug; + } HotplugHandlerClass; +@@ -84,6 +87,14 @@ void hotplug_handler_pre_plug(HotplugHandler *plug_handler, + Error **errp); + + /** ++ * hotplug_handler_post_plug: ++ * ++ * Call #HotplugHandlerClass.post_plug callback of @plug_handler. ++ */ ++void hotplug_handler_post_plug(HotplugHandler *plug_handler, ++ DeviceState *plugged_dev); ++ ++/** + * hotplug_handler_unplug_request: + * + * Calls #HotplugHandlerClass.unplug_request callback of @plug_handler. +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qdev-add-qdev_add_vm_change_state_handler.patch b/SOURCES/kvm-qdev-add-qdev_add_vm_change_state_handler.patch new file mode 100644 index 0000000..e5a2f73 --- /dev/null +++ b/SOURCES/kvm-qdev-add-qdev_add_vm_change_state_handler.patch @@ -0,0 +1,133 @@ +From 032fc7e1bc37a62ea45d77be9e96847a58bb25c9 Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +Date: Tue, 16 Jul 2019 13:22:14 +0200 +Subject: [PATCH 21/23] qdev: add qdev_add_vm_change_state_handler() + +RH-Author: Stefan Hajnoczi +Message-id: <20190716132215.18503-3-stefanha@redhat.com> +Patchwork-id: 89537 +O-Subject: [RHEL-7.8 RHEL-7.7.z qemu-kvm-rhev PATCH 2/3] qdev: add qdev_add_vm_change_state_handler() +Bugzilla: 1673546 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf + +Children sometimes depend on their parent's vm change state handler +having completed. Add a vm change state handler API for devices that +guarantees tree depth ordering. + +Signed-off-by: Stefan Hajnoczi +Signed-off-by: Kevin Wolf +(cherry picked from commit e965ffa70ac8ddc334dd5990f6907789bd9e6af6) +Signed-off-by: Stefan Hajnoczi +Signed-off-by: Miroslav Rezanina +--- + hw/core/Makefile.objs | 1 + + hw/core/vm-change-state-handler.c | 61 +++++++++++++++++++++++++++++++++++++++ + include/hw/qdev-core.h | 5 ++++ + 3 files changed, 67 insertions(+) + create mode 100644 hw/core/vm-change-state-handler.c + +diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs +index e967fb2..28531b0 100644 +--- a/hw/core/Makefile.objs ++++ b/hw/core/Makefile.objs +@@ -7,6 +7,7 @@ common-obj-$(CONFIG_SOFTMMU) += fw-path-provider.o + common-obj-y += irq.o + common-obj-y += hotplug.o + common-obj-$(CONFIG_SOFTMMU) += nmi.o ++common-obj-$(CONFIG_SOFTMMU) += vm-change-state-handler.o + + common-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o + common-obj-$(CONFIG_XILINX_AXI) += stream.o +diff --git a/hw/core/vm-change-state-handler.c b/hw/core/vm-change-state-handler.c +new file mode 100644 +index 0000000..f814813 +--- /dev/null ++++ b/hw/core/vm-change-state-handler.c +@@ -0,0 +1,61 @@ ++/* ++ * qdev vm change state handlers ++ * ++ * 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 "hw/qdev.h" ++ ++static int qdev_get_dev_tree_depth(DeviceState *dev) ++{ ++ int depth; ++ ++ for (depth = 0; dev; depth++) { ++ BusState *bus = dev->parent_bus; ++ ++ if (!bus) { ++ break; ++ } ++ ++ dev = bus->parent; ++ } ++ ++ return depth; ++} ++ ++/** ++ * qdev_add_vm_change_state_handler: ++ * @dev: the device that owns this handler ++ * @cb: the callback function to be invoked ++ * @opaque: user data passed to the callback function ++ * ++ * This function works like qemu_add_vm_change_state_handler() except callbacks ++ * are invoked in qdev tree depth order. Ordering is desirable when callbacks ++ * of children depend on their parent's callback having completed first. ++ * ++ * For example, when qdev_add_vm_change_state_handler() is used, a host ++ * controller's callback is invoked before the children on its bus when the VM ++ * starts running. The order is reversed when the VM stops running. ++ * ++ * Returns: an entry to be freed with qemu_del_vm_change_state_handler() ++ */ ++VMChangeStateEntry *qdev_add_vm_change_state_handler(DeviceState *dev, ++ VMChangeStateHandler *cb, ++ void *opaque) ++{ ++ int depth = qdev_get_dev_tree_depth(dev); ++ ++ return qemu_add_vm_change_state_handler_prio(cb, opaque, depth); ++} +diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h +index 9453588..ff8bd5a 100644 +--- a/include/hw/qdev-core.h ++++ b/include/hw/qdev-core.h +@@ -6,6 +6,7 @@ + #include "qom/object.h" + #include "hw/irq.h" + #include "hw/hotplug.h" ++#include "sysemu/sysemu.h" + + enum { + DEV_NVECTORS_UNSPECIFIED = -1, +@@ -446,4 +447,8 @@ static inline bool qbus_is_hotpluggable(BusState *bus) + void device_listener_register(DeviceListener *listener); + void device_listener_unregister(DeviceListener *listener); + ++VMChangeStateEntry *qdev_add_vm_change_state_handler(DeviceState *dev, ++ VMChangeStateHandler *cb, ++ void *opaque); ++ + #endif +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-error-introduce-error-warn-_report_once.patch b/SOURCES/kvm-qemu-error-introduce-error-warn-_report_once.patch new file mode 100644 index 0000000..aaba848 --- /dev/null +++ b/SOURCES/kvm-qemu-error-introduce-error-warn-_report_once.patch @@ -0,0 +1,102 @@ +From 22b617c9f567f65f72a29fff454a40f4ed67b45d Mon Sep 17 00:00:00 2001 +From: Peter Xu +Date: Thu, 8 Nov 2018 05:37:15 +0100 +Subject: [PATCH 08/22] qemu-error: introduce {error|warn}_report_once +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Peter Xu +Message-id: <20181108053721.13162-2-peterx@redhat.com> +Patchwork-id: 82951 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/7] qemu-error: introduce {error|warn}_report_once +Bugzilla: 1627272 +RH-Acked-by: Auger Eric +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Philippe Mathieu-Daudé + +There are many error_report()s that can be used in frequently called +functions, especially on IO paths. That can be unideal in that +malicious guest can try to trigger the error tons of time which might +use up the log space on the host (e.g., libvirt can capture the stderr +of QEMU and put it persistently onto disk). In VT-d emulation code, we +have trace_vtd_error() tracer. AFAIU all those places can be replaced +by something like error_report() but trace points are mostly used to +avoid the DDOS attack that mentioned above. However using trace points +mean that errors are not dumped if trace not enabled. + +It's not a big deal in most modern server managements since we have +things like logrotate to maintain the logs and make sure the quota is +expected. However it'll still be nice that we just provide another way +to restrict message generations. In most cases, this kind of +error_report()s will only provide valid information on the first message +sent, and all the rest of similar messages will be mostly talking about +the same thing. This patch introduces *_report_once() helpers to allow +a message to be dumped only once during one QEMU process's life cycle. +It will make sure: (1) it's on by deffault, so we can even get something +without turning the trace on and reproducing, and (2) it won't be +affected by DDOS attack. + +To implement it, I stole the printk_once() macro from Linux. + +CC: Eric Blake +CC: Markus Armbruster +Signed-off-by: Peter Xu +Message-Id: <20180815095328.32414-2-peterx@redhat.com> +Reviewed-by: Markus Armbruster +[Whitespace adjusted, comments improved] +Signed-off-by: Markus Armbruster +(cherry picked from commit bc6a69dd4bfa41ae56235dcbb9a28a56e12a7dc6) +Signed-off-by: Peter Xu + +Signed-off-by: Miroslav Rezanina +--- + include/qemu/error-report.h | 32 ++++++++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +diff --git a/include/qemu/error-report.h b/include/qemu/error-report.h +index e1c8ae1..72fab2b 100644 +--- a/include/qemu/error-report.h ++++ b/include/qemu/error-report.h +@@ -44,6 +44,38 @@ void error_report(const char *fmt, ...) GCC_FMT_ATTR(1, 2); + void warn_report(const char *fmt, ...) GCC_FMT_ATTR(1, 2); + void info_report(const char *fmt, ...) GCC_FMT_ATTR(1, 2); + ++/* ++ * 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_; \ ++ bool ret_print_once_ = !print_once_; \ ++ \ ++ if (!print_once_) { \ ++ print_once_ = true; \ ++ error_report(fmt, ##__VA_ARGS__); \ ++ } \ ++ unlikely(ret_print_once_); \ ++ }) ++ ++/* ++ * 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_; \ ++ bool ret_print_once_ = !print_once_; \ ++ \ ++ if (!print_once_) { \ ++ print_once_ = true; \ ++ warn_report(fmt, ##__VA_ARGS__); \ ++ } \ ++ unlikely(ret_print_once_); \ ++ }) ++ + const char *error_get_progname(void); + extern bool enable_timestamp_msg; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-img-Add-C-option-for-convert-with-copy-offloadi.patch b/SOURCES/kvm-qemu-img-Add-C-option-for-convert-with-copy-offloadi.patch new file mode 100644 index 0000000..b9d59f9 --- /dev/null +++ b/SOURCES/kvm-qemu-img-Add-C-option-for-convert-with-copy-offloadi.patch @@ -0,0 +1,147 @@ +From 2d72f0886af14ab032974dd228e1c55e1501f117 Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Wed, 1 Aug 2018 06:35:37 +0200 +Subject: [PATCH 01/13] qemu-img: Add -C option for convert with copy + offloading + +RH-Author: Fam Zheng +Message-id: <20180801063538.32582-2-famz@redhat.com> +Patchwork-id: 81560 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/2] qemu-img: Add -C option for convert with copy offloading +Bugzilla: 1607774 +RH-Acked-by: Kevin Wolf +RH-Acked-by: John Snow +RH-Acked-by: Miroslav Rezanina + +Signed-off-by: Fam Zheng +Signed-off-by: Kevin Wolf +(cherry picked from commit e11ce12f5eb26438419e486a3ae2c9bb58a23c1f) +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina + +Conflicts: + + qemu-img-cmds.hx + qemu-img.texi + qemu-img.c + +All contextual conflicts. + +1) Downstream doesn't have 46e8d272ba (qemu-img: Remove deprecated -s +snapshot_id_or_name option). +2) Downstream doesn't have 88481329c0 (qemu-img: allow compressed +not-in-order writes). +--- + qemu-img-cmds.hx | 2 +- + qemu-img.c | 21 +++++++++++++++++---- + qemu-img.texi | 8 +++++++- + 3 files changed, 25 insertions(+), 6 deletions(-) + +diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx +index 2fe3189..48815c0 100644 +--- a/qemu-img-cmds.hx ++++ b/qemu-img-cmds.hx +@@ -41,7 +41,7 @@ STEXI + ETEXI + + DEF("convert", img_convert, +- "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename") ++ "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename") + STEXI + @item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} + ETEXI +diff --git a/qemu-img.c b/qemu-img.c +index eaee6d6..c9ccc1e 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -2000,11 +2000,12 @@ static int img_convert(int argc, char **argv) + skip_create = false, progress = false, tgt_image_opts = false; + int64_t ret = -EINVAL; + bool force_share = false; ++ bool explict_min_sparse = false; + + ImgConvertState s = (ImgConvertState) { + /* Need at least 4k of zeros for sparse detection */ + .min_sparse = 8, +- .copy_range = true, ++ .copy_range = false, + .buf_sectors = IO_BUF_SIZE / BDRV_SECTOR_SIZE, + .wr_in_order = true, + .num_coroutines = 8, +@@ -2019,7 +2020,7 @@ static int img_convert(int argc, char **argv) + {"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS}, + {0, 0, 0, 0} + }; +- c = getopt_long(argc, argv, ":hf:O:B:co:s:l:S:pt:T:qnm:WU", ++ c = getopt_long(argc, argv, ":hf:O:B:Cco:s:l:S:pt:T:qnm:WU", + long_options, NULL); + if (c == -1) { + break; +@@ -2043,9 +2044,11 @@ static int img_convert(int argc, char **argv) + case 'B': + out_baseimg = optarg; + break; ++ case 'C': ++ s.copy_range = true; ++ break; + case 'c': + s.compressed = true; +- s.copy_range = false; + break; + case 'o': + if (!is_valid_option_list(optarg)) { +@@ -2087,7 +2090,7 @@ static int img_convert(int argc, char **argv) + } + + s.min_sparse = sval / BDRV_SECTOR_SIZE; +- s.copy_range = false; ++ explict_min_sparse = true; + break; + } + case 'p': +@@ -2152,6 +2155,16 @@ static int img_convert(int argc, char **argv) + 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 (tgt_image_opts && !skip_create) { + error_report("--target-image-opts requires use of -n flag"); + goto fail_getopt; +diff --git a/qemu-img.texi b/qemu-img.texi +index 8a26400..74d7290 100644 +--- a/qemu-img.texi ++++ b/qemu-img.texi +@@ -172,6 +172,12 @@ Number of parallel coroutines for the convert process + Allow out-of-order writes to the destination. This option improves performance, + but is only recommended for preallocated devices like host devices or other + raw block devices. ++@item -C ++Try to use copy offloading to move data from source image to target. This may ++improve performance if the data is remote, such as with NFS or iSCSI backends, ++but will not automatically sparsify zero sectors, and may result in a fully ++allocated target image depending on the host support for getting allocation ++information. + @end table + + Parameters to dd subcommand: +@@ -340,7 +346,7 @@ Error on reading data + + @end table + +-@item convert [-c] [-p] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-m @var{num_coroutines}] [-W] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename} ++@item convert [-C] [-c] [-p] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-m @var{num_coroutines}] [-W] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename} + + Convert the disk image @var{filename} or a snapshot @var{snapshot_param}(@var{snapshot_id_or_name} is deprecated) + to disk image @var{output_filename} using format @var{output_fmt}. It can be optionally compressed (@code{-c} +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-img-Add-print_amend_option_help.patch b/SOURCES/kvm-qemu-img-Add-print_amend_option_help.patch new file mode 100644 index 0000000..7bc0118 --- /dev/null +++ b/SOURCES/kvm-qemu-img-Add-print_amend_option_help.patch @@ -0,0 +1,238 @@ +From dfc5e2a61a96cf4f727d2e7b4f41124933746f6d Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 14:59:40 +0200 +Subject: [PATCH 06/89] qemu-img: Add print_amend_option_help() + +RH-Author: Max Reitz +Message-id: <20180618145943.4489-5-mreitz@redhat.com> +Patchwork-id: 80753 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 4/7] qemu-img: Add print_amend_option_help() +Bugzilla: 1537956 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Stefan Hajnoczi + +The more generic print_block_option_help() function is not really +suitable for qemu-img amend, for a couple of reasons: +(1) We do not need to append the protocol-level options, as amendment + happens only on one node and does not descend downwards to its + children. +(2) print_block_option_help() says those options are "supported". For + option amendment, we do not really know that. So this new function + explicitly says that those options are the creation options, and not + all of them may be supported. +(3) If the driver does not support option amendment, we should not print + anything (except for an error message that amendment is not + supported). + +Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1537956 +Signed-off-by: Max Reitz +Reviewed-by: John Snow +Reviewed-by: Eric Blake +Message-id: 20180509210023.20283-5-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit 51641351420e4f9dc6613d0dd03a5f0f214ed5b6) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + qemu-img.c | 30 ++++++++++++++++++++++++++++-- + tests/qemu-iotests/082.out | 44 +++++++++++++++++++++++++++----------------- + 2 files changed, 55 insertions(+), 19 deletions(-) + +diff --git a/qemu-img.c b/qemu-img.c +index cdeb01b..d4cbb63 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -3609,6 +3609,32 @@ static void amend_status_cb(BlockDriverState *bs, + qemu_progress_print(100.f * offset / total_work_size, 0); + } + ++static int print_amend_option_help(const char *format) ++{ ++ BlockDriver *drv; ++ ++ /* 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 create_opts */ ++ assert(drv->create_opts); ++ ++ printf("Creation options for '%s':\n", format); ++ qemu_opts_print_help(drv->create_opts); ++ printf("\nNote that not all of these options may be amendable.\n"); ++ return 0; ++} ++ + static int img_amend(int argc, char **argv) + { + Error *err = NULL; +@@ -3708,7 +3734,7 @@ static int img_amend(int argc, char **argv) + if (fmt && has_help_option(options)) { + /* If a format is explicitly specified (and possibly no filename is + * given), print option help here */ +- ret = print_block_option_help(filename, fmt); ++ ret = print_amend_option_help(fmt); + goto out; + } + +@@ -3737,7 +3763,7 @@ static int img_amend(int argc, char **argv) + + if (has_help_option(options)) { + /* If the format was auto-detected, print option help here */ +- ret = print_block_option_help(filename, fmt); ++ ret = print_amend_option_help(fmt); + goto out; + } + +diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out +index 1527fbe..4e52dce 100644 +--- a/tests/qemu-iotests/082.out ++++ b/tests/qemu-iotests/082.out +@@ -546,7 +546,7 @@ cluster_size: 65536 + === amend: help for -o === + + Testing: amend -f qcow2 -o help TEST_DIR/t.qcow2 +-Supported options: ++Creation options for 'qcow2': + size Virtual disk size + compat Compatibility level (0.10 or 1.1) + backing_file File name of a base image +@@ -564,10 +564,11 @@ cluster_size qcow2 cluster size + preallocation Preallocation mode (allowed values: off, metadata, falloc, full) + lazy_refcounts Postpone refcount updates + refcount_bits Width of a reference count entry in bits +-nocow Turn off copy-on-write (valid only on btrfs) ++ ++Note that not all of these options may be amendable. + + Testing: amend -f qcow2 -o ? TEST_DIR/t.qcow2 +-Supported options: ++Creation options for 'qcow2': + size Virtual disk size + compat Compatibility level (0.10 or 1.1) + backing_file File name of a base image +@@ -585,10 +586,11 @@ cluster_size qcow2 cluster size + preallocation Preallocation mode (allowed values: off, metadata, falloc, full) + lazy_refcounts Postpone refcount updates + refcount_bits Width of a reference count entry in bits +-nocow Turn off copy-on-write (valid only on btrfs) ++ ++Note that not all of these options may be amendable. + + Testing: amend -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 +-Supported options: ++Creation options for 'qcow2': + size Virtual disk size + compat Compatibility level (0.10 or 1.1) + backing_file File name of a base image +@@ -606,10 +608,11 @@ cluster_size qcow2 cluster size + preallocation Preallocation mode (allowed values: off, metadata, falloc, full) + lazy_refcounts Postpone refcount updates + refcount_bits Width of a reference count entry in bits +-nocow Turn off copy-on-write (valid only on btrfs) ++ ++Note that not all of these options may be amendable. + + Testing: amend -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 +-Supported options: ++Creation options for 'qcow2': + size Virtual disk size + compat Compatibility level (0.10 or 1.1) + backing_file File name of a base image +@@ -627,10 +630,11 @@ cluster_size qcow2 cluster size + preallocation Preallocation mode (allowed values: off, metadata, falloc, full) + lazy_refcounts Postpone refcount updates + refcount_bits Width of a reference count entry in bits +-nocow Turn off copy-on-write (valid only on btrfs) ++ ++Note that not all of these options may be amendable. + + Testing: amend -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 +-Supported options: ++Creation options for 'qcow2': + size Virtual disk size + compat Compatibility level (0.10 or 1.1) + backing_file File name of a base image +@@ -648,10 +652,11 @@ cluster_size qcow2 cluster size + preallocation Preallocation mode (allowed values: off, metadata, falloc, full) + lazy_refcounts Postpone refcount updates + refcount_bits Width of a reference count entry in bits +-nocow Turn off copy-on-write (valid only on btrfs) ++ ++Note that not all of these options may be amendable. + + Testing: amend -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 +-Supported options: ++Creation options for 'qcow2': + size Virtual disk size + compat Compatibility level (0.10 or 1.1) + backing_file File name of a base image +@@ -669,10 +674,11 @@ cluster_size qcow2 cluster size + preallocation Preallocation mode (allowed values: off, metadata, falloc, full) + lazy_refcounts Postpone refcount updates + refcount_bits Width of a reference count entry in bits +-nocow Turn off copy-on-write (valid only on btrfs) ++ ++Note that not all of these options may be amendable. + + Testing: amend -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 +-Supported options: ++Creation options for 'qcow2': + size Virtual disk size + compat Compatibility level (0.10 or 1.1) + backing_file File name of a base image +@@ -690,10 +696,11 @@ cluster_size qcow2 cluster size + preallocation Preallocation mode (allowed values: off, metadata, falloc, full) + lazy_refcounts Postpone refcount updates + refcount_bits Width of a reference count entry in bits +-nocow Turn off copy-on-write (valid only on btrfs) ++ ++Note that not all of these options may be amendable. + + Testing: amend -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 +-Supported options: ++Creation options for 'qcow2': + size Virtual disk size + compat Compatibility level (0.10 or 1.1) + backing_file File name of a base image +@@ -711,7 +718,8 @@ cluster_size qcow2 cluster size + preallocation Preallocation mode (allowed values: off, metadata, falloc, full) + lazy_refcounts Postpone refcount updates + refcount_bits Width of a reference count entry in bits +-nocow Turn off copy-on-write (valid only on btrfs) ++ ++Note that not all of these options may be amendable. + + Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 + +@@ -731,7 +739,7 @@ Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2 -o ,, -o help TEST_DIR/ + qemu-img: Invalid option list: ,, + + Testing: amend -f qcow2 -o help +-Supported options: ++Creation options for 'qcow2': + size Virtual disk size + compat Compatibility level (0.10 or 1.1) + backing_file File name of a base image +@@ -750,6 +758,8 @@ preallocation Preallocation mode (allowed values: off, metadata, falloc, full + lazy_refcounts Postpone refcount updates + refcount_bits Width of a reference count entry in bits + ++Note that not all of these options may be amendable. ++ + Testing: convert -o help + Supported options: + size Virtual disk size +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-img-Amendment-support-implies-create_opts.patch b/SOURCES/kvm-qemu-img-Amendment-support-implies-create_opts.patch new file mode 100644 index 0000000..6a6691b --- /dev/null +++ b/SOURCES/kvm-qemu-img-Amendment-support-implies-create_opts.patch @@ -0,0 +1,61 @@ +From d43212510cea4b1eda20cb43f357473590d54ffd Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 14:59:37 +0200 +Subject: [PATCH 03/89] qemu-img: Amendment support implies create_opts + +RH-Author: Max Reitz +Message-id: <20180618145943.4489-2-mreitz@redhat.com> +Patchwork-id: 80758 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/7] qemu-img: Amendment support implies create_opts +Bugzilla: 1537956 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Stefan Hajnoczi + +Instead of checking whether a driver has a non-NULL create_opts we +should check whether it supports image amendment in the first place. If +it does, it must have create_opts. + +On the other hand, if it does not have create_opts (so it does not +support amendment either), the error message "does not support any +options" is a bit useless. Stating clearly that the driver has no +amendment support whatsoever is probably better. + +Signed-off-by: Max Reitz +Reviewed-by: John Snow +Reviewed-by: Eric Blake +Message-id: 20180509210023.20283-2-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit 1f996683ad908fd41c572221a63d9fc31ce04d07) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + qemu-img.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/qemu-img.c b/qemu-img.c +index 60e45ec..2f7c491 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -3740,13 +3740,16 @@ static int img_amend(int argc, char **argv) + goto out; + } + +- if (!bs->drv->create_opts) { +- error_report("Format driver '%s' does not support any options to amend", ++ if (!bs->drv->bdrv_amend_options) { ++ error_report("Format driver '%s' does not support option amendment", + fmt); + ret = -1; + goto out; + } + ++ /* Every driver supporting amendment must have create_opts */ ++ assert(bs->drv->create_opts); ++ + create_opts = qemu_opts_append(create_opts, bs->drv->create_opts); + opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); + qemu_opts_do_parse(opts, options, NULL, &err); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-img-Check-post-truncation-size.patch b/SOURCES/kvm-qemu-img-Check-post-truncation-size.patch new file mode 100644 index 0000000..1a43ecd --- /dev/null +++ b/SOURCES/kvm-qemu-img-Check-post-truncation-size.patch @@ -0,0 +1,100 @@ +From 28b3e17abefac49221fe9b8826ed10f4e0f3f585 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Wed, 16 May 2018 12:00:18 +0200 +Subject: [PATCH 1/8] qemu-img: Check post-truncation size + +RH-Author: Max Reitz +Message-id: <20180516120018.27565-2-mreitz@redhat.com> +Patchwork-id: 80281 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/1] qemu-img: Check post-truncation size +Bugzilla: 1523065 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Kevin Wolf + +Some block drivers (iscsi and file-posix when dealing with device files) +do not actually support truncation, even though they provide a +.bdrv_truncate() method and will happily return success when providing a +new size that does not exceed the current size. This is because these +drivers expect the user to resize the image outside of qemu and then +provide qemu with that information through the block_resize command +(compare cb1b83e740384b4e0d950f3d7c81c02b8ce86c2e). + +Of course, anyone using qemu-img resize will find that behavior useless. +So we should check the actual size of the image after the supposedly +successful truncation took place, emit an error if nothing changed and +emit a warning if the target size was not met. + +Signed-off-by: Max Reitz +Message-id: 20180421163957.29872-1-mreitz@redhat.com +Reviewed-by: Stefan Hajnoczi +Signed-off-by: Max Reitz +(cherry picked from commit 5279b30392da7a3248b320c75f20c61e3a95863c) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + qemu-img.c | 39 +++++++++++++++++++++++++++++++++++---- + 1 file changed, 35 insertions(+), 4 deletions(-) + +diff --git a/qemu-img.c b/qemu-img.c +index 855fa52..8320887 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -3381,7 +3381,7 @@ 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; ++ int64_t n, total_size, current_size, new_size; + bool quiet = false; + BlockBackend *blk = NULL; + PreallocMode prealloc = PREALLOC_MODE_OFF; +@@ -3557,11 +3557,42 @@ static int img_resize(int argc, char **argv) + } + + ret = blk_truncate(blk, total_size, prealloc, &err); +- if (!ret) { +- qprintf(quiet, "Image resized.\n"); +- } else { ++ if (ret < 0) { + error_report_err(err); ++ goto out; ++ } ++ ++ new_size = blk_getlength(blk); ++ if (new_size < 0) { ++ error_report("Failed to verify truncated image length: %s", ++ strerror(-new_size)); ++ ret = -1; ++ goto out; + } ++ ++ /* Some block drivers implement a truncation method, but only so ++ * the user can cause qemu to refresh the image's size from disk. ++ * The idea is that the user resizes the image outside of qemu and ++ * then invokes block_resize to inform qemu about it. ++ * (This includes iscsi and file-posix for device files.) ++ * Of course, that is not the behavior someone invoking ++ * qemu-img resize would find useful, so we catch that behavior ++ * here and tell the user. */ ++ if (new_size != total_size && new_size == current_size) { ++ error_report("Image was not resized; resizing may not be supported " ++ "for this image"); ++ ret = -1; ++ goto out; ++ } ++ ++ if (new_size != total_size) { ++ warn_report("Image should have been resized to %" PRIi64 ++ " bytes, but was resized to %" PRIi64 " bytes", ++ total_size, new_size); ++ } ++ ++ qprintf(quiet, "Image resized.\n"); ++ + out: + blk_unref(blk); + if (ret) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-img-Convert-with-copy-offloading.patch b/SOURCES/kvm-qemu-img-Convert-with-copy-offloading.patch new file mode 100644 index 0000000..b3864b9 --- /dev/null +++ b/SOURCES/kvm-qemu-img-Convert-with-copy-offloading.patch @@ -0,0 +1,145 @@ +From 2b47a16d95ebd4d50a0a62d30acd007eb50ce0ea Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Wed, 4 Jul 2018 07:56:31 +0200 +Subject: [PATCH 46/57] qemu-img: Convert with copy offloading + +RH-Author: Fam Zheng +Message-id: <20180629061153.12687-11-famz@redhat.com> +Patchwork-id: 81158 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 10/13] qemu-img: Convert with copy offloading +Bugzilla: 1482537 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Kevin Wolf + +The new blk_co_copy_range interface offers a more efficient way in the +case of network based storage. Make use of it to allow faster convert +operation. + +Since copy offloading cannot do zero detection ('-S') and compression +(-c), only try it when these options are not used. + +Signed-off-by: Fam Zheng +Reviewed-by: Stefan Hajnoczi +Message-id: 20180601092648.24614-11-famz@redhat.com +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit ee5306d0923377439776e8a30b9fd2de34b5cbfb) +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + qemu-img.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 48 insertions(+), 2 deletions(-) + +diff --git a/qemu-img.c b/qemu-img.c +index 9fc8e66..b2ef54e 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -1561,6 +1561,7 @@ typedef struct ImgConvertState { + bool target_has_backing; + int64_t target_backing_sectors; /* negative if unknown */ + bool wr_in_order; ++ bool copy_range; + int min_sparse; + size_t cluster_sectors; + size_t buf_sectors; +@@ -1765,6 +1766,37 @@ static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num, + 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); ++ 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; +@@ -1787,6 +1819,7 @@ static void coroutine_fn convert_co_do_copy(void *opaque) + 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) { +@@ -1816,7 +1849,9 @@ static void coroutine_fn convert_co_do_copy(void *opaque) + s->allocated_sectors, 0); + } + +- if (status == BLK_DATA) { ++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 sector %" PRId64 +@@ -1838,7 +1873,15 @@ static void coroutine_fn convert_co_do_copy(void *opaque) + } + + if (s->ret == -EINPROGRESS) { +- ret = convert_co_write(s, sector_num, n, buf, status); ++ if (copy_range) { ++ 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 sector %" PRId64 + ": %s", sector_num, strerror(-ret)); +@@ -1961,6 +2004,7 @@ static int img_convert(int argc, char **argv) + ImgConvertState s = (ImgConvertState) { + /* Need at least 4k of zeros for sparse detection */ + .min_sparse = 8, ++ .copy_range = true, + .buf_sectors = IO_BUF_SIZE / BDRV_SECTOR_SIZE, + .wr_in_order = true, + .num_coroutines = 8, +@@ -2001,6 +2045,7 @@ static int img_convert(int argc, char **argv) + break; + case 'c': + s.compressed = true; ++ s.copy_range = false; + break; + case 'o': + if (!is_valid_option_list(optarg)) { +@@ -2042,6 +2087,7 @@ static int img_convert(int argc, char **argv) + } + + s.min_sparse = sval / BDRV_SECTOR_SIZE; ++ s.copy_range = false; + break; + } + case 'p': +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-img-Enable-BDRV_REQ_MAY_UNMAP-in-convert.patch b/SOURCES/kvm-qemu-img-Enable-BDRV_REQ_MAY_UNMAP-in-convert.patch new file mode 100644 index 0000000..0c7448b --- /dev/null +++ b/SOURCES/kvm-qemu-img-Enable-BDRV_REQ_MAY_UNMAP-in-convert.patch @@ -0,0 +1,103 @@ +From c018040299805135af45098641391a2818ddac8a Mon Sep 17 00:00:00 2001 +From: Maxim Levitsky +Date: Mon, 26 Aug 2019 09:19:44 +0200 +Subject: [PATCH 4/4] qemu-img: Enable BDRV_REQ_MAY_UNMAP in convert + +RH-Author: Maxim Levitsky +Message-id: <20190826091944.16113-2-mlevitsk@redhat.com> +Patchwork-id: 90164 +O-Subject: [RHEL-7.7.z qemu-kvm-rhev PATCH v3 1/1] qemu-img: Enable BDRV_REQ_MAY_UNMAP in convert +Bugzilla: 1648622 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella + +From: Nir Soffer + +With Kevin's "block: Fix slow pre-zeroing in qemu-img convert"[1] +(commit c9fdcf202f, 'qemu-img: Use BDRV_REQ_NO_FALLBACK for +pre-zeroing') we skip the pre zero step called like this: + + blk_make_zero(s->target, BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) + +And we write zeroes later using: + + blk_co_pwrite_zeroes(s->target, + sector_num << BDRV_SECTOR_BITS, + n << BDRV_SECTOR_BITS, 0); + +Since we use flags=0, this is translated to NBD_CMD_WRITE_ZEROES with +NBD_CMD_FLAG_NO_HOLE flag, which cause the NBD server to allocated space +instead of punching a hole. + +Here is an example failure: + +$ dd if=/dev/urandom of=src.img bs=1M count=5 +$ truncate -s 50m src.img +$ truncate -s 50m dst.img +$ nbdkit -f -v -e '' -U nbd.sock file file=dst.img + +$ ./qemu-img convert -n src.img nbd:unix:nbd.sock + +We can see in nbdkit log that it received the NBD_CMD_FLAG_NO_HOLE +(may_trim=0): + +nbdkit: file[1]: debug: newstyle negotiation: flags: export 0x4d +nbdkit: file[1]: debug: pwrite count=2097152 offset=0 +nbdkit: file[1]: debug: pwrite count=2097152 offset=2097152 +nbdkit: file[1]: debug: pwrite count=1048576 offset=4194304 +nbdkit: file[1]: debug: zero count=33554432 offset=5242880 may_trim=0 +nbdkit: file[1]: debug: zero count=13631488 offset=38797312 may_trim=0 +nbdkit: file[1]: debug: flush + +And the image became fully allocated: + +$ qemu-img info dst.img +virtual size: 50M (52428800 bytes) +disk size: 50M + +With this change we see that nbdkit did not receive the +NBD_CMD_FLAG_NO_HOLE (may_trim=1): + +nbdkit: file[1]: debug: newstyle negotiation: flags: export 0x4d +nbdkit: file[1]: debug: pwrite count=2097152 offset=0 +nbdkit: file[1]: debug: pwrite count=2097152 offset=2097152 +nbdkit: file[1]: debug: pwrite count=1048576 offset=4194304 +nbdkit: file[1]: debug: zero count=33554432 offset=5242880 may_trim=1 +nbdkit: file[1]: debug: zero count=13631488 offset=38797312 may_trim=1 +nbdkit: file[1]: debug: flush + +And the file is sparse as expected: + +$ qemu-img info dst.img +virtual size: 50M (52428800 bytes) +disk size: 5.0M + +[1] http://lists.nongnu.org/archive/html/qemu-block/2019-03/msg00761.html + +Signed-off-by: Nir Soffer +Signed-off-by: Kevin Wolf +(cherry picked from commit a3d6ae2299eaab1bced05551d0a0abfbcd9d08d0) +Signed-off-by: Maxim Levitsky +Signed-off-by: Miroslav Rezanina +--- + qemu-img.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/qemu-img.c b/qemu-img.c +index d588183..f7c1483 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -1738,7 +1738,8 @@ static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num, + } + ret = blk_co_pwrite_zeroes(s->target, + sector_num << BDRV_SECTOR_BITS, +- n << BDRV_SECTOR_BITS, 0); ++ n << BDRV_SECTOR_BITS, ++ BDRV_REQ_MAY_UNMAP); + if (ret < 0) { + return ret; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-img-Fix-assert-when-mapping-unaligned-raw-file.patch b/SOURCES/kvm-qemu-img-Fix-assert-when-mapping-unaligned-raw-file.patch new file mode 100644 index 0000000..f7bd9f8 --- /dev/null +++ b/SOURCES/kvm-qemu-img-Fix-assert-when-mapping-unaligned-raw-file.patch @@ -0,0 +1,61 @@ +From bd6ce734563c0308b021c013f1e622ff185be60b Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 6 Aug 2018 16:35:52 +0200 +Subject: [PATCH 03/13] qemu-img: Fix assert when mapping unaligned raw file + +RH-Author: Max Reitz +Message-id: <20180806163553.13344-2-mreitz@redhat.com> +Patchwork-id: 81647 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/2] qemu-img: Fix assert when mapping unaligned raw file +Bugzilla: 1601310 +RH-Acked-by: Eric Blake +RH-Acked-by: Kevin Wolf +RH-Acked-by: John Snow + +From: Eric Blake + +Commit a290f085 exposed a latent bug in qemu-img map introduced +during the conversion of block status to be byte-based. Earlier in +commit 5e344dd8, the internal interface get_block_status() switched +to take byte-based parameters, but still called a sector-based +block layer function; as such, rounding was added in the lone +caller to obey the contract. However, commit 237d78f8 changed +get_block_status() to truly be byte-based, at which point rounding +to sector boundaries can result in calling bdrv_block_status() with +'bytes == 0' (a coding error) when the boundary between data and a +hole falls mid-sector (true for the past-EOF implicit hole present +in POSIX files). Fix things by removing the rounding that is now +no longer necessary. + +See also https://bugzilla.redhat.com/1589738 + +Fixes: 237d78f8 +Reported-by: Dan Kenigsberg +Reported-by: Nir Soffer +Reported-by: Maor Lipchuk +CC: qemu-stable@nongnu.org +Signed-off-by: Eric Blake +Signed-off-by: Kevin Wolf +(cherry picked from commit e0b371ed5e2db079051139136fd0478728b6a58f) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + qemu-img.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/qemu-img.c b/qemu-img.c +index c9ccc1e..f42750a 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -2925,7 +2925,7 @@ static int img_map(int argc, char **argv) + int64_t n; + + /* Probe up to 1 GiB at a time. */ +- n = QEMU_ALIGN_DOWN(MIN(1 << 30, length - offset), BDRV_SECTOR_SIZE); ++ n = MIN(1 << 30, length - offset); + ret = get_block_status(bs, offset, n, &next); + + if (ret < 0) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-img-Gracefully-shutdown-when-map-can-t-finish.patch b/SOURCES/kvm-qemu-img-Gracefully-shutdown-when-map-can-t-finish.patch new file mode 100644 index 0000000..d392d4b --- /dev/null +++ b/SOURCES/kvm-qemu-img-Gracefully-shutdown-when-map-can-t-finish.patch @@ -0,0 +1,91 @@ +From f93730ef94f17043f240950d40d4661629b89aa3 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 6 May 2019 17:56:15 +0200 +Subject: [PATCH 05/53] qemu-img: Gracefully shutdown when map can't finish + +RH-Author: John Snow +Message-id: <20190506175629.11079-6-jsnow@redhat.com> +Patchwork-id: 87190 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 05/19] qemu-img: Gracefully shutdown when map can't finish +Bugzilla: 1692018 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Thomas Huth + +From: Eric Blake + +Trying 'qemu-img map -f raw nbd://localhost:10809' causes the +NBD server to output a scary message: + +qemu-nbd: Disconnect client, due to: Failed to read request: Unexpected end-of-file before all bytes were read + +This is because the NBD client, being remote, has no way to expose a +human-readable map (the --output=json data is fine, however). But +because we exit(1) right after the message, causing the client to +bypass all block cleanup, the server sees the abrupt exit and warns, +whereas it would be silent had the client had a chance to send +NBD_CMD_DISC. Other protocols may have similar cleanup issues, where +failure to blk_unref() could cause unintended effects. + +Signed-off-by: Eric Blake +Message-Id: <20190326184043.7544-1-eblake@redhat.com> +Reviewed-by: John Snow +Reviewed-by: Kevin Wolf +(cherry picked from commit 30065d142443981924786da72828ba683da35e8f) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + qemu-img.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +diff --git a/qemu-img.c b/qemu-img.c +index 096e844..5be2abf 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -2713,14 +2713,14 @@ static int img_info(int argc, char **argv) + return 0; + } + +-static void dump_map_entry(OutputFormat output_format, MapEntry *e, +- MapEntry *next) ++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."); +- exit(1); ++ return -1; + } + if (e->data && !e->zero) { + printf("%#-16"PRIx64"%#-16"PRIx64"%#-16"PRIx64"%s\n", +@@ -2753,6 +2753,7 @@ static void dump_map_entry(OutputFormat output_format, MapEntry *e, + } + break; + } ++ return 0; + } + + static int get_block_status(BlockDriverState *bs, int64_t offset, +@@ -2939,12 +2940,15 @@ static int img_map(int argc, char **argv) + } + + if (curr.length > 0) { +- dump_map_entry(output_format, &curr, &next); ++ ret = dump_map_entry(output_format, &curr, &next); ++ if (ret < 0) { ++ goto out; ++ } + } + curr = next; + } + +- dump_map_entry(output_format, &curr, NULL); ++ ret = dump_map_entry(output_format, &curr, NULL); + + out: + blk_unref(blk); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-img-Recognize-no-creation-support-in-o-help.patch b/SOURCES/kvm-qemu-img-Recognize-no-creation-support-in-o-help.patch new file mode 100644 index 0000000..2135443 --- /dev/null +++ b/SOURCES/kvm-qemu-img-Recognize-no-creation-support-in-o-help.patch @@ -0,0 +1,74 @@ +From 7fafbdbc3b764038e30ec184edb1ec2207c86218 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 14:59:41 +0200 +Subject: [PATCH 07/89] qemu-img: Recognize no creation support in -o help + +RH-Author: Max Reitz +Message-id: <20180618145943.4489-6-mreitz@redhat.com> +Patchwork-id: 80759 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 5/7] qemu-img: Recognize no creation support in -o help +Bugzilla: 1537956 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Stefan Hajnoczi + +The only users of print_block_option_help() are qemu-img create and +qemu-img convert for the output image, so this function is always used +for image creation (it used to be used for amendment also, but that is +no longer the case). + +So if image creation is not supported by either the format or the +protocol, there is no need to print any option description, because the +user cannot create an image like this anyway. + +This also fixes an assertion failure: + + $ qemu-img create -f bochs -o help + Supported options: + qemu-img: util/qemu-option.c:219: + qemu_opts_print_help: Assertion `list' failed. + [1] 24831 abort (core dumped) qemu-img create -f bochs -o help + +Signed-off-by: Max Reitz +Reviewed-by: John Snow +Reviewed-by: Eric Blake +Message-id: 20180509210023.20283-6-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit d402b6a21a825a5c07aac9251990860723d49f5d) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + qemu-img.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/qemu-img.c b/qemu-img.c +index d4cbb63..4bcf157 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -250,6 +250,11 @@ static int print_block_option_help(const char *filename, const char *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); +@@ -258,6 +263,11 @@ static int print_block_option_help(const char *filename, const char *fmt) + qemu_opts_free(create_opts); + return 1; + } ++ if (!proto_drv->create_opts) { ++ error_report("Protocal driver '%s' does not support image creation", ++ proto_drv->format_name); ++ return 1; ++ } + create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-img-Report-bdrv_block_status-failures.patch b/SOURCES/kvm-qemu-img-Report-bdrv_block_status-failures.patch new file mode 100644 index 0000000..528e422 --- /dev/null +++ b/SOURCES/kvm-qemu-img-Report-bdrv_block_status-failures.patch @@ -0,0 +1,57 @@ +From 1df02b6de7da262eb8349560f27f6c41d85953cd Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 6 May 2019 17:56:11 +0200 +Subject: [PATCH 01/53] qemu-img: Report bdrv_block_status failures + +RH-Author: John Snow +Message-id: <20190506175629.11079-2-jsnow@redhat.com> +Patchwork-id: 87181 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 01/19] qemu-img: Report bdrv_block_status failures +Bugzilla: 1692018 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Thomas Huth + +From: Eric Blake + +If bdrv_block_status_above() fails, we are aborting the convert +process but failing to print an error message. Broken in commit +690c7301 (v2.4) when rewriting convert's logic. + +Discovered when teaching nbdkit to support NBD_CMD_BLOCK_STATUS, and +accidentally violating the protocol by returning more than one extent +in spite of qemu asking for NBD_CMD_FLAG_REQ_ONE. The qemu NBD code +should probably handle the server's non-compliance more gracefully +than failing with EINVAL, but qemu-img shouldn't be silently +squelching any block status failures. It doesn't help that qemu 3.1 +masks the qemu-img bug with extra noise that the nbd code is dumping +to stderr (that noise was cleaned up in d8b4bad8). + +Reported-by: Richard W.M. Jones +Signed-off-by: Eric Blake +Message-Id: <20190323212639.579-2-eblake@redhat.com> +Reviewed-by: Kevin Wolf +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit 2058c2ad261de7f58fae01d63d3d0efa484caf2a) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + qemu-img.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/qemu-img.c b/qemu-img.c +index 3b147ca..096e844 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -1607,6 +1607,8 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) + count, &count, NULL, NULL); + } + if (ret < 0) { ++ error_report("error while reading block status of sector %" PRId64 ++ ": %s", sector_num, strerror(-ret)); + return ret; + } + n = DIV_ROUND_UP(count, BDRV_SECTOR_SIZE); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-img-Resolve-relative-backing-paths-in-rebase.patch b/SOURCES/kvm-qemu-img-Resolve-relative-backing-paths-in-rebase.patch new file mode 100644 index 0000000..e6b5a38 --- /dev/null +++ b/SOURCES/kvm-qemu-img-Resolve-relative-backing-paths-in-rebase.patch @@ -0,0 +1,90 @@ +From 3a3096956fec2d02a3ffe5bc4163a7a35e49707d Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 17:48:32 +0200 +Subject: [PATCH 10/89] qemu-img: Resolve relative backing paths in rebase + +RH-Author: Max Reitz +Message-id: <20180618174833.19439-2-mreitz@redhat.com> +Patchwork-id: 80790 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/2] qemu-img: Resolve relative backing paths in rebase +Bugzilla: 1569835 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Stefan Hajnoczi + +Currently, rebase interprets a relative path for the new backing image +as follows: +(1) Open the new backing image with the given relative path (thus relative to + qemu-img's working directory). +(2) Write it directly into the overlay's backing path field (thus + relative to the overlay). + +If the overlay is not in qemu-img's working directory, both will be +different interpretations, which may either lead to an error somewhere +(either rebase fails because it cannot open the new backing image, or +your overlay becomes unusable because its backing path does not point to +a file), or, even worse, it may result in your rebase being performed +for a different backing file than what your overlay will point to after +the rebase. + +Fix this by interpreting the target backing path as relative to the +overlay, like qemu-img does everywhere else. + +Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1569835 +Cc: qemu-stable@nongnu.org +Signed-off-by: Max Reitz +Message-id: 20180509182002.8044-2-mreitz@redhat.com +Reviewed-by: Eric Blake +Signed-off-by: Max Reitz +(cherry picked from commit d16699b64671466b42079c45b89127aeea1ca565) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + qemu-img.c | 23 ++++++++++++++++++++++- + 1 file changed, 22 insertions(+), 1 deletion(-) + +diff --git a/qemu-img.c b/qemu-img.c +index 4bcf157..76d6e47 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -3202,6 +3202,9 @@ static int img_rebase(int argc, char **argv) + } + + 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); +@@ -3210,8 +3213,26 @@ static int img_rebase(int argc, char **argv) + qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true); + } + +- blk_new_backing = blk_new_open(out_baseimg, NULL, ++ overlay_filename = bs->exact_filename[0] ? bs->exact_filename ++ : bs->filename; ++ out_real_path = g_malloc(PATH_MAX); ++ ++ bdrv_get_full_backing_filename_from_filename(overlay_filename, ++ out_baseimg, ++ out_real_path, ++ PATH_MAX, ++ &local_err); ++ if (local_err) { ++ error_reportf_err(local_err, ++ "Could not resolve backing filename: "); ++ ret = -1; ++ g_free(out_real_path); ++ goto out; ++ } ++ ++ 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': ", +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-img-Special-post-backing-convert-handling.patch b/SOURCES/kvm-qemu-img-Special-post-backing-convert-handling.patch new file mode 100644 index 0000000..3781ae9 --- /dev/null +++ b/SOURCES/kvm-qemu-img-Special-post-backing-convert-handling.patch @@ -0,0 +1,113 @@ +From 66e67847293522848a74624c0477299cb87dafa4 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 18:04:50 +0200 +Subject: [PATCH 12/89] qemu-img: Special post-backing convert handling + +RH-Author: Max Reitz +Message-id: <20180618180451.23808-2-mreitz@redhat.com> +Patchwork-id: 80795 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/2] qemu-img: Special post-backing convert handling +Bugzilla: 1527898 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Kevin Wolf +RH-Acked-by: Jeffrey Cody + +Currently, qemu-img convert writes zeroes when it reads zeroes. +Sometimes it does not because the target is initialized to zeroes +anyway, so we do not need to overwrite (and thus potentially allocate) +it. This is never the case for targets with backing files, though. But +even they may have an area that is initialized to zeroes, and that is +the area past the end of the backing file (if that is shorter than the +overlay). + +So if the target format's unallocated blocks are zero and there is a gap +between the target's backing file's end and the target's end, we do not +have to explicitly write zeroes there. + +Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1527898 +Signed-off-by: Max Reitz +Message-id: 20180501165750.19242-2-mreitz@redhat.com +Reviewed-by: Eric Blake +Signed-off-by: Max Reitz +(cherry picked from commit 351c8efff9ad809c822d55620df54d575d536f68) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + qemu-img.c | 26 +++++++++++++++++++++++++- + 1 file changed, 25 insertions(+), 1 deletion(-) + +diff --git a/qemu-img.c b/qemu-img.c +index 76d6e47..e2395b9 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -1553,7 +1553,9 @@ typedef struct ImgConvertState { + BlockBackend *target; + bool has_zero_init; + bool compressed; ++ bool unallocated_blocks_are_zero; + bool target_has_backing; ++ int64_t target_backing_sectors; /* negative if unknown */ + bool wr_in_order; + int min_sparse; + size_t cluster_sectors; +@@ -1582,12 +1584,23 @@ static int 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 = s->unallocated_blocks_are_zero; ++ } 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) { + int64_t count = n * BDRV_SECTOR_SIZE; + +@@ -1609,7 +1622,7 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) + n = DIV_ROUND_UP(count, BDRV_SECTOR_SIZE); + + if (ret & BDRV_BLOCK_ZERO) { +- s->status = BLK_ZERO; ++ s->status = post_backing_zero ? BLK_BACKING_FILE : BLK_ZERO; + } else if (ret & BDRV_BLOCK_DATA) { + s->status = BLK_DATA; + } else { +@@ -2330,6 +2343,16 @@ static int img_convert(int argc, char **argv) + } + } + ++ if (s.target_has_backing) { ++ /* 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. */ ++ s.target_backing_sectors = bdrv_nb_sectors(out_bs->backing->bs); ++ } else { ++ s.target_backing_sectors = -1; ++ } ++ + ret = bdrv_get_info(out_bs, &bdi); + if (ret < 0) { + if (s.compressed) { +@@ -2339,6 +2362,7 @@ static int img_convert(int argc, char **argv) + } else { + s.compressed = s.compressed || bdi.needs_compressed_writes; + s.cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE; ++ s.unallocated_blocks_are_zero = bdi.unallocated_blocks_are_zero; + } + + ret = convert_do_copy(&s); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-img-Use-BDRV_REQ_NO_FALLBACK-for-pre-zeroing.patch b/SOURCES/kvm-qemu-img-Use-BDRV_REQ_NO_FALLBACK-for-pre-zeroing.patch new file mode 100644 index 0000000..627a596 --- /dev/null +++ b/SOURCES/kvm-qemu-img-Use-BDRV_REQ_NO_FALLBACK-for-pre-zeroing.patch @@ -0,0 +1,57 @@ +From 7a2aff323f83e6f98f81a2cbe59005e34a094f93 Mon Sep 17 00:00:00 2001 +From: Maxim Levitsky +Date: Wed, 5 Jun 2019 13:57:04 +0200 +Subject: [PATCH 16/23] qemu-img: Use BDRV_REQ_NO_FALLBACK for pre-zeroing + +RH-Author: Maxim Levitsky +Message-id: <20190605135705.24526-9-mlevitsk@redhat.com> +Patchwork-id: 88558 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 8/9] qemu-img: Use BDRV_REQ_NO_FALLBACK for pre-zeroing +Bugzilla: 1648622 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow + +From: Kevin Wolf + +If qemu-img convert sees that the target image isn't zero-initialised +yet, it tries to do an efficient zero write for the whole image first +to save the overhead of repeated explicit zero writes during the +conversion. Obviously, this provides only an advantage if the +pre-zeroing is actually efficient. Otherwise, we can end up writing +zeroes slowly while zeroing out the whole image, and then overwrite the +same blocks again with real data, potentially doubling the written data. + +Pass BDRV_REQ_NO_FALLBACK to blk_make_zero() to avoid this case. If we +can't efficiently zero out, we'll instead write explicit zeroes only if +there is no data to be written to a block. + +Signed-off-by: Kevin Wolf +Acked-by: Eric Blake + +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1648622 +Signed-off-by: Maxim Levitsky + +(Cherry-picked from c9fdcf202f19fc2acdcb1ee0522ff5d61bf8c906, no conflicts) + +Signed-off-by: Miroslav Rezanina +--- + qemu-img.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/qemu-img.c b/qemu-img.c +index 5be2abf..d588183 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -1916,7 +1916,7 @@ static int convert_do_copy(ImgConvertState *s) + if (!s->has_zero_init && !s->target_has_backing && + bdrv_can_write_zeroes_with_unmap(blk_bs(s->target))) + { +- ret = blk_make_zero(s->target, BDRV_REQ_MAY_UNMAP); ++ ret = blk_make_zero(s->target, BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK); + if (ret == 0) { + s->has_zero_init = true; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-img-Use-only-string-options-in-img_open_opts.patch b/SOURCES/kvm-qemu-img-Use-only-string-options-in-img_open_opts.patch new file mode 100644 index 0000000..65635b9 --- /dev/null +++ b/SOURCES/kvm-qemu-img-Use-only-string-options-in-img_open_opts.patch @@ -0,0 +1,61 @@ +From b7988ef42d1a3b892c8b3cc99ad7782f2dc3e05c Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 25 Jun 2018 13:06:56 +0200 +Subject: [PATCH 38/54] qemu-img: Use only string options in img_open_opts + +RH-Author: Max Reitz +Message-id: <20180618163106.23010-3-mreitz@redhat.com> +Patchwork-id: 80774 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 2/3] qemu-img: Use only string options in img_open_opts +Bugzilla: 1576598 +RH-Acked-by: Kevin Wolf +RH-Acked-by: Fam Zheng +RH-Acked-by: Miroslav Rezanina + +img_open_opts() takes a QemuOpts and converts them to a QDict, so all +values therein are strings. Then it may try to call qdict_get_bool(), +however, which will fail with a segmentation fault every time: + +$ ./qemu-img info -U --image-opts \ + driver=file,filename=/dev/null,force-share=off +[1] 27869 segmentation fault (core dumped) ./qemu-img info -U +--image-opts driver=file,filename=/dev/null,force-share=off + +Fix this by using qdict_get_str() and comparing the value as a string. +Also, when adding a force-share value to the QDict, add it as a string +so it fits the rest of the dict. + +Cc: qemu-stable@nongnu.org +Signed-off-by: Max Reitz +Message-id: 20180502202051.15493-3-mreitz@redhat.com +Reviewed-by: Eric Blake +Signed-off-by: Max Reitz +(cherry picked from commit 4615f87832d2fcb7a544bedeece2741bf8c21f94) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + qemu-img.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/qemu-img.c b/qemu-img.c +index 62b29e7..60e45ec 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -277,12 +277,12 @@ static BlockBackend *img_open_opts(const char *optstr, + options = qemu_opts_to_qdict(opts, NULL); + if (force_share) { + if (qdict_haskey(options, BDRV_OPT_FORCE_SHARE) +- && !qdict_get_bool(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_bool(options, BDRV_OPT_FORCE_SHARE, true); ++ qdict_put_str(options, BDRV_OPT_FORCE_SHARE, "on"); + } + blk = blk_new_open(NULL, NULL, options, flags, &local_err); + if (!blk) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-img-fix-error-reporting-for-object.patch b/SOURCES/kvm-qemu-img-fix-error-reporting-for-object.patch new file mode 100644 index 0000000..a710d19 --- /dev/null +++ b/SOURCES/kvm-qemu-img-fix-error-reporting-for-object.patch @@ -0,0 +1,168 @@ +From 021b459bdd231286b9029da55fc7847cd338d66e Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 5 Apr 2019 21:41:02 +0200 +Subject: [PATCH 096/163] qemu-img: fix error reporting for -object +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190405214102.17467-1-jsnow@redhat.com> +Patchwork-id: 85467 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 21.5/55] qemu-img: fix error reporting for -object +Bugzilla: 1691009 +RH-Acked-by: Max Reitz +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Miroslav Rezanina + +From: Daniel P. Berrangé + +Error reporting for user_creatable_add_opts_foreach was changed so that +it no longer called 'error_report_err' in: + + commit 7e1e0c11127bde81cff260fc6859690435c509d6 + Author: Markus Armbruster + Date: Wed Oct 17 10:26:43 2018 +0200 + + qom: Clean up error reporting in user_creatable_add_opts_foreach() + +Some callers were updated to pass in "&error_fatal" but all the ones in +qemu-img were left passing NULL. As a result all errors went to +/dev/null instead of being reported to the user. + +Signed-off-by: Daniel P. Berrangé +Reviewed-by: Philippe Mathieu-Daudé +Reviewed-by: Markus Armbruster +Reviewed-by: Stefano Garzarella +Signed-off-by: Kevin Wolf +(cherry picked from commit 334c43e2c342e878311c66b4e62343f0a7c2c6be) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + qemu-img.c | 26 +++++++++++++------------- + 1 file changed, 13 insertions(+), 13 deletions(-) + +diff --git a/qemu-img.c b/qemu-img.c +index fa0cbd7..3b147ca 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -505,7 +505,7 @@ static int img_create(int argc, char **argv) + + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, +- NULL, NULL)) { ++ NULL, &error_fatal)) { + goto fail; + } + +@@ -755,7 +755,7 @@ static int img_check(int argc, char **argv) + + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, +- NULL, NULL)) { ++ NULL, &error_fatal)) { + return 1; + } + +@@ -968,7 +968,7 @@ static int img_commit(int argc, char **argv) + + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, +- NULL, NULL)) { ++ NULL, &error_fatal)) { + return 1; + } + +@@ -1327,7 +1327,7 @@ static int img_compare(int argc, char **argv) + + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, +- NULL, NULL)) { ++ NULL, &error_fatal)) { + ret = 2; + goto out4; + } +@@ -2132,7 +2132,7 @@ static int img_convert(int argc, char **argv) + + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, +- NULL, NULL)) { ++ NULL, &error_fatal)) { + goto fail_getopt; + } + +@@ -2684,7 +2684,7 @@ static int img_info(int argc, char **argv) + + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, +- NULL, NULL)) { ++ NULL, &error_fatal)) { + return 1; + } + +@@ -2903,7 +2903,7 @@ static int img_map(int argc, char **argv) + + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, +- NULL, NULL)) { ++ NULL, &error_fatal)) { + return 1; + } + +@@ -3052,7 +3052,7 @@ static int img_snapshot(int argc, char **argv) + + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, +- NULL, NULL)) { ++ NULL, &error_fatal)) { + return 1; + } + +@@ -3212,7 +3212,7 @@ static int img_rebase(int argc, char **argv) + + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, +- NULL, NULL)) { ++ NULL, &error_fatal)) { + return 1; + } + +@@ -3592,7 +3592,7 @@ static int img_resize(int argc, char **argv) + + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, +- NULL, NULL)) { ++ NULL, &error_fatal)) { + return 1; + } + +@@ -3836,7 +3836,7 @@ static int img_amend(int argc, char **argv) + + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, +- NULL, NULL)) { ++ NULL, &error_fatal)) { + ret = -1; + goto out_no_progress; + } +@@ -4480,7 +4480,7 @@ static int img_dd(int argc, char **argv) + + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, +- NULL, NULL)) { ++ NULL, &error_fatal)) { + ret = -1; + goto out; + } +@@ -4757,7 +4757,7 @@ static int img_measure(int argc, char **argv) + + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, +- NULL, NULL)) { ++ NULL, &error_fatal)) { + goto out; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-img-fix-regression-copying-secrets-during-conve.patch b/SOURCES/kvm-qemu-img-fix-regression-copying-secrets-during-conve.patch new file mode 100644 index 0000000..3b67a8b --- /dev/null +++ b/SOURCES/kvm-qemu-img-fix-regression-copying-secrets-during-conve.patch @@ -0,0 +1,124 @@ +From c8b73d0f85b2435aabbdec92452cb7a0446b1d36 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Mon, 3 Sep 2018 09:41:40 +0200 +Subject: [PATCH 27/29] qemu-img: fix regression copying secrets during convert +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Kevin Wolf +Message-id: <20180903094140.12988-2-kwolf@redhat.com> +Patchwork-id: 82031 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/1] qemu-img: fix regression copying secrets during convert +Bugzilla: 1575578 +RH-Acked-by: Max Reitz +RH-Acked-by: Daniel P. Berrange +RH-Acked-by: Miroslav Rezanina + +From: Daniel P. Berrangé + +When the convert command is creating an output file that needs +secrets, we need to ensure those secrets are passed to both the +blk_new_open and bdrv_create API calls. + +This is done by qemu-img extracting all opts matching the name +suffix "key-secret". Unfortunately the code doing this was run after the +call to bdrv_create(), which meant the QemuOpts it was extracting +secrets from was now empty. + +Previously this worked by luks as a bug meant the "key-secret" +parameters were not purged from the QemuOpts. This bug was fixed in + + commit b76b4f604521e59f857d6177bc55f6f2e41fd392 + Author: Kevin Wolf + Date: Thu Jan 11 16:18:08 2018 +0100 + + qcow2: Use visitor for options in qcow2_create() + +Exposing the latent bug in qemu-img. This fix simply moves the copying +of secrets to before the bdrv_create() call. + +Cc: qemu-stable@nongnu.org +Signed-off-by: Daniel P. Berrangé +Signed-off-by: Kevin Wolf +(cherry picked from commit 8d65a3ccfd5db7f0436e095cd952f5d0c3a873ba) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + qemu-img.c | 32 +++++++++++++++----------------- + 1 file changed, 15 insertions(+), 17 deletions(-) + +diff --git a/qemu-img.c b/qemu-img.c +index f42750a..fa0cbd7 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -348,21 +348,6 @@ static int img_add_key_secrets(void *opaque, + return 0; + } + +-static BlockBackend *img_open_new_file(const char *filename, +- QemuOpts *create_opts, +- const char *fmt, int flags, +- bool writethrough, bool quiet, +- bool force_share) +-{ +- QDict *options = NULL; +- +- options = qdict_new(); +- qemu_opt_foreach(create_opts, img_add_key_secrets, options, &error_abort); +- +- return img_open_file(filename, options, fmt, flags, writethrough, quiet, +- force_share); +-} +- + + static BlockBackend *img_open(bool image_opts, + const char *filename, +@@ -1994,6 +1979,7 @@ static int img_convert(int argc, char **argv) + 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, quiet = false, image_opts = false, +@@ -2342,6 +2328,16 @@ static int img_convert(int argc, char **argv) + } + } + ++ /* ++ * 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); ++ } ++ + if (!skip_create) { + /* Create the new image */ + ret = bdrv_create(drv, out_filename, opts, &local_err); +@@ -2368,8 +2364,9 @@ static int img_convert(int argc, char **argv) + * That has to wait for bdrv_create to be improved + * to allow filenames in option syntax + */ +- s.target = img_open_new_file(out_filename, opts, out_fmt, +- flags, writethrough, quiet, false); ++ s.target = img_open_file(out_filename, open_opts, out_fmt, ++ flags, writethrough, quiet, false); ++ open_opts = NULL; /* blk_new_open will have freed it */ + } + if (!s.target) { + ret = -1; +@@ -2437,6 +2434,7 @@ out: + qemu_opts_del(opts); + qemu_opts_free(create_opts); + qemu_opts_del(sn_opts); ++ qobject_unref(open_opts); + blk_unref(s.target); + if (s.src) { + for (bs_i = 0; bs_i < s.src_num; bs_i++) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-io-Add-write-n-for-BDRV_REQ_NO_FALLBACK.patch b/SOURCES/kvm-qemu-io-Add-write-n-for-BDRV_REQ_NO_FALLBACK.patch new file mode 100644 index 0000000..1f309f8 --- /dev/null +++ b/SOURCES/kvm-qemu-io-Add-write-n-for-BDRV_REQ_NO_FALLBACK.patch @@ -0,0 +1,87 @@ +From de5542b5a8dc74f413bf2f1b0d5f4b370aa6801b Mon Sep 17 00:00:00 2001 +From: Maxim Levitsky +Date: Wed, 5 Jun 2019 13:57:05 +0200 +Subject: [PATCH 17/23] qemu-io: Add write -n for BDRV_REQ_NO_FALLBACK + +RH-Author: Maxim Levitsky +Message-id: <20190605135705.24526-10-mlevitsk@redhat.com> +Patchwork-id: 88565 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 9/9] qemu-io: Add write -n for BDRV_REQ_NO_FALLBACK +Bugzilla: 1648622 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow + +From: Kevin Wolf + +This makes the new BDRV_REQ_NO_FALLBACK flag available in the qemu-io +write command. + +Signed-off-by: Kevin Wolf +Acked-by: Eric Blake + +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1648622 + +Signed-off-by: Maxim Levitsky +(Cherry picked from c6e3f520c802c5cb2de80576aba7f9f1fe985d8b) + +Signed-off-by: Miroslav Rezanina +--- + qemu-io-cmds.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c +index 9c51e57..ac8c533 100644 +--- a/qemu-io-cmds.c ++++ b/qemu-io-cmds.c +@@ -945,6 +945,7 @@ static void write_help(void) + " -b, -- write to the VM state rather than the virtual disk\n" + " -c, -- write compressed data with blk_write_compressed\n" + " -f, -- use Force Unit Access semantics\n" ++" -n, -- with -z, don't allow slow fallback\n" + " -p, -- ignored for backwards compatibility\n" + " -P, -- use different pattern to fill file\n" + " -C, -- report statistics in a machine parsable format\n" +@@ -963,7 +964,7 @@ static const cmdinfo_t write_cmd = { + .perm = BLK_PERM_WRITE, + .argmin = 2, + .argmax = -1, +- .args = "[-bcCfquz] [-P pattern] off len", ++ .args = "[-bcCfnquz] [-P pattern] off len", + .oneline = "writes a number of bytes at a specified offset", + .help = write_help, + }; +@@ -982,7 +983,7 @@ static int write_f(BlockBackend *blk, int argc, char **argv) + int64_t total = 0; + int pattern = 0xcd; + +- while ((c = getopt(argc, argv, "bcCfpP:quz")) != -1) { ++ while ((c = getopt(argc, argv, "bcCfnpP:quz")) != -1) { + switch (c) { + case 'b': + bflag = true; +@@ -996,6 +997,9 @@ static int write_f(BlockBackend *blk, int argc, char **argv) + case 'f': + flags |= BDRV_REQ_FUA; + break; ++ case 'n': ++ flags |= BDRV_REQ_NO_FALLBACK; ++ break; + case 'p': + /* Ignored for backwards compatibility */ + break; +@@ -1036,6 +1040,11 @@ static int write_f(BlockBackend *blk, int argc, char **argv) + return -EINVAL; + } + ++ if ((flags & BDRV_REQ_NO_FALLBACK) && !zflag) { ++ printf("-n requires -z to be specified\n"); ++ return -EINVAL; ++ } ++ + if ((flags & BDRV_REQ_MAY_UNMAP) && !zflag) { + printf("-u requires -z to be specified\n"); + return -EINVAL; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-io-Drop-command-functions-return-values.patch b/SOURCES/kvm-qemu-io-Drop-command-functions-return-values.patch new file mode 100644 index 0000000..64ef59c --- /dev/null +++ b/SOURCES/kvm-qemu-io-Drop-command-functions-return-values.patch @@ -0,0 +1,1336 @@ +From c1bce8261e118c2efdf6b46a7f271552cb7dacbc Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 25 Jun 2018 13:09:51 +0200 +Subject: [PATCH 40/54] qemu-io: Drop command functions' return values + +RH-Author: Max Reitz +Message-id: <20180618164312.24423-2-mreitz@redhat.com> +Patchwork-id: 80778 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/5] qemu-io: Drop command functions' return values +Bugzilla: 1519617 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Miroslav Rezanina + +For qemu-io, a function returns an integer with two possible values: 0 +for "qemu-io may continue execution", or 1 for "qemu-io should exit". +However, there is only a single command that returns 1, and that is +"quit". + +So let's turn this case into a global variable instead so we can make +better use of the return value in a later patch. + +Signed-off-by: Max Reitz +Reviewed-by: Eric Blake +Message-id: 20180509194302.21585-2-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit b444d0e9d1ae323fed1ef7c35a359ce064f36b32) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + include/qemu-io.h | 6 +- + qemu-io-cmds.c | 294 +++++++++++++++++++++++++----------------------------- + qemu-io.c | 36 +++---- + 3 files changed, 157 insertions(+), 179 deletions(-) + +diff --git a/include/qemu-io.h b/include/qemu-io.h +index 196fde0..06cdfbf 100644 +--- a/include/qemu-io.h ++++ b/include/qemu-io.h +@@ -22,7 +22,7 @@ + + #define CMD_FLAG_GLOBAL ((int)0x80000000) /* don't iterate "args" */ + +-typedef int (*cfunc_t)(BlockBackend *blk, int argc, char **argv); ++typedef void (*cfunc_t)(BlockBackend *blk, int argc, char **argv); + typedef void (*helpfunc_t)(void); + + typedef struct cmdinfo { +@@ -41,10 +41,10 @@ typedef struct cmdinfo { + + extern bool qemuio_misalign; + +-bool qemuio_command(BlockBackend *blk, const char *cmd); ++void qemuio_command(BlockBackend *blk, const char *cmd); + + void qemuio_add_command(const cmdinfo_t *ci); +-int qemuio_command_usage(const cmdinfo_t *ci); ++void qemuio_command_usage(const cmdinfo_t *ci); + void qemuio_complete_command(const char *input, + void (*fn)(const char *cmd, void *opaque), + void *opaque); +diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c +index 9b3cd00..c2fbaae 100644 +--- a/qemu-io-cmds.c ++++ b/qemu-io-cmds.c +@@ -48,10 +48,9 @@ void qemuio_add_command(const cmdinfo_t *ci) + qsort(cmdtab, ncmds, sizeof(*cmdtab), compare_cmdname); + } + +-int qemuio_command_usage(const cmdinfo_t *ci) ++void qemuio_command_usage(const cmdinfo_t *ci) + { + printf("%s %s -- %s\n", ci->name, ci->args, ci->oneline); +- return 0; + } + + static int init_check_command(BlockBackend *blk, const cmdinfo_t *ct) +@@ -66,13 +65,13 @@ static int init_check_command(BlockBackend *blk, const cmdinfo_t *ct) + return 1; + } + +-static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc, +- char **argv) ++static void command(BlockBackend *blk, const cmdinfo_t *ct, int argc, ++ char **argv) + { + char *cmd = argv[0]; + + if (!init_check_command(blk, ct)) { +- return 0; ++ return; + } + + if (argc - 1 < ct->argmin || (ct->argmax != -1 && argc - 1 > ct->argmax)) { +@@ -89,7 +88,7 @@ static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc, + "bad argument count %d to %s, expected between %d and %d arguments\n", + argc-1, cmd, ct->argmin, ct->argmax); + } +- return 0; ++ return; + } + + /* Request additional permissions if necessary for this command. The caller +@@ -109,13 +108,13 @@ static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc, + ret = blk_set_perm(blk, new_perm, orig_shared_perm, &local_err); + if (ret < 0) { + error_report_err(local_err); +- return 0; ++ return; + } + } + } + + optind = 0; +- return ct->cfunc(blk, argc, argv); ++ ct->cfunc(blk, argc, argv); + } + + static const cmdinfo_t *find_command(const char *cmd) +@@ -634,7 +633,7 @@ static void read_help(void) + "\n"); + } + +-static int read_f(BlockBackend *blk, int argc, char **argv); ++static void read_f(BlockBackend *blk, int argc, char **argv); + + static const cmdinfo_t read_cmd = { + .name = "read", +@@ -647,7 +646,7 @@ static const cmdinfo_t read_cmd = { + .help = read_help, + }; + +-static int read_f(BlockBackend *blk, int argc, char **argv) ++static void read_f(BlockBackend *blk, int argc, char **argv) + { + struct timeval t1, t2; + bool Cflag = false, qflag = false, vflag = false; +@@ -674,7 +673,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv) + pattern_count = cvtnum(optarg); + if (pattern_count < 0) { + print_cvtnum_err(pattern_count, optarg); +- return 0; ++ return; + } + break; + case 'p': +@@ -684,7 +683,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv) + Pflag = true; + pattern = parse_pattern(optarg); + if (pattern < 0) { +- return 0; ++ return; + } + break; + case 'q': +@@ -695,40 +694,43 @@ static int read_f(BlockBackend *blk, int argc, char **argv) + pattern_offset = cvtnum(optarg); + if (pattern_offset < 0) { + print_cvtnum_err(pattern_offset, optarg); +- return 0; ++ return; + } + break; + case 'v': + vflag = true; + break; + default: +- return qemuio_command_usage(&read_cmd); ++ qemuio_command_usage(&read_cmd); ++ return; + } + } + + if (optind != argc - 2) { +- return qemuio_command_usage(&read_cmd); ++ qemuio_command_usage(&read_cmd); ++ return; + } + + offset = cvtnum(argv[optind]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); +- return 0; ++ return; + } + + optind++; + count = cvtnum(argv[optind]); + if (count < 0) { + print_cvtnum_err(count, argv[optind]); +- return 0; ++ return; + } else if (count > BDRV_REQUEST_MAX_BYTES) { + printf("length cannot exceed %" PRIu64 ", given %s\n", + (uint64_t)BDRV_REQUEST_MAX_BYTES, argv[optind]); +- return 0; ++ return; + } + + if (!Pflag && (lflag || sflag)) { +- return qemuio_command_usage(&read_cmd); ++ qemuio_command_usage(&read_cmd); ++ return; + } + + if (!lflag) { +@@ -737,19 +739,19 @@ static int read_f(BlockBackend *blk, int argc, char **argv) + + if ((pattern_count < 0) || (pattern_count + pattern_offset > count)) { + printf("pattern verification range exceeds end of read data\n"); +- return 0; ++ return; + } + + if (bflag) { + if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) { + printf("%" PRId64 " is not a sector-aligned value for 'offset'\n", + offset); +- return 0; ++ return; + } + if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) { + printf("%"PRId64" is not a sector-aligned value for 'count'\n", + count); +- return 0; ++ return; + } + } + +@@ -793,8 +795,6 @@ static int read_f(BlockBackend *blk, int argc, char **argv) + + out: + qemu_io_free(buf); +- +- return 0; + } + + static void readv_help(void) +@@ -816,7 +816,7 @@ static void readv_help(void) + "\n"); + } + +-static int readv_f(BlockBackend *blk, int argc, char **argv); ++static void readv_f(BlockBackend *blk, int argc, char **argv); + + static const cmdinfo_t readv_cmd = { + .name = "readv", +@@ -828,7 +828,7 @@ static const cmdinfo_t readv_cmd = { + .help = readv_help, + }; + +-static int readv_f(BlockBackend *blk, int argc, char **argv) ++static void readv_f(BlockBackend *blk, int argc, char **argv) + { + struct timeval t1, t2; + bool Cflag = false, qflag = false, vflag = false; +@@ -851,7 +851,7 @@ static int readv_f(BlockBackend *blk, int argc, char **argv) + Pflag = true; + pattern = parse_pattern(optarg); + if (pattern < 0) { +- return 0; ++ return; + } + break; + case 'q': +@@ -861,26 +861,28 @@ static int readv_f(BlockBackend *blk, int argc, char **argv) + vflag = true; + break; + default: +- return qemuio_command_usage(&readv_cmd); ++ qemuio_command_usage(&readv_cmd); ++ return; + } + } + + if (optind > argc - 2) { +- return qemuio_command_usage(&readv_cmd); ++ qemuio_command_usage(&readv_cmd); ++ return; + } + + + offset = cvtnum(argv[optind]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); +- return 0; ++ return; + } + optind++; + + nr_iov = argc - optind; + buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, 0xab); + if (buf == NULL) { +- return 0; ++ return; + } + + gettimeofday(&t1, NULL); +@@ -917,7 +919,6 @@ static int readv_f(BlockBackend *blk, int argc, char **argv) + out: + qemu_iovec_destroy(&qiov); + qemu_io_free(buf); +- return 0; + } + + static void write_help(void) +@@ -943,7 +944,7 @@ static void write_help(void) + "\n"); + } + +-static int write_f(BlockBackend *blk, int argc, char **argv); ++static void write_f(BlockBackend *blk, int argc, char **argv); + + static const cmdinfo_t write_cmd = { + .name = "write", +@@ -957,7 +958,7 @@ static const cmdinfo_t write_cmd = { + .help = write_help, + }; + +-static int write_f(BlockBackend *blk, int argc, char **argv) ++static void write_f(BlockBackend *blk, int argc, char **argv) + { + struct timeval t1, t2; + bool Cflag = false, qflag = false, bflag = false; +@@ -992,7 +993,7 @@ static int write_f(BlockBackend *blk, int argc, char **argv) + Pflag = true; + pattern = parse_pattern(optarg); + if (pattern < 0) { +- return 0; ++ return; + } + break; + case 'q': +@@ -1005,62 +1006,64 @@ static int write_f(BlockBackend *blk, int argc, char **argv) + zflag = true; + break; + default: +- return qemuio_command_usage(&write_cmd); ++ qemuio_command_usage(&write_cmd); ++ return; + } + } + + if (optind != argc - 2) { +- return qemuio_command_usage(&write_cmd); ++ qemuio_command_usage(&write_cmd); ++ return; + } + + if (bflag && zflag) { + printf("-b and -z cannot be specified at the same time\n"); +- return 0; ++ return; + } + + if ((flags & BDRV_REQ_FUA) && (bflag || cflag)) { + printf("-f and -b or -c cannot be specified at the same time\n"); +- return 0; ++ return; + } + + if ((flags & BDRV_REQ_MAY_UNMAP) && !zflag) { + printf("-u requires -z to be specified\n"); +- return 0; ++ return; + } + + if (zflag && Pflag) { + printf("-z and -P cannot be specified at the same time\n"); +- return 0; ++ return; + } + + offset = cvtnum(argv[optind]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); +- return 0; ++ return; + } + + optind++; + count = cvtnum(argv[optind]); + if (count < 0) { + print_cvtnum_err(count, argv[optind]); +- return 0; ++ return; + } else if (count > BDRV_REQUEST_MAX_BYTES) { + printf("length cannot exceed %" PRIu64 ", given %s\n", + (uint64_t)BDRV_REQUEST_MAX_BYTES, argv[optind]); +- return 0; ++ return; + } + + if (bflag || cflag) { + if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) { + printf("%" PRId64 " is not a sector-aligned value for 'offset'\n", + offset); +- return 0; ++ return; + } + + if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) { + printf("%"PRId64" is not a sector-aligned value for 'count'\n", + count); +- return 0; ++ return; + } + } + +@@ -1097,8 +1100,6 @@ out: + if (!zflag) { + qemu_io_free(buf); + } +- +- return 0; + } + + static void +@@ -1120,7 +1121,7 @@ writev_help(void) + "\n"); + } + +-static int writev_f(BlockBackend *blk, int argc, char **argv); ++static void writev_f(BlockBackend *blk, int argc, char **argv); + + static const cmdinfo_t writev_cmd = { + .name = "writev", +@@ -1133,7 +1134,7 @@ static const cmdinfo_t writev_cmd = { + .help = writev_help, + }; + +-static int writev_f(BlockBackend *blk, int argc, char **argv) ++static void writev_f(BlockBackend *blk, int argc, char **argv) + { + struct timeval t1, t2; + bool Cflag = false, qflag = false; +@@ -1161,29 +1162,31 @@ static int writev_f(BlockBackend *blk, int argc, char **argv) + case 'P': + pattern = parse_pattern(optarg); + if (pattern < 0) { +- return 0; ++ return; + } + break; + default: +- return qemuio_command_usage(&writev_cmd); ++ qemuio_command_usage(&writev_cmd); ++ return; + } + } + + if (optind > argc - 2) { +- return qemuio_command_usage(&writev_cmd); ++ qemuio_command_usage(&writev_cmd); ++ return; + } + + offset = cvtnum(argv[optind]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); +- return 0; ++ return; + } + optind++; + + nr_iov = argc - optind; + buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, pattern); + if (buf == NULL) { +- return 0; ++ return; + } + + gettimeofday(&t1, NULL); +@@ -1205,7 +1208,6 @@ static int writev_f(BlockBackend *blk, int argc, char **argv) + out: + qemu_iovec_destroy(&qiov); + qemu_io_free(buf); +- return 0; + } + + struct aio_ctx { +@@ -1320,7 +1322,7 @@ static void aio_read_help(void) + "\n"); + } + +-static int aio_read_f(BlockBackend *blk, int argc, char **argv); ++static void aio_read_f(BlockBackend *blk, int argc, char **argv); + + static const cmdinfo_t aio_read_cmd = { + .name = "aio_read", +@@ -1332,7 +1334,7 @@ static const cmdinfo_t aio_read_cmd = { + .help = aio_read_help, + }; + +-static int aio_read_f(BlockBackend *blk, int argc, char **argv) ++static void aio_read_f(BlockBackend *blk, int argc, char **argv) + { + int nr_iov, c; + struct aio_ctx *ctx = g_new0(struct aio_ctx, 1); +@@ -1348,14 +1350,14 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv) + ctx->pattern = parse_pattern(optarg); + if (ctx->pattern < 0) { + g_free(ctx); +- return 0; ++ return; + } + break; + case 'i': + printf("injecting invalid read request\n"); + block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_READ); + g_free(ctx); +- return 0; ++ return; + case 'q': + ctx->qflag = true; + break; +@@ -1364,20 +1366,22 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv) + break; + default: + g_free(ctx); +- return qemuio_command_usage(&aio_read_cmd); ++ qemuio_command_usage(&aio_read_cmd); ++ return; + } + } + + if (optind > argc - 2) { + g_free(ctx); +- return qemuio_command_usage(&aio_read_cmd); ++ qemuio_command_usage(&aio_read_cmd); ++ return; + } + + ctx->offset = cvtnum(argv[optind]); + if (ctx->offset < 0) { + print_cvtnum_err(ctx->offset, argv[optind]); + g_free(ctx); +- return 0; ++ return; + } + optind++; + +@@ -1386,14 +1390,13 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv) + if (ctx->buf == NULL) { + block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_READ); + g_free(ctx); +- return 0; ++ return; + } + + gettimeofday(&ctx->t1, NULL); + block_acct_start(blk_get_stats(blk), &ctx->acct, ctx->qiov.size, + BLOCK_ACCT_READ); + blk_aio_preadv(blk, ctx->offset, &ctx->qiov, 0, aio_read_done, ctx); +- return 0; + } + + static void aio_write_help(void) +@@ -1420,7 +1423,7 @@ static void aio_write_help(void) + "\n"); + } + +-static int aio_write_f(BlockBackend *blk, int argc, char **argv); ++static void aio_write_f(BlockBackend *blk, int argc, char **argv); + + static const cmdinfo_t aio_write_cmd = { + .name = "aio_write", +@@ -1433,7 +1436,7 @@ static const cmdinfo_t aio_write_cmd = { + .help = aio_write_help, + }; + +-static int aio_write_f(BlockBackend *blk, int argc, char **argv) ++static void aio_write_f(BlockBackend *blk, int argc, char **argv) + { + int nr_iov, c; + int pattern = 0xcd; +@@ -1459,51 +1462,53 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) + pattern = parse_pattern(optarg); + if (pattern < 0) { + g_free(ctx); +- return 0; ++ return; + } + break; + case 'i': + printf("injecting invalid write request\n"); + block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_WRITE); + g_free(ctx); +- return 0; ++ return; + case 'z': + ctx->zflag = true; + break; + default: + g_free(ctx); +- return qemuio_command_usage(&aio_write_cmd); ++ qemuio_command_usage(&aio_write_cmd); ++ return; + } + } + + if (optind > argc - 2) { + g_free(ctx); +- return qemuio_command_usage(&aio_write_cmd); ++ qemuio_command_usage(&aio_write_cmd); ++ return; + } + + if (ctx->zflag && optind != argc - 2) { + printf("-z supports only a single length parameter\n"); + g_free(ctx); +- return 0; ++ return; + } + + if ((flags & BDRV_REQ_MAY_UNMAP) && !ctx->zflag) { + printf("-u requires -z to be specified\n"); + g_free(ctx); +- return 0; ++ return; + } + + if (ctx->zflag && ctx->Pflag) { + printf("-z and -P cannot be specified at the same time\n"); + g_free(ctx); +- return 0; ++ return; + } + + ctx->offset = cvtnum(argv[optind]); + if (ctx->offset < 0) { + print_cvtnum_err(ctx->offset, argv[optind]); + g_free(ctx); +- return 0; ++ return; + } + optind++; + +@@ -1512,7 +1517,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) + if (count < 0) { + print_cvtnum_err(count, argv[optind]); + g_free(ctx); +- return 0; ++ return; + } + + ctx->qiov.size = count; +@@ -1525,7 +1530,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) + if (ctx->buf == NULL) { + block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_WRITE); + g_free(ctx); +- return 0; ++ return; + } + + gettimeofday(&ctx->t1, NULL); +@@ -1535,16 +1540,14 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) + blk_aio_pwritev(blk, ctx->offset, &ctx->qiov, flags, aio_write_done, + ctx); + } +- return 0; + } + +-static int aio_flush_f(BlockBackend *blk, int argc, char **argv) ++static void aio_flush_f(BlockBackend *blk, int argc, char **argv) + { + BlockAcctCookie cookie; + block_acct_start(blk_get_stats(blk), &cookie, 0, BLOCK_ACCT_FLUSH); + blk_drain_all(); + block_acct_done(blk_get_stats(blk), &cookie); +- return 0; + } + + static const cmdinfo_t aio_flush_cmd = { +@@ -1553,10 +1556,9 @@ static const cmdinfo_t aio_flush_cmd = { + .oneline = "completes all outstanding aio requests" + }; + +-static int flush_f(BlockBackend *blk, int argc, char **argv) ++static void flush_f(BlockBackend *blk, int argc, char **argv) + { + blk_flush(blk); +- return 0; + } + + static const cmdinfo_t flush_cmd = { +@@ -1566,7 +1568,7 @@ static const cmdinfo_t flush_cmd = { + .oneline = "flush all in-core file state to disk", + }; + +-static int truncate_f(BlockBackend *blk, int argc, char **argv) ++static void truncate_f(BlockBackend *blk, int argc, char **argv) + { + Error *local_err = NULL; + int64_t offset; +@@ -1575,16 +1577,14 @@ static int truncate_f(BlockBackend *blk, int argc, char **argv) + offset = cvtnum(argv[1]); + if (offset < 0) { + print_cvtnum_err(offset, argv[1]); +- return 0; ++ return; + } + + ret = blk_truncate(blk, offset, PREALLOC_MODE_OFF, &local_err); + if (ret < 0) { + error_report_err(local_err); +- return 0; ++ return; + } +- +- return 0; + } + + static const cmdinfo_t truncate_cmd = { +@@ -1598,7 +1598,7 @@ static const cmdinfo_t truncate_cmd = { + .oneline = "truncates the current file at the given offset", + }; + +-static int length_f(BlockBackend *blk, int argc, char **argv) ++static void length_f(BlockBackend *blk, int argc, char **argv) + { + int64_t size; + char s1[64]; +@@ -1606,12 +1606,11 @@ static int length_f(BlockBackend *blk, int argc, char **argv) + size = blk_getlength(blk); + if (size < 0) { + printf("getlength: %s\n", strerror(-size)); +- return 0; ++ return; + } + + cvtstr(size, s1, sizeof(s1)); + printf("%s\n", s1); +- return 0; + } + + +@@ -1623,7 +1622,7 @@ static const cmdinfo_t length_cmd = { + }; + + +-static int info_f(BlockBackend *blk, int argc, char **argv) ++static void info_f(BlockBackend *blk, int argc, char **argv) + { + BlockDriverState *bs = blk_bs(blk); + BlockDriverInfo bdi; +@@ -1640,7 +1639,7 @@ static int info_f(BlockBackend *blk, int argc, char **argv) + + ret = bdrv_get_info(bs, &bdi); + if (ret) { +- return 0; ++ return; + } + + cvtstr(bdi.cluster_size, s1, sizeof(s1)); +@@ -1655,8 +1654,6 @@ static int info_f(BlockBackend *blk, int argc, char **argv) + bdrv_image_info_specific_dump(fprintf, stdout, spec_info); + qapi_free_ImageInfoSpecific(spec_info); + } +- +- return 0; + } + + +@@ -1683,7 +1680,7 @@ static void discard_help(void) + "\n"); + } + +-static int discard_f(BlockBackend *blk, int argc, char **argv); ++static void discard_f(BlockBackend *blk, int argc, char **argv); + + static const cmdinfo_t discard_cmd = { + .name = "discard", +@@ -1697,7 +1694,7 @@ static const cmdinfo_t discard_cmd = { + .help = discard_help, + }; + +-static int discard_f(BlockBackend *blk, int argc, char **argv) ++static void discard_f(BlockBackend *blk, int argc, char **argv) + { + struct timeval t1, t2; + bool Cflag = false, qflag = false; +@@ -1713,30 +1710,32 @@ static int discard_f(BlockBackend *blk, int argc, char **argv) + qflag = true; + break; + default: +- return qemuio_command_usage(&discard_cmd); ++ qemuio_command_usage(&discard_cmd); ++ return; + } + } + + if (optind != argc - 2) { +- return qemuio_command_usage(&discard_cmd); ++ qemuio_command_usage(&discard_cmd); ++ return; + } + + offset = cvtnum(argv[optind]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); +- return 0; ++ return; + } + + optind++; + bytes = cvtnum(argv[optind]); + if (bytes < 0) { + print_cvtnum_err(bytes, argv[optind]); +- return 0; ++ return; + } else if (bytes >> BDRV_SECTOR_BITS > BDRV_REQUEST_MAX_SECTORS) { + printf("length cannot exceed %"PRIu64", given %s\n", + (uint64_t)BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS, + argv[optind]); +- return 0; ++ return; + } + + gettimeofday(&t1, NULL); +@@ -1745,7 +1744,7 @@ static int discard_f(BlockBackend *blk, int argc, char **argv) + + if (ret < 0) { + printf("discard failed: %s\n", strerror(-ret)); +- goto out; ++ return; + } + + /* Finally, report back -- -C gives a parsable format */ +@@ -1753,12 +1752,9 @@ static int discard_f(BlockBackend *blk, int argc, char **argv) + t2 = tsub(t2, t1); + print_report("discard", &t2, offset, bytes, bytes, 1, Cflag); + } +- +-out: +- return 0; + } + +-static int alloc_f(BlockBackend *blk, int argc, char **argv) ++static void alloc_f(BlockBackend *blk, int argc, char **argv) + { + BlockDriverState *bs = blk_bs(blk); + int64_t offset, start, remaining, count; +@@ -1769,14 +1765,14 @@ static int alloc_f(BlockBackend *blk, int argc, char **argv) + start = offset = cvtnum(argv[1]); + if (offset < 0) { + print_cvtnum_err(offset, argv[1]); +- return 0; ++ return; + } + + if (argc == 3) { + count = cvtnum(argv[2]); + if (count < 0) { + print_cvtnum_err(count, argv[2]); +- return 0; ++ return; + } + } else { + count = BDRV_SECTOR_SIZE; +@@ -1788,7 +1784,7 @@ static int alloc_f(BlockBackend *blk, int argc, char **argv) + ret = bdrv_is_allocated(bs, offset, remaining, &num); + if (ret < 0) { + printf("is_allocated failed: %s\n", strerror(-ret)); +- return 0; ++ return; + } + offset += num; + remaining -= num; +@@ -1805,7 +1801,6 @@ static int alloc_f(BlockBackend *blk, int argc, char **argv) + + printf("%"PRId64"/%"PRId64" bytes allocated at offset %s\n", + sum_alloc, count, s1); +- return 0; + } + + static const cmdinfo_t alloc_cmd = { +@@ -1851,7 +1846,7 @@ static int map_is_allocated(BlockDriverState *bs, int64_t offset, + return firstret; + } + +-static int map_f(BlockBackend *blk, int argc, char **argv) ++static void map_f(BlockBackend *blk, int argc, char **argv) + { + int64_t offset, bytes; + char s1[64], s2[64]; +@@ -1863,17 +1858,17 @@ static int map_f(BlockBackend *blk, int argc, char **argv) + bytes = blk_getlength(blk); + if (bytes < 0) { + error_report("Failed to query image length: %s", strerror(-bytes)); +- return 0; ++ return; + } + + while (bytes) { + ret = map_is_allocated(blk_bs(blk), offset, bytes, &num); + if (ret < 0) { + error_report("Failed to get allocation status: %s", strerror(-ret)); +- return 0; ++ return; + } else if (!num) { + error_report("Unexpected end of image"); +- return 0; ++ return; + } + + retstr = ret ? " allocated" : "not allocated"; +@@ -1885,8 +1880,6 @@ static int map_f(BlockBackend *blk, int argc, char **argv) + offset += num; + bytes -= num; + } +- +- return 0; + } + + static const cmdinfo_t map_cmd = { +@@ -1914,7 +1907,7 @@ static void reopen_help(void) + "\n"); + } + +-static int reopen_f(BlockBackend *blk, int argc, char **argv); ++static void reopen_f(BlockBackend *blk, int argc, char **argv); + + static QemuOptsList reopen_opts = { + .name = "reopen", +@@ -1936,7 +1929,7 @@ static const cmdinfo_t reopen_cmd = { + .help = reopen_help, + }; + +-static int reopen_f(BlockBackend *blk, int argc, char **argv) ++static void reopen_f(BlockBackend *blk, int argc, char **argv) + { + BlockDriverState *bs = blk_bs(blk); + QemuOpts *qopts; +@@ -1954,19 +1947,19 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) + case 'c': + if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) { + error_report("Invalid cache option: %s", optarg); +- return 0; ++ return; + } + break; + case 'o': + if (!qemu_opts_parse_noisily(&reopen_opts, optarg, 0)) { + qemu_opts_reset(&reopen_opts); +- return 0; ++ return; + } + break; + case 'r': + if (has_rw_option) { + error_report("Only one -r/-w option may be given"); +- return 0; ++ return; + } + flags &= ~BDRV_O_RDWR; + has_rw_option = true; +@@ -1974,20 +1967,22 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) + case 'w': + if (has_rw_option) { + error_report("Only one -r/-w option may be given"); +- return 0; ++ return; + } + flags |= BDRV_O_RDWR; + has_rw_option = true; + break; + default: + qemu_opts_reset(&reopen_opts); +- return qemuio_command_usage(&reopen_cmd); ++ qemuio_command_usage(&reopen_cmd); ++ return; + } + } + + if (optind != argc) { + qemu_opts_reset(&reopen_opts); +- return qemuio_command_usage(&reopen_cmd); ++ qemuio_command_usage(&reopen_cmd); ++ return; + } + + if (writethrough != blk_enable_write_cache(blk) && +@@ -1995,7 +1990,7 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) + { + error_report("Cannot change cache.writeback: Device attached"); + qemu_opts_reset(&reopen_opts); +- return 0; ++ return; + } + + if (!(flags & BDRV_O_RDWR)) { +@@ -2024,11 +2019,9 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) + } else { + blk_set_enable_write_cache(blk, !writethrough); + } +- +- return 0; + } + +-static int break_f(BlockBackend *blk, int argc, char **argv) ++static void break_f(BlockBackend *blk, int argc, char **argv) + { + int ret; + +@@ -2036,11 +2029,9 @@ static int break_f(BlockBackend *blk, int argc, char **argv) + if (ret < 0) { + printf("Could not set breakpoint: %s\n", strerror(-ret)); + } +- +- return 0; + } + +-static int remove_break_f(BlockBackend *blk, int argc, char **argv) ++static void remove_break_f(BlockBackend *blk, int argc, char **argv) + { + int ret; + +@@ -2048,8 +2039,6 @@ static int remove_break_f(BlockBackend *blk, int argc, char **argv) + if (ret < 0) { + printf("Could not remove breakpoint %s: %s\n", argv[1], strerror(-ret)); + } +- +- return 0; + } + + static const cmdinfo_t break_cmd = { +@@ -2071,7 +2060,7 @@ static const cmdinfo_t remove_break_cmd = { + .oneline = "remove a breakpoint by tag", + }; + +-static int resume_f(BlockBackend *blk, int argc, char **argv) ++static void resume_f(BlockBackend *blk, int argc, char **argv) + { + int ret; + +@@ -2079,8 +2068,6 @@ static int resume_f(BlockBackend *blk, int argc, char **argv) + if (ret < 0) { + printf("Could not resume request: %s\n", strerror(-ret)); + } +- +- return 0; + } + + static const cmdinfo_t resume_cmd = { +@@ -2092,13 +2079,11 @@ static const cmdinfo_t resume_cmd = { + .oneline = "resumes the request tagged as tag", + }; + +-static int wait_break_f(BlockBackend *blk, int argc, char **argv) ++static void wait_break_f(BlockBackend *blk, int argc, char **argv) + { + while (!bdrv_debug_is_suspended(blk_bs(blk), argv[1])) { + aio_poll(blk_get_aio_context(blk), true); + } +- +- return 0; + } + + static const cmdinfo_t wait_break_cmd = { +@@ -2110,7 +2095,7 @@ static const cmdinfo_t wait_break_cmd = { + .oneline = "waits for the suspension of a request", + }; + +-static int abort_f(BlockBackend *blk, int argc, char **argv) ++static void abort_f(BlockBackend *blk, int argc, char **argv) + { + abort(); + } +@@ -2136,7 +2121,7 @@ static void sigraise_help(void) + "\n", SIGTERM); + } + +-static int sigraise_f(BlockBackend *blk, int argc, char **argv); ++static void sigraise_f(BlockBackend *blk, int argc, char **argv); + + static const cmdinfo_t sigraise_cmd = { + .name = "sigraise", +@@ -2149,16 +2134,16 @@ static const cmdinfo_t sigraise_cmd = { + .help = sigraise_help, + }; + +-static int sigraise_f(BlockBackend *blk, int argc, char **argv) ++static void sigraise_f(BlockBackend *blk, int argc, char **argv) + { + int64_t sig = cvtnum(argv[1]); + if (sig < 0) { + print_cvtnum_err(sig, argv[1]); +- return 0; ++ return; + } else if (sig > NSIG) { + printf("signal argument '%s' is too large to be a valid signal\n", + argv[1]); +- return 0; ++ return; + } + + /* Using raise() to kill this process does not necessarily flush all open +@@ -2168,7 +2153,6 @@ static int sigraise_f(BlockBackend *blk, int argc, char **argv) + fflush(stderr); + + raise(sig); +- return 0; + } + + static void sleep_cb(void *opaque) +@@ -2177,7 +2161,7 @@ static void sleep_cb(void *opaque) + *expired = true; + } + +-static int sleep_f(BlockBackend *blk, int argc, char **argv) ++static void sleep_f(BlockBackend *blk, int argc, char **argv) + { + char *endptr; + long ms; +@@ -2187,7 +2171,7 @@ static int sleep_f(BlockBackend *blk, int argc, char **argv) + ms = strtol(argv[1], &endptr, 0); + if (ms < 0 || *endptr != '\0') { + printf("%s is not a valid number\n", argv[1]); +- return 0; ++ return; + } + + timer = timer_new_ns(QEMU_CLOCK_HOST, sleep_cb, &expired); +@@ -2198,8 +2182,6 @@ static int sleep_f(BlockBackend *blk, int argc, char **argv) + } + + timer_free(timer); +- +- return 0; + } + + static const cmdinfo_t sleep_cmd = { +@@ -2246,23 +2228,22 @@ static void help_all(void) + printf("\nUse 'help commandname' for extended help.\n"); + } + +-static int help_f(BlockBackend *blk, int argc, char **argv) ++static void help_f(BlockBackend *blk, int argc, char **argv) + { + const cmdinfo_t *ct; + + if (argc == 1) { + help_all(); +- return 0; ++ return; + } + + ct = find_command(argv[1]); + if (ct == NULL) { + printf("command %s not found\n", argv[1]); +- return 0; ++ return; + } + + help_onecmd(argv[1], ct); +- return 0; + } + + static const cmdinfo_t help_cmd = { +@@ -2276,14 +2257,13 @@ static const cmdinfo_t help_cmd = { + .oneline = "help for one or all commands", + }; + +-bool qemuio_command(BlockBackend *blk, const char *cmd) ++void qemuio_command(BlockBackend *blk, const char *cmd) + { + AioContext *ctx; + char *input; + const cmdinfo_t *ct; + char **v; + int c; +- bool done = false; + + input = g_strdup(cmd); + v = breakline(input, &c); +@@ -2292,7 +2272,7 @@ bool qemuio_command(BlockBackend *blk, const char *cmd) + if (ct) { + ctx = blk ? blk_get_aio_context(blk) : qemu_get_aio_context(); + aio_context_acquire(ctx); +- done = command(blk, ct, c, v); ++ command(blk, ct, c, v); + aio_context_release(ctx); + } else { + fprintf(stderr, "command \"%s\" not found\n", v[0]); +@@ -2300,8 +2280,6 @@ bool qemuio_command(BlockBackend *blk, const char *cmd) + } + g_free(input); + g_free(v); +- +- return done; + } + + static void __attribute((constructor)) init_qemuio_commands(void) +diff --git a/qemu-io.c b/qemu-io.c +index 73c638f..02a67c9 100644 +--- a/qemu-io.c ++++ b/qemu-io.c +@@ -37,6 +37,7 @@ + static char *progname; + + static BlockBackend *qemuio_blk; ++static bool quit_qemu_io; + + /* qemu-io commands passed using -c */ + static int ncmdline; +@@ -65,11 +66,10 @@ static int get_eof_char(void) + #endif + } + +-static int close_f(BlockBackend *blk, int argc, char **argv) ++static void close_f(BlockBackend *blk, int argc, char **argv) + { + blk_unref(qemuio_blk); + qemuio_blk = NULL; +- return 0; + } + + static const cmdinfo_t close_cmd = { +@@ -136,7 +136,7 @@ static void open_help(void) + "\n"); + } + +-static int open_f(BlockBackend *blk, int argc, char **argv); ++static void open_f(BlockBackend *blk, int argc, char **argv); + + static const cmdinfo_t open_cmd = { + .name = "open", +@@ -160,7 +160,7 @@ static QemuOptsList empty_opts = { + }, + }; + +-static int open_f(BlockBackend *blk, int argc, char **argv) ++static void open_f(BlockBackend *blk, int argc, char **argv) + { + int flags = BDRV_O_UNMAP; + int readonly = 0; +@@ -192,25 +192,25 @@ static int open_f(BlockBackend *blk, int argc, char **argv) + if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) { + error_report("Invalid cache option: %s", optarg); + qemu_opts_reset(&empty_opts); +- return 0; ++ return; + } + break; + case 'd': + if (bdrv_parse_discard_flags(optarg, &flags) < 0) { + error_report("Invalid discard option: %s", optarg); + qemu_opts_reset(&empty_opts); +- return 0; ++ return; + } + break; + case 'o': + if (imageOpts) { + printf("--image-opts and 'open -o' are mutually exclusive\n"); + qemu_opts_reset(&empty_opts); +- return 0; ++ return; + } + if (!qemu_opts_parse_noisily(&empty_opts, optarg, false)) { + qemu_opts_reset(&empty_opts); +- return 0; ++ return; + } + break; + case 'U': +@@ -218,7 +218,8 @@ static int open_f(BlockBackend *blk, int argc, char **argv) + break; + default: + qemu_opts_reset(&empty_opts); +- return qemuio_command_usage(&open_cmd); ++ qemuio_command_usage(&open_cmd); ++ return; + } + } + +@@ -229,7 +230,7 @@ static int open_f(BlockBackend *blk, int argc, char **argv) + if (imageOpts && (optind == argc - 1)) { + if (!qemu_opts_parse_noisily(&empty_opts, argv[optind], false)) { + qemu_opts_reset(&empty_opts); +- return 0; ++ return; + } + optind++; + } +@@ -246,12 +247,11 @@ static int open_f(BlockBackend *blk, int argc, char **argv) + qobject_unref(opts); + qemuio_command_usage(&open_cmd); + } +- return 0; + } + +-static int quit_f(BlockBackend *blk, int argc, char **argv) ++static void quit_f(BlockBackend *blk, int argc, char **argv) + { +- return 1; ++ quit_qemu_io = true; + } + + static const cmdinfo_t quit_cmd = { +@@ -392,18 +392,18 @@ static void prep_fetchline(void *opaque) + + static void command_loop(void) + { +- int i, done = 0, fetchable = 0, prompted = 0; ++ int i, fetchable = 0, prompted = 0; + char *input; + +- for (i = 0; !done && i < ncmdline; i++) { +- done = qemuio_command(qemuio_blk, cmdline[i]); ++ for (i = 0; !quit_qemu_io && i < ncmdline; i++) { ++ qemuio_command(qemuio_blk, cmdline[i]); + } + if (cmdline) { + g_free(cmdline); + return; + } + +- while (!done) { ++ while (!quit_qemu_io) { + if (!prompted) { + printf("%s", get_prompt()); + fflush(stdout); +@@ -421,7 +421,7 @@ static void command_loop(void) + if (input == NULL) { + break; + } +- done = qemuio_command(qemuio_blk, input); ++ qemuio_command(qemuio_blk, input); + g_free(input); + + prompted = 0; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-io-Exit-with-error-when-a-command-failed.patch b/SOURCES/kvm-qemu-io-Exit-with-error-when-a-command-failed.patch new file mode 100644 index 0000000..0c92993 --- /dev/null +++ b/SOURCES/kvm-qemu-io-Exit-with-error-when-a-command-failed.patch @@ -0,0 +1,116 @@ +From 4dad2c0644a22a935a87f86b4d63747a9598bb5d Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 16:43:10 +0200 +Subject: [PATCH 42/54] qemu-io: Exit with error when a command failed + +RH-Author: Max Reitz +Message-id: <20180618164312.24423-4-mreitz@redhat.com> +Patchwork-id: 80780 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 3/5] qemu-io: Exit with error when a command failed +Bugzilla: 1519617 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Miroslav Rezanina + +Currently, qemu-io basically always returns success when it gets to +interactive mode (so once the whole command line has been parsed; even +before the commands on the command line are interpreted). That is not +very useful. + +This patch makes qemu-io return failure when any of the executed +commands failed. + +Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1519617 +Signed-off-by: Max Reitz +Reviewed-by: Eric Blake +Message-id: 20180509194302.21585-4-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit 6b3aa8485ca8e61b168f51d465188855cf549bd2) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + qemu-io.c | 28 ++++++++++++++++++++++------ + 1 file changed, 22 insertions(+), 6 deletions(-) + +diff --git a/qemu-io.c b/qemu-io.c +index ec66838..13829f5 100644 +--- a/qemu-io.c ++++ b/qemu-io.c +@@ -400,17 +400,21 @@ static void prep_fetchline(void *opaque) + *fetchable= 1; + } + +-static void command_loop(void) ++static int command_loop(void) + { + int i, fetchable = 0, prompted = 0; ++ int ret, last_error = 0; + char *input; + + for (i = 0; !quit_qemu_io && i < ncmdline; i++) { +- qemuio_command(qemuio_blk, cmdline[i]); ++ ret = qemuio_command(qemuio_blk, cmdline[i]); ++ if (ret < 0) { ++ last_error = ret; ++ } + } + if (cmdline) { + g_free(cmdline); +- return; ++ return last_error; + } + + while (!quit_qemu_io) { +@@ -431,13 +435,19 @@ static void command_loop(void) + if (input == NULL) { + break; + } +- qemuio_command(qemuio_blk, input); ++ ret = qemuio_command(qemuio_blk, input); + g_free(input); + ++ if (ret < 0) { ++ last_error = ret; ++ } ++ + prompted = 0; + fetchable = 0; + } + qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL); ++ ++ return last_error; + } + + static void add_user_command(char *optarg) +@@ -502,6 +512,7 @@ int main(int argc, char **argv) + int c; + int opt_index = 0; + int flags = BDRV_O_UNMAP; ++ int ret; + bool writethrough = true; + Error *local_error = NULL; + QDict *opts = NULL; +@@ -663,7 +674,7 @@ int main(int argc, char **argv) + } + } + } +- command_loop(); ++ ret = command_loop(); + + /* + * Make sure all outstanding requests complete before the program exits. +@@ -672,5 +683,10 @@ int main(int argc, char **argv) + + blk_unref(qemuio_blk); + g_free(readline_state); +- return 0; ++ ++ if (ret < 0) { ++ return 1; ++ } else { ++ return 0; ++ } + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-io-Let-command-functions-return-error-code.patch b/SOURCES/kvm-qemu-io-Let-command-functions-return-error-code.patch new file mode 100644 index 0000000..945e85b --- /dev/null +++ b/SOURCES/kvm-qemu-io-Let-command-functions-return-error-code.patch @@ -0,0 +1,1452 @@ +From cc8c7cffb3786855de0032b376c7b22c461b695c Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 25 Jun 2018 13:12:14 +0200 +Subject: [PATCH 41/54] qemu-io: Let command functions return error code + +RH-Author: Max Reitz +Message-id: <20180618164312.24423-3-mreitz@redhat.com> +Patchwork-id: 80781 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 2/5] qemu-io: Let command functions return error code +Bugzilla: 1519617 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Miroslav Rezanina + +This is basically what everything else in the qemu code base does, so we +can do it here, too. + +Signed-off-by: Max Reitz +Reviewed-by: Eric Blake +Message-id: 20180509194302.21585-3-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit b32d7a39af488d280ce5f02a2ed94871d696f87a) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + include/qemu-io.h | 9 +- + qemu-io-cmds.c | 346 ++++++++++++++++++++++++++++++++---------------------- + qemu-io.c | 34 ++++-- + 3 files changed, 232 insertions(+), 157 deletions(-) + +diff --git a/include/qemu-io.h b/include/qemu-io.h +index 06cdfbf..7433239 100644 +--- a/include/qemu-io.h ++++ b/include/qemu-io.h +@@ -22,7 +22,12 @@ + + #define CMD_FLAG_GLOBAL ((int)0x80000000) /* don't iterate "args" */ + +-typedef void (*cfunc_t)(BlockBackend *blk, int argc, char **argv); ++/* Implement a qemu-io command. ++ * Operate on @blk using @argc/@argv as the command's arguments, and ++ * return 0 on success or negative errno on failure. ++ */ ++typedef int (*cfunc_t)(BlockBackend *blk, int argc, char **argv); ++ + typedef void (*helpfunc_t)(void); + + typedef struct cmdinfo { +@@ -41,7 +46,7 @@ typedef struct cmdinfo { + + extern bool qemuio_misalign; + +-void qemuio_command(BlockBackend *blk, const char *cmd); ++int qemuio_command(BlockBackend *blk, const char *cmd); + + void qemuio_add_command(const cmdinfo_t *ci); + void qemuio_command_usage(const cmdinfo_t *ci); +diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c +index c2fbaae..5bf5f28 100644 +--- a/qemu-io-cmds.c ++++ b/qemu-io-cmds.c +@@ -65,13 +65,13 @@ static int init_check_command(BlockBackend *blk, const cmdinfo_t *ct) + return 1; + } + +-static void command(BlockBackend *blk, const cmdinfo_t *ct, int argc, +- char **argv) ++static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc, ++ char **argv) + { + char *cmd = argv[0]; + + if (!init_check_command(blk, ct)) { +- return; ++ return -EINVAL; + } + + if (argc - 1 < ct->argmin || (ct->argmax != -1 && argc - 1 > ct->argmax)) { +@@ -88,7 +88,7 @@ static void command(BlockBackend *blk, const cmdinfo_t *ct, int argc, + "bad argument count %d to %s, expected between %d and %d arguments\n", + argc-1, cmd, ct->argmin, ct->argmax); + } +- return; ++ return -EINVAL; + } + + /* Request additional permissions if necessary for this command. The caller +@@ -108,13 +108,13 @@ static void command(BlockBackend *blk, const cmdinfo_t *ct, int argc, + ret = blk_set_perm(blk, new_perm, orig_shared_perm, &local_err); + if (ret < 0) { + error_report_err(local_err); +- return; ++ return ret; + } + } + } + + optind = 0; +- ct->cfunc(blk, argc, argv); ++ return ct->cfunc(blk, argc, argv); + } + + static const cmdinfo_t *find_command(const char *cmd) +@@ -633,7 +633,7 @@ static void read_help(void) + "\n"); + } + +-static void read_f(BlockBackend *blk, int argc, char **argv); ++static int read_f(BlockBackend *blk, int argc, char **argv); + + static const cmdinfo_t read_cmd = { + .name = "read", +@@ -646,12 +646,12 @@ static const cmdinfo_t read_cmd = { + .help = read_help, + }; + +-static void read_f(BlockBackend *blk, int argc, char **argv) ++static int read_f(BlockBackend *blk, int argc, char **argv) + { + struct timeval t1, t2; + bool Cflag = false, qflag = false, vflag = false; + bool Pflag = false, sflag = false, lflag = false, bflag = false; +- int c, cnt; ++ int c, cnt, ret; + char *buf; + int64_t offset; + int64_t count; +@@ -673,7 +673,7 @@ static void read_f(BlockBackend *blk, int argc, char **argv) + pattern_count = cvtnum(optarg); + if (pattern_count < 0) { + print_cvtnum_err(pattern_count, optarg); +- return; ++ return pattern_count; + } + break; + case 'p': +@@ -683,7 +683,7 @@ static void read_f(BlockBackend *blk, int argc, char **argv) + Pflag = true; + pattern = parse_pattern(optarg); + if (pattern < 0) { +- return; ++ return -EINVAL; + } + break; + case 'q': +@@ -694,7 +694,7 @@ static void read_f(BlockBackend *blk, int argc, char **argv) + pattern_offset = cvtnum(optarg); + if (pattern_offset < 0) { + print_cvtnum_err(pattern_offset, optarg); +- return; ++ return pattern_offset; + } + break; + case 'v': +@@ -702,35 +702,35 @@ static void read_f(BlockBackend *blk, int argc, char **argv) + break; + default: + qemuio_command_usage(&read_cmd); +- return; ++ return -EINVAL; + } + } + + if (optind != argc - 2) { + qemuio_command_usage(&read_cmd); +- return; ++ return -EINVAL; + } + + offset = cvtnum(argv[optind]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); +- return; ++ return offset; + } + + optind++; + count = cvtnum(argv[optind]); + if (count < 0) { + print_cvtnum_err(count, argv[optind]); +- return; ++ return count; + } else if (count > BDRV_REQUEST_MAX_BYTES) { + printf("length cannot exceed %" PRIu64 ", given %s\n", + (uint64_t)BDRV_REQUEST_MAX_BYTES, argv[optind]); +- return; ++ return -EINVAL; + } + + if (!Pflag && (lflag || sflag)) { + qemuio_command_usage(&read_cmd); +- return; ++ return -EINVAL; + } + + if (!lflag) { +@@ -739,19 +739,19 @@ static void read_f(BlockBackend *blk, int argc, char **argv) + + if ((pattern_count < 0) || (pattern_count + pattern_offset > count)) { + printf("pattern verification range exceeds end of read data\n"); +- return; ++ return -EINVAL; + } + + if (bflag) { + if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) { + printf("%" PRId64 " is not a sector-aligned value for 'offset'\n", + offset); +- return; ++ return -EINVAL; + } + if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) { + printf("%"PRId64" is not a sector-aligned value for 'count'\n", + count); +- return; ++ return -EINVAL; + } + } + +@@ -759,16 +759,19 @@ static void read_f(BlockBackend *blk, int argc, char **argv) + + gettimeofday(&t1, NULL); + if (bflag) { +- cnt = do_load_vmstate(blk, buf, offset, count, &total); ++ ret = do_load_vmstate(blk, buf, offset, count, &total); + } else { +- cnt = do_pread(blk, buf, offset, count, &total); ++ ret = do_pread(blk, buf, offset, count, &total); + } + gettimeofday(&t2, NULL); + +- if (cnt < 0) { +- printf("read failed: %s\n", strerror(-cnt)); ++ if (ret < 0) { ++ printf("read failed: %s\n", strerror(-ret)); + goto out; + } ++ cnt = ret; ++ ++ ret = 0; + + if (Pflag) { + void *cmp_buf = g_malloc(pattern_count); +@@ -777,6 +780,7 @@ static void read_f(BlockBackend *blk, int argc, char **argv) + printf("Pattern verification failed at offset %" + PRId64 ", %"PRId64" bytes\n", + offset + pattern_offset, pattern_count); ++ ret = -EINVAL; + } + g_free(cmp_buf); + } +@@ -795,6 +799,7 @@ static void read_f(BlockBackend *blk, int argc, char **argv) + + out: + qemu_io_free(buf); ++ return ret; + } + + static void readv_help(void) +@@ -816,7 +821,7 @@ static void readv_help(void) + "\n"); + } + +-static void readv_f(BlockBackend *blk, int argc, char **argv); ++static int readv_f(BlockBackend *blk, int argc, char **argv); + + static const cmdinfo_t readv_cmd = { + .name = "readv", +@@ -828,11 +833,11 @@ static const cmdinfo_t readv_cmd = { + .help = readv_help, + }; + +-static void readv_f(BlockBackend *blk, int argc, char **argv) ++static int readv_f(BlockBackend *blk, int argc, char **argv) + { + struct timeval t1, t2; + bool Cflag = false, qflag = false, vflag = false; +- int c, cnt; ++ int c, cnt, ret; + char *buf; + int64_t offset; + /* Some compilers get confused and warn if this is not initialized. */ +@@ -851,7 +856,7 @@ static void readv_f(BlockBackend *blk, int argc, char **argv) + Pflag = true; + pattern = parse_pattern(optarg); + if (pattern < 0) { +- return; ++ return -EINVAL; + } + break; + case 'q': +@@ -862,37 +867,40 @@ static void readv_f(BlockBackend *blk, int argc, char **argv) + break; + default: + qemuio_command_usage(&readv_cmd); +- return; ++ return -EINVAL; + } + } + + if (optind > argc - 2) { + qemuio_command_usage(&readv_cmd); +- return; ++ return -EINVAL; + } + + + offset = cvtnum(argv[optind]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); +- return; ++ return offset; + } + optind++; + + nr_iov = argc - optind; + buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, 0xab); + if (buf == NULL) { +- return; ++ return -EINVAL; + } + + gettimeofday(&t1, NULL); +- cnt = do_aio_readv(blk, &qiov, offset, &total); ++ ret = do_aio_readv(blk, &qiov, offset, &total); + gettimeofday(&t2, NULL); + +- if (cnt < 0) { +- printf("readv failed: %s\n", strerror(-cnt)); ++ if (ret < 0) { ++ printf("readv failed: %s\n", strerror(-ret)); + goto out; + } ++ cnt = ret; ++ ++ ret = 0; + + if (Pflag) { + void *cmp_buf = g_malloc(qiov.size); +@@ -900,6 +908,7 @@ static void readv_f(BlockBackend *blk, int argc, char **argv) + if (memcmp(buf, cmp_buf, qiov.size)) { + printf("Pattern verification failed at offset %" + PRId64 ", %zd bytes\n", offset, qiov.size); ++ ret = -EINVAL; + } + g_free(cmp_buf); + } +@@ -919,6 +928,7 @@ static void readv_f(BlockBackend *blk, int argc, char **argv) + out: + qemu_iovec_destroy(&qiov); + qemu_io_free(buf); ++ return ret; + } + + static void write_help(void) +@@ -944,7 +954,7 @@ static void write_help(void) + "\n"); + } + +-static void write_f(BlockBackend *blk, int argc, char **argv); ++static int write_f(BlockBackend *blk, int argc, char **argv); + + static const cmdinfo_t write_cmd = { + .name = "write", +@@ -958,13 +968,13 @@ static const cmdinfo_t write_cmd = { + .help = write_help, + }; + +-static void write_f(BlockBackend *blk, int argc, char **argv) ++static int write_f(BlockBackend *blk, int argc, char **argv) + { + struct timeval t1, t2; + bool Cflag = false, qflag = false, bflag = false; + bool Pflag = false, zflag = false, cflag = false; + int flags = 0; +- int c, cnt; ++ int c, cnt, ret; + char *buf = NULL; + int64_t offset; + int64_t count; +@@ -993,7 +1003,7 @@ static void write_f(BlockBackend *blk, int argc, char **argv) + Pflag = true; + pattern = parse_pattern(optarg); + if (pattern < 0) { +- return; ++ return -EINVAL; + } + break; + case 'q': +@@ -1007,63 +1017,63 @@ static void write_f(BlockBackend *blk, int argc, char **argv) + break; + default: + qemuio_command_usage(&write_cmd); +- return; ++ return -EINVAL; + } + } + + if (optind != argc - 2) { + qemuio_command_usage(&write_cmd); +- return; ++ return -EINVAL; + } + + if (bflag && zflag) { + printf("-b and -z cannot be specified at the same time\n"); +- return; ++ return -EINVAL; + } + + if ((flags & BDRV_REQ_FUA) && (bflag || cflag)) { + printf("-f and -b or -c cannot be specified at the same time\n"); +- return; ++ return -EINVAL; + } + + if ((flags & BDRV_REQ_MAY_UNMAP) && !zflag) { + printf("-u requires -z to be specified\n"); +- return; ++ return -EINVAL; + } + + if (zflag && Pflag) { + printf("-z and -P cannot be specified at the same time\n"); +- return; ++ return -EINVAL; + } + + offset = cvtnum(argv[optind]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); +- return; ++ return offset; + } + + optind++; + count = cvtnum(argv[optind]); + if (count < 0) { + print_cvtnum_err(count, argv[optind]); +- return; ++ return count; + } else if (count > BDRV_REQUEST_MAX_BYTES) { + printf("length cannot exceed %" PRIu64 ", given %s\n", + (uint64_t)BDRV_REQUEST_MAX_BYTES, argv[optind]); +- return; ++ return -EINVAL; + } + + if (bflag || cflag) { + if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) { + printf("%" PRId64 " is not a sector-aligned value for 'offset'\n", + offset); +- return; ++ return -EINVAL; + } + + if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) { + printf("%"PRId64" is not a sector-aligned value for 'count'\n", + count); +- return; ++ return -EINVAL; + } + } + +@@ -1073,20 +1083,23 @@ static void write_f(BlockBackend *blk, int argc, char **argv) + + gettimeofday(&t1, NULL); + if (bflag) { +- cnt = do_save_vmstate(blk, buf, offset, count, &total); ++ ret = do_save_vmstate(blk, buf, offset, count, &total); + } else if (zflag) { +- cnt = do_co_pwrite_zeroes(blk, offset, count, flags, &total); ++ ret = do_co_pwrite_zeroes(blk, offset, count, flags, &total); + } else if (cflag) { +- cnt = do_write_compressed(blk, buf, offset, count, &total); ++ ret = do_write_compressed(blk, buf, offset, count, &total); + } else { +- cnt = do_pwrite(blk, buf, offset, count, flags, &total); ++ ret = do_pwrite(blk, buf, offset, count, flags, &total); + } + gettimeofday(&t2, NULL); + +- if (cnt < 0) { +- printf("write failed: %s\n", strerror(-cnt)); ++ if (ret < 0) { ++ printf("write failed: %s\n", strerror(-ret)); + goto out; + } ++ cnt = ret; ++ ++ ret = 0; + + if (qflag) { + goto out; +@@ -1100,6 +1113,7 @@ out: + if (!zflag) { + qemu_io_free(buf); + } ++ return ret; + } + + static void +@@ -1121,7 +1135,7 @@ writev_help(void) + "\n"); + } + +-static void writev_f(BlockBackend *blk, int argc, char **argv); ++static int writev_f(BlockBackend *blk, int argc, char **argv); + + static const cmdinfo_t writev_cmd = { + .name = "writev", +@@ -1134,12 +1148,12 @@ static const cmdinfo_t writev_cmd = { + .help = writev_help, + }; + +-static void writev_f(BlockBackend *blk, int argc, char **argv) ++static int writev_f(BlockBackend *blk, int argc, char **argv) + { + struct timeval t1, t2; + bool Cflag = false, qflag = false; + int flags = 0; +- int c, cnt; ++ int c, cnt, ret; + char *buf; + int64_t offset; + /* Some compilers get confused and warn if this is not initialized. */ +@@ -1162,41 +1176,44 @@ static void writev_f(BlockBackend *blk, int argc, char **argv) + case 'P': + pattern = parse_pattern(optarg); + if (pattern < 0) { +- return; ++ return -EINVAL; + } + break; + default: + qemuio_command_usage(&writev_cmd); +- return; ++ return -EINVAL; + } + } + + if (optind > argc - 2) { + qemuio_command_usage(&writev_cmd); +- return; ++ return -EINVAL; + } + + offset = cvtnum(argv[optind]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); +- return; ++ return offset; + } + optind++; + + nr_iov = argc - optind; + buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, pattern); + if (buf == NULL) { +- return; ++ return -EINVAL; + } + + gettimeofday(&t1, NULL); +- cnt = do_aio_writev(blk, &qiov, offset, flags, &total); ++ ret = do_aio_writev(blk, &qiov, offset, flags, &total); + gettimeofday(&t2, NULL); + +- if (cnt < 0) { +- printf("writev failed: %s\n", strerror(-cnt)); ++ if (ret < 0) { ++ printf("writev failed: %s\n", strerror(-ret)); + goto out; + } ++ cnt = ret; ++ ++ ret = 0; + + if (qflag) { + goto out; +@@ -1208,6 +1225,7 @@ static void writev_f(BlockBackend *blk, int argc, char **argv) + out: + qemu_iovec_destroy(&qiov); + qemu_io_free(buf); ++ return ret; + } + + struct aio_ctx { +@@ -1314,6 +1332,9 @@ static void aio_read_help(void) + " standard output stream (with -v option) for subsequent inspection.\n" + " The read is performed asynchronously and the aio_flush command must be\n" + " used to ensure all outstanding aio requests have been completed.\n" ++" Note that due to its asynchronous nature, this command will be\n" ++" considered successful once the request is submitted, independently\n" ++" of potential I/O errors or pattern mismatches.\n" + " -C, -- report statistics in a machine parsable format\n" + " -P, -- use a pattern to verify read data\n" + " -i, -- treat request as invalid, for exercising stats\n" +@@ -1322,7 +1343,7 @@ static void aio_read_help(void) + "\n"); + } + +-static void aio_read_f(BlockBackend *blk, int argc, char **argv); ++static int aio_read_f(BlockBackend *blk, int argc, char **argv); + + static const cmdinfo_t aio_read_cmd = { + .name = "aio_read", +@@ -1334,7 +1355,7 @@ static const cmdinfo_t aio_read_cmd = { + .help = aio_read_help, + }; + +-static void aio_read_f(BlockBackend *blk, int argc, char **argv) ++static int aio_read_f(BlockBackend *blk, int argc, char **argv) + { + int nr_iov, c; + struct aio_ctx *ctx = g_new0(struct aio_ctx, 1); +@@ -1350,14 +1371,14 @@ static void aio_read_f(BlockBackend *blk, int argc, char **argv) + ctx->pattern = parse_pattern(optarg); + if (ctx->pattern < 0) { + g_free(ctx); +- return; ++ return -EINVAL; + } + break; + case 'i': + printf("injecting invalid read request\n"); + block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_READ); + g_free(ctx); +- return; ++ return 0; + case 'q': + ctx->qflag = true; + break; +@@ -1367,21 +1388,22 @@ static void aio_read_f(BlockBackend *blk, int argc, char **argv) + default: + g_free(ctx); + qemuio_command_usage(&aio_read_cmd); +- return; ++ return -EINVAL; + } + } + + if (optind > argc - 2) { + g_free(ctx); + qemuio_command_usage(&aio_read_cmd); +- return; ++ return -EINVAL; + } + + ctx->offset = cvtnum(argv[optind]); + if (ctx->offset < 0) { +- print_cvtnum_err(ctx->offset, argv[optind]); ++ int ret = ctx->offset; ++ print_cvtnum_err(ret, argv[optind]); + g_free(ctx); +- return; ++ return ret; + } + optind++; + +@@ -1390,13 +1412,14 @@ static void aio_read_f(BlockBackend *blk, int argc, char **argv) + if (ctx->buf == NULL) { + block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_READ); + g_free(ctx); +- return; ++ return -EINVAL; + } + + gettimeofday(&ctx->t1, NULL); + block_acct_start(blk_get_stats(blk), &ctx->acct, ctx->qiov.size, + BLOCK_ACCT_READ); + blk_aio_preadv(blk, ctx->offset, &ctx->qiov, 0, aio_read_done, ctx); ++ return 0; + } + + static void aio_write_help(void) +@@ -1413,6 +1436,9 @@ static void aio_write_help(void) + " filled with a set pattern (0xcdcdcdcd).\n" + " The write is performed asynchronously and the aio_flush command must be\n" + " used to ensure all outstanding aio requests have been completed.\n" ++" Note that due to its asynchronous nature, this command will be\n" ++" considered successful once the request is submitted, independently\n" ++" of potential I/O errors or pattern mismatches.\n" + " -P, -- use different pattern to fill file\n" + " -C, -- report statistics in a machine parsable format\n" + " -f, -- use Force Unit Access semantics\n" +@@ -1423,7 +1449,7 @@ static void aio_write_help(void) + "\n"); + } + +-static void aio_write_f(BlockBackend *blk, int argc, char **argv); ++static int aio_write_f(BlockBackend *blk, int argc, char **argv); + + static const cmdinfo_t aio_write_cmd = { + .name = "aio_write", +@@ -1436,7 +1462,7 @@ static const cmdinfo_t aio_write_cmd = { + .help = aio_write_help, + }; + +-static void aio_write_f(BlockBackend *blk, int argc, char **argv) ++static int aio_write_f(BlockBackend *blk, int argc, char **argv) + { + int nr_iov, c; + int pattern = 0xcd; +@@ -1462,53 +1488,54 @@ static void aio_write_f(BlockBackend *blk, int argc, char **argv) + pattern = parse_pattern(optarg); + if (pattern < 0) { + g_free(ctx); +- return; ++ return -EINVAL; + } + break; + case 'i': + printf("injecting invalid write request\n"); + block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_WRITE); + g_free(ctx); +- return; ++ return 0; + case 'z': + ctx->zflag = true; + break; + default: + g_free(ctx); + qemuio_command_usage(&aio_write_cmd); +- return; ++ return -EINVAL; + } + } + + if (optind > argc - 2) { + g_free(ctx); + qemuio_command_usage(&aio_write_cmd); +- return; ++ return -EINVAL; + } + + if (ctx->zflag && optind != argc - 2) { + printf("-z supports only a single length parameter\n"); + g_free(ctx); +- return; ++ return -EINVAL; + } + + if ((flags & BDRV_REQ_MAY_UNMAP) && !ctx->zflag) { + printf("-u requires -z to be specified\n"); + g_free(ctx); +- return; ++ return -EINVAL; + } + + if (ctx->zflag && ctx->Pflag) { + printf("-z and -P cannot be specified at the same time\n"); + g_free(ctx); +- return; ++ return -EINVAL; + } + + ctx->offset = cvtnum(argv[optind]); + if (ctx->offset < 0) { +- print_cvtnum_err(ctx->offset, argv[optind]); ++ int ret = ctx->offset; ++ print_cvtnum_err(ret, argv[optind]); + g_free(ctx); +- return; ++ return ret; + } + optind++; + +@@ -1517,7 +1544,7 @@ static void aio_write_f(BlockBackend *blk, int argc, char **argv) + if (count < 0) { + print_cvtnum_err(count, argv[optind]); + g_free(ctx); +- return; ++ return count; + } + + ctx->qiov.size = count; +@@ -1530,7 +1557,7 @@ static void aio_write_f(BlockBackend *blk, int argc, char **argv) + if (ctx->buf == NULL) { + block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_WRITE); + g_free(ctx); +- return; ++ return -EINVAL; + } + + gettimeofday(&ctx->t1, NULL); +@@ -1540,14 +1567,17 @@ static void aio_write_f(BlockBackend *blk, int argc, char **argv) + blk_aio_pwritev(blk, ctx->offset, &ctx->qiov, flags, aio_write_done, + ctx); + } ++ ++ return 0; + } + +-static void aio_flush_f(BlockBackend *blk, int argc, char **argv) ++static int aio_flush_f(BlockBackend *blk, int argc, char **argv) + { + BlockAcctCookie cookie; + block_acct_start(blk_get_stats(blk), &cookie, 0, BLOCK_ACCT_FLUSH); + blk_drain_all(); + block_acct_done(blk_get_stats(blk), &cookie); ++ return 0; + } + + static const cmdinfo_t aio_flush_cmd = { +@@ -1556,9 +1586,9 @@ static const cmdinfo_t aio_flush_cmd = { + .oneline = "completes all outstanding aio requests" + }; + +-static void flush_f(BlockBackend *blk, int argc, char **argv) ++static int flush_f(BlockBackend *blk, int argc, char **argv) + { +- blk_flush(blk); ++ return blk_flush(blk); + } + + static const cmdinfo_t flush_cmd = { +@@ -1568,7 +1598,7 @@ static const cmdinfo_t flush_cmd = { + .oneline = "flush all in-core file state to disk", + }; + +-static void truncate_f(BlockBackend *blk, int argc, char **argv) ++static int truncate_f(BlockBackend *blk, int argc, char **argv) + { + Error *local_err = NULL; + int64_t offset; +@@ -1577,14 +1607,16 @@ static void truncate_f(BlockBackend *blk, int argc, char **argv) + offset = cvtnum(argv[1]); + if (offset < 0) { + print_cvtnum_err(offset, argv[1]); +- return; ++ return offset; + } + + ret = blk_truncate(blk, offset, PREALLOC_MODE_OFF, &local_err); + if (ret < 0) { + error_report_err(local_err); +- return; ++ return ret; + } ++ ++ return 0; + } + + static const cmdinfo_t truncate_cmd = { +@@ -1598,7 +1630,7 @@ static const cmdinfo_t truncate_cmd = { + .oneline = "truncates the current file at the given offset", + }; + +-static void length_f(BlockBackend *blk, int argc, char **argv) ++static int length_f(BlockBackend *blk, int argc, char **argv) + { + int64_t size; + char s1[64]; +@@ -1606,11 +1638,12 @@ static void length_f(BlockBackend *blk, int argc, char **argv) + size = blk_getlength(blk); + if (size < 0) { + printf("getlength: %s\n", strerror(-size)); +- return; ++ return size; + } + + cvtstr(size, s1, sizeof(s1)); + printf("%s\n", s1); ++ return 0; + } + + +@@ -1622,7 +1655,7 @@ static const cmdinfo_t length_cmd = { + }; + + +-static void info_f(BlockBackend *blk, int argc, char **argv) ++static int info_f(BlockBackend *blk, int argc, char **argv) + { + BlockDriverState *bs = blk_bs(blk); + BlockDriverInfo bdi; +@@ -1639,7 +1672,7 @@ static void info_f(BlockBackend *blk, int argc, char **argv) + + ret = bdrv_get_info(bs, &bdi); + if (ret) { +- return; ++ return ret; + } + + cvtstr(bdi.cluster_size, s1, sizeof(s1)); +@@ -1654,6 +1687,8 @@ static void info_f(BlockBackend *blk, int argc, char **argv) + bdrv_image_info_specific_dump(fprintf, stdout, spec_info); + qapi_free_ImageInfoSpecific(spec_info); + } ++ ++ return 0; + } + + +@@ -1680,7 +1715,7 @@ static void discard_help(void) + "\n"); + } + +-static void discard_f(BlockBackend *blk, int argc, char **argv); ++static int discard_f(BlockBackend *blk, int argc, char **argv); + + static const cmdinfo_t discard_cmd = { + .name = "discard", +@@ -1694,7 +1729,7 @@ static const cmdinfo_t discard_cmd = { + .help = discard_help, + }; + +-static void discard_f(BlockBackend *blk, int argc, char **argv) ++static int discard_f(BlockBackend *blk, int argc, char **argv) + { + struct timeval t1, t2; + bool Cflag = false, qflag = false; +@@ -1711,31 +1746,31 @@ static void discard_f(BlockBackend *blk, int argc, char **argv) + break; + default: + qemuio_command_usage(&discard_cmd); +- return; ++ return -EINVAL; + } + } + + if (optind != argc - 2) { + qemuio_command_usage(&discard_cmd); +- return; ++ return -EINVAL; + } + + offset = cvtnum(argv[optind]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); +- return; ++ return offset; + } + + optind++; + bytes = cvtnum(argv[optind]); + if (bytes < 0) { + print_cvtnum_err(bytes, argv[optind]); +- return; ++ return bytes; + } else if (bytes >> BDRV_SECTOR_BITS > BDRV_REQUEST_MAX_SECTORS) { + printf("length cannot exceed %"PRIu64", given %s\n", + (uint64_t)BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS, + argv[optind]); +- return; ++ return -EINVAL; + } + + gettimeofday(&t1, NULL); +@@ -1744,7 +1779,7 @@ static void discard_f(BlockBackend *blk, int argc, char **argv) + + if (ret < 0) { + printf("discard failed: %s\n", strerror(-ret)); +- return; ++ return ret; + } + + /* Finally, report back -- -C gives a parsable format */ +@@ -1752,9 +1787,11 @@ static void discard_f(BlockBackend *blk, int argc, char **argv) + t2 = tsub(t2, t1); + print_report("discard", &t2, offset, bytes, bytes, 1, Cflag); + } ++ ++ return 0; + } + +-static void alloc_f(BlockBackend *blk, int argc, char **argv) ++static int alloc_f(BlockBackend *blk, int argc, char **argv) + { + BlockDriverState *bs = blk_bs(blk); + int64_t offset, start, remaining, count; +@@ -1765,14 +1802,14 @@ static void alloc_f(BlockBackend *blk, int argc, char **argv) + start = offset = cvtnum(argv[1]); + if (offset < 0) { + print_cvtnum_err(offset, argv[1]); +- return; ++ return offset; + } + + if (argc == 3) { + count = cvtnum(argv[2]); + if (count < 0) { + print_cvtnum_err(count, argv[2]); +- return; ++ return count; + } + } else { + count = BDRV_SECTOR_SIZE; +@@ -1784,7 +1821,7 @@ static void alloc_f(BlockBackend *blk, int argc, char **argv) + ret = bdrv_is_allocated(bs, offset, remaining, &num); + if (ret < 0) { + printf("is_allocated failed: %s\n", strerror(-ret)); +- return; ++ return ret; + } + offset += num; + remaining -= num; +@@ -1801,6 +1838,7 @@ static void alloc_f(BlockBackend *blk, int argc, char **argv) + + printf("%"PRId64"/%"PRId64" bytes allocated at offset %s\n", + sum_alloc, count, s1); ++ return 0; + } + + static const cmdinfo_t alloc_cmd = { +@@ -1846,7 +1884,7 @@ static int map_is_allocated(BlockDriverState *bs, int64_t offset, + return firstret; + } + +-static void map_f(BlockBackend *blk, int argc, char **argv) ++static int map_f(BlockBackend *blk, int argc, char **argv) + { + int64_t offset, bytes; + char s1[64], s2[64]; +@@ -1858,17 +1896,17 @@ static void map_f(BlockBackend *blk, int argc, char **argv) + bytes = blk_getlength(blk); + if (bytes < 0) { + error_report("Failed to query image length: %s", strerror(-bytes)); +- return; ++ return bytes; + } + + while (bytes) { + ret = map_is_allocated(blk_bs(blk), offset, bytes, &num); + if (ret < 0) { + error_report("Failed to get allocation status: %s", strerror(-ret)); +- return; ++ return ret; + } else if (!num) { + error_report("Unexpected end of image"); +- return; ++ return -EIO; + } + + retstr = ret ? " allocated" : "not allocated"; +@@ -1880,6 +1918,8 @@ static void map_f(BlockBackend *blk, int argc, char **argv) + offset += num; + bytes -= num; + } ++ ++ return 0; + } + + static const cmdinfo_t map_cmd = { +@@ -1907,7 +1947,7 @@ static void reopen_help(void) + "\n"); + } + +-static void reopen_f(BlockBackend *blk, int argc, char **argv); ++static int reopen_f(BlockBackend *blk, int argc, char **argv); + + static QemuOptsList reopen_opts = { + .name = "reopen", +@@ -1929,7 +1969,7 @@ static const cmdinfo_t reopen_cmd = { + .help = reopen_help, + }; + +-static void reopen_f(BlockBackend *blk, int argc, char **argv) ++static int reopen_f(BlockBackend *blk, int argc, char **argv) + { + BlockDriverState *bs = blk_bs(blk); + QemuOpts *qopts; +@@ -1947,19 +1987,19 @@ static void reopen_f(BlockBackend *blk, int argc, char **argv) + case 'c': + if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) { + error_report("Invalid cache option: %s", optarg); +- return; ++ return -EINVAL; + } + break; + case 'o': + if (!qemu_opts_parse_noisily(&reopen_opts, optarg, 0)) { + qemu_opts_reset(&reopen_opts); +- return; ++ return -EINVAL; + } + break; + case 'r': + if (has_rw_option) { + error_report("Only one -r/-w option may be given"); +- return; ++ return -EINVAL; + } + flags &= ~BDRV_O_RDWR; + has_rw_option = true; +@@ -1967,7 +2007,7 @@ static void reopen_f(BlockBackend *blk, int argc, char **argv) + case 'w': + if (has_rw_option) { + error_report("Only one -r/-w option may be given"); +- return; ++ return -EINVAL; + } + flags |= BDRV_O_RDWR; + has_rw_option = true; +@@ -1975,14 +2015,14 @@ static void reopen_f(BlockBackend *blk, int argc, char **argv) + default: + qemu_opts_reset(&reopen_opts); + qemuio_command_usage(&reopen_cmd); +- return; ++ return -EINVAL; + } + } + + if (optind != argc) { + qemu_opts_reset(&reopen_opts); + qemuio_command_usage(&reopen_cmd); +- return; ++ return -EINVAL; + } + + if (writethrough != blk_enable_write_cache(blk) && +@@ -1990,7 +2030,7 @@ static void reopen_f(BlockBackend *blk, int argc, char **argv) + { + error_report("Cannot change cache.writeback: Device attached"); + qemu_opts_reset(&reopen_opts); +- return; ++ return -EBUSY; + } + + if (!(flags & BDRV_O_RDWR)) { +@@ -2016,29 +2056,37 @@ static void reopen_f(BlockBackend *blk, int argc, char **argv) + + if (local_err) { + error_report_err(local_err); +- } else { +- blk_set_enable_write_cache(blk, !writethrough); ++ return -EINVAL; + } ++ ++ blk_set_enable_write_cache(blk, !writethrough); ++ return 0; + } + +-static void break_f(BlockBackend *blk, int argc, char **argv) ++static int break_f(BlockBackend *blk, int argc, char **argv) + { + int ret; + + ret = bdrv_debug_breakpoint(blk_bs(blk), argv[1], argv[2]); + if (ret < 0) { + printf("Could not set breakpoint: %s\n", strerror(-ret)); ++ return ret; + } ++ ++ return 0; + } + +-static void remove_break_f(BlockBackend *blk, int argc, char **argv) ++static int remove_break_f(BlockBackend *blk, int argc, char **argv) + { + int ret; + + ret = bdrv_debug_remove_breakpoint(blk_bs(blk), argv[1]); + if (ret < 0) { + printf("Could not remove breakpoint %s: %s\n", argv[1], strerror(-ret)); ++ return ret; + } ++ ++ return 0; + } + + static const cmdinfo_t break_cmd = { +@@ -2060,14 +2108,17 @@ static const cmdinfo_t remove_break_cmd = { + .oneline = "remove a breakpoint by tag", + }; + +-static void resume_f(BlockBackend *blk, int argc, char **argv) ++static int resume_f(BlockBackend *blk, int argc, char **argv) + { + int ret; + + ret = bdrv_debug_resume(blk_bs(blk), argv[1]); + if (ret < 0) { + printf("Could not resume request: %s\n", strerror(-ret)); ++ return ret; + } ++ ++ return 0; + } + + static const cmdinfo_t resume_cmd = { +@@ -2079,11 +2130,12 @@ static const cmdinfo_t resume_cmd = { + .oneline = "resumes the request tagged as tag", + }; + +-static void wait_break_f(BlockBackend *blk, int argc, char **argv) ++static int wait_break_f(BlockBackend *blk, int argc, char **argv) + { + while (!bdrv_debug_is_suspended(blk_bs(blk), argv[1])) { + aio_poll(blk_get_aio_context(blk), true); + } ++ return 0; + } + + static const cmdinfo_t wait_break_cmd = { +@@ -2095,7 +2147,7 @@ static const cmdinfo_t wait_break_cmd = { + .oneline = "waits for the suspension of a request", + }; + +-static void abort_f(BlockBackend *blk, int argc, char **argv) ++static int abort_f(BlockBackend *blk, int argc, char **argv) + { + abort(); + } +@@ -2121,7 +2173,7 @@ static void sigraise_help(void) + "\n", SIGTERM); + } + +-static void sigraise_f(BlockBackend *blk, int argc, char **argv); ++static int sigraise_f(BlockBackend *blk, int argc, char **argv); + + static const cmdinfo_t sigraise_cmd = { + .name = "sigraise", +@@ -2134,16 +2186,16 @@ static const cmdinfo_t sigraise_cmd = { + .help = sigraise_help, + }; + +-static void sigraise_f(BlockBackend *blk, int argc, char **argv) ++static int sigraise_f(BlockBackend *blk, int argc, char **argv) + { + int64_t sig = cvtnum(argv[1]); + if (sig < 0) { + print_cvtnum_err(sig, argv[1]); +- return; ++ return sig; + } else if (sig > NSIG) { + printf("signal argument '%s' is too large to be a valid signal\n", + argv[1]); +- return; ++ return -EINVAL; + } + + /* Using raise() to kill this process does not necessarily flush all open +@@ -2153,6 +2205,8 @@ static void sigraise_f(BlockBackend *blk, int argc, char **argv) + fflush(stderr); + + raise(sig); ++ ++ return 0; + } + + static void sleep_cb(void *opaque) +@@ -2161,7 +2215,7 @@ static void sleep_cb(void *opaque) + *expired = true; + } + +-static void sleep_f(BlockBackend *blk, int argc, char **argv) ++static int sleep_f(BlockBackend *blk, int argc, char **argv) + { + char *endptr; + long ms; +@@ -2171,7 +2225,7 @@ static void sleep_f(BlockBackend *blk, int argc, char **argv) + ms = strtol(argv[1], &endptr, 0); + if (ms < 0 || *endptr != '\0') { + printf("%s is not a valid number\n", argv[1]); +- return; ++ return -EINVAL; + } + + timer = timer_new_ns(QEMU_CLOCK_HOST, sleep_cb, &expired); +@@ -2182,6 +2236,7 @@ static void sleep_f(BlockBackend *blk, int argc, char **argv) + } + + timer_free(timer); ++ return 0; + } + + static const cmdinfo_t sleep_cmd = { +@@ -2228,22 +2283,23 @@ static void help_all(void) + printf("\nUse 'help commandname' for extended help.\n"); + } + +-static void help_f(BlockBackend *blk, int argc, char **argv) ++static int help_f(BlockBackend *blk, int argc, char **argv) + { + const cmdinfo_t *ct; + + if (argc == 1) { + help_all(); +- return; ++ return 0; + } + + ct = find_command(argv[1]); + if (ct == NULL) { + printf("command %s not found\n", argv[1]); +- return; ++ return -EINVAL; + } + + help_onecmd(argv[1], ct); ++ return 0; + } + + static const cmdinfo_t help_cmd = { +@@ -2257,13 +2313,14 @@ static const cmdinfo_t help_cmd = { + .oneline = "help for one or all commands", + }; + +-void qemuio_command(BlockBackend *blk, const char *cmd) ++int qemuio_command(BlockBackend *blk, const char *cmd) + { + AioContext *ctx; + char *input; + const cmdinfo_t *ct; + char **v; + int c; ++ int ret = 0; + + input = g_strdup(cmd); + v = breakline(input, &c); +@@ -2272,14 +2329,17 @@ void qemuio_command(BlockBackend *blk, const char *cmd) + if (ct) { + ctx = blk ? blk_get_aio_context(blk) : qemu_get_aio_context(); + aio_context_acquire(ctx); +- command(blk, ct, c, v); ++ ret = command(blk, ct, c, v); + aio_context_release(ctx); + } else { + fprintf(stderr, "command \"%s\" not found\n", v[0]); ++ ret = -EINVAL; + } + } + g_free(input); + g_free(v); ++ ++ return ret; + } + + static void __attribute((constructor)) init_qemuio_commands(void) +diff --git a/qemu-io.c b/qemu-io.c +index 02a67c9..ec66838 100644 +--- a/qemu-io.c ++++ b/qemu-io.c +@@ -66,10 +66,11 @@ static int get_eof_char(void) + #endif + } + +-static void close_f(BlockBackend *blk, int argc, char **argv) ++static int close_f(BlockBackend *blk, int argc, char **argv) + { + blk_unref(qemuio_blk); + qemuio_blk = NULL; ++ return 0; + } + + static const cmdinfo_t close_cmd = { +@@ -136,7 +137,7 @@ static void open_help(void) + "\n"); + } + +-static void open_f(BlockBackend *blk, int argc, char **argv); ++static int open_f(BlockBackend *blk, int argc, char **argv); + + static const cmdinfo_t open_cmd = { + .name = "open", +@@ -160,12 +161,13 @@ static QemuOptsList empty_opts = { + }, + }; + +-static void open_f(BlockBackend *blk, int argc, char **argv) ++static int open_f(BlockBackend *blk, int argc, char **argv) + { + int flags = BDRV_O_UNMAP; + int readonly = 0; + bool writethrough = true; + int c; ++ int ret; + QemuOpts *qopts; + QDict *opts; + bool force_share = false; +@@ -192,25 +194,25 @@ static void open_f(BlockBackend *blk, int argc, char **argv) + if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) { + error_report("Invalid cache option: %s", optarg); + qemu_opts_reset(&empty_opts); +- return; ++ return -EINVAL; + } + break; + case 'd': + if (bdrv_parse_discard_flags(optarg, &flags) < 0) { + error_report("Invalid discard option: %s", optarg); + qemu_opts_reset(&empty_opts); +- return; ++ return -EINVAL; + } + break; + case 'o': + if (imageOpts) { + printf("--image-opts and 'open -o' are mutually exclusive\n"); + qemu_opts_reset(&empty_opts); +- return; ++ return -EINVAL; + } + if (!qemu_opts_parse_noisily(&empty_opts, optarg, false)) { + qemu_opts_reset(&empty_opts); +- return; ++ return -EINVAL; + } + break; + case 'U': +@@ -219,7 +221,7 @@ static void open_f(BlockBackend *blk, int argc, char **argv) + default: + qemu_opts_reset(&empty_opts); + qemuio_command_usage(&open_cmd); +- return; ++ return -EINVAL; + } + } + +@@ -230,7 +232,7 @@ static void open_f(BlockBackend *blk, int argc, char **argv) + if (imageOpts && (optind == argc - 1)) { + if (!qemu_opts_parse_noisily(&empty_opts, argv[optind], false)) { + qemu_opts_reset(&empty_opts); +- return; ++ return -EINVAL; + } + optind++; + } +@@ -240,18 +242,26 @@ static void open_f(BlockBackend *blk, int argc, char **argv) + qemu_opts_reset(&empty_opts); + + if (optind == argc - 1) { +- openfile(argv[optind], flags, writethrough, force_share, opts); ++ ret = openfile(argv[optind], flags, writethrough, force_share, opts); + } else if (optind == argc) { +- openfile(NULL, flags, writethrough, force_share, opts); ++ ret = openfile(NULL, flags, writethrough, force_share, opts); + } else { + qobject_unref(opts); + qemuio_command_usage(&open_cmd); ++ return -EINVAL; ++ } ++ ++ if (ret) { ++ return -EINVAL; + } ++ ++ return 0; + } + +-static void quit_f(BlockBackend *blk, int argc, char **argv) ++static int quit_f(BlockBackend *blk, int argc, char **argv) + { + quit_qemu_io = true; ++ return 0; + } + + static const cmdinfo_t quit_cmd = { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-io-Use-purely-string-blockdev-options.patch b/SOURCES/kvm-qemu-io-Use-purely-string-blockdev-options.patch new file mode 100644 index 0000000..e656b42 --- /dev/null +++ b/SOURCES/kvm-qemu-io-Use-purely-string-blockdev-options.patch @@ -0,0 +1,65 @@ +From 31e4b214ef764476e99ddbbfd4b039235e3a4ea1 Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 25 Jun 2018 13:04:46 +0200 +Subject: [PATCH 37/54] qemu-io: Use purely string blockdev options + +RH-Author: Max Reitz +Message-id: <20180618163106.23010-2-mreitz@redhat.com> +Patchwork-id: 80775 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/3] qemu-io: Use purely string blockdev options +Bugzilla: 1576598 +RH-Acked-by: Kevin Wolf +RH-Acked-by: Fam Zheng +RH-Acked-by: Miroslav Rezanina + +Currently, qemu-io only uses string-valued blockdev options (as all are +converted directly from QemuOpts) -- with one exception: -U adds the +force-share option as a boolean. This in itself is already a bit +questionable, but a real issue is that it also assumes the value already +existing in the options QDict would be a boolean, which is wrong. + +That has the following effect: + +$ ./qemu-io -r -U --image-opts \ + driver=file,filename=/dev/null,force-share=off +[1] 15200 segmentation fault (core dumped) ./qemu-io -r -U +--image-opts driver=file,filename=/dev/null,force-share=off + +Since @opts is converted from QemuOpts, the value must be a string, and +we have to compare it as such. Consequently, it makes sense to also set +it as a string instead of a boolean. + +Cc: qemu-stable@nongnu.org +Signed-off-by: Max Reitz +Message-id: 20180502202051.15493-2-mreitz@redhat.com +Reviewed-by: Eric Blake +Signed-off-by: Max Reitz +(cherry picked from commit 2a01c01f9ecb43af4c0a85fe6adc429ffc9c31b5) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + qemu-io.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/qemu-io.c b/qemu-io.c +index 72fee0d..73c638f 100644 +--- a/qemu-io.c ++++ b/qemu-io.c +@@ -95,12 +95,12 @@ static int openfile(char *name, int flags, bool writethrough, bool force_share, + opts = qdict_new(); + } + if (qdict_haskey(opts, BDRV_OPT_FORCE_SHARE) +- && !qdict_get_bool(opts, BDRV_OPT_FORCE_SHARE)) { ++ && strcmp(qdict_get_str(opts, BDRV_OPT_FORCE_SHARE), "on")) { + error_report("-U conflicts with image options"); + qobject_unref(opts); + return 1; + } +- qdict_put_bool(opts, BDRV_OPT_FORCE_SHARE, true); ++ qdict_put_str(opts, BDRV_OPT_FORCE_SHARE, "on"); + } + qemuio_blk = blk_new_open(name, NULL, opts, flags, &local_err); + if (!qemuio_blk) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-iotests-Add-VM.get_qmp_events_filtered.patch b/SOURCES/kvm-qemu-iotests-Add-VM.get_qmp_events_filtered.patch new file mode 100644 index 0000000..cd39ba4 --- /dev/null +++ b/SOURCES/kvm-qemu-iotests-Add-VM.get_qmp_events_filtered.patch @@ -0,0 +1,46 @@ +From a1633706e8bd21f323a9718b495b101590b9c985 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:41 +0200 +Subject: [PATCH 72/89] qemu-iotests: Add VM.get_qmp_events_filtered() + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-59-kwolf@redhat.com> +Patchwork-id: 81122 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 58/73] qemu-iotests: Add VM.get_qmp_events_filtered() +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This adds a helper function that returns a list of QMP events that are +already filtered through filter_qmp_event(). + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: Jeff Cody +(cherry picked from commit 5ad1dbf76a97b6f07d685585175832e990fe9a92) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/iotests.py | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index daf008a..7e311ed 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -392,6 +392,11 @@ class VM(qtest.QEMUQtestMachine): + output_list += [key + '=' + obj[key]] + return ','.join(output_list) + ++ def get_qmp_events_filtered(self, wait=True): ++ result = [] ++ for ev in self.get_qmp_events(wait=wait): ++ result.append(filter_qmp_event(ev)) ++ return result + + + index_re = re.compile(r'([^\[]+)\[([^\]]+)\]') +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-iotests-Add-VM.qmp_log.patch b/SOURCES/kvm-qemu-iotests-Add-VM.qmp_log.patch new file mode 100644 index 0000000..fc05bf5 --- /dev/null +++ b/SOURCES/kvm-qemu-iotests-Add-VM.qmp_log.patch @@ -0,0 +1,59 @@ +From d44774708b8db31c5e2658ea64ee930ffdae39be Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:42 +0200 +Subject: [PATCH 73/89] qemu-iotests: Add VM.qmp_log() + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-60-kwolf@redhat.com> +Patchwork-id: 81067 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 59/73] qemu-iotests: Add VM.qmp_log() +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This adds a helper function that logs both the QMP request and the +received response before returning it. + +Signed-off-by: Kevin Wolf +Reviewed-by: Jeff Cody +Reviewed-by: Max Reitz +(cherry picked from commit e234398a8e142fd0cfe571f7efb0e6a2f34fe73d) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/iotests.py | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index 7e311ed..bc5731a 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -215,6 +215,10 @@ def filter_qmp_event(event): + event['timestamp']['microseconds'] = 'USECS' + return event + ++def filter_testfiles(msg): ++ prefix = os.path.join(test_dir, "%s-" % (os.getpid())) ++ return msg.replace(prefix, 'TEST_DIR/PID-') ++ + def log(msg, filters=[]): + for flt in filters: + msg = flt(msg) +@@ -398,6 +402,13 @@ class VM(qtest.QEMUQtestMachine): + result.append(filter_qmp_event(ev)) + return result + ++ def qmp_log(self, cmd, filters=[filter_testfiles], **kwargs): ++ logmsg = "{'execute': '%s', 'arguments': %s}" % (cmd, kwargs) ++ log(logmsg, filters) ++ result = self.qmp(cmd, **kwargs) ++ log(str(result), filters) ++ return result ++ + + index_re = re.compile(r'([^\[]+)\[([^\]]+)\]') + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-iotests-Add-VM.run_job.patch b/SOURCES/kvm-qemu-iotests-Add-VM.run_job.patch new file mode 100644 index 0000000..ff0c2d5 --- /dev/null +++ b/SOURCES/kvm-qemu-iotests-Add-VM.run_job.patch @@ -0,0 +1,61 @@ +From f841d0a47ba5ba9a731d41e949f4261bd335a2c2 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:44 +0200 +Subject: [PATCH 75/89] qemu-iotests: Add VM.run_job() + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-62-kwolf@redhat.com> +Patchwork-id: 81080 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 61/73] qemu-iotests: Add VM.run_job() +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Add an iotests.py function that runs a job and only returns when it is +destroyed. An error is logged when the job failed and job-finalize and +job-dismiss commands are issued if necessary. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: Jeff Cody +(cherry picked from commit fc47d8513b45b1968b0ae32d4bb2f96d3aca066a) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/iotests.py | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index 4b0760f..390da6b 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -427,6 +427,25 @@ class VM(qtest.QEMUQtestMachine): + log(str(result), filters) + return result + ++ def run_job(self, job, auto_finalize=True, auto_dismiss=False): ++ while True: ++ for ev in self.get_qmp_events_filtered(wait=True): ++ if ev['event'] == 'JOB_STATUS_CHANGE': ++ status = ev['data']['status'] ++ if status == 'aborting': ++ result = self.qmp('query-jobs') ++ for j in result['return']: ++ if j['id'] == job: ++ log('Job failed: %s' % (j['error'])) ++ elif status == 'pending' and not auto_finalize: ++ self.qmp_log('job-finalize', id=job) ++ elif status == 'concluded' and not auto_dismiss: ++ self.qmp_log('job-dismiss', id=job) ++ elif status == 'null': ++ return ++ else: ++ iotests.log(ev) ++ + + index_re = re.compile(r'([^\[]+)\[([^\]]+)\]') + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-iotests-Add-iotests.img_info_log.patch b/SOURCES/kvm-qemu-iotests-Add-iotests.img_info_log.patch new file mode 100644 index 0000000..83a047f --- /dev/null +++ b/SOURCES/kvm-qemu-iotests-Add-iotests.img_info_log.patch @@ -0,0 +1,67 @@ +From ca9184bc1b0c0748b2c2e1395518f402be6f69e3 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:43 +0200 +Subject: [PATCH 74/89] qemu-iotests: Add iotests.img_info_log() + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-61-kwolf@redhat.com> +Patchwork-id: 81082 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 60/73] qemu-iotests: Add iotests.img_info_log() +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This adds a filter function to postprocess 'qemu-img info' input +(similar to what _img_info does), and an img_info_log() function that +calls 'qemu-img info' and logs the filtered output. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: Jeff Cody +(cherry picked from commit 6b605adec4d7491488d9cfb50bc256e667d8caf1) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/iotests.py | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index bc5731a..4b0760f 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -109,6 +109,12 @@ def qemu_img_pipe(*args): + sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args)))) + return subp.communicate()[0] + ++def img_info_log(filename, filter_path=None): ++ output = qemu_img_pipe('info', '-f', imgfmt, filename) ++ if not filter_path: ++ filter_path = filename ++ log(filter_img_info(output, filter_path)) ++ + def qemu_io(*args): + '''Run qemu-io and return the stdout data''' + args = qemu_io_args + list(args) +@@ -219,6 +225,18 @@ def filter_testfiles(msg): + prefix = os.path.join(test_dir, "%s-" % (os.getpid())) + return msg.replace(prefix, 'TEST_DIR/PID-') + ++def filter_img_info(output, filename): ++ lines = [] ++ for line in output.split('\n'): ++ if 'disk size' in line or 'actual-size' in line: ++ continue ++ line = line.replace(filename, 'TEST_IMG') \ ++ .replace(imgfmt, 'IMGFMT') ++ line = re.sub('iters: [0-9]+', 'iters: XXX', line) ++ line = re.sub('uuid: [-a-f0-9]+', 'uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', line) ++ lines.append(line) ++ return '\n'.join(lines) ++ + def log(msg, filters=[]): + for flt in filters: + msg = flt(msg) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-iotests-Modern-shell-scripting-use-instead-of.patch b/SOURCES/kvm-qemu-iotests-Modern-shell-scripting-use-instead-of.patch new file mode 100644 index 0000000..87a6334 --- /dev/null +++ b/SOURCES/kvm-qemu-iotests-Modern-shell-scripting-use-instead-of.patch @@ -0,0 +1,249 @@ +From 551c981778ea501b674fb4f6ae2cb5e86d9f5354 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:28 +0100 +Subject: [PATCH 061/163] qemu-iotests: Modern shell scripting (use $() instead + of ``) + +RH-Author: John Snow +Message-id: <20190322032241.8111-16-jsnow@redhat.com> +Patchwork-id: 85098 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 15/28] qemu-iotests: Modern shell scripting (use $() instead of ``) +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Mao Zhongyi + +Various shell files contain a mix between obsolete `` +and modern $(); It would be nice to convert to using +$() everywhere. For now, just do the qemu-iotests directory. + +Cc: kwolf@redhat.com +Cc: mreitz@redhat.com +Cc: eblake@redhat.com +Suggested-by: Eric Blake +Signed-off-by: Mao Zhongyi +Message-Id: <20181024094051.4470-4-maozhongyi@cmss.chinamobile.com> +Reviewed-by: Eric Blake +[eblake: tweak commit message] +Signed-off-by: Eric Blake +(cherry picked from commit 4a9e751f61522991c2fa94a4da8feda6a4d09c70) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/check | 60 ++++++++++++++++++++-------------------- + tests/qemu-iotests/common.config | 4 +-- + 2 files changed, 32 insertions(+), 32 deletions(-) + +diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check +index b377132..89ed275 100755 +--- a/tests/qemu-iotests/check ++++ b/tests/qemu-iotests/check +@@ -80,17 +80,17 @@ _full_imgfmt_details() + + _full_platform_details() + { +- os=`uname -s` +- host=`hostname -s` +- kernel=`uname -r` +- platform=`uname -m` ++ os=$(uname -s) ++ host=$(hostname -s) ++ kernel=$(uname -r) ++ platform=$(uname -m) + echo "$os/$platform $host $kernel" + } + + # $1 = prog to look for + set_prog_path() + { +- p=`command -v $1 2> /dev/null` ++ p=$(command -v $1 2> /dev/null) + if [ -n "$p" -a -x "$p" ]; then + type -p "$p" + else +@@ -147,9 +147,9 @@ do + if $group + then + # arg after -g +- group_list=`sed -n <"$source_iotests/group" -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{ ++ group_list=$(sed -n <"$source_iotests/group" -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{ + s/ .*//p +-}'` ++}') + if [ -z "$group_list" ] + then + echo "Group \"$r\" is empty or not defined?" +@@ -173,9 +173,9 @@ s/ .*//p + # arg after -x + # Populate $tmp.list with all tests + awk '/^[0-9]{3,}/ {print $1}' "${source_iotests}/group" > $tmp.list 2>/dev/null +- group_list=`sed -n <"$source_iotests/group" -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{ ++ group_list=$(sed -n <"$source_iotests/group" -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{ + s/ .*//p +-}'` ++}') + if [ -z "$group_list" ] + then + echo "Group \"$r\" is empty or not defined?" +@@ -193,7 +193,7 @@ s/ .*//p + rm -f $tmp.sed + fi + echo "/^$t\$/d" >>$tmp.sed +- numsed=`expr $numsed + 1` ++ numsed=$(expr $numsed + 1) + done + sed -f $tmp.sed <$tmp.list >$tmp.tmp + mv $tmp.tmp $tmp.list +@@ -433,12 +433,12 @@ testlist options + ;; + + [0-9]*-[0-9]*) +- eval `echo $r | sed -e 's/^/start=/' -e 's/-/ end=/'` ++ eval $(echo $r | sed -e 's/^/start=/' -e 's/-/ end=/') + ;; + + [0-9]*-) +- eval `echo $r | sed -e 's/^/start=/' -e 's/-//'` +- end=`echo [0-9][0-9][0-9] [0-9][0-9][0-9][0-9] | sed -e 's/\[0-9]//g' -e 's/ *$//' -e 's/.* //'` ++ eval $(echo $r | sed -e 's/^/start=/' -e 's/-//') ++ end=$(echo [0-9][0-9][0-9] [0-9][0-9][0-9][0-9] | sed -e 's/\[0-9]//g' -e 's/ *$//' -e 's/.* //') + if [ -z "$end" ] + then + echo "No tests in range \"$r\"?" +@@ -455,8 +455,8 @@ testlist options + esac + + # get rid of leading 0s as can be interpreted as octal +- start=`echo $start | sed 's/^0*//'` +- end=`echo $end | sed 's/^0*//'` ++ start=$(echo $start | sed 's/^0*//') ++ end=$(echo $end | sed 's/^0*//') + + if $xpand + then +@@ -531,7 +531,7 @@ fi + # should be sort -n, but this did not work for Linux when this + # was ported from IRIX + # +-list=`sort $tmp.list` ++list=$(sort $tmp.list) + rm -f $tmp.list $tmp.tmp $tmp.sed + + if [ -z "$QEMU_PROG" ] +@@ -590,7 +590,7 @@ fi + export QEMU_NBD_PROG="$(type -p "$QEMU_NBD_PROG")" + + if [ -z "$QEMU_VXHS_PROG" ]; then +- export QEMU_VXHS_PROG="`set_prog_path qnio_server`" ++ export QEMU_VXHS_PROG="$(set_prog_path qnio_server)" + fi + + if [ -x "$build_iotests/socket_scm_helper" ] +@@ -616,7 +616,7 @@ _wallclock() + + _timestamp() + { +- now=`date "+%T"` ++ now=$(date "+%T") + printf %s " [$now]" + } + +@@ -642,9 +642,9 @@ END { if (NR > 0) { + + if [ -f $tmp.expunged ] + then +- notrun=`wc -l <$tmp.expunged | sed -e 's/ *//g'` +- try=`expr $try - $notrun` +- list=`echo "$list" | sed -f $tmp.expunged` ++ notrun=$(wc -l <$tmp.expunged | sed -e 's/ *//g') ++ try=$(expr $try - $notrun) ++ list=$(echo "$list" | sed -f $tmp.expunged) + fi + + echo "" >>check.log +@@ -682,8 +682,8 @@ trap "_wrapup; exit \$status" 0 1 2 3 15 + + [ -f $TIMESTAMP_FILE ] || touch $TIMESTAMP_FILE + +-FULL_IMGFMT_DETAILS=`_full_imgfmt_details` +-FULL_HOST_DETAILS=`_full_platform_details` ++FULL_IMGFMT_DETAILS=$(_full_imgfmt_details) ++FULL_HOST_DETAILS=$(_full_platform_details) + + cat <>$tmp.time ++ echo "$seq $(expr $stop - $start)" >>$tmp.time + fi + else + echo " - output mismatch (see $seq.out.bad)" +@@ -824,14 +824,14 @@ do + if $err + then + bad="$bad $seq" +- n_bad=`expr $n_bad + 1` ++ n_bad=$(expr $n_bad + 1) + quick=false + fi +- [ -f $seq.notrun ] || try=`expr $try + 1` ++ [ -f $seq.notrun ] || try=$(expr $try + 1) + + seq="after_$seq" + done + + interrupt=false +-status=`expr $n_bad` ++status=$(expr $n_bad) + exit +diff --git a/tests/qemu-iotests/common.config b/tests/qemu-iotests/common.config +index 3cda0fe..9f460f2 100644 +--- a/tests/qemu-iotests/common.config ++++ b/tests/qemu-iotests/common.config +@@ -21,8 +21,8 @@ export LANG=C + + PATH=".:$PATH" + +-HOSTOS=`uname -s` +-arch=`uname -m` ++HOSTOS=$(uname -s) ++arch=$(uname -m) + [[ "$arch" =~ "ppc64" ]] && qemu_arch=ppc64 || qemu_arch="$arch" + + # make sure we have a standard umask +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-iotests-Rewrite-206-for-blockdev-create-job.patch b/SOURCES/kvm-qemu-iotests-Rewrite-206-for-blockdev-create-job.patch new file mode 100644 index 0000000..3ac27b4 --- /dev/null +++ b/SOURCES/kvm-qemu-iotests-Rewrite-206-for-blockdev-create-job.patch @@ -0,0 +1,1080 @@ +From 9b3ed457aa8c255cb8a5975f5aacd149d0b0397a Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:46 +0200 +Subject: [PATCH 77/89] qemu-iotests: Rewrite 206 for blockdev-create job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-64-kwolf@redhat.com> +Patchwork-id: 81077 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 63/73] qemu-iotests: Rewrite 206 for blockdev-create job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This rewrites the test case 206 to work with the new x-blockdev-create +job rather than the old synchronous version of the command. + +All of the test cases stay the same as before, but in order to be able +to implement proper job handling, the test case is rewritten in Python. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit 4de110f8fd2811171013254fd7ed2384f1de87b4) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/206 | 680 ++++++++++++++++++--------------------------- + tests/qemu-iotests/206.out | 253 ++++++++++------- + tests/qemu-iotests/group | 2 +- + 3 files changed, 414 insertions(+), 521 deletions(-) + +diff --git a/tests/qemu-iotests/206 b/tests/qemu-iotests/206 +index 0a18b2b..b8cf2e7 100755 +--- a/tests/qemu-iotests/206 ++++ b/tests/qemu-iotests/206 +@@ -1,9 +1,11 @@ +-#!/bin/bash ++#!/usr/bin/env python + # + # Test qcow2 and file image creation + # + # Copyright (C) 2018 Red Hat, Inc. + # ++# Creator/Owner: Kevin Wolf ++# + # 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 +@@ -18,419 +20,263 @@ + # along with this program. If not, see . + # + +-# creator +-owner=kwolf@redhat.com +- +-seq=`basename $0` +-echo "QA output created by $seq" +- +-here=`pwd` +-status=1 # failure is the default! +- +-# get standard environment, filters and checks +-. ./common.rc +-. ./common.filter +- +-_supported_fmt qcow2 +-_supported_proto file +-_supported_os Linux +- +-function do_run_qemu() +-{ +- echo Testing: "$@" +- $QEMU -nographic -qmp stdio -serial none "$@" +- echo +-} +- +-function run_qemu() +-{ +- do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ +- | _filter_qemu | _filter_imgfmt \ +- | _filter_actual_image_size +-} +- +-echo +-echo "=== Successful image creation (defaults) ===" +-echo +- +-size=$((128 * 1024 * 1024)) +- +-run_qemu < +Date: Tue, 26 Jun 2018 09:48:47 +0200 +Subject: [PATCH 78/89] qemu-iotests: Rewrite 207 for blockdev-create job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-65-kwolf@redhat.com> +Patchwork-id: 81083 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 64/73] qemu-iotests: Rewrite 207 for blockdev-create job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This rewrites the test case 207 to work with the new x-blockdev-create +job rather than the old synchronous version of the command. + +Most of the test cases stay the same as before (the exception being some +improved 'size' options that allow distinguishing which command created +the image), but in order to be able to implement proper job handling, +the test case is rewritten in Python. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit 00af19359e8d77e53a09de9a5d3ed6f6e149e0d2) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/207 | 440 ++++++++++++++++++++------------------------- + tests/qemu-iotests/207.out | 107 +++++------ + tests/qemu-iotests/group | 6 +- + 3 files changed, 257 insertions(+), 296 deletions(-) + +diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207 +index f5c7785..b595c92 100755 +--- a/tests/qemu-iotests/207 ++++ b/tests/qemu-iotests/207 +@@ -1,9 +1,11 @@ +-#!/bin/bash ++#!/usr/bin/env python + # + # Test ssh image creation + # + # Copyright (C) 2018 Red Hat, Inc. + # ++# Creator/Owner: Kevin Wolf ++# + # 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 +@@ -18,244 +20,198 @@ + # along with this program. If not, see . + # + +-# creator +-owner=kwolf@redhat.com +- +-seq=`basename $0` +-echo "QA output created by $seq" +- +-here=`pwd` +-status=1 # failure is the default! +- +-# get standard environment, filters and checks +-. ./common.rc +-. ./common.filter +- +-_supported_fmt raw +-_supported_proto ssh +-_supported_os Linux +- +-function do_run_qemu() +-{ +- echo Testing: "$@" +- $QEMU -nographic -qmp stdio -serial none "$@" +- echo +-} +- +-function run_qemu() +-{ +- do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ +- | _filter_qemu | _filter_imgfmt \ +- | _filter_actual_image_size +-} +- +-echo +-echo "=== Successful image creation (defaults) ===" +-echo +- +-run_qemu </dev/null | grep -v "\\^#" | +- cut -d" " -f3 | base64 -d | md5sum -b | cut -d" " -f1) +- +-run_qemu </dev/null | grep -v "\\^#" | +- cut -d" " -f3 | base64 -d | sha1sum -b | cut -d" " -f1) +- +-run_qemu </dev/null | grep -v "\\^#" | ' + ++ 'cut -d" " -f3 | base64 -d | md5sum -b | cut -d" " -f1', ++ shell=True).rstrip() ++ ++ vm.launch() ++ blockdev_create(vm, { 'driver': 'ssh', ++ 'location': { ++ 'path': disk_path, ++ 'server': { ++ 'host': '127.0.0.1', ++ 'port': '22' ++ }, ++ 'host-key-check': { ++ 'mode': 'hash', ++ 'type': 'md5', ++ 'hash': 'wrong', ++ } ++ }, ++ 'size': 2097152 }) ++ blockdev_create(vm, { 'driver': 'ssh', ++ 'location': { ++ 'path': disk_path, ++ 'server': { ++ 'host': '127.0.0.1', ++ 'port': '22' ++ }, ++ 'host-key-check': { ++ 'mode': 'hash', ++ 'type': 'md5', ++ 'hash': md5_key, ++ } ++ }, ++ 'size': 8388608 }) ++ vm.shutdown() ++ ++ iotests.img_info_log(remote_path, filter_path=disk_path) ++ ++ sha1_key = subprocess.check_output( ++ 'ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' + ++ 'cut -d" " -f3 | base64 -d | sha1sum -b | cut -d" " -f1', ++ shell=True).rstrip() ++ ++ vm.launch() ++ blockdev_create(vm, { 'driver': 'ssh', ++ 'location': { ++ 'path': disk_path, ++ 'server': { ++ 'host': '127.0.0.1', ++ 'port': '22' ++ }, ++ 'host-key-check': { ++ 'mode': 'hash', ++ 'type': 'sha1', ++ 'hash': 'wrong', ++ } ++ }, ++ 'size': 2097152 }) ++ blockdev_create(vm, { 'driver': 'ssh', ++ 'location': { ++ 'path': disk_path, ++ 'server': { ++ 'host': '127.0.0.1', ++ 'port': '22' ++ }, ++ 'host-key-check': { ++ 'mode': 'hash', ++ 'type': 'sha1', ++ 'hash': sha1_key, ++ } ++ }, ++ 'size': 4194304 }) ++ vm.shutdown() ++ ++ iotests.img_info_log(remote_path, filter_path=disk_path) ++ ++ # ++ # Invalid path and user ++ # ++ iotests.log("=== Invalid path and user ===") ++ iotests.log("") ++ ++ vm.launch() ++ blockdev_create(vm, { 'driver': 'ssh', ++ 'location': { ++ 'path': '/this/is/not/an/existing/path', ++ 'server': { ++ 'host': '127.0.0.1', ++ 'port': '22' ++ }, ++ 'host-key-check': { ++ 'mode': 'none' ++ } ++ }, ++ 'size': 4194304 }) ++ blockdev_create(vm, { 'driver': 'ssh', ++ 'location': { ++ 'path': disk_path, ++ 'user': 'invalid user', ++ 'server': { ++ 'host': '127.0.0.1', ++ 'port': '22' ++ }, ++ 'host-key-check': { ++ 'mode': 'none' ++ } ++ }, ++ 'size': 4194304 }) ++ vm.shutdown() +diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out +index 417deee..5eee17b 100644 +--- a/tests/qemu-iotests/207.out ++++ b/tests/qemu-iotests/207.out +@@ -1,75 +1,80 @@ +-QA output created by 207 +- + === Successful image creation (defaults) === + +-Testing: +-QMP_VERSION +-{"return": {}} +-{"return": {}} +-{"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} ++{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} ++{u'return': {}} ++{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} ++{u'return': {}} + +-image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}} ++image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} + file format: IMGFMT + virtual size: 4.0M (4194304 bytes) + +-image: TEST_DIR/t.IMGFMT ++ ++image: TEST_IMG + file format: IMGFMT + virtual size: 4.0M (4194304 bytes) + + === Test host-key-check options === + +-Testing: +-QMP_VERSION +-{"return": {}} +-{"return": {}} +-{"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} ++{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'none'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 8388608}}} ++{u'return': {}} ++{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} ++{u'return': {}} + +-image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}} ++image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} + file format: IMGFMT + virtual size: 8.0M (8388608 bytes) +-Testing: +-QMP_VERSION +-{"return": {}} +-{"return": {}} +-{"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +- +-image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}} ++ ++{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'known_hosts'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} ++{u'return': {}} ++{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} ++{u'return': {}} ++ ++image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} + file format: IMGFMT + virtual size: 4.0M (4194304 bytes) +-Testing: +-QMP_VERSION +-{"return": {}} +-{"error": {"class": "GenericError", "desc": "remote host key does not match host_key_check 'wrong'"}} +-{"return": {}} +-{"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +- +-image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}} ++ ++{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': 'wrong', 'type': 'md5', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 2097152}}} ++{u'return': {}} ++Job failed: remote host key does not match host_key_check 'wrong' ++{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} ++{u'return': {}} ++ ++{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': HASH, 'type': 'md5', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 8388608}}} ++{u'return': {}} ++{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} ++{u'return': {}} ++ ++image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} + file format: IMGFMT + virtual size: 8.0M (8388608 bytes) +-Testing: +-QMP_VERSION +-{"return": {}} +-{"error": {"class": "GenericError", "desc": "remote host key does not match host_key_check 'wrong'"}} +-{"return": {}} +-{"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +- +-image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}} ++ ++{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': 'wrong', 'type': 'sha1', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 2097152}}} ++{u'return': {}} ++Job failed: remote host key does not match host_key_check 'wrong' ++{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} ++{u'return': {}} ++ ++{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': HASH, 'type': 'sha1', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} ++{u'return': {}} ++{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} ++{u'return': {}} ++ ++image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} + file format: IMGFMT + virtual size: 4.0M (4194304 bytes) + + === Invalid path and user === + +-Testing: +-QMP_VERSION +-{"return": {}} +-{"error": {"class": "GenericError", "desc": "failed to open remote file '/this/is/not/an/existing/path': Failed opening remote file (libssh2 error code: -31)"}} +-{"error": {"class": "GenericError", "desc": "failed to authenticate using publickey authentication and the identities held by your ssh-agent"}} +-{"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} ++{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': '/this/is/not/an/existing/path', 'host-key-check': {'mode': 'none'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} ++{u'return': {}} ++Job failed: failed to open remote file '/this/is/not/an/existing/path': Failed opening remote file (libssh2 error code: -31) ++{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} ++{u'return': {}} ++ ++{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'none'}, 'user': 'invalid user', 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} ++{u'return': {}} ++Job failed: failed to authenticate using publickey authentication and the identities held by your ssh-agent ++{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} ++{u'return': {}} + +-*** done +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index 3d6ae02..649291a 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -205,11 +205,11 @@ + 204 rw auto quick + 205 rw auto quick + 206 rw auto +-# TODO The following commented out tests need to be reworked to work +-# with the x-blockdev-create job +-#207 rw auto ++207 rw auto + 208 rw auto quick + 209 rw auto quick ++# TODO The following commented out tests need to be reworked to work ++# with the x-blockdev-create job + #210 rw auto + #211 rw auto quick + #212 rw auto quick +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-iotests-Rewrite-210-for-blockdev-create-job.patch b/SOURCES/kvm-qemu-iotests-Rewrite-210-for-blockdev-create-job.patch new file mode 100644 index 0000000..d644f06 --- /dev/null +++ b/SOURCES/kvm-qemu-iotests-Rewrite-210-for-blockdev-create-job.patch @@ -0,0 +1,745 @@ +From 609b0ba5919f07e407127911ae6f3b15cc15e324 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:48 +0200 +Subject: [PATCH 79/89] qemu-iotests: Rewrite 210 for blockdev-create job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-66-kwolf@redhat.com> +Patchwork-id: 81090 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 65/73] qemu-iotests: Rewrite 210 for blockdev-create job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This rewrites the test case 210 to work with the new x-blockdev-create +job rather than the old synchronous version of the command. + +All of the test cases stay the same as before, but in order to be able +to implement proper job handling, the test case is rewritten in Python. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: Jeff Cody +(cherry picked from commit 5ba141dc6f17ca0f250f107aace2df19558c8bc4) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/210 | 393 ++++++++++++++++++------------------------ + tests/qemu-iotests/210.out | 197 ++++++++++++++------- + tests/qemu-iotests/group | 2 +- + tests/qemu-iotests/iotests.py | 12 +- + 4 files changed, 314 insertions(+), 290 deletions(-) + +diff --git a/tests/qemu-iotests/210 b/tests/qemu-iotests/210 +index e607c0d..ff4fdde 100755 +--- a/tests/qemu-iotests/210 ++++ b/tests/qemu-iotests/210 +@@ -1,9 +1,11 @@ +-#!/bin/bash ++#!/usr/bin/env python + # + # Test luks and file image creation + # + # Copyright (C) 2018 Red Hat, Inc. + # ++# Creator/Owner: Kevin Wolf ++# + # 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 +@@ -18,230 +20,165 @@ + # along with this program. If not, see . + # + +-# creator +-owner=kwolf@redhat.com +- +-seq=`basename $0` +-echo "QA output created by $seq" +- +-here=`pwd` +-status=1 # failure is the default! +- +-# get standard environment, filters and checks +-. ./common.rc +-. ./common.filter +- +-_supported_fmt luks +-_supported_proto file +-_supported_os Linux +- +-function do_run_qemu() +-{ +- echo Testing: "$@" +- $QEMU -nographic -qmp stdio -serial none "$@" +- echo +-} +- +-function run_qemu() +-{ +- do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ +- | _filter_qemu | _filter_imgfmt \ +- | _filter_actual_image_size +-} +- +-echo +-echo "=== Successful image creation (defaults) ===" +-echo +- +-size=$((128 * 1024 * 1024)) +- +-run_qemu -object secret,id=keysec0,data="foo" <0 size"}} +-{"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +- +-image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "key-secret": "keysec0"} ++{'execute': 'block_resize', 'arguments': {'size': 9223372036854775296, 'node_name': 'node1'}} ++{u'error': {u'class': u'GenericError', u'desc': u'The requested file size is too large'}} ++{'execute': 'block_resize', 'arguments': {'size': 9223372036854775808L, 'node_name': 'node1'}} ++{u'error': {u'class': u'GenericError', u'desc': u"Invalid parameter type for 'size', expected: integer"}} ++{'execute': 'block_resize', 'arguments': {'size': 18446744073709551104L, 'node_name': 'node1'}} ++{u'error': {u'class': u'GenericError', u'desc': u"Invalid parameter type for 'size', expected: integer"}} ++{'execute': 'block_resize', 'arguments': {'size': -9223372036854775808, 'node_name': 'node1'}} ++{u'error': {u'class': u'GenericError', u'desc': u"Parameter 'size' expects a >0 size"}} ++image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_IMG"}, "key-secret": "keysec0"} + file format: IMGFMT + virtual size: 0 (0 bytes) +-*** done ++encrypted: yes ++Format specific information: ++ ivgen alg: plain64 ++ hash alg: sha256 ++ cipher alg: aes-256 ++ uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX ++ cipher mode: xts ++ slots: ++ [0]: ++ active: true ++ iters: XXX ++ key offset: 4096 ++ stripes: 4000 ++ [1]: ++ active: false ++ key offset: 262144 ++ [2]: ++ active: false ++ key offset: 520192 ++ [3]: ++ active: false ++ key offset: 778240 ++ [4]: ++ active: false ++ key offset: 1036288 ++ [5]: ++ active: false ++ key offset: 1294336 ++ [6]: ++ active: false ++ key offset: 1552384 ++ [7]: ++ active: false ++ key offset: 1810432 ++ payload offset: 2068480 ++ master key iters: XXX ++ +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index 649291a..3ecdafa 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -208,9 +208,9 @@ + 207 rw auto + 208 rw auto quick + 209 rw auto quick ++210 rw auto + # TODO The following commented out tests need to be reworked to work + # with the x-blockdev-create job +-#210 rw auto + #211 rw auto quick + #212 rw auto quick + #213 rw auto quick +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index 9f1759d..0b204dc 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -109,8 +109,16 @@ def qemu_img_pipe(*args): + sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args)))) + return subp.communicate()[0] + +-def img_info_log(filename, filter_path=None): +- output = qemu_img_pipe('info', '-f', imgfmt, filename) ++def img_info_log(filename, filter_path=None, imgopts=False, extra_args=[]): ++ args = [ 'info' ] ++ if imgopts: ++ args.append('--image-opts') ++ else: ++ args += [ '-f', imgfmt ] ++ args += extra_args ++ args.append(filename) ++ ++ output = qemu_img_pipe(*args) + if not filter_path: + filter_path = filename + log(filter_img_info(output, filter_path)) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-iotests-Rewrite-211-for-blockdev-create-job.patch b/SOURCES/kvm-qemu-iotests-Rewrite-211-for-blockdev-create-job.patch new file mode 100644 index 0000000..e0ae39a --- /dev/null +++ b/SOURCES/kvm-qemu-iotests-Rewrite-211-for-blockdev-create-job.patch @@ -0,0 +1,623 @@ +From 71efcd6903a5a857fb6a4b03ee19bb62c47b9dfd Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:49 +0200 +Subject: [PATCH 80/89] qemu-iotests: Rewrite 211 for blockdev-create job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-67-kwolf@redhat.com> +Patchwork-id: 81096 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 66/73] qemu-iotests: Rewrite 211 for blockdev-create job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This rewrites the test case 211 to work with the new x-blockdev-create +job rather than the old synchronous version of the command. + +All of the test cases stay the same as before, but in order to be able +to implement proper job handling, the test case is rewritten in Python. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit abbab72cad2eafcaf3b0f4e970add813b4264e5f) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/211 | 381 ++++++++++++++++++--------------------------- + tests/qemu-iotests/211.out | 133 +++++++++------- + tests/qemu-iotests/group | 2 +- + 3 files changed, 229 insertions(+), 287 deletions(-) + +diff --git a/tests/qemu-iotests/211 b/tests/qemu-iotests/211 +index 1edec26..b45f886 100755 +--- a/tests/qemu-iotests/211 ++++ b/tests/qemu-iotests/211 +@@ -1,9 +1,11 @@ +-#!/bin/bash ++#!/usr/bin/env python + # + # Test VDI and file image creation + # + # Copyright (C) 2018 Red Hat, Inc. + # ++# Creator/Owner: Kevin Wolf ++# + # 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 +@@ -18,229 +20,154 @@ + # along with this program. If not, see . + # + +-# creator +-owner=kwolf@redhat.com +- +-seq=`basename $0` +-echo "QA output created by $seq" +- +-here=`pwd` +-status=1 # failure is the default! +- +-# get standard environment, filters and checks +-. ./common.rc +-. ./common.filter +- +-_supported_fmt vdi +-_supported_proto file +-_supported_os Linux +- +-function do_run_qemu() +-{ +- echo Testing: "$@" +- $QEMU -nographic -qmp stdio -serial none "$@" +- echo +-} +- +-function run_qemu() +-{ +- do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ +- | _filter_qemu | _filter_imgfmt \ +- | _filter_actual_image_size +-} +- +-echo +-echo "=== Successful image creation (defaults) ===" +-echo +- +-size=$((128 * 1024 * 1024)) +- +-run_qemu < +Date: Tue, 26 Jun 2018 09:48:50 +0200 +Subject: [PATCH 81/89] qemu-iotests: Rewrite 212 for blockdev-create job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-68-kwolf@redhat.com> +Patchwork-id: 81100 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 67/73] qemu-iotests: Rewrite 212 for blockdev-create job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This rewrites the test case 212 to work with the new x-blockdev-create +job rather than the old synchronous version of the command. + +All of the test cases stay the same as before, but in order to be able +to implement proper job handling, the test case is rewritten in Python. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit 2d7abfbeb75fbe624a8b3a2ff253dbf5674ccb65) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/212 | 483 +++++++++++++++++---------------------------- + tests/qemu-iotests/212.out | 191 +++++++++++------- + tests/qemu-iotests/group | 2 +- + 3 files changed, 295 insertions(+), 381 deletions(-) + +diff --git a/tests/qemu-iotests/212 b/tests/qemu-iotests/212 +index e5a1ba7..03cf41d 100755 +--- a/tests/qemu-iotests/212 ++++ b/tests/qemu-iotests/212 +@@ -1,9 +1,11 @@ +-#!/bin/bash ++#!/usr/bin/env python + # + # Test parallels and file image creation + # + # Copyright (C) 2018 Red Hat, Inc. + # ++# Creator/Owner: Kevin Wolf ++# + # 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 +@@ -18,309 +20,176 @@ + # along with this program. If not, see . + # + +-# creator +-owner=kwolf@redhat.com +- +-seq=`basename $0` +-echo "QA output created by $seq" +- +-here=`pwd` +-status=1 # failure is the default! +- +-# get standard environment, filters and checks +-. ./common.rc +-. ./common.filter +- +-_supported_fmt parallels +-_supported_proto file +-_supported_os Linux +- +-function do_run_qemu() +-{ +- echo Testing: "$@" +- $QEMU -nographic -qmp stdio -serial none "$@" +- echo +-} +- +-function run_qemu() +-{ +- do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ +- | _filter_qemu | _filter_imgfmt \ +- | _filter_actual_image_size +-} +- +-echo +-echo "=== Successful image creation (defaults) ===" +-echo +- +-size=$((128 * 1024 * 1024)) +- +-run_qemu < +Date: Tue, 26 Jun 2018 09:48:51 +0200 +Subject: [PATCH 82/89] qemu-iotests: Rewrite 213 for blockdev-create job + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-69-kwolf@redhat.com> +Patchwork-id: 81109 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 68/73] qemu-iotests: Rewrite 213 for blockdev-create job +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This rewrites the test case 213 to work with the new x-blockdev-create +job rather than the old synchronous version of the command. + +All of the test cases stay the same as before, but in order to be able +to implement proper job handling, the test case is rewritten in Python. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit 0c46a69a5eb8a061134719a0a85898854eb8e533) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/213 | 520 +++++++++++++++++---------------------------- + tests/qemu-iotests/213.out | 208 +++++++++++------- + tests/qemu-iotests/group | 4 +- + 3 files changed, 319 insertions(+), 413 deletions(-) + +diff --git a/tests/qemu-iotests/213 b/tests/qemu-iotests/213 +index 3a00a0f..29d25bc 100755 +--- a/tests/qemu-iotests/213 ++++ b/tests/qemu-iotests/213 +@@ -1,9 +1,11 @@ +-#!/bin/bash ++#!/usr/bin/env python + # + # Test vhdx and file image creation + # + # Copyright (C) 2018 Red Hat, Inc. + # ++# Creator/Owner: Kevin Wolf ++# + # 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 +@@ -18,332 +20,190 @@ + # along with this program. If not, see . + # + +-# creator +-owner=kwolf@redhat.com +- +-seq=`basename $0` +-echo "QA output created by $seq" +- +-here=`pwd` +-status=1 # failure is the default! +- +-# get standard environment, filters and checks +-. ./common.rc +-. ./common.filter +- +-_supported_fmt vhdx +-_supported_proto file +-_supported_os Linux +- +-function do_run_qemu() +-{ +- echo Testing: "$@" +- $QEMU -nographic -qmp stdio -serial none "$@" +- echo +-} +- +-function run_qemu() +-{ +- do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ +- | _filter_qemu | _filter_imgfmt \ +- | _filter_actual_image_size +-} +- +-echo +-echo "=== Successful image creation (defaults) ===" +-echo +- +-size=$((128 * 1024 * 1024)) +- +-run_qemu < +Date: Fri, 23 Nov 2018 10:41:53 +0100 +Subject: [PATCH 12/34] qemu-iotests: Test auto-read-only with -drive and + -blockdev + +RH-Author: Kevin Wolf +Message-id: <20181123104154.13541-12-kwolf@redhat.com> +Patchwork-id: 83119 +O-Subject: [RHEL-7.7/7.6.z qemu-kvm-rhev PATCH v2 11/12] qemu-iotests: Test auto-read-only with -drive and -blockdev +Bugzilla: 1623986 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina +RH-Acked-by: John Snow + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +(cherry picked from commit 36f808fa15f85a894c2f6cce9df46d27e8f0f129) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/232 | 147 +++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/232.out | 59 ++++++++++++++++++ + tests/qemu-iotests/group | 1 + + 3 files changed, 207 insertions(+) + create mode 100755 tests/qemu-iotests/232 + create mode 100644 tests/qemu-iotests/232.out + +diff --git a/tests/qemu-iotests/232 b/tests/qemu-iotests/232 +new file mode 100755 +index 0000000..bc2972d +--- /dev/null ++++ b/tests/qemu-iotests/232 +@@ -0,0 +1,147 @@ ++#!/bin/bash ++# ++# Test for auto-read-only ++# ++# Copyright (C) 2018 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 . ++# ++ ++# creator ++owner=kwolf@redhat.com ++ ++seq=`basename $0` ++echo "QA output created by $seq" ++ ++here=`pwd` ++status=1 # failure is the default! ++ ++_cleanup() ++{ ++ _cleanup_test_img ++ rm -f $TEST_IMG.snap ++} ++trap "_cleanup; exit \$status" 0 1 2 3 15 ++ ++# get standard environment, filters and checks ++. ./common.rc ++. ./common.filter ++ ++_supported_fmt generic ++_supported_proto file ++_supported_os Linux ++ ++function do_run_qemu() ++{ ++ echo Testing: "$@" ++ ( ++ if ! test -t 0; then ++ while read cmd; do ++ echo $cmd ++ done ++ fi ++ echo quit ++ ) | $QEMU -nographic -monitor stdio -nodefaults "$@" ++ echo ++} ++ ++function run_qemu() ++{ ++ do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_hmp | ++ _filter_generated_node_ids | _filter_imgfmt ++} ++ ++function run_qemu_info_block() ++{ ++ echo "info block -n" | run_qemu "$@" | grep -e "(file" -e "QEMU_PROG" ++} ++ ++size=128M ++ ++_make_test_img $size ++ ++echo ++echo "=== -drive with read-write image: read-only/auto-read-only combinations ===" ++echo ++ ++run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=on,auto-read-only=off ++run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=on,auto-read-only=on ++run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=on ++echo ++run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=off,auto-read-only=off ++run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=off,auto-read-only=on ++run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=off ++echo ++run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,auto-read-only=off ++run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,auto-read-only=on ++run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none ++ ++echo ++echo "=== -drive with read-only image: read-only/auto-read-only combinations ===" ++echo ++ ++chmod a-w $TEST_IMG ++ ++run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=on,auto-read-only=off ++run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=on,auto-read-only=on ++run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=on ++echo ++run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=off,auto-read-only=off ++run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=off,auto-read-only=on ++run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=off ++echo ++run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,auto-read-only=off ++run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,auto-read-only=on ++run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none ++ ++echo ++echo "=== -blockdev with read-write image: read-only/auto-read-only combinations ===" ++echo ++ ++chmod a+w $TEST_IMG ++ ++run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=on,auto-read-only=off ++run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=on,auto-read-only=on ++run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=on ++echo ++run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=off,auto-read-only=off ++run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=off,auto-read-only=on ++run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=off ++echo ++run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,auto-read-only=off ++run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,auto-read-only=on ++run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0 ++ ++echo ++echo "=== -blockdev with read-only image: read-only/auto-read-only combinations ===" ++echo ++ ++chmod a-w $TEST_IMG ++ ++run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=on,auto-read-only=off ++run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=on,auto-read-only=on ++run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=on ++echo ++run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=off,auto-read-only=off ++run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=off,auto-read-only=on ++run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=off ++echo ++run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,auto-read-only=off ++run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,auto-read-only=on ++run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0 ++ ++# success, all done ++echo "*** done" ++rm -f $seq.full ++status=0 +diff --git a/tests/qemu-iotests/232.out b/tests/qemu-iotests/232.out +new file mode 100644 +index 0000000..dcb683a +--- /dev/null ++++ b/tests/qemu-iotests/232.out +@@ -0,0 +1,59 @@ ++QA output created by 232 ++Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 ++ ++=== -drive with read-write image: read-only/auto-read-only combinations === ++ ++NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) ++NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) ++NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) ++ ++NODE_NAME: TEST_DIR/t.IMGFMT (file) ++NODE_NAME: TEST_DIR/t.IMGFMT (file) ++NODE_NAME: TEST_DIR/t.IMGFMT (file) ++ ++NODE_NAME: TEST_DIR/t.IMGFMT (file) ++NODE_NAME: TEST_DIR/t.IMGFMT (file) ++NODE_NAME: TEST_DIR/t.IMGFMT (file) ++ ++=== -drive with read-only image: read-only/auto-read-only combinations === ++ ++NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) ++NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) ++NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) ++ ++QEMU_PROG: -drive driver=file,file=TEST_DIR/t.IMGFMT,if=none,read-only=off,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied ++NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) ++NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) ++ ++QEMU_PROG: -drive driver=file,file=TEST_DIR/t.IMGFMT,if=none,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied ++NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) ++NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) ++ ++=== -blockdev with read-write image: read-only/auto-read-only combinations === ++ ++node0: TEST_DIR/t.IMGFMT (file, read-only) ++node0: TEST_DIR/t.IMGFMT (file, read-only) ++node0: TEST_DIR/t.IMGFMT (file, read-only) ++ ++node0: TEST_DIR/t.IMGFMT (file) ++node0: TEST_DIR/t.IMGFMT (file) ++node0: TEST_DIR/t.IMGFMT (file) ++ ++node0: TEST_DIR/t.IMGFMT (file) ++node0: TEST_DIR/t.IMGFMT (file) ++node0: TEST_DIR/t.IMGFMT (file) ++ ++=== -blockdev with read-only image: read-only/auto-read-only combinations === ++ ++node0: TEST_DIR/t.IMGFMT (file, read-only) ++node0: TEST_DIR/t.IMGFMT (file, read-only) ++node0: TEST_DIR/t.IMGFMT (file, read-only) ++ ++QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,read-only=off,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied ++node0: TEST_DIR/t.IMGFMT (file, read-only) ++QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied ++ ++QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied ++node0: TEST_DIR/t.IMGFMT (file, read-only) ++QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0: Could not open 'TEST_DIR/t.IMGFMT': Permission denied ++*** done +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index 06a7107..fcbf3f3 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -225,3 +225,4 @@ + 227 auto quick + 229 auto quick + 231 auto quick ++232 auto quick +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-iotests-Test-commit-with-top-node-base-node.patch b/SOURCES/kvm-qemu-iotests-Test-commit-with-top-node-base-node.patch new file mode 100644 index 0000000..7a0ddb2 --- /dev/null +++ b/SOURCES/kvm-qemu-iotests-Test-commit-with-top-node-base-node.patch @@ -0,0 +1,125 @@ +From 2adafb92a0fab77bfa39b9ad24f9ebf34db4aaeb Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 11 Sep 2018 13:07:04 +0200 +Subject: [PATCH 02/49] qemu-iotests: Test commit with top-node/base-node + +RH-Author: Kevin Wolf +Message-id: <20180911130704.6641-3-kwolf@redhat.com> +Patchwork-id: 82115 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 2/2] qemu-iotests: Test commit with top-node/base-node +Bugzilla: 1624012 +RH-Acked-by: Max Reitz +RH-Acked-by: John Snow +RH-Acked-by: Fam Zheng + +This adds some tests for block-commit with the new options top-node and +base-node (taking node names) instead of top and base (taking file +names). + +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/040 | 52 ++++++++++++++++++++++++++++++++++++++++++++-- + tests/qemu-iotests/040.out | 4 ++-- + 2 files changed, 52 insertions(+), 4 deletions(-) + +diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040 +index 1beb5e6..1cb1cee 100755 +--- a/tests/qemu-iotests/040 ++++ b/tests/qemu-iotests/040 +@@ -57,9 +57,12 @@ class ImageCommitTestCase(iotests.QMPTestCase): + self.assert_no_active_block_jobs() + self.vm.shutdown() + +- def run_commit_test(self, top, base, need_ready=False): ++ def run_commit_test(self, top, base, need_ready=False, node_names=False): + self.assert_no_active_block_jobs() +- result = self.vm.qmp('block-commit', device='drive0', top=top, base=base) ++ if node_names: ++ result = self.vm.qmp('block-commit', device='drive0', top_node=top, base_node=base) ++ else: ++ result = self.vm.qmp('block-commit', device='drive0', top=top, base=base) + self.assert_qmp(result, 'return', {}) + self.wait_for_complete(need_ready) + +@@ -101,6 +104,11 @@ class TestSingleDrive(ImageCommitTestCase): + self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) + self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) + ++ def test_commit_node(self): ++ self.run_commit_test("mid", "base", node_names=True) ++ self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) ++ self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) ++ + def test_device_not_found(self): + result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % mid_img) + self.assert_qmp(result, 'error/class', 'DeviceNotFound') +@@ -123,6 +131,30 @@ class TestSingleDrive(ImageCommitTestCase): + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', 'Base \'badfile\' not found') + ++ def test_top_node_invalid(self): ++ self.assert_no_active_block_jobs() ++ result = self.vm.qmp('block-commit', device='drive0', top_node='badfile', base_node='base') ++ self.assert_qmp(result, 'error/class', 'GenericError') ++ self.assert_qmp(result, 'error/desc', "Cannot find device= nor node_name=badfile") ++ ++ def test_base_node_invalid(self): ++ self.assert_no_active_block_jobs() ++ result = self.vm.qmp('block-commit', device='drive0', top_node='mid', base_node='badfile') ++ self.assert_qmp(result, 'error/class', 'GenericError') ++ self.assert_qmp(result, 'error/desc', "Cannot find device= nor node_name=badfile") ++ ++ def test_top_path_and_node(self): ++ self.assert_no_active_block_jobs() ++ result = self.vm.qmp('block-commit', device='drive0', top_node='mid', base_node='base', top='%s' % mid_img) ++ self.assert_qmp(result, 'error/class', 'GenericError') ++ self.assert_qmp(result, 'error/desc', "'top-node' and 'top' are mutually exclusive") ++ ++ def test_base_path_and_node(self): ++ self.assert_no_active_block_jobs() ++ result = self.vm.qmp('block-commit', device='drive0', top_node='mid', base_node='base', base='%s' % backing_img) ++ self.assert_qmp(result, 'error/class', 'GenericError') ++ self.assert_qmp(result, 'error/desc', "'base-node' and 'base' are mutually exclusive") ++ + def test_top_is_active(self): + self.run_commit_test(test_img, backing_img, need_ready=True) + self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) +@@ -139,6 +171,22 @@ class TestSingleDrive(ImageCommitTestCase): + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % mid_img) + ++ def test_top_and_base_node_reversed(self): ++ self.assert_no_active_block_jobs() ++ result = self.vm.qmp('block-commit', device='drive0', top_node='base', base_node='top') ++ self.assert_qmp(result, 'error/class', 'GenericError') ++ self.assert_qmp(result, 'error/desc', "'top' is not in this backing file chain") ++ ++ def test_top_node_in_wrong_chain(self): ++ self.assert_no_active_block_jobs() ++ ++ result = self.vm.qmp('blockdev-add', driver='null-co', node_name='null') ++ self.assert_qmp(result, 'return', {}) ++ ++ result = self.vm.qmp('block-commit', device='drive0', top_node='null', base_node='base') ++ self.assert_qmp(result, 'error/class', 'GenericError') ++ self.assert_qmp(result, 'error/desc', "'null' is not in this backing file chain") ++ + # When the job is running on a BB that is automatically deleted on hot + # unplug, the job is cancelled when the device disappears + def test_hot_unplug(self): +diff --git a/tests/qemu-iotests/040.out b/tests/qemu-iotests/040.out +index e20a75c..802ffaa 100644 +--- a/tests/qemu-iotests/040.out ++++ b/tests/qemu-iotests/040.out +@@ -1,5 +1,5 @@ +-............................. ++........................................... + ---------------------------------------------------------------------- +-Ran 29 tests ++Ran 43 tests + + OK +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-iotests-Test-job-with-block-jobs.patch b/SOURCES/kvm-qemu-iotests-Test-job-with-block-jobs.patch new file mode 100644 index 0000000..a0ade44 --- /dev/null +++ b/SOURCES/kvm-qemu-iotests-Test-job-with-block-jobs.patch @@ -0,0 +1,589 @@ +From 5649220e08e99f848e013f1e0b432d6d2fc706fc Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:36 +0200 +Subject: [PATCH 67/89] qemu-iotests: Test job-* with block jobs + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-54-kwolf@redhat.com> +Patchwork-id: 81101 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 53/73] qemu-iotests: Test job-* with block jobs +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This adds a test case that tests the new job-* QMP commands with +mirror and backup block jobs. + +Signed-off-by: Kevin Wolf +(cherry picked from commit bdebdc712b06ba82e103d617c335830682cde242) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/219 | 209 +++++++++++++++++++++++++++++ + tests/qemu-iotests/219.out | 327 +++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/group | 1 + + 3 files changed, 537 insertions(+) + create mode 100755 tests/qemu-iotests/219 + create mode 100644 tests/qemu-iotests/219.out + +diff --git a/tests/qemu-iotests/219 b/tests/qemu-iotests/219 +new file mode 100755 +index 0000000..898a26e +--- /dev/null ++++ b/tests/qemu-iotests/219 +@@ -0,0 +1,209 @@ ++#!/usr/bin/env python ++# ++# Copyright (C) 2018 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 . ++# ++# Creator/Owner: Kevin Wolf ++# ++# Check using the job-* QMP commands with block jobs ++ ++import iotests ++ ++iotests.verify_image_format(supported_fmts=['qcow2']) ++ ++def pause_wait(vm, job_id): ++ with iotests.Timeout(3, "Timeout waiting for job to pause"): ++ while True: ++ result = vm.qmp('query-jobs') ++ for job in result['return']: ++ if job['id'] == job_id and job['status'] in ['paused', 'standby']: ++ return job ++ ++# Test that block-job-pause/resume and job-pause/resume can be mixed ++def test_pause_resume(vm): ++ for pause_cmd, pause_arg in [('block-job-pause', 'device'), ++ ('job-pause', 'id')]: ++ for resume_cmd, resume_arg in [('block-job-resume', 'device'), ++ ('job-resume', 'id')]: ++ iotests.log('=== Testing %s/%s ===' % (pause_cmd, resume_cmd)) ++ ++ iotests.log(vm.qmp(pause_cmd, **{pause_arg: 'job0'})) ++ pause_wait(vm, 'job0') ++ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) ++ iotests.log(vm.qmp('query-jobs')) ++ ++ iotests.log(vm.qmp(resume_cmd, **{resume_arg: 'job0'})) ++ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) ++ iotests.log(vm.qmp('query-jobs')) ++ ++def test_job_lifecycle(vm, job, job_args, has_ready=False): ++ iotests.log('') ++ iotests.log('') ++ iotests.log('Starting block job: %s (auto-finalize: %s; auto-dismiss: %s)' % ++ (job, ++ job_args.get('auto-finalize', True), ++ job_args.get('auto-dismiss', True))) ++ iotests.log(vm.qmp(job, job_id='job0', **job_args)) ++ ++ # Depending on the storage, the first request may or may not have completed ++ # yet, so filter out the progress. Later query-job calls don't need the ++ # filtering because the progress is made deterministic by the block job ++ # speed ++ result = vm.qmp('query-jobs') ++ for j in result['return']: ++ del j['current-progress'] ++ iotests.log(result) ++ ++ # undefined -> created -> running ++ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) ++ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) ++ ++ # RUNNING state: ++ # pause/resume should work, complete/finalize/dismiss should error out ++ iotests.log('') ++ iotests.log('Pause/resume in RUNNING') ++ test_pause_resume(vm) ++ ++ iotests.log(vm.qmp('job-complete', id='job0')) ++ iotests.log(vm.qmp('job-finalize', id='job0')) ++ iotests.log(vm.qmp('job-dismiss', id='job0')) ++ ++ iotests.log(vm.qmp('block-job-complete', device='job0')) ++ iotests.log(vm.qmp('block-job-finalize', id='job0')) ++ iotests.log(vm.qmp('block-job-dismiss', id='job0')) ++ ++ # Let the job complete (or transition to READY if it supports that) ++ iotests.log(vm.qmp('block-job-set-speed', device='job0', speed=0)) ++ if has_ready: ++ iotests.log('') ++ iotests.log('Waiting for READY state...') ++ vm.event_wait('BLOCK_JOB_READY') ++ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) ++ iotests.log(vm.qmp('query-jobs')) ++ ++ # READY state: ++ # pause/resume/complete should work, finalize/dismiss should error out ++ iotests.log('') ++ iotests.log('Pause/resume in READY') ++ test_pause_resume(vm) ++ ++ iotests.log(vm.qmp('job-finalize', id='job0')) ++ iotests.log(vm.qmp('job-dismiss', id='job0')) ++ ++ iotests.log(vm.qmp('block-job-finalize', id='job0')) ++ iotests.log(vm.qmp('block-job-dismiss', id='job0')) ++ ++ # Transition to WAITING ++ iotests.log(vm.qmp('job-complete', id='job0')) ++ ++ # Move to WAITING and PENDING state ++ iotests.log('') ++ iotests.log('Waiting for PENDING state...') ++ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) ++ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) ++ ++ if not job_args.get('auto-finalize', True): ++ # PENDING state: ++ # finalize should work, pause/complete/dismiss should error out ++ iotests.log(vm.qmp('query-jobs')) ++ ++ iotests.log(vm.qmp('job-pause', id='job0')) ++ iotests.log(vm.qmp('job-complete', id='job0')) ++ iotests.log(vm.qmp('job-dismiss', id='job0')) ++ ++ iotests.log(vm.qmp('block-job-pause', device='job0')) ++ iotests.log(vm.qmp('block-job-complete', device='job0')) ++ iotests.log(vm.qmp('block-job-dismiss', id='job0')) ++ ++ # Transition to CONCLUDED ++ iotests.log(vm.qmp('job-finalize', id='job0')) ++ ++ ++ # Move to CONCLUDED state ++ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) ++ ++ if not job_args.get('auto-dismiss', True): ++ # CONCLUDED state: ++ # dismiss should work, pause/complete/finalize should error out ++ iotests.log(vm.qmp('query-jobs')) ++ ++ iotests.log(vm.qmp('job-pause', id='job0')) ++ iotests.log(vm.qmp('job-complete', id='job0')) ++ iotests.log(vm.qmp('job-finalize', id='job0')) ++ ++ iotests.log(vm.qmp('block-job-pause', device='job0')) ++ iotests.log(vm.qmp('block-job-complete', device='job0')) ++ iotests.log(vm.qmp('block-job-finalize', id='job0')) ++ ++ # Transition to NULL ++ iotests.log(vm.qmp('job-dismiss', id='job0')) ++ ++ # Move to NULL state ++ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) ++ iotests.log(vm.qmp('query-jobs')) ++ ++ ++with iotests.FilePath('disk.img') as disk_path, \ ++ iotests.FilePath('copy.img') as copy_path, \ ++ iotests.VM() as vm: ++ ++ img_size = '4M' ++ iotests.qemu_img_create('-f', iotests.imgfmt, disk_path, img_size) ++ iotests.qemu_io('-c', 'write 0 %s' % (img_size), ++ '-f', iotests.imgfmt, disk_path) ++ ++ iotests.log('Launching VM...') ++ vm.add_blockdev(vm.qmp_to_opts({ ++ 'driver': iotests.imgfmt, ++ 'node-name': 'drive0-node', ++ 'file': { ++ 'driver': 'file', ++ 'filename': disk_path, ++ }, ++ })) ++ vm.launch() ++ ++ # In order to keep things deterministic (especially progress in query-job, ++ # but related to this also automatic state transitions like job ++ # completion), but still get pause points often enough to avoid making this ++ # test very slow, it's important to have the right ratio between speed and ++ # buf_size. ++ # ++ # For backup, buf_size is hard-coded to the source image cluster size (64k), ++ # so we'll pick the same for mirror. The slice time, i.e. the granularity ++ # of the rate limiting is 100ms. With a speed of 256k per second, we can ++ # get four pause points per second. This gives us 250ms per iteration, ++ # which should be enough to stay deterministic. ++ ++ test_job_lifecycle(vm, 'drive-mirror', has_ready=True, job_args={ ++ 'device': 'drive0-node', ++ 'target': copy_path, ++ 'sync': 'full', ++ 'speed': 262144, ++ 'buf_size': 65536, ++ }) ++ ++ for auto_finalize in [True, False]: ++ for auto_dismiss in [True, False]: ++ test_job_lifecycle(vm, 'drive-backup', job_args={ ++ 'device': 'drive0-node', ++ 'target': copy_path, ++ 'sync': 'full', ++ 'speed': 262144, ++ 'auto-finalize': auto_finalize, ++ 'auto-dismiss': auto_dismiss, ++ }) ++ ++ vm.shutdown() +diff --git a/tests/qemu-iotests/219.out b/tests/qemu-iotests/219.out +new file mode 100644 +index 0000000..346801b +--- /dev/null ++++ b/tests/qemu-iotests/219.out +@@ -0,0 +1,327 @@ ++Launching VM... ++ ++ ++Starting block job: drive-mirror (auto-finalize: True; auto-dismiss: True) ++{u'return': {}} ++{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++ ++Pause/resume in RUNNING ++=== Testing block-job-pause/block-job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++=== Testing block-job-pause/job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++=== Testing job-pause/block-job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++=== Testing job-pause/job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} ++{u'return': {}} ++ ++Waiting for READY state... ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++ ++Pause/resume in READY ++=== Testing block-job-pause/block-job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++=== Testing block-job-pause/job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++=== Testing job-pause/block-job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++=== Testing job-pause/job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}} ++{u'return': {}} ++ ++Waiting for PENDING state... ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': []} ++ ++ ++Starting block job: drive-backup (auto-finalize: True; auto-dismiss: True) ++{u'return': {}} ++{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++ ++Pause/resume in RUNNING ++=== Testing block-job-pause/block-job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++=== Testing block-job-pause/job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++=== Testing job-pause/block-job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++=== Testing job-pause/job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} ++{u'return': {}} ++ ++Waiting for PENDING state... ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': []} ++ ++ ++Starting block job: drive-backup (auto-finalize: True; auto-dismiss: False) ++{u'return': {}} ++{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++ ++Pause/resume in RUNNING ++=== Testing block-job-pause/block-job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++=== Testing block-job-pause/job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++=== Testing job-pause/block-job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++=== Testing job-pause/job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} ++{u'return': {}} ++ ++Waiting for PENDING state... ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'concluded', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': []} ++ ++ ++Starting block job: drive-backup (auto-finalize: False; auto-dismiss: True) ++{u'return': {}} ++{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++ ++Pause/resume in RUNNING ++=== Testing block-job-pause/block-job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++=== Testing block-job-pause/job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++=== Testing job-pause/block-job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++=== Testing job-pause/job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} ++{u'return': {}} ++ ++Waiting for PENDING state... ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'pending', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': []} ++ ++ ++Starting block job: drive-backup (auto-finalize: False; auto-dismiss: False) ++{u'return': {}} ++{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++ ++Pause/resume in RUNNING ++=== Testing block-job-pause/block-job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++=== Testing block-job-pause/job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++=== Testing job-pause/block-job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++=== Testing job-pause/job-resume === ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} ++{u'return': {}} ++ ++Waiting for PENDING state... ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'pending', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': [{u'status': u'concluded', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} ++{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} ++{u'return': {}} ++{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} ++{u'return': []} +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index cd7bc29..5c55adc 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -217,3 +217,4 @@ + 216 rw auto quick + 217 rw auto quick + 218 rw auto quick ++219 rw auto +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-iotests-Test-qcow2-not-leaking-clusters-on-writ.patch b/SOURCES/kvm-qemu-iotests-Test-qcow2-not-leaking-clusters-on-writ.patch new file mode 100644 index 0000000..d70e82a --- /dev/null +++ b/SOURCES/kvm-qemu-iotests-Test-qcow2-not-leaking-clusters-on-writ.patch @@ -0,0 +1,95 @@ +From 52970228a0ba4956ed98cb1ae370b16dd740c9e4 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Mon, 2 Jul 2018 15:40:08 +0200 +Subject: [PATCH 55/57] qemu-iotests: Test qcow2 not leaking clusters on write + error + +RH-Author: Kevin Wolf +Message-id: <20180702154008.15533-4-kwolf@redhat.com> +Patchwork-id: 81187 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 3/3] qemu-iotests: Test qcow2 not leaking clusters on write error +Bugzilla: 1528541 +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng +RH-Acked-by: Stefan Hajnoczi + +This adds a test for a temporary write failure, which simulates the +situation after werror=stop/enospc has stopped the VM. We shouldn't +leave leaked clusters behind in such cases. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit ae376c6255d0eee4b3c4d60acc4679aa99c0d2c8) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/026 | 17 +++++++++++++++++ + tests/qemu-iotests/026.out | 8 ++++++++ + tests/qemu-iotests/026.out.nocache | 8 ++++++++ + 3 files changed, 33 insertions(+) + +diff --git a/tests/qemu-iotests/026 b/tests/qemu-iotests/026 +index 7fadfba..582d254 100755 +--- a/tests/qemu-iotests/026 ++++ b/tests/qemu-iotests/026 +@@ -200,6 +200,23 @@ done + done + done + ++echo ++echo === Avoid cluster leaks after temporary failure === ++echo ++ ++cat > "$TEST_DIR/blkdebug.conf" < +Date: Tue, 7 Aug 2018 14:04:01 +0200 +Subject: [PATCH 08/13] qemu-iotests: Test query-blockstats with -drive and + -blockdev + +RH-Author: Kevin Wolf +Message-id: <20180807140401.23995-4-kwolf@redhat.com> +Patchwork-id: 81665 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 3/3] qemu-iotests: Test query-blockstats with -drive and -blockdev +Bugzilla: 1612114 +RH-Acked-by: John Snow +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Markus Armbruster + +Make sure that query-blockstats returns information for every +BlockBackend that is named or attached to a device model (or both). + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +Signed-off-by: Kevin Wolf +(cherry picked from commit 1239ac241fe170bb9fcf0be74bfff04f6f1c2560) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/227 | 101 ++++++++++++++++++++++ + tests/qemu-iotests/227.out | 205 +++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/group | 1 + + 3 files changed, 307 insertions(+) + create mode 100755 tests/qemu-iotests/227 + create mode 100644 tests/qemu-iotests/227.out + +diff --git a/tests/qemu-iotests/227 b/tests/qemu-iotests/227 +new file mode 100755 +index 0000000..9a5f7f9 +--- /dev/null ++++ b/tests/qemu-iotests/227 +@@ -0,0 +1,101 @@ ++#!/bin/bash ++# ++# Test query-blockstats with different ways to create a BB ++# ++# Copyright (C) 2018 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 . ++# ++ ++# creator ++owner=kwolf@redhat.com ++ ++seq=$(basename $0) ++echo "QA output created by $seq" ++ ++here=$PWD ++status=1 # failure is the default! ++ ++_cleanup() ++{ ++ _cleanup_test_img ++} ++trap "_cleanup; exit \$status" 0 1 2 3 15 ++ ++# get standard environment, filters and checks ++. ./common.rc ++. ./common.filter ++ ++_supported_fmt generic ++_supported_proto file ++_supported_os Linux ++ ++function do_run_qemu() ++{ ++ echo Testing: "$@" ++ $QEMU -nographic -qmp-pretty stdio -serial none "$@" ++ echo ++} ++ ++function run_qemu() ++{ ++ do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ ++ | _filter_qemu | _filter_imgfmt \ ++ | _filter_generated_node_ids ++} ++ ++echo ++echo '=== blockstats with -drive if=virtio ===' ++echo ++ ++run_qemu -drive driver=null-co,if=virtio < +Date: Fri, 15 Mar 2019 18:10:03 +0100 +Subject: [PATCH 007/163] qemu-iotests: Test snapshot=on with nonexistent + TMPDIR + +RH-Author: Kevin Wolf +Message-id: <20190315181010.14964-8-kwolf@redhat.com> +Patchwork-id: 84884 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 07/14] qemu-iotests: Test snapshot=on with nonexistent TMPDIR +Bugzilla: 1685989 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Alberto Garcia + +We just fixed a bug that was causing a use-after-free when QEMU was +unable to create a temporary snapshot. This is a test case for this +scenario. + +Signed-off-by: Alberto Garcia +Signed-off-by: Kevin Wolf +(cherry picked from commit 6a7014ef22ad3cf9110ca0e178f73a67a6483e00) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/051 | 3 +++ + tests/qemu-iotests/051.out | 3 +++ + tests/qemu-iotests/051.pc.out | 3 +++ + 3 files changed, 9 insertions(+) + +diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 +index c5cc0ee..4899f84 100755 +--- a/tests/qemu-iotests/051 ++++ b/tests/qemu-iotests/051 +@@ -354,6 +354,9 @@ printf %b "qemu-io $device_id \"write -P 0x33 0 4k\"\ncommit $device_id\n" | + + $QEMU_IO -c "read -P 0x33 0 4k" "$TEST_IMG" | _filter_qemu_io + ++# Using snapshot=on with a non-existent TMPDIR ++TMPDIR=/nonexistent run_qemu -drive driver=null-co,snapshot=on ++ + # success, all done + echo "*** done" + rm -f $seq.full +diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out +index b727350..793af2a 100644 +--- a/tests/qemu-iotests/051.out ++++ b/tests/qemu-iotests/051.out +@@ -455,4 +455,7 @@ wrote 4096/4096 bytes at offset 0 + + read 4096/4096 bytes at offset 0 + 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++Testing: -drive driver=null-co,snapshot=on ++QEMU_PROG: -drive driver=null-co,snapshot=on: Could not get temporary filename: No such file or directory ++ + *** done +diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out +index e9257fe..ca64eda 100644 +--- a/tests/qemu-iotests/051.pc.out ++++ b/tests/qemu-iotests/051.pc.out +@@ -527,4 +527,7 @@ wrote 4096/4096 bytes at offset 0 + + read 4096/4096 bytes at offset 0 + 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++Testing: -drive driver=null-co,snapshot=on ++QEMU_PROG: -drive driver=null-co,snapshot=on: Could not get temporary filename: No such file or directory ++ + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-iotests-Update-026.out.nocache-reference-output.patch b/SOURCES/kvm-qemu-iotests-Update-026.out.nocache-reference-output.patch new file mode 100644 index 0000000..4bcbce7 --- /dev/null +++ b/SOURCES/kvm-qemu-iotests-Update-026.out.nocache-reference-output.patch @@ -0,0 +1,60 @@ +From 0c2dc6236b6a86a87c26994772e32fbb979e48a3 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Mon, 2 Jul 2018 15:40:06 +0200 +Subject: [PATCH 53/57] qemu-iotests: Update 026.out.nocache reference output + +RH-Author: Kevin Wolf +Message-id: <20180702154008.15533-2-kwolf@redhat.com> +Patchwork-id: 81186 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/3] qemu-iotests: Update 026.out.nocache reference output +Bugzilla: 1528541 +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng +RH-Acked-by: Stefan Hajnoczi + +Commit abf754fe406 updated 026.out, but forgot to also update +026.out.nocache. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +(cherry picked from commit 93a3642efcadf4ad6045ccea38a05ff5297dfe26) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/026.out.nocache | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/tests/qemu-iotests/026.out.nocache b/tests/qemu-iotests/026.out.nocache +index ea2e166..650ccd8 100644 +--- a/tests/qemu-iotests/026.out.nocache ++++ b/tests/qemu-iotests/026.out.nocache +@@ -541,7 +541,7 @@ Failed to flush the L2 table cache: No space left on device + Failed to flush the refcount block cache: No space left on device + write failed: No space left on device + +-11 leaked clusters were found on the image. ++10 leaked clusters were found on the image. + This means waste of disk space, but no harm to data. + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +@@ -569,7 +569,7 @@ Failed to flush the L2 table cache: No space left on device + Failed to flush the refcount block cache: No space left on device + write failed: No space left on device + +-11 leaked clusters were found on the image. ++10 leaked clusters were found on the image. + This means waste of disk space, but no harm to data. + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +@@ -597,7 +597,7 @@ Failed to flush the L2 table cache: No space left on device + Failed to flush the refcount block cache: No space left on device + write failed: No space left on device + +-11 leaked clusters were found on the image. ++10 leaked clusters were found on the image. + This means waste of disk space, but no harm to data. + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-iotests-convert-pwd-and-pwd-to-PWD.patch b/SOURCES/kvm-qemu-iotests-convert-pwd-and-pwd-to-PWD.patch new file mode 100644 index 0000000..b4fcc19 --- /dev/null +++ b/SOURCES/kvm-qemu-iotests-convert-pwd-and-pwd-to-PWD.patch @@ -0,0 +1,135 @@ +From 2da89bf5ccc55e0535fecc5b507bacac5c6ad3b4 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:27 +0100 +Subject: [PATCH 060/163] qemu-iotests: convert `pwd` and $(pwd) to $PWD + +RH-Author: John Snow +Message-id: <20190322032241.8111-15-jsnow@redhat.com> +Patchwork-id: 85097 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 14/28] qemu-iotests: convert `pwd` and $(pwd) to $PWD +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Mao Zhongyi + +POSIX requires $PWD to be reliable, and we expect all +shells used by qemu scripts to be relatively close to +POSIX. Thus, it is smarter to avoid forking the pwd +executable for something that is already available in +the environment. + +So replace it with the following: + +sed -i 's/\(`pwd`\|\$(pwd)\)/$PWD/g' $(git grep -l pwd) + +Then delete a pointless line assigning PWD to itself. + +Cc: kwolf@redhat.com +Cc: mreitz@redhat.com +Cc: eblake@redhat.com +Suggested-by: Eric Blake +Signed-off-by: Mao Zhongyi +Message-Id: <20181024094051.4470-2-maozhongyi@cmss.chinamobile.com> +Reviewed-by: Eric Blake +[eblake: touch up commit message, reorder series, tweak a couple more files] +Signed-off-by: Eric Blake +(cherry picked from commit e8d81a61e1b9e28267164f751dee5b9b59444e71) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + configure | 2 +- + scripts/coccinelle/tcg_gen_extract.cocci | 2 +- + tests/check-block.sh | 6 +++--- + tests/qemu-iotests/check | 2 +- + tests/qemu-iotests/common.config | 2 -- + tests/qemu-iotests/common.rc | 2 +- + 6 files changed, 7 insertions(+), 9 deletions(-) + +diff --git a/configure b/configure +index f9c8365..285fd47 100755 +--- a/configure ++++ b/configure +@@ -821,7 +821,7 @@ Linux) + vhost_crypto="yes" + vhost_scsi="yes" + vhost_vsock="yes" +- QEMU_INCLUDES="-I\$(SRC_PATH)/linux-headers -I$(pwd)/linux-headers $QEMU_INCLUDES" ++ QEMU_INCLUDES="-I\$(SRC_PATH)/linux-headers -I$PWD/linux-headers $QEMU_INCLUDES" + supported_os="yes" + ;; + esac +diff --git a/scripts/coccinelle/tcg_gen_extract.cocci b/scripts/coccinelle/tcg_gen_extract.cocci +index 81e66a3..c10c863 100644 +--- a/scripts/coccinelle/tcg_gen_extract.cocci ++++ b/scripts/coccinelle/tcg_gen_extract.cocci +@@ -17,7 +17,7 @@ + // --keep-comments --in-place \ + // --use-gitgrep --dir target + // +-// $ docker run --rm -v `pwd`:`pwd` -w `pwd` philmd/coccinelle \ ++// $ docker run --rm -v $PWD:$PWD -w $PWD philmd/coccinelle \ + // --macro-file scripts/cocci-macro-file.h \ + // --sp-file scripts/coccinelle/tcg_gen_extract.cocci \ + // --keep-comments --in-place \ +diff --git a/tests/check-block.sh b/tests/check-block.sh +index c3de378..f3d12fd 100755 +--- a/tests/check-block.sh ++++ b/tests/check-block.sh +@@ -5,9 +5,9 @@ if [ "$#" -ne 0 ]; then + FORMAT_LIST="$@" + fi + +-export QEMU_PROG="$(pwd)/x86_64-softmmu/qemu-system-x86_64" +-export QEMU_IMG_PROG="$(pwd)/qemu-img" +-export QEMU_IO_PROG="$(pwd)/qemu-io" ++export QEMU_PROG="$PWD/x86_64-softmmu/qemu-system-x86_64" ++export QEMU_IMG_PROG="$PWD/qemu-img" ++export QEMU_IO_PROG="$PWD/qemu-io" + + if [ ! -x $QEMU_PROG ]; then + echo "'make check-block' requires qemu-system-x86_64" +diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check +index aa94c6c..b377132 100755 +--- a/tests/qemu-iotests/check ++++ b/tests/qemu-iotests/check +@@ -99,7 +99,7 @@ set_prog_path() + } + + if [ -z "$TEST_DIR" ]; then +- TEST_DIR=`pwd`/scratch ++ TEST_DIR=$PWD/scratch + fi + + if [ ! -e "$TEST_DIR" ]; then +diff --git a/tests/qemu-iotests/common.config b/tests/qemu-iotests/common.config +index 102aa68..3cda0fe 100644 +--- a/tests/qemu-iotests/common.config ++++ b/tests/qemu-iotests/common.config +@@ -25,8 +25,6 @@ HOSTOS=`uname -s` + arch=`uname -m` + [[ "$arch" =~ "ppc64" ]] && qemu_arch=ppc64 || qemu_arch="$arch" + +-export PWD=`pwd` +- + # make sure we have a standard umask + umask 022 + +diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc +index 1e567b0..bb03a4b 100644 +--- a/tests/qemu-iotests/common.rc ++++ b/tests/qemu-iotests/common.rc +@@ -159,7 +159,7 @@ fi + ORIG_TEST_IMG="$TEST_IMG" + + if [ -z "$TEST_DIR" ]; then +- TEST_DIR=`pwd`/scratch ++ TEST_DIR=$PWD/scratch + fi + + QEMU_TEST_DIR="${TEST_DIR}" +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-iotests-iotests.py-helper-for-non-file-protocol.patch b/SOURCES/kvm-qemu-iotests-iotests.py-helper-for-non-file-protocol.patch new file mode 100644 index 0000000..5b314cf --- /dev/null +++ b/SOURCES/kvm-qemu-iotests-iotests.py-helper-for-non-file-protocol.patch @@ -0,0 +1,65 @@ +From a50830277403643e090bec4bfe7cf77d693f90e8 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:45 +0200 +Subject: [PATCH 76/89] qemu-iotests: iotests.py helper for non-file protocols + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-63-kwolf@redhat.com> +Patchwork-id: 81081 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 62/73] qemu-iotests: iotests.py helper for non-file protocols +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This adds two helper functions that are useful for test cases that make +use of a non-file protocol (specifically ssh). + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: Jeff Cody +(cherry picked from commit 5a259e868b308564de9f2bfaef0606074a4f9028) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/iotests.py | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index 390da6b..9f1759d 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -312,6 +312,13 @@ def file_path(*names): + + return paths[0] if len(paths) == 1 else paths + ++def remote_filename(path): ++ if imgproto == 'file': ++ return path ++ elif imgproto == 'ssh': ++ return "ssh://127.0.0.1%s" % (path) ++ else: ++ raise Exception("Protocol %s not supported" % (imgproto)) + + class VM(qtest.QEMUQtestMachine): + '''A QEMU VM''' +@@ -610,6 +617,16 @@ def verify_image_format(supported_fmts=[], unsupported_fmts=[]): + if not_sup or (imgfmt in unsupported_fmts): + notrun('not suitable for this image format: %s' % imgfmt) + ++def verify_protocol(supported=[], unsupported=[]): ++ assert not (supported and unsupported) ++ ++ if 'generic' in supported: ++ return ++ ++ not_sup = supported and (imgproto not in supported) ++ if not_sup or (imgproto in unsupported): ++ notrun('not suitable for this protocol: %s' % imgproto) ++ + def verify_platform(supported_oses=['linux']): + if True not in [sys.platform.startswith(x) for x in supported_oses]: + notrun('not suitable for this OS: %s' % sys.platform) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-iotests-reduce-chance-of-races-in-185.patch b/SOURCES/kvm-qemu-iotests-reduce-chance-of-races-in-185.patch new file mode 100644 index 0000000..6fec60a --- /dev/null +++ b/SOURCES/kvm-qemu-iotests-reduce-chance-of-races-in-185.patch @@ -0,0 +1,90 @@ +From 03240b36532d828a71fd3c9fee7b1ed6e8faa7a5 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:47:45 +0200 +Subject: [PATCH 16/89] qemu-iotests: reduce chance of races in 185 + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-3-kwolf@redhat.com> +Patchwork-id: 81061 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 02/73] qemu-iotests: reduce chance of races in 185 +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +From: Stefan Hajnoczi + +Commit 8565c3ab537e78f3e69977ec2c609dc9417a806e ("qemu-iotests: fix +185") identified a race condition in a sub-test. + +Similar issues also affect the other sub-tests. If disk I/O completes +quickly, it races with the QMP 'quit' command. This causes spurious +test failures because QMP events are emitted in an unpredictable order. + +This test relies on QEMU internals and there is no QMP API for getting +deterministic behavior needed to make this test 100% reliable. At the +same time, the test is useful and it would be a shame to remove it. + +Add sleep 0.5 to reduce the chance of races. This is not a real fix but +appears to reduce spurious failures in practice. + +Cc: Vladimir Sementsov-Ogievskiy +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Eric Blake +Message-id: 20180508135436.30140-2-stefanha@redhat.com +Reviewed-by: Jeff Cody +Signed-off-by: Jeff Cody +(cherry picked from commit ddf2d98a94c8a98a661a217fb629cfd15f4dcec7) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/185 | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185 +index 298d88d..975404c 100755 +--- a/tests/qemu-iotests/185 ++++ b/tests/qemu-iotests/185 +@@ -118,6 +118,9 @@ _send_qemu_cmd $h \ + 'speed': 65536 } }" \ + "return" + ++# If we don't sleep here 'quit' command races with disk I/O ++sleep 0.5 ++ + _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" + wait=1 _cleanup_qemu + +@@ -137,6 +140,9 @@ _send_qemu_cmd $h \ + 'speed': 65536 } }" \ + "return" + ++# If we don't sleep here 'quit' command races with disk I/O ++sleep 0.5 ++ + _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" + wait=1 _cleanup_qemu + +@@ -183,6 +189,9 @@ _send_qemu_cmd $h \ + 'speed': 65536 } }" \ + "return" + ++# If we don't sleep here 'quit' command races with disk I/O ++sleep 0.5 ++ + _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" + wait=1 _cleanup_qemu + +@@ -201,6 +210,9 @@ _send_qemu_cmd $h \ + 'speed': 65536 } }" \ + "return" + ++# If we don't sleep here 'quit' command races with disk I/O ++sleep 0.5 ++ + _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" + wait=1 _cleanup_qemu + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-iotests-remove-unused-variable-here.patch b/SOURCES/kvm-qemu-iotests-remove-unused-variable-here.patch new file mode 100644 index 0000000..8ae8120 --- /dev/null +++ b/SOURCES/kvm-qemu-iotests-remove-unused-variable-here.patch @@ -0,0 +1,2242 @@ +From 7a29e3639569a4fd765bf293b40caa465786fddc Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:26 +0100 +Subject: [PATCH 059/163] qemu-iotests: remove unused variable 'here' + +RH-Author: John Snow +Message-id: <20190322032241.8111-14-jsnow@redhat.com> +Patchwork-id: 85101 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 13/28] qemu-iotests: remove unused variable 'here' +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Mao Zhongyi + +Running +git grep '\$here' tests/qemu-iotests + +has 0 hits, which means we are setting a variable that has +no use. It appears that commit e8f8624d removed the last +use. So execute the following cmd to remove all of +the 'here=...' lines as dead code. + +sed -i '/^here=/d' $(git grep -l '^here=' tests/qemu-iotests) + +Cc: kwolf@redhat.com +Cc: mreitz@redhat.com +Cc: eblake@redhat.com +Suggested-by: Eric Blake +Signed-off-by: Mao Zhongyi +Message-Id: <20181024094051.4470-3-maozhongyi@cmss.chinamobile.com> +Reviewed-by: Eric Blake +[eblake: touch up commit message, reorder series, rebase to master] +Signed-off-by: Eric Blake +(cherry picked from commit bf22957309369cf6f642e715ff6c470671920e7e) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/001 | 1 - + tests/qemu-iotests/002 | 1 - + tests/qemu-iotests/003 | 1 - + tests/qemu-iotests/004 | 1 - + tests/qemu-iotests/005 | 1 - + tests/qemu-iotests/007 | 1 - + tests/qemu-iotests/008 | 1 - + tests/qemu-iotests/009 | 1 - + tests/qemu-iotests/010 | 1 - + tests/qemu-iotests/011 | 1 - + tests/qemu-iotests/012 | 1 - + tests/qemu-iotests/013 | 1 - + tests/qemu-iotests/014 | 1 - + tests/qemu-iotests/015 | 1 - + tests/qemu-iotests/017 | 1 - + tests/qemu-iotests/018 | 1 - + tests/qemu-iotests/019 | 1 - + tests/qemu-iotests/020 | 1 - + tests/qemu-iotests/021 | 1 - + tests/qemu-iotests/022 | 1 - + tests/qemu-iotests/023 | 1 - + tests/qemu-iotests/024 | 1 - + tests/qemu-iotests/025 | 1 - + tests/qemu-iotests/026 | 1 - + tests/qemu-iotests/027 | 1 - + tests/qemu-iotests/028 | 1 - + tests/qemu-iotests/029 | 1 - + tests/qemu-iotests/031 | 1 - + tests/qemu-iotests/032 | 1 - + tests/qemu-iotests/033 | 1 - + tests/qemu-iotests/034 | 1 - + tests/qemu-iotests/035 | 1 - + tests/qemu-iotests/036 | 1 - + tests/qemu-iotests/037 | 1 - + tests/qemu-iotests/038 | 1 - + tests/qemu-iotests/039 | 1 - + tests/qemu-iotests/042 | 1 - + tests/qemu-iotests/043 | 1 - + tests/qemu-iotests/046 | 1 - + tests/qemu-iotests/047 | 1 - + tests/qemu-iotests/049 | 1 - + tests/qemu-iotests/050 | 1 - + tests/qemu-iotests/051 | 1 - + tests/qemu-iotests/052 | 1 - + tests/qemu-iotests/053 | 1 - + tests/qemu-iotests/054 | 1 - + tests/qemu-iotests/058 | 1 - + tests/qemu-iotests/059 | 1 - + tests/qemu-iotests/060 | 1 - + tests/qemu-iotests/061 | 1 - + tests/qemu-iotests/062 | 1 - + tests/qemu-iotests/063 | 1 - + tests/qemu-iotests/064 | 1 - + tests/qemu-iotests/066 | 1 - + tests/qemu-iotests/067 | 1 - + tests/qemu-iotests/068 | 1 - + tests/qemu-iotests/069 | 1 - + tests/qemu-iotests/070 | 1 - + tests/qemu-iotests/071 | 1 - + tests/qemu-iotests/072 | 1 - + tests/qemu-iotests/073 | 1 - + tests/qemu-iotests/075 | 1 - + tests/qemu-iotests/076 | 1 - + tests/qemu-iotests/077 | 1 - + tests/qemu-iotests/078 | 1 - + tests/qemu-iotests/079 | 1 - + tests/qemu-iotests/080 | 1 - + tests/qemu-iotests/081 | 1 - + tests/qemu-iotests/082 | 1 - + tests/qemu-iotests/083 | 1 - + tests/qemu-iotests/084 | 1 - + tests/qemu-iotests/085 | 1 - + tests/qemu-iotests/086 | 1 - + tests/qemu-iotests/087 | 1 - + tests/qemu-iotests/088 | 1 - + tests/qemu-iotests/089 | 1 - + tests/qemu-iotests/090 | 1 - + tests/qemu-iotests/091 | 1 - + tests/qemu-iotests/092 | 1 - + tests/qemu-iotests/094 | 1 - + tests/qemu-iotests/095 | 1 - + tests/qemu-iotests/097 | 1 - + tests/qemu-iotests/098 | 1 - + tests/qemu-iotests/099 | 1 - + tests/qemu-iotests/101 | 1 - + tests/qemu-iotests/102 | 1 - + tests/qemu-iotests/103 | 1 - + tests/qemu-iotests/104 | 1 - + tests/qemu-iotests/105 | 1 - + tests/qemu-iotests/106 | 1 - + tests/qemu-iotests/107 | 1 - + tests/qemu-iotests/108 | 1 - + tests/qemu-iotests/109 | 1 - + tests/qemu-iotests/110 | 1 - + tests/qemu-iotests/111 | 1 - + tests/qemu-iotests/112 | 1 - + tests/qemu-iotests/113 | 1 - + tests/qemu-iotests/114 | 1 - + tests/qemu-iotests/115 | 1 - + tests/qemu-iotests/116 | 1 - + tests/qemu-iotests/117 | 1 - + tests/qemu-iotests/119 | 1 - + tests/qemu-iotests/120 | 1 - + tests/qemu-iotests/121 | 1 - + tests/qemu-iotests/122 | 1 - + tests/qemu-iotests/123 | 1 - + tests/qemu-iotests/125 | 1 - + tests/qemu-iotests/126 | 1 - + tests/qemu-iotests/127 | 1 - + tests/qemu-iotests/128 | 1 - + tests/qemu-iotests/130 | 1 - + tests/qemu-iotests/131 | 1 - + tests/qemu-iotests/133 | 1 - + tests/qemu-iotests/134 | 1 - + tests/qemu-iotests/135 | 1 - + tests/qemu-iotests/137 | 1 - + tests/qemu-iotests/138 | 1 - + tests/qemu-iotests/140 | 1 - + tests/qemu-iotests/141 | 1 - + tests/qemu-iotests/142 | 1 - + tests/qemu-iotests/143 | 1 - + tests/qemu-iotests/144 | 1 - + tests/qemu-iotests/145 | 1 - + tests/qemu-iotests/146 | 1 - + tests/qemu-iotests/150 | 1 - + tests/qemu-iotests/153 | 1 - + tests/qemu-iotests/154 | 1 - + tests/qemu-iotests/156 | 1 - + tests/qemu-iotests/157 | 1 - + tests/qemu-iotests/158 | 1 - + tests/qemu-iotests/159 | 1 - + tests/qemu-iotests/160 | 1 - + tests/qemu-iotests/162 | 1 - + tests/qemu-iotests/170 | 1 - + tests/qemu-iotests/171 | 1 - + tests/qemu-iotests/172 | 1 - + tests/qemu-iotests/173 | 1 - + tests/qemu-iotests/174 | 1 - + tests/qemu-iotests/175 | 1 - + tests/qemu-iotests/176 | 1 - + tests/qemu-iotests/177 | 1 - + tests/qemu-iotests/178 | 1 - + tests/qemu-iotests/179 | 1 - + tests/qemu-iotests/181 | 1 - + tests/qemu-iotests/182 | 1 - + tests/qemu-iotests/183 | 1 - + tests/qemu-iotests/184 | 1 - + tests/qemu-iotests/185 | 1 - + tests/qemu-iotests/186 | 1 - + tests/qemu-iotests/187 | 1 - + tests/qemu-iotests/188 | 1 - + tests/qemu-iotests/189 | 1 - + tests/qemu-iotests/190 | 1 - + tests/qemu-iotests/191 | 1 - + tests/qemu-iotests/192 | 1 - + tests/qemu-iotests/195 | 1 - + tests/qemu-iotests/197 | 1 - + tests/qemu-iotests/198 | 1 - + tests/qemu-iotests/200 | 1 - + tests/qemu-iotests/204 | 1 - + tests/qemu-iotests/214 | 1 - + tests/qemu-iotests/215 | 1 - + tests/qemu-iotests/221 | 1 - + tests/qemu-iotests/223 | 1 - + tests/qemu-iotests/226 | 1 - + tests/qemu-iotests/227 | 1 - + tests/qemu-iotests/229 | 1 - + tests/qemu-iotests/231 | 1 - + tests/qemu-iotests/232 | 1 - + 169 files changed, 169 deletions(-) + +diff --git a/tests/qemu-iotests/001 b/tests/qemu-iotests/001 +index ffd14e2..55dcbb7 100755 +--- a/tests/qemu-iotests/001 ++++ b/tests/qemu-iotests/001 +@@ -24,7 +24,6 @@ owner=hch@lst.de + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/002 b/tests/qemu-iotests/002 +index d4f8e91..74572b4 100755 +--- a/tests/qemu-iotests/002 ++++ b/tests/qemu-iotests/002 +@@ -24,7 +24,6 @@ owner=hch@lst.de + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/003 b/tests/qemu-iotests/003 +index 19889b9..bf25955 100755 +--- a/tests/qemu-iotests/003 ++++ b/tests/qemu-iotests/003 +@@ -24,7 +24,6 @@ owner=hch@lst.de + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/004 b/tests/qemu-iotests/004 +index 6f2aa3d..841b15d 100755 +--- a/tests/qemu-iotests/004 ++++ b/tests/qemu-iotests/004 +@@ -24,7 +24,6 @@ owner=hch@lst.de + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/005 b/tests/qemu-iotests/005 +index 4447377..8aa4283 100755 +--- a/tests/qemu-iotests/005 ++++ b/tests/qemu-iotests/005 +@@ -27,7 +27,6 @@ owner=hch@lst.de + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/007 b/tests/qemu-iotests/007 +index fa543ee..b983022 100755 +--- a/tests/qemu-iotests/007 ++++ b/tests/qemu-iotests/007 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/008 b/tests/qemu-iotests/008 +index 8e89d74..8dfa10b 100755 +--- a/tests/qemu-iotests/008 ++++ b/tests/qemu-iotests/008 +@@ -24,7 +24,6 @@ owner=hch@lst.de + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/009 b/tests/qemu-iotests/009 +index 16e4475..73ae09d 100755 +--- a/tests/qemu-iotests/009 ++++ b/tests/qemu-iotests/009 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/010 b/tests/qemu-iotests/010 +index 151dac2..751aca9 100755 +--- a/tests/qemu-iotests/010 ++++ b/tests/qemu-iotests/010 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/011 b/tests/qemu-iotests/011 +index f8d044e..3590956 100755 +--- a/tests/qemu-iotests/011 ++++ b/tests/qemu-iotests/011 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/012 b/tests/qemu-iotests/012 +index 01a770d..de9a5fb 100755 +--- a/tests/qemu-iotests/012 ++++ b/tests/qemu-iotests/012 +@@ -26,7 +26,6 @@ owner=hch@lst.de + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/013 b/tests/qemu-iotests/013 +index d013f87..5e1efce 100755 +--- a/tests/qemu-iotests/013 ++++ b/tests/qemu-iotests/013 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/014 b/tests/qemu-iotests/014 +index 2ea79e8..9ade571 100755 +--- a/tests/qemu-iotests/014 ++++ b/tests/qemu-iotests/014 +@@ -26,7 +26,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/015 b/tests/qemu-iotests/015 +index aaf9c3f..21f7d42 100755 +--- a/tests/qemu-iotests/015 ++++ b/tests/qemu-iotests/015 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/017 b/tests/qemu-iotests/017 +index 4f9302d..1ac6f74 100755 +--- a/tests/qemu-iotests/017 ++++ b/tests/qemu-iotests/017 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/018 b/tests/qemu-iotests/018 +index 1d39d35..bba30a1 100755 +--- a/tests/qemu-iotests/018 ++++ b/tests/qemu-iotests/018 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/019 b/tests/qemu-iotests/019 +index 24a789a..8f911a7 100755 +--- a/tests/qemu-iotests/019 ++++ b/tests/qemu-iotests/019 +@@ -26,7 +26,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/020 b/tests/qemu-iotests/020 +index eac5080..6b972d0 100755 +--- a/tests/qemu-iotests/020 ++++ b/tests/qemu-iotests/020 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/021 b/tests/qemu-iotests/021 +index 11e8ed7..c15ebf9 100755 +--- a/tests/qemu-iotests/021 ++++ b/tests/qemu-iotests/021 +@@ -24,7 +24,6 @@ owner=hch@lst.de + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/022 b/tests/qemu-iotests/022 +index 2452a9f..44765c7 100755 +--- a/tests/qemu-iotests/022 ++++ b/tests/qemu-iotests/022 +@@ -26,7 +26,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/023 b/tests/qemu-iotests/023 +index 497ae1e..c8e1b9a 100755 +--- a/tests/qemu-iotests/023 ++++ b/tests/qemu-iotests/023 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/024 b/tests/qemu-iotests/024 +index 4071ed6..428b5c8 100755 +--- a/tests/qemu-iotests/024 ++++ b/tests/qemu-iotests/024 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/025 b/tests/qemu-iotests/025 +index 70dd5f1..fcd4d97 100755 +--- a/tests/qemu-iotests/025 ++++ b/tests/qemu-iotests/025 +@@ -24,7 +24,6 @@ owner=stefanha@linux.vnet.ibm.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/026 b/tests/qemu-iotests/026 +index 582d254..31276d9 100755 +--- a/tests/qemu-iotests/026 ++++ b/tests/qemu-iotests/026 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/027 b/tests/qemu-iotests/027 +index 08593da..2c46ae1 100755 +--- a/tests/qemu-iotests/027 ++++ b/tests/qemu-iotests/027 +@@ -24,7 +24,6 @@ owner=stefanha@linux.vnet.ibm.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/028 b/tests/qemu-iotests/028 +index 97a8869..a2a7c93 100755 +--- a/tests/qemu-iotests/028 ++++ b/tests/qemu-iotests/028 +@@ -27,7 +27,6 @@ owner=stefanha@linux.vnet.ibm.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/029 b/tests/qemu-iotests/029 +index 30bab24..51fd0d4 100755 +--- a/tests/qemu-iotests/029 ++++ b/tests/qemu-iotests/029 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/031 b/tests/qemu-iotests/031 +index 1e08abc..ac0dfae 100755 +--- a/tests/qemu-iotests/031 ++++ b/tests/qemu-iotests/031 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/032 b/tests/qemu-iotests/032 +index 24bcb52..3e86bb0 100755 +--- a/tests/qemu-iotests/032 ++++ b/tests/qemu-iotests/032 +@@ -26,7 +26,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/033 b/tests/qemu-iotests/033 +index ee8a133..46b9138 100755 +--- a/tests/qemu-iotests/033 ++++ b/tests/qemu-iotests/033 +@@ -24,7 +24,6 @@ owner=pbonzini@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/034 b/tests/qemu-iotests/034 +index 1b28bda..62812cd 100755 +--- a/tests/qemu-iotests/034 ++++ b/tests/qemu-iotests/034 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/035 b/tests/qemu-iotests/035 +index efc38e4..a5716ca 100755 +--- a/tests/qemu-iotests/035 ++++ b/tests/qemu-iotests/035 +@@ -25,7 +25,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/036 b/tests/qemu-iotests/036 +index ce638d6..4e76602 100755 +--- a/tests/qemu-iotests/036 ++++ b/tests/qemu-iotests/036 +@@ -27,7 +27,6 @@ owner=stefanha@linux.vnet.ibm.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/037 b/tests/qemu-iotests/037 +index c476b82..2e43b19 100755 +--- a/tests/qemu-iotests/037 ++++ b/tests/qemu-iotests/037 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/038 b/tests/qemu-iotests/038 +index d99a150..4e03976 100755 +--- a/tests/qemu-iotests/038 ++++ b/tests/qemu-iotests/038 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039 +index 1f48339..b3c344c 100755 +--- a/tests/qemu-iotests/039 ++++ b/tests/qemu-iotests/039 +@@ -27,7 +27,6 @@ owner=stefanha@linux.vnet.ibm.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/042 b/tests/qemu-iotests/042 +index a53e7cb..beaa339 100755 +--- a/tests/qemu-iotests/042 ++++ b/tests/qemu-iotests/042 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/043 b/tests/qemu-iotests/043 +index 1c6c22d..fc9005b 100755 +--- a/tests/qemu-iotests/043 ++++ b/tests/qemu-iotests/043 +@@ -24,7 +24,6 @@ owner=stefanha@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/046 b/tests/qemu-iotests/046 +index f2ebecf..01c0de6 100755 +--- a/tests/qemu-iotests/046 ++++ b/tests/qemu-iotests/046 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/047 b/tests/qemu-iotests/047 +index 1b8f3d4..c168373 100755 +--- a/tests/qemu-iotests/047 ++++ b/tests/qemu-iotests/047 +@@ -25,7 +25,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/049 b/tests/qemu-iotests/049 +index df35b6d..4d0ad5c 100755 +--- a/tests/qemu-iotests/049 ++++ b/tests/qemu-iotests/049 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/050 b/tests/qemu-iotests/050 +index 03b4a5d..963a0db 100755 +--- a/tests/qemu-iotests/050 ++++ b/tests/qemu-iotests/050 +@@ -24,7 +24,6 @@ owner=pbonzini@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 +index 23f9678..4f20265 100755 +--- a/tests/qemu-iotests/051 ++++ b/tests/qemu-iotests/051 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/052 b/tests/qemu-iotests/052 +index 842eace..b992adf 100755 +--- a/tests/qemu-iotests/052 ++++ b/tests/qemu-iotests/052 +@@ -24,7 +24,6 @@ owner=stefanha@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/053 b/tests/qemu-iotests/053 +index 2a04f5f..afa109c 100755 +--- a/tests/qemu-iotests/053 ++++ b/tests/qemu-iotests/053 +@@ -24,7 +24,6 @@ owner=stefanha@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/054 b/tests/qemu-iotests/054 +index bf47ef9..cf88a7c 100755 +--- a/tests/qemu-iotests/054 ++++ b/tests/qemu-iotests/054 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/058 b/tests/qemu-iotests/058 +index 5eb8784..0d741a7 100755 +--- a/tests/qemu-iotests/058 ++++ b/tests/qemu-iotests/058 +@@ -26,7 +26,6 @@ owner=xiawenc@linux.vnet.ibm.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + nbd_unix_socket=$TEST_DIR/test_qemu_nbd_socket +diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059 +index 530bbbe..54d5567 100755 +--- a/tests/qemu-iotests/059 ++++ b/tests/qemu-iotests/059 +@@ -24,7 +24,6 @@ owner=famz@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/060 b/tests/qemu-iotests/060 +index 7bdf609..890617b 100755 +--- a/tests/qemu-iotests/060 ++++ b/tests/qemu-iotests/060 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061 +index 911b6f2..1a50163 100755 +--- a/tests/qemu-iotests/061 ++++ b/tests/qemu-iotests/061 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/062 b/tests/qemu-iotests/062 +index 051fb9f..985fbef 100755 +--- a/tests/qemu-iotests/062 ++++ b/tests/qemu-iotests/062 +@@ -25,7 +25,6 @@ owner=mreitz@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/063 b/tests/qemu-iotests/063 +index adc037c..041fb5c 100755 +--- a/tests/qemu-iotests/063 ++++ b/tests/qemu-iotests/063 +@@ -25,7 +25,6 @@ owner=alex@alex.org.uk + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/064 b/tests/qemu-iotests/064 +index 5792fbb..f55ff37 100755 +--- a/tests/qemu-iotests/064 ++++ b/tests/qemu-iotests/064 +@@ -24,7 +24,6 @@ owner=jcody@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/066 b/tests/qemu-iotests/066 +index 8638217..26c0437 100755 +--- a/tests/qemu-iotests/066 ++++ b/tests/qemu-iotests/066 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/067 b/tests/qemu-iotests/067 +index fe259f6..f8d584f 100755 +--- a/tests/qemu-iotests/067 ++++ b/tests/qemu-iotests/067 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + # get standard environment, filters and checks +diff --git a/tests/qemu-iotests/068 b/tests/qemu-iotests/068 +index e7fca6a..f0583d5 100755 +--- a/tests/qemu-iotests/068 ++++ b/tests/qemu-iotests/068 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/069 b/tests/qemu-iotests/069 +index 96e55ef..fdee121 100755 +--- a/tests/qemu-iotests/069 ++++ b/tests/qemu-iotests/069 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/070 b/tests/qemu-iotests/070 +index 8d08d74..78e0390 100755 +--- a/tests/qemu-iotests/070 ++++ b/tests/qemu-iotests/070 +@@ -25,7 +25,6 @@ owner=jcody@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/071 b/tests/qemu-iotests/071 +index 48b4955..6448e9e 100755 +--- a/tests/qemu-iotests/071 ++++ b/tests/qemu-iotests/071 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/072 b/tests/qemu-iotests/072 +index aa027c7..08ef29f 100755 +--- a/tests/qemu-iotests/072 ++++ b/tests/qemu-iotests/072 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/073 b/tests/qemu-iotests/073 +index 40f85b1..5e7f76c 100755 +--- a/tests/qemu-iotests/073 ++++ b/tests/qemu-iotests/073 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/075 b/tests/qemu-iotests/075 +index caa30d4..45b8901 100755 +--- a/tests/qemu-iotests/075 ++++ b/tests/qemu-iotests/075 +@@ -24,7 +24,6 @@ owner=stefanha@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/076 b/tests/qemu-iotests/076 +index ef9e6a4..3b5ab3f 100755 +--- a/tests/qemu-iotests/076 ++++ b/tests/qemu-iotests/076 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/077 b/tests/qemu-iotests/077 +index b3c6fb1..a40f319 100755 +--- a/tests/qemu-iotests/077 ++++ b/tests/qemu-iotests/077 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/078 b/tests/qemu-iotests/078 +index a106c26..68d0ea8 100755 +--- a/tests/qemu-iotests/078 ++++ b/tests/qemu-iotests/078 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/079 b/tests/qemu-iotests/079 +index b2e3f74..fca2f77 100755 +--- a/tests/qemu-iotests/079 ++++ b/tests/qemu-iotests/079 +@@ -24,7 +24,6 @@ owner=hutao@cn.fujitsu.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080 +index 4dbe68e..8d56089 100755 +--- a/tests/qemu-iotests/080 ++++ b/tests/qemu-iotests/080 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/081 b/tests/qemu-iotests/081 +index da3fb09..c5d4616 100755 +--- a/tests/qemu-iotests/081 ++++ b/tests/qemu-iotests/081 +@@ -24,7 +24,6 @@ owner=benoit@irqsave.net + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/082 b/tests/qemu-iotests/082 +index 3e605d5..14f6631 100755 +--- a/tests/qemu-iotests/082 ++++ b/tests/qemu-iotests/082 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/083 b/tests/qemu-iotests/083 +index 3c1adbf..982adec 100755 +--- a/tests/qemu-iotests/083 ++++ b/tests/qemu-iotests/083 +@@ -24,7 +24,6 @@ owner=stefanha@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/084 b/tests/qemu-iotests/084 +index 04f2aa9..e131fa9 100755 +--- a/tests/qemu-iotests/084 ++++ b/tests/qemu-iotests/084 +@@ -25,7 +25,6 @@ owner=jcody@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/085 b/tests/qemu-iotests/085 +index 5c7668c..2ef8407 100755 +--- a/tests/qemu-iotests/085 ++++ b/tests/qemu-iotests/085 +@@ -29,7 +29,6 @@ owner=jcody@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + snapshot_virt0="snapshot-v0.qcow2" +diff --git a/tests/qemu-iotests/086 b/tests/qemu-iotests/086 +index cd4494a..f9e4722 100755 +--- a/tests/qemu-iotests/086 ++++ b/tests/qemu-iotests/086 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/087 b/tests/qemu-iotests/087 +index 2561a14..109cdf5 100755 +--- a/tests/qemu-iotests/087 ++++ b/tests/qemu-iotests/087 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + # get standard environment, filters and checks +diff --git a/tests/qemu-iotests/088 b/tests/qemu-iotests/088 +index b8076f2..c5e9ab4 100755 +--- a/tests/qemu-iotests/088 ++++ b/tests/qemu-iotests/088 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/089 b/tests/qemu-iotests/089 +index aa1ba4a..3165d79 100755 +--- a/tests/qemu-iotests/089 ++++ b/tests/qemu-iotests/089 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/090 b/tests/qemu-iotests/090 +index 7380503..1450993 100755 +--- a/tests/qemu-iotests/090 ++++ b/tests/qemu-iotests/090 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/091 b/tests/qemu-iotests/091 +index 10ac4a8..2f2f98e 100755 +--- a/tests/qemu-iotests/091 ++++ b/tests/qemu-iotests/091 +@@ -26,7 +26,6 @@ owner=jcody@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + MIG_FIFO="${TEST_DIR}/migrate" +diff --git a/tests/qemu-iotests/092 b/tests/qemu-iotests/092 +index 5bbdd07..8e318f1 100755 +--- a/tests/qemu-iotests/092 ++++ b/tests/qemu-iotests/092 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/094 b/tests/qemu-iotests/094 +index 9aa01e3..7adc9b9 100755 +--- a/tests/qemu-iotests/094 ++++ b/tests/qemu-iotests/094 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/095 b/tests/qemu-iotests/095 +index 72ecc22..9fc47f6 100755 +--- a/tests/qemu-iotests/095 ++++ b/tests/qemu-iotests/095 +@@ -27,7 +27,6 @@ owner=jcody@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/097 b/tests/qemu-iotests/097 +index e22670c..7234b16 100755 +--- a/tests/qemu-iotests/097 ++++ b/tests/qemu-iotests/097 +@@ -25,7 +25,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/098 b/tests/qemu-iotests/098 +index b002e96..c7977da 100755 +--- a/tests/qemu-iotests/098 ++++ b/tests/qemu-iotests/098 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/099 b/tests/qemu-iotests/099 +index caaf58e..4a6275d 100755 +--- a/tests/qemu-iotests/099 ++++ b/tests/qemu-iotests/099 +@@ -25,7 +25,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/101 b/tests/qemu-iotests/101 +index ea53f8b..3001ba3 100755 +--- a/tests/qemu-iotests/101 ++++ b/tests/qemu-iotests/101 +@@ -24,7 +24,6 @@ owner=stefanha@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/102 b/tests/qemu-iotests/102 +index 04b3f28..29a6a94 100755 +--- a/tests/qemu-iotests/102 ++++ b/tests/qemu-iotests/102 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq=$(basename $0) + echo "QA output created by $seq" + +-here=$PWD + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/103 b/tests/qemu-iotests/103 +index 2841318..66f8167 100755 +--- a/tests/qemu-iotests/103 ++++ b/tests/qemu-iotests/103 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq=$(basename $0) + echo "QA output created by $seq" + +-here=$PWD + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/104 b/tests/qemu-iotests/104 +index 726d467..34bb0d2 100755 +--- a/tests/qemu-iotests/104 ++++ b/tests/qemu-iotests/104 +@@ -24,7 +24,6 @@ owner=hutao@cn.fujitsu.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + trap "exit \$status" 0 1 2 3 15 +diff --git a/tests/qemu-iotests/105 b/tests/qemu-iotests/105 +index 3db4ce3..943bda2 100755 +--- a/tests/qemu-iotests/105 ++++ b/tests/qemu-iotests/105 +@@ -24,7 +24,6 @@ owner=famz@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/106 b/tests/qemu-iotests/106 +index 5e51f88..4129fee 100755 +--- a/tests/qemu-iotests/106 ++++ b/tests/qemu-iotests/106 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq=$(basename $0) + echo "QA output created by $seq" + +-here=$PWD + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/107 b/tests/qemu-iotests/107 +index d7222dc..5d70ad2 100755 +--- a/tests/qemu-iotests/107 ++++ b/tests/qemu-iotests/107 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/108 b/tests/qemu-iotests/108 +index 2355d98..58e8ad7 100755 +--- a/tests/qemu-iotests/108 ++++ b/tests/qemu-iotests/108 +@@ -25,7 +25,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/109 b/tests/qemu-iotests/109 +index acbd079..90bdae4 100755 +--- a/tests/qemu-iotests/109 ++++ b/tests/qemu-iotests/109 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/110 b/tests/qemu-iotests/110 +index 9de7369..b64b3b2 100755 +--- a/tests/qemu-iotests/110 ++++ b/tests/qemu-iotests/110 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/111 b/tests/qemu-iotests/111 +index a1c152d..e15e66a 100755 +--- a/tests/qemu-iotests/111 ++++ b/tests/qemu-iotests/111 +@@ -25,7 +25,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/112 b/tests/qemu-iotests/112 +index 28eb9aa..fba2965 100755 +--- a/tests/qemu-iotests/112 ++++ b/tests/qemu-iotests/112 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/113 b/tests/qemu-iotests/113 +index 4e09810..d8d78c4 100755 +--- a/tests/qemu-iotests/113 ++++ b/tests/qemu-iotests/113 +@@ -25,7 +25,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/114 b/tests/qemu-iotests/114 +index 5b7dc54..e17fb51 100755 +--- a/tests/qemu-iotests/114 ++++ b/tests/qemu-iotests/114 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/115 b/tests/qemu-iotests/115 +index 665c2ea..0581e03 100755 +--- a/tests/qemu-iotests/115 ++++ b/tests/qemu-iotests/115 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/116 b/tests/qemu-iotests/116 +index df0172f..f8a27b9 100755 +--- a/tests/qemu-iotests/116 ++++ b/tests/qemu-iotests/116 +@@ -27,7 +27,6 @@ owner=stefanha@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/117 b/tests/qemu-iotests/117 +index 6c83461..e533e23 100755 +--- a/tests/qemu-iotests/117 ++++ b/tests/qemu-iotests/117 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/119 b/tests/qemu-iotests/119 +index 4f34fb4..32810d5 100755 +--- a/tests/qemu-iotests/119 ++++ b/tests/qemu-iotests/119 +@@ -25,7 +25,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/120 b/tests/qemu-iotests/120 +index f40b97d..76afdf4 100755 +--- a/tests/qemu-iotests/120 ++++ b/tests/qemu-iotests/120 +@@ -25,7 +25,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/121 b/tests/qemu-iotests/121 +index 6d6f55a..d2885c7 100755 +--- a/tests/qemu-iotests/121 ++++ b/tests/qemu-iotests/121 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/122 b/tests/qemu-iotests/122 +index d8c8ad7..eab3399 100755 +--- a/tests/qemu-iotests/122 ++++ b/tests/qemu-iotests/122 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/123 b/tests/qemu-iotests/123 +index b18e3fc..168b985 100755 +--- a/tests/qemu-iotests/123 ++++ b/tests/qemu-iotests/123 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/125 b/tests/qemu-iotests/125 +index c20c715..778c874 100755 +--- a/tests/qemu-iotests/125 ++++ b/tests/qemu-iotests/125 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq=$(basename $0) + echo "QA output created by $seq" + +-here=$PWD + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/126 b/tests/qemu-iotests/126 +index a2d4d6c..9114838 100755 +--- a/tests/qemu-iotests/126 ++++ b/tests/qemu-iotests/126 +@@ -25,7 +25,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + # get standard environment, filters and checks +diff --git a/tests/qemu-iotests/127 b/tests/qemu-iotests/127 +index 9e0d7d3..c9139ed 100755 +--- a/tests/qemu-iotests/127 ++++ b/tests/qemu-iotests/127 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq=$(basename $0) + echo "QA output created by $seq" + +-here=$PWD + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/128 b/tests/qemu-iotests/128 +index 0976a18..925f5c7 100755 +--- a/tests/qemu-iotests/128 ++++ b/tests/qemu-iotests/128 +@@ -24,7 +24,6 @@ owner=stefanha@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + devname="eiodev$$" +diff --git a/tests/qemu-iotests/130 b/tests/qemu-iotests/130 +index 2c4b94d..f2f2706 100755 +--- a/tests/qemu-iotests/130 ++++ b/tests/qemu-iotests/130 +@@ -26,7 +26,6 @@ owner=kwolf@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/131 b/tests/qemu-iotests/131 +index 94a9ae7..58c25f7 100755 +--- a/tests/qemu-iotests/131 ++++ b/tests/qemu-iotests/131 +@@ -24,7 +24,6 @@ owner=den@openvz.org + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/133 b/tests/qemu-iotests/133 +index af6b3e1..a9a47a3 100755 +--- a/tests/qemu-iotests/133 ++++ b/tests/qemu-iotests/133 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/134 b/tests/qemu-iotests/134 +index 9914415..cacabcd 100755 +--- a/tests/qemu-iotests/134 ++++ b/tests/qemu-iotests/134 +@@ -24,7 +24,6 @@ owner=berrange@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/135 b/tests/qemu-iotests/135 +index ce60831..a18a0c7 100755 +--- a/tests/qemu-iotests/135 ++++ b/tests/qemu-iotests/135 +@@ -24,7 +24,6 @@ owner=jcody@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/137 b/tests/qemu-iotests/137 +index 19e8597..09cd445 100755 +--- a/tests/qemu-iotests/137 ++++ b/tests/qemu-iotests/137 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/138 b/tests/qemu-iotests/138 +index 21650d8..eccbcae 100755 +--- a/tests/qemu-iotests/138 ++++ b/tests/qemu-iotests/138 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/140 b/tests/qemu-iotests/140 +index a8fc951..d4623b5 100755 +--- a/tests/qemu-iotests/140 ++++ b/tests/qemu-iotests/140 +@@ -28,7 +28,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141 +index 4246d38..e2408c7 100755 +--- a/tests/qemu-iotests/141 ++++ b/tests/qemu-iotests/141 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/142 b/tests/qemu-iotests/142 +index 1639c83..c7c360d 100755 +--- a/tests/qemu-iotests/142 ++++ b/tests/qemu-iotests/142 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/143 b/tests/qemu-iotests/143 +index 5ff1944..d6302cc 100755 +--- a/tests/qemu-iotests/143 ++++ b/tests/qemu-iotests/143 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/144 b/tests/qemu-iotests/144 +index 4b91571..118c099 100755 +--- a/tests/qemu-iotests/144 ++++ b/tests/qemu-iotests/144 +@@ -26,7 +26,6 @@ owner=jcody@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + TMP_SNAP1=${TEST_DIR}/tmp.qcow2 +diff --git a/tests/qemu-iotests/145 b/tests/qemu-iotests/145 +index c371b3c..6ce8a46 100755 +--- a/tests/qemu-iotests/145 ++++ b/tests/qemu-iotests/145 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/146 b/tests/qemu-iotests/146 +index 043711b..3f61351 100755 +--- a/tests/qemu-iotests/146 ++++ b/tests/qemu-iotests/146 +@@ -24,7 +24,6 @@ owner=jcody@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/150 b/tests/qemu-iotests/150 +index ee8f637..955b877 100755 +--- a/tests/qemu-iotests/150 ++++ b/tests/qemu-iotests/150 +@@ -24,7 +24,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153 +index 0daeb1b..00092b8 100755 +--- a/tests/qemu-iotests/153 ++++ b/tests/qemu-iotests/153 +@@ -24,7 +24,6 @@ owner=famz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + tmp=/tmp/$$ + status=1 # failure is the default! + +diff --git a/tests/qemu-iotests/154 b/tests/qemu-iotests/154 +index fde03b0..4a4abf0 100755 +--- a/tests/qemu-iotests/154 ++++ b/tests/qemu-iotests/154 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/156 b/tests/qemu-iotests/156 +index 0a9a098..f97f96f 100755 +--- a/tests/qemu-iotests/156 ++++ b/tests/qemu-iotests/156 +@@ -32,7 +32,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/157 b/tests/qemu-iotests/157 +index 2bf02be..c3231b7 100755 +--- a/tests/qemu-iotests/157 ++++ b/tests/qemu-iotests/157 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/158 b/tests/qemu-iotests/158 +index 24ac600..d277ddc 100755 +--- a/tests/qemu-iotests/158 ++++ b/tests/qemu-iotests/158 +@@ -24,7 +24,6 @@ owner=berrange@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/159 b/tests/qemu-iotests/159 +index 9b0e1ec..e74b273 100755 +--- a/tests/qemu-iotests/159 ++++ b/tests/qemu-iotests/159 +@@ -23,7 +23,6 @@ owner=fullmanet@gmail.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 + + _cleanup() +diff --git a/tests/qemu-iotests/160 b/tests/qemu-iotests/160 +index 5c910e5..92fff45 100755 +--- a/tests/qemu-iotests/160 ++++ b/tests/qemu-iotests/160 +@@ -23,7 +23,6 @@ owner=fullmanet@gmail.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 + + _cleanup() +diff --git a/tests/qemu-iotests/162 b/tests/qemu-iotests/162 +index 477a806..ef02d84 100755 +--- a/tests/qemu-iotests/162 ++++ b/tests/qemu-iotests/162 +@@ -25,7 +25,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/170 b/tests/qemu-iotests/170 +index b79359f..861eabf 100755 +--- a/tests/qemu-iotests/170 ++++ b/tests/qemu-iotests/170 +@@ -23,7 +23,6 @@ owner=fullmanet@gmail.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 + + _cleanup() +diff --git a/tests/qemu-iotests/171 b/tests/qemu-iotests/171 +index bcfaaf1..5b46069 100755 +--- a/tests/qemu-iotests/171 ++++ b/tests/qemu-iotests/171 +@@ -25,7 +25,6 @@ owner=tgolembi@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/172 b/tests/qemu-iotests/172 +index 02c5f79..c5ee33e 100755 +--- a/tests/qemu-iotests/172 ++++ b/tests/qemu-iotests/172 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/173 b/tests/qemu-iotests/173 +index bdaa092..1fe8c5d 100755 +--- a/tests/qemu-iotests/173 ++++ b/tests/qemu-iotests/173 +@@ -24,7 +24,6 @@ owner=jcody@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/174 b/tests/qemu-iotests/174 +index 552879d..d8bb05c 100755 +--- a/tests/qemu-iotests/174 ++++ b/tests/qemu-iotests/174 +@@ -24,7 +24,6 @@ owner=nirsof@gmail.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/175 b/tests/qemu-iotests/175 +index ca56e82..ebbeb6e 100755 +--- a/tests/qemu-iotests/175 ++++ b/tests/qemu-iotests/175 +@@ -24,7 +24,6 @@ owner=nirsof@gmail.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/176 b/tests/qemu-iotests/176 +index 32baa11..c091d0b 100755 +--- a/tests/qemu-iotests/176 ++++ b/tests/qemu-iotests/176 +@@ -29,7 +29,6 @@ owner=mreitz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/177 b/tests/qemu-iotests/177 +index 396986d..7bf8e1d 100755 +--- a/tests/qemu-iotests/177 ++++ b/tests/qemu-iotests/177 +@@ -24,7 +24,6 @@ owner=eblake@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/178 b/tests/qemu-iotests/178 +index 6af52c6..3f4b4a4 100755 +--- a/tests/qemu-iotests/178 ++++ b/tests/qemu-iotests/178 +@@ -24,7 +24,6 @@ owner=stefanha@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/179 b/tests/qemu-iotests/179 +index 115944a..3040631 100755 +--- a/tests/qemu-iotests/179 ++++ b/tests/qemu-iotests/179 +@@ -24,7 +24,6 @@ owner=eblake@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/181 b/tests/qemu-iotests/181 +index 5e767c6..e0ae965 100755 +--- a/tests/qemu-iotests/181 ++++ b/tests/qemu-iotests/181 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + MIG_SOCKET="${TEST_DIR}/migrate" +diff --git a/tests/qemu-iotests/182 b/tests/qemu-iotests/182 +index 3b7689c..9e078c5 100755 +--- a/tests/qemu-iotests/182 ++++ b/tests/qemu-iotests/182 +@@ -24,7 +24,6 @@ owner=famz@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + tmp=/tmp/$$ + status=1 # failure is the default! + +diff --git a/tests/qemu-iotests/183 b/tests/qemu-iotests/183 +index c49e1ad..ebb5e30 100755 +--- a/tests/qemu-iotests/183 ++++ b/tests/qemu-iotests/183 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + MIG_SOCKET="${TEST_DIR}/migrate" +diff --git a/tests/qemu-iotests/184 b/tests/qemu-iotests/184 +index 2b68284..2f3259d 100755 +--- a/tests/qemu-iotests/184 ++++ b/tests/qemu-iotests/184 +@@ -24,7 +24,6 @@ owner="Manos Pitsidianakis" + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + trap "exit \$status" 0 1 2 3 15 +diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185 +index 567ba67..dacfcb5 100755 +--- a/tests/qemu-iotests/185 ++++ b/tests/qemu-iotests/185 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + MIG_SOCKET="${TEST_DIR}/migrate" +diff --git a/tests/qemu-iotests/186 b/tests/qemu-iotests/186 +index 0aa4395..29681bf 100755 +--- a/tests/qemu-iotests/186 ++++ b/tests/qemu-iotests/186 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/187 b/tests/qemu-iotests/187 +index 7bb7833..1feddca 100755 +--- a/tests/qemu-iotests/187 ++++ b/tests/qemu-iotests/187 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/188 b/tests/qemu-iotests/188 +index 83ed03e..af40e49 100755 +--- a/tests/qemu-iotests/188 ++++ b/tests/qemu-iotests/188 +@@ -24,7 +24,6 @@ owner=berrange@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/189 b/tests/qemu-iotests/189 +index e695475..222bec1 100755 +--- a/tests/qemu-iotests/189 ++++ b/tests/qemu-iotests/189 +@@ -24,7 +24,6 @@ owner=berrange@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/190 b/tests/qemu-iotests/190 +index 8f808fe..95ba06d 100755 +--- a/tests/qemu-iotests/190 ++++ b/tests/qemu-iotests/190 +@@ -24,7 +24,6 @@ owner=eblake@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/191 b/tests/qemu-iotests/191 +index b3629ff..ff0fdec 100755 +--- a/tests/qemu-iotests/191 ++++ b/tests/qemu-iotests/191 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + MIG_SOCKET="${TEST_DIR}/migrate" +diff --git a/tests/qemu-iotests/192 b/tests/qemu-iotests/192 +index 595f0d7..415c706 100755 +--- a/tests/qemu-iotests/192 ++++ b/tests/qemu-iotests/192 +@@ -25,7 +25,6 @@ owner=famz@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/195 b/tests/qemu-iotests/195 +index e7a403d..f56f255 100755 +--- a/tests/qemu-iotests/195 ++++ b/tests/qemu-iotests/195 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197 +index 0369aa5..8170f5d 100755 +--- a/tests/qemu-iotests/197 ++++ b/tests/qemu-iotests/197 +@@ -24,7 +24,6 @@ owner=eblake@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + # get standard environment, filters and checks +diff --git a/tests/qemu-iotests/198 b/tests/qemu-iotests/198 +index 54eaaf5..4d961f4 100755 +--- a/tests/qemu-iotests/198 ++++ b/tests/qemu-iotests/198 +@@ -24,7 +24,6 @@ owner=berrange@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/200 b/tests/qemu-iotests/200 +index ddbdedc..b9ebd5a 100755 +--- a/tests/qemu-iotests/200 ++++ b/tests/qemu-iotests/200 +@@ -26,7 +26,6 @@ owner=jcody@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/204 b/tests/qemu-iotests/204 +index feb69d2..57f3afe 100755 +--- a/tests/qemu-iotests/204 ++++ b/tests/qemu-iotests/204 +@@ -24,7 +24,6 @@ owner=eblake@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/214 b/tests/qemu-iotests/214 +index c46ca2a..7a2d539 100755 +--- a/tests/qemu-iotests/214 ++++ b/tests/qemu-iotests/214 +@@ -22,7 +22,6 @@ + seq=$(basename "$0") + echo "QA output created by $seq" + +-here=$PWD + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/215 b/tests/qemu-iotests/215 +index 2e616ed..230fd25 100755 +--- a/tests/qemu-iotests/215 ++++ b/tests/qemu-iotests/215 +@@ -21,7 +21,6 @@ + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + # get standard environment, filters and checks +diff --git a/tests/qemu-iotests/221 b/tests/qemu-iotests/221 +index 41c4e4b..06f48f1 100755 +--- a/tests/qemu-iotests/221 ++++ b/tests/qemu-iotests/221 +@@ -21,7 +21,6 @@ + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 +index d011b3e..29e1951 100755 +--- a/tests/qemu-iotests/223 ++++ b/tests/qemu-iotests/223 +@@ -21,7 +21,6 @@ + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/226 b/tests/qemu-iotests/226 +index 0bc227f..687a182 100755 +--- a/tests/qemu-iotests/226 ++++ b/tests/qemu-iotests/226 +@@ -25,7 +25,6 @@ owner=jsnow@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/227 b/tests/qemu-iotests/227 +index 9a5f7f9..43f2323 100755 +--- a/tests/qemu-iotests/227 ++++ b/tests/qemu-iotests/227 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=$(basename $0) + echo "QA output created by $seq" + +-here=$PWD + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/229 b/tests/qemu-iotests/229 +index ff851ec..8660243 100755 +--- a/tests/qemu-iotests/229 ++++ b/tests/qemu-iotests/229 +@@ -25,7 +25,6 @@ owner=jcody@redhat.com + seq="$(basename $0)" + echo "QA output created by $seq" + +-here="$PWD" + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/231 b/tests/qemu-iotests/231 +index 3e28370..e9f8aaa 100755 +--- a/tests/qemu-iotests/231 ++++ b/tests/qemu-iotests/231 +@@ -26,7 +26,6 @@ owner=jcody@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +diff --git a/tests/qemu-iotests/232 b/tests/qemu-iotests/232 +index 2ed39d2..ae63f13 100755 +--- a/tests/qemu-iotests/232 ++++ b/tests/qemu-iotests/232 +@@ -24,7 +24,6 @@ owner=kwolf@redhat.com + seq=`basename $0` + echo "QA output created by $seq" + +-here=`pwd` + status=1 # failure is the default! + + _cleanup() +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-nbd-Add-bitmap-NAME-option.patch b/SOURCES/kvm-qemu-nbd-Add-bitmap-NAME-option.patch new file mode 100644 index 0000000..761478d --- /dev/null +++ b/SOURCES/kvm-qemu-nbd-Add-bitmap-NAME-option.patch @@ -0,0 +1,189 @@ +From 608c35120c7e8026232a2c2b7083648fca20144d Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:33 +0100 +Subject: [PATCH 094/163] qemu-nbd: Add --bitmap=NAME option + +RH-Author: John Snow +Message-id: <20190327172308.31077-21-jsnow@redhat.com> +Patchwork-id: 85200 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 20/55] qemu-nbd: Add --bitmap=NAME option +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Having to fire up qemu, then use QMP commands for nbd-server-start +and nbd-server-add, just to expose a persistent dirty bitmap, is +rather tedious. Make it possible to expose a dirty bitmap using +just qemu-nbd (of course, for now this only works when qemu-nbd is +visiting a BDS formatted as qcow2). + +Of course, any good feature also needs unit testing, so expand +iotest 223 to cover it. + +Signed-off-by: Eric Blake +Message-Id: <20190111194720.15671-9-eblake@redhat.com> +(cherry picked from commit 636192c4b6052820ea126a5287c58a8f53f3c84f) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + qemu-nbd.c | 10 ++++++++-- + qemu-nbd.texi | 4 ++++ + tests/qemu-iotests/223 | 18 +++++++++++++++++- + tests/qemu-iotests/223.out | 12 +++++++++++- + 4 files changed, 40 insertions(+), 4 deletions(-) + +diff --git a/qemu-nbd.c b/qemu-nbd.c +index ac4c958..0c92f62 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -95,6 +95,7 @@ static void usage(const char *name) + "Exposing part of the image:\n" + " -o, --offset=OFFSET offset into the image\n" + " -P, --partition=NUM only expose partition NUM\n" ++" -B, --bitmap=NAME expose a persistent dirty bitmap\n" + "\n" + "General purpose options:\n" + " --object type,id=ID,... define an object such as 'secret' for providing\n" +@@ -509,7 +510,7 @@ int main(int argc, char **argv) + off_t fd_size; + QemuOpts *sn_opts = NULL; + const char *sn_id_or_name = NULL; +- const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:"; ++ const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:B:"; + struct option lopt[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, +@@ -519,6 +520,7 @@ int main(int argc, char **argv) + { "offset", required_argument, NULL, 'o' }, + { "read-only", no_argument, NULL, 'r' }, + { "partition", required_argument, NULL, 'P' }, ++ { "bitmap", required_argument, NULL, 'B' }, + { "connect", required_argument, NULL, 'c' }, + { "disconnect", no_argument, NULL, 'd' }, + { "snapshot", no_argument, NULL, 's' }, +@@ -558,6 +560,7 @@ int main(int argc, char **argv) + QDict *options = NULL; + const char *export_name = ""; /* Default export name */ + const char *export_description = NULL; ++ const char *bitmap = NULL; + const char *tlscredsid = NULL; + bool imageOpts = false; + bool writethrough = true; +@@ -695,6 +698,9 @@ int main(int argc, char **argv) + exit(EXIT_FAILURE); + } + break; ++ case 'B': ++ bitmap = optarg; ++ break; + case 'k': + sockpath = optarg; + if (sockpath[0] != '/') { +@@ -1018,7 +1024,7 @@ int main(int argc, char **argv) + } + + export = nbd_export_new(bs, dev_offset, fd_size, export_name, +- export_description, NULL, nbdflags, ++ export_description, bitmap, nbdflags, + nbd_export_closed, writethrough, NULL, + &error_fatal); + +diff --git a/qemu-nbd.texi b/qemu-nbd.texi +index 9a84e81..96b1546 100644 +--- a/qemu-nbd.texi ++++ b/qemu-nbd.texi +@@ -45,6 +45,10 @@ auto-detecting + Export the disk as read-only + @item -P, --partition=@var{num} + Only expose partition @var{num} ++@item -B, --bitmap=@var{name} ++If @var{filename} has a qcow2 persistent bitmap @var{name}, expose ++that bitmap via the ``qemu:dirty-bitmap:@var{name}'' context ++accessible through NBD_OPT_SET_META_CONTEXT. + @item -s, --snapshot + Use @var{filename} as an external snapshot, create a temporary + file with backing_file=@var{filename}, redirect the write to +diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 +index 0bcc98a..773892d 100755 +--- a/tests/qemu-iotests/223 ++++ b/tests/qemu-iotests/223 +@@ -25,6 +25,7 @@ status=1 # failure is the default! + + _cleanup() + { ++ nbd_server_stop + _cleanup_test_img + _cleanup_qemu + rm -f "$TEST_DIR/nbd" +@@ -35,6 +36,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 + . ./common.rc + . ./common.filter + . ./common.qemu ++. ./common.nbd + + _supported_fmt qcow2 + _supported_proto file # uses NBD as well +@@ -163,7 +165,7 @@ $QEMU_IMG map --output=json --image-opts \ + "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b2" | _filter_qemu_img_map + + echo +-echo "=== End NBD server ===" ++echo "=== End qemu NBD server ===" + echo + + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove", +@@ -176,6 +178,20 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-stop"}' "return" + _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-stop"}' "error" # Again + _send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' "return" + ++echo ++echo "=== Use qemu-nbd as server ===" ++echo ++ ++nbd_server_start_unix_socket -r -f $IMGFMT -B b "$TEST_IMG" ++IMG="driver=nbd,server.type=unix,server.path=$nbd_unix_socket" ++$QEMU_IMG map --output=json --image-opts \ ++ "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b" | _filter_qemu_img_map ++ ++nbd_server_start_unix_socket -f $IMGFMT -B b2 "$TEST_IMG" ++IMG="driver=nbd,server.type=unix,server.path=$nbd_unix_socket" ++$QEMU_IMG map --output=json --image-opts \ ++ "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b2" | _filter_qemu_img_map ++ + # success, all done + echo '*** done' + rm -f $seq.full +diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out +index a0c2dec..4012bb0 100644 +--- a/tests/qemu-iotests/223.out ++++ b/tests/qemu-iotests/223.out +@@ -61,7 +61,7 @@ read 2097152/2097152 bytes at offset 2097152 + { "start": 1024, "length": 2096128, "depth": 0, "zero": false, "data": true}, + { "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}] + +-=== End NBD server === ++=== End qemu NBD server === + + {"return": {}} + {"return": {}} +@@ -69,4 +69,14 @@ read 2097152/2097152 bytes at offset 2097152 + {"return": {}} + {"error": {"class": "GenericError", "desc": "NBD server not running"}} + {"return": {}} ++ ++=== Use qemu-nbd as server === ++ ++[{ "start": 0, "length": 65536, "depth": 0, "zero": false, "data": false}, ++{ "start": 65536, "length": 2031616, "depth": 0, "zero": false, "data": true}, ++{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}] ++[{ "start": 0, "length": 512, "depth": 0, "zero": false, "data": true}, ++{ "start": 512, "length": 512, "depth": 0, "zero": false, "data": false}, ++{ "start": 1024, "length": 2096128, "depth": 0, "zero": false, "data": true}, ++{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}] + *** done +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-nbd-Add-list-option.patch b/SOURCES/kvm-qemu-nbd-Add-list-option.patch new file mode 100644 index 0000000..7878644 --- /dev/null +++ b/SOURCES/kvm-qemu-nbd-Add-list-option.patch @@ -0,0 +1,440 @@ +From 7d72cea18905a4b635ac75997d5ce719ebc5ffb3 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:53 +0100 +Subject: [PATCH 115/163] qemu-nbd: Add --list option + +RH-Author: John Snow +Message-id: <20190327172308.31077-41-jsnow@redhat.com> +Patchwork-id: 85204 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 40/55] qemu-nbd: Add --list option +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +We want to be able to detect whether a given qemu NBD server is +exposing the right export(s) and dirty bitmaps, at least for +regression testing. We could use 'nbd-client -l' from the upstream +NBD project to list exports, but it's annoying to rely on +out-of-tree binaries; furthermore, nbd-client doesn't necessarily +know about all of the qemu NBD extensions. Thus, it is time to add +a new mode to qemu-nbd that merely sniffs all possible information +from the server during handshake phase, then disconnects and dumps +the information. + +This patch actually implements --list/-L, while reusing other +options such as --tls-creds for now designating how to connect +as the client (rather than their non-list usage of how to operate +as the server). + +I debated about adding this functionality to something akin to +'qemu-img info' - but that tool does not readily lend itself +to connecting to an arbitrary NBD server without also tying to +a specific export (I may, however, still add ImageInfoSpecificNBD +for reporting the bitmaps available when connecting to a single +export). And, while it may feel a bit odd that normally +qemu-nbd is a server but 'qemu-nbd -L' is a client, we are not +really making the qemu-nbd binary that much larger, because +'qemu-nbd -c' has to operate as both server and client +simultaneously across two threads when feeding the kernel module +for /dev/nbdN access. + +Sample output: +$ qemu-nbd -L +exports available: 1 + export: '' + size: 65536 + flags: 0x4ed ( flush fua trim zeroes df cache ) + min block: 512 + opt block: 4096 + max block: 33554432 + available meta contexts: 1 + base:allocation + +Note that the output only lists sizes if the server sent +NBD_FLAG_HAS_FLAGS, because a newstyle server does not give +the size otherwise. It has the side effect that for really +old servers that did not send any flags, the size is not +output even though it was available. However, I'm not too +concerned about that - oldstyle servers are (rightfully) +getting less common to encounter (qemu 3.0 was the last +version where we even serve it), and most existing servers +that still even offer oldstyle negotiation (such as nbdkit) +still send flags (since that was added to the NBD protocol +in 2007 to permit read-only connections). + +Not done here, but maybe worth future experiments: capture +the meat of NBDExportInfo into a QAPI struct, and use the +generated QAPI pretty-printers instead of hand-rolling our +output loop. It would also permit us to add a JSON output +mode for machine parsing. + +Signed-off-by: Eric Blake +Reviewed-by: Richard W.M. Jones +Message-Id: <20190117193658.16413-20-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit 68b96f15838d309ef791cb83b5eec1bd7da271c2) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + qemu-nbd.c | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- + qemu-nbd.texi | 29 +++++++++-- + 2 files changed, 167 insertions(+), 17 deletions(-) + +diff --git a/qemu-nbd.c b/qemu-nbd.c +index 3c53870..1f7b2a0 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -76,7 +76,8 @@ static void usage(const char *name) + { + (printf) ( + "Usage: %s [OPTIONS] FILE\n" +-"QEMU Disk Network Block Device Server\n" ++" or: %s -L [OPTIONS]\n" ++"QEMU Disk Network Block Device Utility\n" + "\n" + " -h, --help display this help and exit\n" + " -V, --version output version information and exit\n" +@@ -98,6 +99,7 @@ static void usage(const char *name) + " -B, --bitmap=NAME expose a persistent dirty bitmap\n" + "\n" + "General purpose options:\n" ++" -L, --list list exports available from another NBD server\n" + " --object type,id=ID,... define an object such as 'secret' for providing\n" + " passwords and/or encryption keys\n" + " --tls-creds=ID use id of an earlier --object to provide TLS\n" +@@ -131,7 +133,7 @@ static void usage(const char *name) + " --image-opts treat FILE as a full set of image options\n" + "\n" + QEMU_HELP_BOTTOM "\n" +- , name, NBD_DEFAULT_PORT, "DEVICE"); ++ , name, name, NBD_DEFAULT_PORT, "DEVICE"); + } + + static void version(const char *name) +@@ -243,6 +245,91 @@ static void termsig_handler(int signum) + } + + ++static int qemu_nbd_client_list(SocketAddress *saddr, QCryptoTLSCreds *tls, ++ const char *hostname) ++{ ++ int ret = EXIT_FAILURE; ++ int rc; ++ Error *err = NULL; ++ QIOChannelSocket *sioc; ++ NBDExportInfo *list; ++ int i, j; ++ ++ sioc = qio_channel_socket_new(); ++ if (qio_channel_socket_connect_sync(sioc, saddr, &err) < 0) { ++ error_report_err(err); ++ return EXIT_FAILURE; ++ } ++ rc = nbd_receive_export_list(QIO_CHANNEL(sioc), tls, hostname, &list, ++ &err); ++ if (rc < 0) { ++ if (err) { ++ error_report_err(err); ++ } ++ goto out; ++ } ++ printf("exports available: %d\n", rc); ++ for (i = 0; i < rc; i++) { ++ printf(" export: '%s'\n", list[i].name); ++ if (list[i].description && *list[i].description) { ++ printf(" description: %s\n", list[i].description); ++ } ++ if (list[i].flags & NBD_FLAG_HAS_FLAGS) { ++ printf(" size: %" PRIu64 "\n", list[i].size); ++ printf(" flags: 0x%x (", list[i].flags); ++ if (list[i].flags & NBD_FLAG_READ_ONLY) { ++ printf(" readonly"); ++ } ++ if (list[i].flags & NBD_FLAG_SEND_FLUSH) { ++ printf(" flush"); ++ } ++ if (list[i].flags & NBD_FLAG_SEND_FUA) { ++ printf(" fua"); ++ } ++ if (list[i].flags & NBD_FLAG_ROTATIONAL) { ++ printf(" rotational"); ++ } ++ if (list[i].flags & NBD_FLAG_SEND_TRIM) { ++ printf(" trim"); ++ } ++ if (list[i].flags & NBD_FLAG_SEND_WRITE_ZEROES) { ++ printf(" zeroes"); ++ } ++ if (list[i].flags & NBD_FLAG_SEND_DF) { ++ printf(" df"); ++ } ++ if (list[i].flags & NBD_FLAG_CAN_MULTI_CONN) { ++ printf(" multi"); ++ } ++ if (list[i].flags & NBD_FLAG_SEND_RESIZE) { ++ printf(" resize"); ++ } ++ if (list[i].flags & NBD_FLAG_SEND_CACHE) { ++ printf(" cache"); ++ } ++ printf(" )\n"); ++ } ++ if (list[i].min_block) { ++ printf(" min block: %u\n", list[i].min_block); ++ printf(" opt block: %u\n", list[i].opt_block); ++ printf(" max block: %u\n", list[i].max_block); ++ } ++ if (list[i].n_contexts) { ++ printf(" available meta contexts: %d\n", list[i].n_contexts); ++ for (j = 0; j < list[i].n_contexts; j++) { ++ printf(" %s\n", list[i].contexts[j]); ++ } ++ } ++ } ++ nbd_free_export_list(list, rc); ++ ++ ret = EXIT_SUCCESS; ++ out: ++ object_unref(OBJECT(sioc)); ++ return ret; ++} ++ ++ + #if HAVE_NBD_DEVICE + static void *show_parts(void *arg) + { +@@ -425,7 +512,8 @@ static QemuOptsList qemu_object_opts = { + + + +-static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) ++static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, bool list, ++ Error **errp) + { + Object *obj; + QCryptoTLSCreds *creds; +@@ -445,10 +533,18 @@ static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) + return NULL; + } + +- if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { +- error_setg(errp, +- "Expecting TLS credentials with a server endpoint"); +- return NULL; ++ if (list) { ++ if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { ++ error_setg(errp, ++ "Expecting TLS credentials with a client endpoint"); ++ return NULL; ++ } ++ } else { ++ if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { ++ error_setg(errp, ++ "Expecting TLS credentials with a server endpoint"); ++ return NULL; ++ } + } + object_ref(obj); + return creds; +@@ -471,7 +567,8 @@ static void setup_address_and_port(const char **address, const char **port) + static const char *socket_activation_validate_opts(const char *device, + const char *sockpath, + const char *address, +- const char *port) ++ const char *port, ++ bool list) + { + if (device != NULL) { + return "NBD device can't be set when using socket activation"; +@@ -489,6 +586,10 @@ static const char *socket_activation_validate_opts(const char *device, + return "TCP port number can't be set when using socket activation"; + } + ++ if (list) { ++ return "List mode is incompatible with socket activation"; ++ } ++ + return NULL; + } + +@@ -512,7 +613,7 @@ int main(int argc, char **argv) + int64_t fd_size; + QemuOpts *sn_opts = NULL; + const char *sn_id_or_name = NULL; +- const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:B:"; ++ const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:B:L"; + struct option lopt[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, +@@ -525,6 +626,7 @@ int main(int argc, char **argv) + { "bitmap", required_argument, NULL, 'B' }, + { "connect", required_argument, NULL, 'c' }, + { "disconnect", no_argument, NULL, 'd' }, ++ { "list", no_argument, NULL, 'L' }, + { "snapshot", no_argument, NULL, 's' }, + { "load-snapshot", required_argument, NULL, 'l' }, + { "nocache", no_argument, NULL, 'n' }, +@@ -559,7 +661,7 @@ int main(int argc, char **argv) + Error *local_err = NULL; + BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; + QDict *options = NULL; +- const char *export_name = ""; /* Default export name */ ++ const char *export_name = NULL; /* defaults to "" later for server mode */ + const char *export_description = NULL; + const char *bitmap = NULL; + const char *tlscredsid = NULL; +@@ -567,6 +669,7 @@ int main(int argc, char **argv) + bool writethrough = true; + char *trace_file = NULL; + bool fork_process = false; ++ bool list = false; + int old_stderr = -1; + unsigned socket_activation; + +@@ -760,13 +863,33 @@ int main(int argc, char **argv) + case QEMU_NBD_OPT_FORK: + fork_process = true; + break; ++ case 'L': ++ list = true; ++ break; + } + } + +- if ((argc - optind) != 1) { ++ if (list) { ++ if (argc != optind) { ++ error_report("List mode is incompatible with a file name"); ++ exit(EXIT_FAILURE); ++ } ++ if (export_name || export_description || dev_offset || partition || ++ device || disconnect || fmt || sn_id_or_name || bitmap || ++ seen_aio || seen_discard || seen_cache) { ++ error_report("List mode is incompatible with per-device settings"); ++ exit(EXIT_FAILURE); ++ } ++ if (fork_process) { ++ error_report("List mode is incompatible with forking"); ++ exit(EXIT_FAILURE); ++ } ++ } else if ((argc - optind) != 1) { + error_report("Invalid number of arguments"); + error_printf("Try `%s --help' for more information.\n", argv[0]); + exit(EXIT_FAILURE); ++ } else if (!export_name) { ++ export_name = ""; + } + + qemu_opts_foreach(&qemu_object_opts, +@@ -785,7 +908,8 @@ int main(int argc, char **argv) + } else { + /* Using socket activation - check user didn't use -p etc. */ + const char *err_msg = socket_activation_validate_opts(device, sockpath, +- bindto, port); ++ bindto, port, ++ list); + if (err_msg != NULL) { + error_report("%s", err_msg); + exit(EXIT_FAILURE); +@@ -808,7 +932,7 @@ int main(int argc, char **argv) + error_report("TLS is not supported with a host device"); + exit(EXIT_FAILURE); + } +- tlscreds = nbd_get_tls_creds(tlscredsid, &local_err); ++ tlscreds = nbd_get_tls_creds(tlscredsid, list, &local_err); + if (local_err) { + error_report("Failed to get TLS creds %s", + error_get_pretty(local_err)); +@@ -816,6 +940,11 @@ int main(int argc, char **argv) + } + } + ++ if (list) { ++ saddr = nbd_build_socket_address(sockpath, bindto, port); ++ return qemu_nbd_client_list(saddr, tlscreds, bindto); ++ } ++ + #if !HAVE_NBD_DEVICE + if (disconnect || device) { + error_report("Kernel /dev/nbdN support not available"); +diff --git a/qemu-nbd.texi b/qemu-nbd.texi +index f218291..386bece 100644 +--- a/qemu-nbd.texi ++++ b/qemu-nbd.texi +@@ -2,6 +2,8 @@ + @c man begin SYNOPSIS + @command{qemu-nbd} [OPTION]... @var{filename} + ++@command{qemu-nbd} @option{-L} [OPTION]... ++ + @command{qemu-nbd} @option{-d} @var{dev} + @c man end + @end example +@@ -14,6 +16,8 @@ Other uses: + @itemize + @item + Bind a /dev/nbdX block device to a QEMU server (on Linux). ++@item ++As a client to query exports of a remote NBD server. + @end itemize + + @c man end +@@ -31,13 +35,15 @@ See the @code{qemu(1)} manual page for full details of the properties + supported. The common object types that it makes sense to define are the + @code{secret} object, which is used to supply passwords and/or encryption + keys, and the @code{tls-creds} object, which is used to supply TLS +-credentials for the qemu-nbd server. ++credentials for the qemu-nbd server or client. + @item -p, --port=@var{port} +-The TCP port to listen on (default @samp{10809}). ++The TCP port to listen on as a server, or connect to as a client ++(default @samp{10809}). + @item -o, --offset=@var{offset} + The offset into the image. + @item -b, --bind=@var{iface} +-The interface to bind to (default @samp{0.0.0.0}). ++The interface to bind to as a server, or connect to as a client ++(default @samp{0.0.0.0}). + @item -k, --socket=@var{path} + Use a unix socket with path @var{path}. + @item --image-opts +@@ -97,10 +103,16 @@ Set the NBD volume export name (default of a zero-length string). + @item -D, --description=@var{description} + Set the NBD volume export description, as a human-readable + string. ++@item -L, --list ++Connect as a client and list all details about the exports exposed by ++a remote NBD server. This enables list mode, and is incompatible ++with options that change behavior related to a specific export (such as ++@option{--export-name}, @option{--offset}, ...). + @item --tls-creds=ID + Enable mandatory TLS encryption for the server by setting the ID + of the TLS credentials object previously created with the --object +-option. ++option; or provide the credentials needed for connecting as a client ++in list mode. + @item --fork + Fork off the server process and exit the parent once the server is running. + @item -v, --verbose +@@ -162,6 +174,15 @@ qemu-nbd -c /dev/nbd0 -f qcow2 file.qcow2 + qemu-nbd -d /dev/nbd0 + @end example + ++Query a remote server to see details about what export(s) it is ++serving on port 10809, and authenticating via PSK: ++ ++@example ++qemu-nbd \ ++ --object tls-creds-psk,id=tls0,dir=/tmp/keys,username=eblake,endpoint=client \ ++ --tls-creds tls0 -L -b remote.example.com ++@end example ++ + @c man end + + @ignore +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-nbd-Avoid-strtol-open-coding.patch b/SOURCES/kvm-qemu-nbd-Avoid-strtol-open-coding.patch new file mode 100644 index 0000000..c0766b6 --- /dev/null +++ b/SOURCES/kvm-qemu-nbd-Avoid-strtol-open-coding.patch @@ -0,0 +1,112 @@ +From 0a0c3e3e1453856848ff9ee25ecc98045f89e194 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:41 +0100 +Subject: [PATCH 103/163] qemu-nbd: Avoid strtol open-coding + +RH-Author: John Snow +Message-id: <20190327172308.31077-29-jsnow@redhat.com> +Patchwork-id: 85191 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 28/55] qemu-nbd: Avoid strtol open-coding +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Our copy-and-pasted open-coding of strtol handling forgot to +handle overflow conditions. Use qemu_strto*() instead. + +In the case of --partition, since we insist on a user-supplied +partition to be non-zero, we can use 0 rather than -1 for our +initial value to distinguish when a partition is not being +served, for slightly more optimal code. + +The error messages for out-of-bounds values are less specific, +but should not be a terrible loss in quality. + +Signed-off-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: Richard W.M. Jones +Message-Id: <20190117193658.16413-8-eblake@redhat.com> +(cherry picked from commit 43b510113bb2c6393c98a31dae9b57022a9c5636) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + qemu-nbd.c | 28 +++++++++------------------- + 1 file changed, 9 insertions(+), 19 deletions(-) + +diff --git a/qemu-nbd.c b/qemu-nbd.c +index 598caa6..efca0e4 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -546,9 +546,8 @@ int main(int argc, char **argv) + }; + int ch; + int opt_ind = 0; +- char *end; + int flags = BDRV_O_RDWR; +- int partition = -1; ++ int partition = 0; + int ret = 0; + bool seen_cache = false; + bool seen_discard = false; +@@ -660,9 +659,8 @@ int main(int argc, char **argv) + port = optarg; + break; + case 'o': +- dev_offset = strtoll (optarg, &end, 0); +- if (*end) { +- error_report("Invalid offset `%s'", optarg); ++ if (qemu_strtou64(optarg, NULL, 0, &dev_offset) < 0) { ++ error_report("Invalid offset '%s'", optarg); + exit(EXIT_FAILURE); + } + break; +@@ -684,13 +682,9 @@ int main(int argc, char **argv) + flags &= ~BDRV_O_RDWR; + break; + case 'P': +- partition = strtol(optarg, &end, 0); +- if (*end) { +- error_report("Invalid partition `%s'", optarg); +- exit(EXIT_FAILURE); +- } +- if (partition < 1 || partition > 8) { +- error_report("Invalid partition %d", partition); ++ if (qemu_strtoi(optarg, NULL, 0, &partition) < 0 || ++ partition < 1 || partition > 8) { ++ error_report("Invalid partition '%s'", optarg); + exit(EXIT_FAILURE); + } + break; +@@ -711,15 +705,11 @@ int main(int argc, char **argv) + device = optarg; + break; + case 'e': +- shared = strtol(optarg, &end, 0); +- if (*end) { ++ if (qemu_strtoi(optarg, NULL, 0, &shared) < 0 || ++ shared < 1) { + error_report("Invalid shared device number '%s'", optarg); + exit(EXIT_FAILURE); + } +- if (shared < 1) { +- error_report("Shared device number must be greater than 0"); +- exit(EXIT_FAILURE); +- } + break; + case 'f': + fmt = optarg; +@@ -1007,7 +997,7 @@ int main(int argc, char **argv) + } + fd_size -= dev_offset; + +- if (partition != -1) { ++ if (partition) { + uint64_t limit; + + if (dev_offset) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-nbd-Deprecate-qemu-nbd-partition.patch b/SOURCES/kvm-qemu-nbd-Deprecate-qemu-nbd-partition.patch new file mode 100644 index 0000000..e962d6f --- /dev/null +++ b/SOURCES/kvm-qemu-nbd-Deprecate-qemu-nbd-partition.patch @@ -0,0 +1,129 @@ +From f9d66802239e15434d7d0c01a8bc11afb2708a75 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:56 +0100 +Subject: [PATCH 118/163] qemu-nbd: Deprecate qemu-nbd --partition + +RH-Author: John Snow +Message-id: <20190327172308.31077-44-jsnow@redhat.com> +Patchwork-id: 85223 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 43/55] qemu-nbd: Deprecate qemu-nbd --partition +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +The existing qemu-nbd --partition code claims to handle logical +partitions up to 8, since its introduction in 2008 (commit 7a5ca86). +However, the implementation is bogus (actual MBR logical partitions +form a sort of linked list, with one partition per extended table +entry, rather than four logical partitions in a single extended +table), making the code unlikely to work for anything beyond -P5 on +actual guest images. What's more, the code does not support GPT +partitions, which are becoming more popular, and maintaining device +subsetting in both NBD and the raw device is unnecessary duplication +of effort (even if it is not too difficult). + +Note that obtaining the offsets of a partition (MBR or GPT) can be +learned by using 'qemu-nbd -c /dev/nbd0 file.qcow2 && sfdisk --dump +/dev/nbd0', but by the time you've done that, you might as well +just mount /dev/nbd0p1 that the kernel creates for you instead of +bothering with qemu exporting a subset. Or, keeping to just +user-space code, use nbdkit's partition filter, which has already +known both GPT and primary MBR partitions for a while, and was +just recently enhanced to support arbitrary logical MBR parititions. + +Start the clock on the deprecation cycle, with examples of how +to accomplish device subsetting without using -P. + +Signed-off-by: Eric Blake +Message-Id: <20190125234837.2272-1-eblake@redhat.com> +Reviewed-by: Richard W.M. Jones +Reviewed-by: Stefano Garzarella +(cherry picked from commit 0ae2d54645eb2888af6dc7f701bc02ca18e4e656) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + qemu-doc.texi | 33 +++++++++++++++++++++++++++++++++ + qemu-nbd.c | 2 ++ + qemu-nbd.texi | 6 ++++-- + 3 files changed, 39 insertions(+), 2 deletions(-) + +diff --git a/qemu-doc.texi b/qemu-doc.texi +index 88358be..2acbec5 100644 +--- a/qemu-doc.texi ++++ b/qemu-doc.texi +@@ -2987,6 +2987,39 @@ The ``xlnx-zcu102'' machine has the same features and capabilites in QEMU. + In order to prevent QEMU from automatically opening an image's backing + chain, use ``"backing": null'' instead. + ++@section Related binaries ++ ++@subsection qemu-nbd --partition (since 4.0.0) ++ ++The ``qemu-nbd --partition $digit'' code (also spelled @option{-P}) ++can only handle MBR partitions, and has never correctly handled ++logical partitions beyond partition 5. If you know the offset and ++length of the partition (perhaps by using @code{sfdisk} within the ++guest), you can achieve the effect of exporting just that subset of ++the disk by use of the @option{--image-opts} option with a raw ++blockdev using the @code{offset} and @code{size} parameters layered on ++top of any other existing blockdev. For example, if partition 1 is ++100MiB long starting at 1MiB, the old command: ++ ++@code{qemu-nbd -t -P 1 -f qcow2 file.qcow2} ++ ++can be rewritten as: ++ ++@code{qemu-nbd -t --image-opts driver=raw,offset=1M,size=100M,file.driver=qcow2,file.backing.driver=file,file.backing.filename=file.qcow2} ++ ++Alternatively, the @code{nbdkit} project provides a more powerful ++partition filter on top of its nbd plugin, which can be used to select ++an arbitrary MBR or GPT partition on top of any other full-image NBD ++export. Using this to rewrite the above example results in: ++ ++@code{qemu-nbd -t -k /tmp/sock -f qcow2 file.qcow2 &} ++@code{nbdkit -f --filter=partition nbd socket=/tmp/sock partition=1} ++ ++Note that if you are exposing the export via /dev/nbd0, it is easier ++to just export the entire image and then mount only /dev/nbd0p1 than ++it is to reinvoke @command{qemu-nbd -c /dev/nbd0} limited to just a ++subset of the image. ++ + @node License + @appendix License + +diff --git a/qemu-nbd.c b/qemu-nbd.c +index 1f7b2a0..00c07fd 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -787,6 +787,8 @@ int main(int argc, char **argv) + flags &= ~BDRV_O_RDWR; + break; + case 'P': ++ warn_report("The '-P' option is deprecated; use --image-opts with " ++ "a raw device wrapper for subset exports instead"); + if (qemu_strtoi(optarg, NULL, 0, &partition) < 0 || + partition < 1 || partition > 8) { + error_report("Invalid partition '%s'", optarg); +diff --git a/qemu-nbd.texi b/qemu-nbd.texi +index 386bece..d0c5182 100644 +--- a/qemu-nbd.texi ++++ b/qemu-nbd.texi +@@ -56,8 +56,10 @@ auto-detecting. + @item -r, --read-only + Export the disk as read-only. + @item -P, --partition=@var{num} +-Only expose MBR partition @var{num}. Understands physical partitions +-1-4 and logical partitions 5-8. ++Deprecated: Only expose MBR partition @var{num}. Understands physical ++partitions 1-4 and logical partition 5. New code should instead use ++@option{--image-opts} with the raw driver wrapping a subset of the ++original image. + @item -B, --bitmap=@var{name} + If @var{filename} has a qcow2 persistent bitmap @var{name}, expose + that bitmap via the ``qemu:dirty-bitmap:@var{name}'' context +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-nbd-Document-tls-creds.patch b/SOURCES/kvm-qemu-nbd-Document-tls-creds.patch new file mode 100644 index 0000000..ad26030 --- /dev/null +++ b/SOURCES/kvm-qemu-nbd-Document-tls-creds.patch @@ -0,0 +1,44 @@ +From 38e564acf5939c83ded3e324091a1e00631a12f8 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:23 +0100 +Subject: [PATCH 056/163] qemu-nbd: Document --tls-creds + +RH-Author: John Snow +Message-id: <20190322032241.8111-11-jsnow@redhat.com> +Patchwork-id: 85103 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 10/28] qemu-nbd: Document --tls-creds +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Commit 145614a1 introduced --tls-creds and documented it in +qemu-nbd.texi, but forgot to document it in 'qemu-nbd --help'. + +Signed-off-by: Eric Blake +Message-Id: <20181003180426.602765-1-eblake@redhat.com> +Reviewed-by: John Snow +(cherry picked from commit f7812df77d7830c6b375066a4e656f3b79232c13) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + qemu-nbd.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/qemu-nbd.c b/qemu-nbd.c +index 51b9d38..66e023f 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -94,6 +94,7 @@ static void usage(const char *name) + "General purpose options:\n" + " --object type,id=ID,... define an object such as 'secret' for providing\n" + " passwords and/or encryption keys\n" ++" --tls-creds=ID use id of an earlier --object to provide TLS\n" + " -T, --trace [[enable=]][,events=][,file=]\n" + " specify tracing options\n" + " --fork fork off the server process and exit the parent\n" +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-nbd-Enhance-man-page.patch b/SOURCES/kvm-qemu-nbd-Enhance-man-page.patch new file mode 100644 index 0000000..38af71b --- /dev/null +++ b/SOURCES/kvm-qemu-nbd-Enhance-man-page.patch @@ -0,0 +1,199 @@ +From 649cb3fdd8a60caa687452be84fb06de78fead9b Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:37 +0100 +Subject: [PATCH 099/163] qemu-nbd: Enhance man page + +RH-Author: John Snow +Message-id: <20190327172308.31077-25-jsnow@redhat.com> +Patchwork-id: 85186 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 24/55] qemu-nbd: Enhance man page +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Document some useful qemu-nbd command lines. Mention some restrictions +on particular options, like -p being only for MBR images, or -c/-d +being Linux-only. Update some text given the recent change to no +longer serve oldstyle protocol (missed in commit 7f7dfe2a). Also, +consistently use trailing '.' in describing options. + +Signed-off-by: Eric Blake +Reviewed-by: Richard W.M. Jones +Message-Id: <20190117193658.16413-4-eblake@redhat.com> +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit 86b7f6771f0cd1552791d1bfc2bdebd65cf967a3) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + qemu-nbd.texi | 94 +++++++++++++++++++++++++++++++++++++++++++++++------------ + 1 file changed, 75 insertions(+), 19 deletions(-) + +diff --git a/qemu-nbd.texi b/qemu-nbd.texi +index 96b1546..f218291 100644 +--- a/qemu-nbd.texi ++++ b/qemu-nbd.texi +@@ -10,11 +10,17 @@ + + Export a QEMU disk image using the NBD protocol. + ++Other uses: ++@itemize ++@item ++Bind a /dev/nbdX block device to a QEMU server (on Linux). ++@end itemize ++ + @c man end + + @c man begin OPTIONS + @var{filename} is a disk image filename, or a set of block +-driver options if @var{--image-opts} is specified. ++driver options if @option{--image-opts} is specified. + + @var{dev} is an NBD device. + +@@ -27,24 +33,25 @@ supported. The common object types that it makes sense to define are the + keys, and the @code{tls-creds} object, which is used to supply TLS + credentials for the qemu-nbd server. + @item -p, --port=@var{port} +-The TCP port to listen on (default @samp{10809}) ++The TCP port to listen on (default @samp{10809}). + @item -o, --offset=@var{offset} +-The offset into the image ++The offset into the image. + @item -b, --bind=@var{iface} +-The interface to bind to (default @samp{0.0.0.0}) ++The interface to bind to (default @samp{0.0.0.0}). + @item -k, --socket=@var{path} +-Use a unix socket with path @var{path} ++Use a unix socket with path @var{path}. + @item --image-opts + Treat @var{filename} as a set of image options, instead of a plain + filename. If this flag is specified, the @var{-f} flag should + not be used, instead the '@code{format=}' option should be set. + @item -f, --format=@var{fmt} + Force the use of the block driver for format @var{fmt} instead of +-auto-detecting ++auto-detecting. + @item -r, --read-only +-Export the disk as read-only ++Export the disk as read-only. + @item -P, --partition=@var{num} +-Only expose partition @var{num} ++Only expose MBR partition @var{num}. Understands physical partitions ++1-4 and logical partitions 5-8. + @item -B, --bitmap=@var{name} + If @var{filename} has a qcow2 persistent bitmap @var{name}, expose + that bitmap via the ``qemu:dirty-bitmap:@var{name}'' context +@@ -52,7 +59,7 @@ accessible through NBD_OPT_SET_META_CONTEXT. + @item -s, --snapshot + Use @var{filename} as an external snapshot, create a temporary + file with backing_file=@var{filename}, redirect the write to +-the temporary one ++the temporary one. + @item -l, --load-snapshot=@var{snapshot_param} + Load an internal snapshot inside @var{filename} and export it + as an read-only device, @var{snapshot_param} format is +@@ -76,19 +83,20 @@ driver-specific optimized zero write commands. @var{detect-zeroes} is one of + converts a zero write to an unmap operation and can only be used if + @var{discard} is set to @samp{unmap}. The default is @samp{off}. + @item -c, --connect=@var{dev} +-Connect @var{filename} to NBD device @var{dev} ++Connect @var{filename} to NBD device @var{dev} (Linux only). + @item -d, --disconnect +-Disconnect the device @var{dev} ++Disconnect the device @var{dev} (Linux only). + @item -e, --shared=@var{num} +-Allow up to @var{num} clients to share the device (default @samp{1}) ++Allow up to @var{num} clients to share the device (default ++@samp{1}). Safe for readers, but for now, consistency is not ++guaranteed between multiple writers. + @item -t, --persistent +-Don't exit on the last connection ++Don't exit on the last connection. + @item -x, --export-name=@var{name} +-Set the NBD volume export name. This switches the server to use +-the new style NBD protocol negotiation ++Set the NBD volume export name (default of a zero-length string). + @item -D, --description=@var{description} + Set the NBD volume export description, as a human-readable +-string. Requires the use of @option{-x} ++string. + @item --tls-creds=ID + Enable mandatory TLS encryption for the server by setting the ID + of the TLS credentials object previously created with the --object +@@ -96,11 +104,11 @@ option. + @item --fork + Fork off the server process and exit the parent once the server is running. + @item -v, --verbose +-Display extra debugging information ++Display extra debugging information. + @item -h, --help +-Display this help and exit ++Display this help and exit. + @item -V, --version +-Display version information and exit ++Display version information and exit. + @item -T, --trace [[enable=]@var{pattern}][,events=@var{file}][,file=@var{file}] + @findex --trace + @include qemu-option-trace.texi +@@ -108,6 +116,54 @@ Display version information and exit + + @c man end + ++@c man begin EXAMPLES ++Start a server listening on port 10809 that exposes only the ++guest-visible contents of a qcow2 file, with no TLS encryption, and ++with the default export name (an empty string). The command is ++one-shot, and will block until the first successful client ++disconnects: ++ ++@example ++qemu-nbd -f qcow2 file.qcow2 ++@end example ++ ++Start a long-running server listening with encryption on port 10810, ++and require clients to have a correct X.509 certificate to connect to ++a 1 megabyte subset of a raw file, using the export name 'subset': ++ ++@example ++qemu-nbd \ ++ --object tls-creds-x509,id=tls0,endpoint=server,dir=/path/to/qemutls \ ++ --tls-creds tls0 -t -x subset -p 10810 \ ++ --image-opts driver=raw,offset=1M,size=1M,file.driver=file,file.filename=file.raw ++@end example ++ ++Serve a read-only copy of just the first MBR partition of a guest ++image over a Unix socket with as many as 5 simultaneous readers, with ++a persistent process forked as a daemon: ++ ++@example ++qemu-nbd --fork --persistent --shared=5 --socket=/path/to/sock \ ++ --partition=1 --read-only --format=qcow2 file.qcow2 ++@end example ++ ++Expose the guest-visible contents of a qcow2 file via a block device ++/dev/nbd0 (and possibly creating /dev/nbd0p1 and friends for ++partitions found within), then disconnect the device when done. ++Access to bind qemu-nbd to an /dev/nbd device generally requires root ++privileges, and may also require the execution of @code{modprobe nbd} ++to enable the kernel NBD client module. @emph{CAUTION}: Do not use ++this method to mount filesystems from an untrusted guest image - a ++malicious guest may have prepared the image to attempt to trigger ++kernel bugs in partition probing or file system mounting. ++ ++@example ++qemu-nbd -c /dev/nbd0 -f qcow2 file.qcow2 ++qemu-nbd -d /dev/nbd0 ++@end example ++ ++@c man end ++ + @ignore + + @setfilename qemu-nbd +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-nbd-Fail-earlier-for-c-d-on-non-linux.patch b/SOURCES/kvm-qemu-nbd-Fail-earlier-for-c-d-on-non-linux.patch new file mode 100644 index 0000000..8302113 --- /dev/null +++ b/SOURCES/kvm-qemu-nbd-Fail-earlier-for-c-d-on-non-linux.patch @@ -0,0 +1,158 @@ +From fd123e372472179657da738e52e87897d0508812 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:23 +0100 +Subject: [PATCH 084/163] qemu-nbd: Fail earlier for -c/-d on non-linux + +RH-Author: John Snow +Message-id: <20190327172308.31077-11-jsnow@redhat.com> +Patchwork-id: 85173 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 10/55] qemu-nbd: Fail earlier for -c/-d on non-linux +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Connecting to a /dev/nbdN device is a Linux-specific action. +We were already masking -c and -d from 'qemu-nbd --help' on +non-linux. However, while -d fails with a sensible error +message, it took hunting through a couple of files to prove +that. What's more, the code for -c doesn't fail until after +it has created a pthread and tried to open a device - possibly +even printing an error message with %m on a non-Linux platform +in spite of the comment that %m is glibc-specific. Make the +failure happen sooner, then get rid of stubs that are no +longer needed because of the early exits. + +While at it: tweak the blank newlines in --help output to be +consistent, whether or not built on Linux. + +Signed-off-by: Eric Blake +Message-Id: <20181215135324.152629-7-eblake@redhat.com> +Reviewed-by: Richard W.M. Jones +Reviewed-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit 3c1fa35d74aabe9c3ab642d2591b087e53d7a616) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + nbd/client.c | 18 +----------------- + qemu-nbd.c | 21 +++++++++++++++++++-- + 2 files changed, 20 insertions(+), 19 deletions(-) + +diff --git a/nbd/client.c b/nbd/client.c +index e774147..5a03a84 100644 +--- a/nbd/client.c ++++ b/nbd/client.c +@@ -1031,23 +1031,7 @@ int nbd_disconnect(int fd) + return 0; + } + +-#else +-int nbd_init(int fd, QIOChannelSocket *ioc, NBDExportInfo *info, +- Error **errp) +-{ +- error_setg(errp, "nbd_init is only supported on Linux"); +- return -ENOTSUP; +-} +- +-int nbd_client(int fd) +-{ +- return -ENOTSUP; +-} +-int nbd_disconnect(int fd) +-{ +- return -ENOTSUP; +-} +-#endif ++#endif /* __linux__ */ + + int nbd_send_request(QIOChannel *ioc, NBDRequest *request) + { +diff --git a/qemu-nbd.c b/qemu-nbd.c +index 7f078b2..652199c 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -43,6 +43,12 @@ + #include "trace/control.h" + #include "qemu-version.h" + ++#ifdef __linux__ ++#define HAVE_NBD_DEVICE 1 ++#else ++#define HAVE_NBD_DEVICE 0 ++#endif ++ + #define SOCKET_PATH "/var/lock/qemu-nbd-%s" + #define QEMU_NBD_OPT_CACHE 256 + #define QEMU_NBD_OPT_AIO 257 +@@ -98,11 +104,11 @@ static void usage(const char *name) + " specify tracing options\n" + " --fork fork off the server process and exit the parent\n" + " once the server is running\n" +-#ifdef __linux__ ++#if HAVE_NBD_DEVICE ++"\n" + "Kernel NBD client support:\n" + " -c, --connect=DEV connect FILE to the local NBD device DEV\n" + " -d, --disconnect disconnect the specified device\n" +-"\n" + #endif + "\n" + "Block device options:\n" +@@ -236,6 +242,7 @@ static void termsig_handler(int signum) + } + + ++#if HAVE_NBD_DEVICE + static void *show_parts(void *arg) + { + char *device = arg; +@@ -321,6 +328,7 @@ out: + kill(getpid(), SIGTERM); + return (void *) EXIT_FAILURE; + } ++#endif /* HAVE_NBD_DEVICE */ + + static int nbd_can_accept(void) + { +@@ -816,6 +824,12 @@ int main(int argc, char **argv) + } + } + ++#if !HAVE_NBD_DEVICE ++ if (disconnect || device) { ++ error_report("Kernel /dev/nbdN support not available"); ++ exit(EXIT_FAILURE); ++ } ++#else /* HAVE_NBD_DEVICE */ + if (disconnect) { + int nbdfd = open(argv[optind], O_RDWR); + if (nbdfd < 0) { +@@ -831,6 +845,7 @@ int main(int argc, char **argv) + + return 0; + } ++#endif + + if ((device && !verbose) || fork_process) { + int stderr_fd[2]; +@@ -1012,6 +1027,7 @@ int main(int argc, char **argv) + nbd_export_set_description(exp, export_description); + + if (device) { ++#if HAVE_NBD_DEVICE + int ret; + + ret = pthread_create(&client_thread, NULL, nbd_client_thread, device); +@@ -1019,6 +1035,7 @@ int main(int argc, char **argv) + error_report("Failed to create client thread: %s", strerror(ret)); + exit(EXIT_FAILURE); + } ++#endif + } else { + /* Shut up GCC warnings. */ + memset(&client_thread, 0, sizeof(client_thread)); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-nbd-Rename-exp-variable-clashing-with-math-exp-.patch b/SOURCES/kvm-qemu-nbd-Rename-exp-variable-clashing-with-math-exp-.patch new file mode 100644 index 0000000..0b34ad8 --- /dev/null +++ b/SOURCES/kvm-qemu-nbd-Rename-exp-variable-clashing-with-math-exp-.patch @@ -0,0 +1,106 @@ +From b7bec5a423cc8bb7792e231e3d81d015d9278d91 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:25 +0100 +Subject: [PATCH 086/163] qemu-nbd: Rename 'exp' variable clashing with + math::exp() symbol +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190327172308.31077-13-jsnow@redhat.com> +Patchwork-id: 85184 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 12/55] qemu-nbd: Rename 'exp' variable clashing with math::exp() symbol +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Philippe Mathieu-Daudé + +The use of a variable named 'exp' prevents includes to import . + +Rename it to avoid: + + qemu-nbd.c:64:19: error: ‘exp’ redeclared as different kind of symbol + static NBDExport *exp; + ^~~ + In file included from /usr/include/features.h:428, + from /usr/include/bits/libc-header-start.h:33, + from /usr/include/stdint.h:26, + from /usr/lib/gcc/x86_64-redhat-linux/8/include/stdint.h:9, + from /source/qemu/include/qemu/osdep.h:80, + from /source/qemu/qemu-nbd.c:19: + /usr/include/bits/mathcalls.h:95:1: note: previous declaration of ‘exp’ was here + __MATHCALL_VEC (exp,, (_Mdouble_ __x)); + ^~~~~~~~~~~~~~ + +Signed-off-by: Philippe Mathieu-Daudé +Reviewed-by: Eric Blake +Message-Id: <20190111163519.11457-1-philmd@redhat.com> +Signed-off-by: Eric Blake +(cherry picked from commit 9d97658020db922b68da05faadcdd61f49fbbdc7) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + qemu-nbd.c | 23 ++++++++++------------- + 1 file changed, 10 insertions(+), 13 deletions(-) + +diff --git a/qemu-nbd.c b/qemu-nbd.c +index 652199c..c37defb 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -61,7 +61,7 @@ + + #define MBR_SIZE 512 + +-static NBDExport *exp; ++static NBDExport *export; + static int verbose; + static char *srcpath; + static SocketAddress *saddr; +@@ -335,7 +335,7 @@ static int nbd_can_accept(void) + return state == RUNNING && nb_fds < shared; + } + +-static void nbd_export_closed(NBDExport *exp) ++static void nbd_export_closed(NBDExport *export) + { + assert(state == TERMINATING); + state = TERMINATED; +@@ -1017,14 +1017,11 @@ int main(int argc, char **argv) + } + } + +- exp = nbd_export_new(bs, dev_offset, fd_size, nbdflags, nbd_export_closed, +- writethrough, NULL, &local_err); +- if (!exp) { +- error_report_err(local_err); +- exit(EXIT_FAILURE); +- } +- nbd_export_set_name(exp, export_name); +- nbd_export_set_description(exp, export_description); ++ export = nbd_export_new(bs, dev_offset, fd_size, nbdflags, ++ nbd_export_closed, writethrough, ++ NULL, &error_fatal); ++ nbd_export_set_name(export, export_name); ++ nbd_export_set_description(export, export_description); + + if (device) { + #if HAVE_NBD_DEVICE +@@ -1061,9 +1058,9 @@ int main(int argc, char **argv) + main_loop_wait(false); + if (state == TERMINATE) { + state = TERMINATING; +- nbd_export_close(exp); +- nbd_export_put(exp); +- exp = NULL; ++ nbd_export_close(export); ++ nbd_export_put(export); ++ export = NULL; + } + } while (state != TERMINATED); + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-nbd-Sanity-check-partition-bounds.patch b/SOURCES/kvm-qemu-nbd-Sanity-check-partition-bounds.patch new file mode 100644 index 0000000..60b8229 --- /dev/null +++ b/SOURCES/kvm-qemu-nbd-Sanity-check-partition-bounds.patch @@ -0,0 +1,110 @@ +From 58096dddd1682a83fb782a49d80199da9a117ef9 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:38 +0100 +Subject: [PATCH 100/163] qemu-nbd: Sanity check partition bounds + +RH-Author: John Snow +Message-id: <20190327172308.31077-26-jsnow@redhat.com> +Patchwork-id: 85207 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 25/55] qemu-nbd: Sanity check partition bounds +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +When the user requests a partition, we were using data read +from the disk as disk offsets without a bounds check. We got +lucky that even when computed offsets are out-of-bounds, +blk_pread() will gracefully catch the error later (so I don't +think a malicious image can crash or exploit qemu-nbd, and am +not treating this as a security flaw), but it's better to +flag the problem up front than to risk permanent EIO death of +the block device down the road. The new bounds check adds +an assertion that will never fail, but rather exists to help +the compiler see that adding two positive 41-bit values +(given MBR constraints) can't overflow 64-bit off_t. + +Using off_t to represent a partition length is a bit of a +misnomer; a later patch will update to saner types, but it +is left separate in case the bounds check needs to be +backported in isolation. + +Also, note that the partition code blindly overwrites any +non-zero offset passed in by the user; so for now, make the +-o/-P combo an error for less confusion. In the future, we +may let -o and -P work together (selecting a subset of a +partition); so it is okay that an explicit '-o 0' behaves +no differently from omitting -o. + +This can be tested with nbdkit: +$ echo hi > file +$ nbdkit -fv --filter=truncate partitioning file truncate=64k + +Pre-patch: +$ qemu-nbd -p 10810 -P 1 -f raw nbd://localhost:10809 & +$ qemu-io -f raw nbd://localhost:10810 +qemu-io> r -v 0 1 +Disconnect client, due to: Failed to send reply: reading from file failed: Input/output error +Connection closed +read failed: Input/output error +qemu-io> q +[1]+ Done qemu-nbd -p 10810 -P 1 -f raw nbd://localhost:10809 + +Post-patch: +$ qemu-nbd -p 10810 -P 1 -f raw nbd://localhost:10809 +qemu-nbd: Discovered partition 1 at offset 1048576 size 512, but size exceeds file length 65536 + +Signed-off-by: Eric Blake +Reviewed-by: Vladimir Sementsov-Ogievskiy +Reviewed-by: Richard W.M. Jones +Message-Id: <20190117193658.16413-5-eblake@redhat.com> +(cherry picked from commit 4485936b6de20afa38138e9d1e8ffed88cf0be73) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + qemu-nbd.c | 22 +++++++++++++++++++++- + 1 file changed, 21 insertions(+), 1 deletion(-) + +diff --git a/qemu-nbd.c b/qemu-nbd.c +index 51b55f2..5c90c5e 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -1013,12 +1013,32 @@ int main(int argc, char **argv) + fd_size -= dev_offset; + + if (partition != -1) { +- ret = find_partition(blk, partition, &dev_offset, &fd_size); ++ off_t limit; ++ ++ if (dev_offset) { ++ error_report("Cannot request partition and offset together"); ++ exit(EXIT_FAILURE); ++ } ++ ret = find_partition(blk, partition, &dev_offset, &limit); + if (ret < 0) { + error_report("Could not find partition %d: %s", partition, + strerror(-ret)); + exit(EXIT_FAILURE); + } ++ /* ++ * MBR partition limits are (32-bit << 9); this assert lets ++ * the compiler know that we have two positive values that ++ * can't overflow 64 bits. ++ */ ++ assert(dev_offset >= 0 && dev_offset + limit >= dev_offset); ++ if (dev_offset + limit > fd_size) { ++ error_report("Discovered partition %d at offset %lld size %lld, " ++ "but size exceeds file length %lld", partition, ++ (long long int) dev_offset, (long long int) limit, ++ (long long int) fd_size); ++ exit(EXIT_FAILURE); ++ } ++ fd_size = limit; + } + + export = nbd_export_new(bs, dev_offset, fd_size, export_name, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-nbd-Use-program-name-in-error-messages.patch b/SOURCES/kvm-qemu-nbd-Use-program-name-in-error-messages.patch new file mode 100644 index 0000000..a112ce0 --- /dev/null +++ b/SOURCES/kvm-qemu-nbd-Use-program-name-in-error-messages.patch @@ -0,0 +1,69 @@ +From 9a2c829fe94fb3bb241cf9f22cd206c4392b87f3 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:20 +0100 +Subject: [PATCH 081/163] qemu-nbd: Use program name in error messages + +RH-Author: John Snow +Message-id: <20190327172308.31077-8-jsnow@redhat.com> +Patchwork-id: 85174 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 07/55] qemu-nbd: Use program name in error messages +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +This changes output from: + +$ qemu-nbd nosuch +Failed to blk_new_open 'nosuch': Could not open 'nosuch': No such file or directory + +to something more consistent with qemu-img and qemu: + +$ qemu-nbd nosuch +qemu-nbd: Failed to blk_new_open 'nosuch': Could not open 'nosuch': No such file or directory + +Update the lone affected test to match. (Hmm - is it sad that we don't +do much testing of expected failures?) + +Signed-off-by: Eric Blake +Reviewed-by: Richard W.M. Jones +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20181215135324.152629-2-eblake@redhat.com> +(cherry picked from commit 3ba1b7baf4b2518e8efdbe50175d909b79c21596) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + qemu-nbd.c | 1 + + tests/qemu-iotests/233.out | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/qemu-nbd.c b/qemu-nbd.c +index e76fe30..7f078b2 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -571,6 +571,7 @@ int main(int argc, char **argv) + #endif + + module_call_init(MODULE_INIT_TRACE); ++ error_set_progname(argv[0]); + qcrypto_init(&error_fatal); + + module_call_init(MODULE_INIT_QOM); +diff --git a/tests/qemu-iotests/233.out b/tests/qemu-iotests/233.out +index 94acd9b..5f41672 100644 +--- a/tests/qemu-iotests/233.out ++++ b/tests/qemu-iotests/233.out +@@ -27,7 +27,7 @@ virtual size: 64M (67108864 bytes) + disk size: unavailable + + == check TLS with different CA fails == +-option negotiation failed: Verify failed: No certificate was found. ++qemu-nbd: option negotiation failed: Verify failed: No certificate was found. + qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': The certificate hasn't got a known issuer + + == perform I/O over TLS == +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-nbd-drop-old-style-negotiation.patch b/SOURCES/kvm-qemu-nbd-drop-old-style-negotiation.patch new file mode 100644 index 0000000..e5ebb7e --- /dev/null +++ b/SOURCES/kvm-qemu-nbd-drop-old-style-negotiation.patch @@ -0,0 +1,121 @@ +From e7b2b502ad9b10b852c507447e34e3212a274cee Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:24 +0100 +Subject: [PATCH 057/163] qemu-nbd: drop old-style negotiation + +RH-Author: John Snow +Message-id: <20190322032241.8111-12-jsnow@redhat.com> +Patchwork-id: 85096 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 11/28] qemu-nbd: drop old-style negotiation +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +Use new-style negotiation always, with default "" (empty) export name +if it is not specified with '-x' option. + +qemu as client can manage either style since 2.6.0, commit 69b49502d8 + +For comparison: + +nbd 3.10 dropped oldstyle long ago (Mar 2015): +https://github.com/NetworkBlockDevice/nbd/commit/36940193 + +nbdkit 1.3 switched its default to newstyle (Jan 2018): +https://github.com/libguestfs/nbdkit/commit/b2a8aecc +https://github.com/libguestfs/nbdkit/commit/8158e773 + +Furthermore, if a client that only speaks oldstyle still needs to +communicate to qemu, nbdkit remains available to perform the +translation between the two protocols. + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Message-Id: <20181003170228.95973-2-vsementsov@virtuozzo.com> +Reviewed-by: Eric Blake +[eblake: enhance commit message] +Signed-off-by: Eric Blake +(cherry picked from commit f5cd0bb5174dcd6e8c160d7992fb89f09f264ef0) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + qemu-nbd.c | 25 ++++++------------------- + 1 file changed, 6 insertions(+), 19 deletions(-) + +diff --git a/qemu-nbd.c b/qemu-nbd.c +index 66e023f..6aaebe7 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -56,7 +56,6 @@ + #define MBR_SIZE 512 + + static NBDExport *exp; +-static bool newproto; + static int verbose; + static char *srcpath; + static SocketAddress *saddr; +@@ -84,8 +83,8 @@ static void usage(const char *name) + " -e, --shared=NUM device can be shared by NUM clients (default '1')\n" + " -t, --persistent don't exit on the last connection\n" + " -v, --verbose display extra debugging information\n" +-" -x, --export-name=NAME expose export by name\n" +-" -D, --description=TEXT with -x, also export a human-readable description\n" ++" -x, --export-name=NAME expose export by name (default is empty string)\n" ++" -D, --description=TEXT export a human-readable description\n" + "\n" + "Exposing part of the image:\n" + " -o, --offset=OFFSET offset into the image\n" +@@ -355,8 +354,7 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc, + + nb_fds++; + nbd_update_server_watch(); +- nbd_client_new(newproto ? NULL : exp, cioc, +- tlscreds, NULL, nbd_client_closed); ++ nbd_client_new(NULL, cioc, tlscreds, NULL, nbd_client_closed); + } + + static void nbd_update_server_watch(void) +@@ -550,7 +548,7 @@ int main(int argc, char **argv) + Error *local_err = NULL; + BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; + QDict *options = NULL; +- const char *export_name = NULL; ++ const char *export_name = ""; /* Default export name */ + const char *export_description = NULL; + const char *tlscredsid = NULL; + bool imageOpts = false; +@@ -809,11 +807,6 @@ int main(int argc, char **argv) + error_report("TLS is not supported with a host device"); + exit(EXIT_FAILURE); + } +- if (!export_name) { +- /* Set the default NBD protocol export name, since +- * we *must* use new style protocol for TLS */ +- export_name = ""; +- } + tlscreds = nbd_get_tls_creds(tlscredsid, &local_err); + if (local_err) { + error_report("Failed to get TLS creds %s", +@@ -1014,14 +1007,8 @@ int main(int argc, char **argv) + error_report_err(local_err); + exit(EXIT_FAILURE); + } +- if (export_name) { +- nbd_export_set_name(exp, export_name); +- nbd_export_set_description(exp, export_description); +- newproto = true; +- } else if (export_description) { +- error_report("Export description requires an export name"); +- exit(EXIT_FAILURE); +- } ++ nbd_export_set_name(exp, export_name); ++ nbd_export_set_description(exp, export_description); + + if (device) { + int ret; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-option-Pull-out-Supported-options-print.patch b/SOURCES/kvm-qemu-option-Pull-out-Supported-options-print.patch new file mode 100644 index 0000000..d2de31b --- /dev/null +++ b/SOURCES/kvm-qemu-option-Pull-out-Supported-options-print.patch @@ -0,0 +1,56 @@ +From 1f1faf92011618b0d32dc6a4a7e5b5119c38c2ac Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 18 Jun 2018 14:59:39 +0200 +Subject: [PATCH 05/89] qemu-option: Pull out "Supported options" print + +RH-Author: Max Reitz +Message-id: <20180618145943.4489-4-mreitz@redhat.com> +Patchwork-id: 80756 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 3/7] qemu-option: Pull out "Supported options" print +Bugzilla: 1537956 +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf +RH-Acked-by: Stefan Hajnoczi + +It really is up to the caller to decide what this list of options means. + +Signed-off-by: Max Reitz +Reviewed-by: John Snow +Reviewed-by: Eric Blake +Message-id: 20180509210023.20283-4-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit 7f3fb00136aaec74df8132492d1f48b8e9840258) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + qemu-img.c | 1 + + util/qemu-option.c | 1 - + 2 files changed, 1 insertion(+), 1 deletion(-) + +diff --git a/qemu-img.c b/qemu-img.c +index e40d6ff..cdeb01b 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -261,6 +261,7 @@ static int print_block_option_help(const char *filename, const char *fmt) + create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); + } + ++ printf("Supported options:\n"); + qemu_opts_print_help(create_opts); + qemu_opts_free(create_opts); + return 0; +diff --git a/util/qemu-option.c b/util/qemu-option.c +index d0756fd..95e6cf4 100644 +--- a/util/qemu-option.c ++++ b/util/qemu-option.c +@@ -218,7 +218,6 @@ void qemu_opts_print_help(QemuOptsList *list) + + assert(list); + desc = list->desc; +- printf("Supported options:\n"); + while (desc && desc->name) { + printf("%-16s %s\n", desc->name, + desc->help ? desc->help : "No description available"); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qemu-options-Add-missing-newline-to-accel-help-text.patch b/SOURCES/kvm-qemu-options-Add-missing-newline-to-accel-help-text.patch new file mode 100644 index 0000000..987a129 --- /dev/null +++ b/SOURCES/kvm-qemu-options-Add-missing-newline-to-accel-help-text.patch @@ -0,0 +1,54 @@ +From 93dbdd330d91333053406b0a2bb9c4404dd210d3 Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Tue, 3 Jul 2018 13:27:24 +0200 +Subject: [PATCH 57/57] qemu-options: Add missing newline to -accel help text + +RH-Author: Eduardo Habkost +Message-id: <20180703132724.21964-2-ehabkost@redhat.com> +Patchwork-id: 81208 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/1] qemu-options: Add missing newline to -accel help text +Bugzilla: 1586313 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Igor Mammedov + +The newline was removed by commit c97d6d2c, and broke -help output: + +Before this patch: + + $ qemu-system-x86_64 -help | grep smp + thread=single|multi (enable multi-threaded TCG)-smp [...] + +After this patch: + + $ qemu-system-x86_64 -help | grep smp + -smp [cpus=]n[,maxcpus=cpus][,cores=cores][,threads=threads][,sockets=sockets] + +Fixes: c97d6d2cdf97edb4aebe832fdba65d701ad7bcb6 +Cc: Sergio Andres Gomez Del Real +Signed-off-by: Eduardo Habkost +Message-Id: <20180611195607.3015-1-ehabkost@redhat.com> +Signed-off-by: Paolo Bonzini +(cherry picked from commit 0b3c5c81bf0a9e32fd08c532acde3caa446b3712) +Signed-off-by: Eduardo Habkost +Signed-off-by: Miroslav Rezanina +--- + qemu-options.hx | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/qemu-options.hx b/qemu-options.hx +index 43f10b1..4271cd3 100644 +--- a/qemu-options.hx ++++ b/qemu-options.hx +@@ -130,7 +130,7 @@ ETEXI + DEF("accel", HAS_ARG, QEMU_OPTION_accel, + "-accel [accel=]accelerator[,thread=single|multi]\n" + " select accelerator (kvm, xen, hax, hvf, whpx or tcg; use 'help' for a list)\n" +- " thread=single|multi (enable multi-threaded TCG)", QEMU_ARCH_ALL) ++ " thread=single|multi (enable multi-threaded TCG)\n", QEMU_ARCH_ALL) + STEXI + @item -accel @var{name}[,prop=@var{value}[,...]] + @findex -accel +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qmp-transaction-support-for-x-block-dirty-bitmap-ena.patch b/SOURCES/kvm-qmp-transaction-support-for-x-block-dirty-bitmap-ena.patch new file mode 100644 index 0000000..d3aece2 --- /dev/null +++ b/SOURCES/kvm-qmp-transaction-support-for-x-block-dirty-bitmap-ena.patch @@ -0,0 +1,163 @@ +From a2c0037a5871c5982b742f255f6580f30482f1fc Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 18 Jul 2018 22:54:39 +0200 +Subject: [PATCH 54/89] qmp: transaction support for + x-block-dirty-bitmap-enable/disable + +RH-Author: John Snow +Message-id: <20180718225511.14878-4-jsnow@redhat.com> +Patchwork-id: 81410 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 03/35] qmp: transaction support for x-block-dirty-bitmap-enable/disable +Bugzilla: 1207657 +RH-Acked-by: Eric Blake +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Fam Zheng + +From: Vladimir Sementsov-Ogievskiy + +Signed-off-by: Vladimir Sementsov-Ogievskiy +Signed-off-by: John Snow +Reviewed-by: Jeff Cody +Message-id: 20180606182449.1607-4-jsnow@redhat.com +[Added x- prefix. --js] +Signed-off-by: John Snow +(cherry picked from commit c6490447402f574bbe45f45c318e0bdd19121baf) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + blockdev.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++- + qapi/transaction.json | 4 +++ + 2 files changed, 84 insertions(+), 1 deletion(-) + +diff --git a/blockdev.c b/blockdev.c +index ca2ffff..a88d792 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -2094,6 +2094,7 @@ typedef struct BlockDirtyBitmapState { + BlockDriverState *bs; + HBitmap *backup; + bool prepared; ++ bool was_enabled; + } BlockDirtyBitmapState; + + static void block_dirty_bitmap_add_prepare(BlkActionState *common, +@@ -2193,6 +2194,74 @@ static void block_dirty_bitmap_clear_commit(BlkActionState *common) + hbitmap_free(state->backup); + } + ++static void block_dirty_bitmap_enable_prepare(BlkActionState *common, ++ Error **errp) ++{ ++ BlockDirtyBitmap *action; ++ BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, ++ common, common); ++ ++ if (action_check_completion_mode(common, errp) < 0) { ++ return; ++ } ++ ++ action = common->action->u.x_block_dirty_bitmap_enable.data; ++ state->bitmap = block_dirty_bitmap_lookup(action->node, ++ action->name, ++ NULL, ++ errp); ++ if (!state->bitmap) { ++ return; ++ } ++ ++ state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap); ++ bdrv_enable_dirty_bitmap(state->bitmap); ++} ++ ++static void block_dirty_bitmap_enable_abort(BlkActionState *common) ++{ ++ BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, ++ common, common); ++ ++ if (!state->was_enabled) { ++ bdrv_disable_dirty_bitmap(state->bitmap); ++ } ++} ++ ++static void block_dirty_bitmap_disable_prepare(BlkActionState *common, ++ Error **errp) ++{ ++ BlockDirtyBitmap *action; ++ BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, ++ common, common); ++ ++ if (action_check_completion_mode(common, errp) < 0) { ++ return; ++ } ++ ++ action = common->action->u.x_block_dirty_bitmap_disable.data; ++ state->bitmap = block_dirty_bitmap_lookup(action->node, ++ action->name, ++ NULL, ++ errp); ++ if (!state->bitmap) { ++ return; ++ } ++ ++ state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap); ++ bdrv_disable_dirty_bitmap(state->bitmap); ++} ++ ++static void block_dirty_bitmap_disable_abort(BlkActionState *common) ++{ ++ BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, ++ common, common); ++ ++ if (state->was_enabled) { ++ bdrv_enable_dirty_bitmap(state->bitmap); ++ } ++} ++ + static void abort_prepare(BlkActionState *common, Error **errp) + { + error_setg(errp, "Transaction aborted using Abort action"); +@@ -2253,7 +2322,17 @@ static const BlkActionOps actions[] = { + .prepare = block_dirty_bitmap_clear_prepare, + .commit = block_dirty_bitmap_clear_commit, + .abort = block_dirty_bitmap_clear_abort, +- } ++ }, ++ [TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_ENABLE] = { ++ .instance_size = sizeof(BlockDirtyBitmapState), ++ .prepare = block_dirty_bitmap_enable_prepare, ++ .abort = block_dirty_bitmap_enable_abort, ++ }, ++ [TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_DISABLE] = { ++ .instance_size = sizeof(BlockDirtyBitmapState), ++ .prepare = block_dirty_bitmap_disable_prepare, ++ .abort = block_dirty_bitmap_disable_abort, ++ } + }; + + /** +diff --git a/qapi/transaction.json b/qapi/transaction.json +index bd31279..d7e4274 100644 +--- a/qapi/transaction.json ++++ b/qapi/transaction.json +@@ -46,6 +46,8 @@ + # - @abort: since 1.6 + # - @block-dirty-bitmap-add: since 2.5 + # - @block-dirty-bitmap-clear: since 2.5 ++# - @x-block-dirty-bitmap-enable: since 3.0 ++# - @x-block-dirty-bitmap-disable: since 3.0 + # - @blockdev-backup: since 2.3 + # - @blockdev-snapshot: since 2.5 + # - @blockdev-snapshot-internal-sync: since 1.7 +@@ -59,6 +61,8 @@ + 'abort': 'Abort', + 'block-dirty-bitmap-add': 'BlockDirtyBitmapAdd', + 'block-dirty-bitmap-clear': 'BlockDirtyBitmap', ++ 'x-block-dirty-bitmap-enable': 'BlockDirtyBitmap', ++ 'x-block-dirty-bitmap-disable': 'BlockDirtyBitmap', + 'blockdev-backup': 'BlockdevBackup', + 'blockdev-snapshot': 'BlockdevSnapshot', + 'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal', +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qobject-Ensure-base-is-at-offset-0.patch b/SOURCES/kvm-qobject-Ensure-base-is-at-offset-0.patch new file mode 100644 index 0000000..74f05aa --- /dev/null +++ b/SOURCES/kvm-qobject-Ensure-base-is-at-offset-0.patch @@ -0,0 +1,76 @@ +From 94cd049b8c81e2b58113db7cc289218f8925d27d Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:09 +0200 +Subject: [PATCH 02/54] qobject: Ensure base is at offset 0 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-3-armbru@redhat.com> +Patchwork-id: 80724 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 02/23] qobject: Ensure base is at offset 0 +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +From: Marc-André Lureau + +All QObject types have the base QObject as their first field. This +allows the simplification of qobject_to(). + +Signed-off-by: Marc-André Lureau +Reviewed-by: Eric Blake +Message-Id: <20180419150145.24795-2-marcandre.lureau@redhat.com> +Reviewed-by: Markus Armbruster +[Commit message paragraph on type casts dropped, to avoid giving the +impression type casting would be okay] +Signed-off-by: Markus Armbruster +(cherry picked from commit 7ee9edfdb117da47c86c9764d90f0be11a648666) + +Signed-off-by: Miroslav Rezanina +--- + include/qapi/qmp/qobject.h | 5 ++--- + qobject/qobject.c | 9 +++++++++ + 2 files changed, 11 insertions(+), 3 deletions(-) + +diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h +index e022707..5206ff9 100644 +--- a/include/qapi/qmp/qobject.h ++++ b/include/qapi/qmp/qobject.h +@@ -61,9 +61,8 @@ struct QObject { + QEMU_BUILD_BUG_MSG(QTYPE__MAX != 7, + "The QTYPE_CAST_TO_* list needs to be extended"); + +-#define qobject_to(type, obj) ({ \ +- QObject *_tmp = qobject_check_type(obj, glue(QTYPE_CAST_TO_, type)); \ +- _tmp ? container_of(_tmp, type, base) : (type *)NULL; }) ++#define qobject_to(type, obj) \ ++ ((type *)qobject_check_type(obj, glue(QTYPE_CAST_TO_, type))) + + /* Initialize an object to default values */ + static inline void qobject_init(QObject *obj, QType type) +diff --git a/qobject/qobject.c b/qobject/qobject.c +index 23600aa..87649c5 100644 +--- a/qobject/qobject.c ++++ b/qobject/qobject.c +@@ -16,6 +16,15 @@ + #include "qapi/qmp/qlist.h" + #include "qapi/qmp/qstring.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 */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qobject-Modify-qobject_ref-to-return-obj.patch b/SOURCES/kvm-qobject-Modify-qobject_ref-to-return-obj.patch new file mode 100644 index 0000000..e24382b --- /dev/null +++ b/SOURCES/kvm-qobject-Modify-qobject_ref-to-return-obj.patch @@ -0,0 +1,437 @@ +From 2b24bd311f6a7a416432078f9e76f772759c7ea2 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:12 +0200 +Subject: [PATCH 05/54] qobject: Modify qobject_ref() to return obj +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-6-armbru@redhat.com> +Patchwork-id: 80738 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 05/23] qobject: Modify qobject_ref() to return obj +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +From: Marc-André Lureau + +For convenience and clarity, make it possible to call qobject_ref() at +the time when the reference is associated with a variable, or +argument, by making qobject_ref() return the same pointer as given. +Use that to simplify the callers. + +Signed-off-by: Marc-André Lureau +Reviewed-by: Eric Blake +Message-Id: <20180419150145.24795-5-marcandre.lureau@redhat.com> +Reviewed-by: Markus Armbruster +[Useless change to qobject_ref_impl() dropped, commit message improved +slightly] +Signed-off-by: Markus Armbruster +(cherry picked from commit f5a74a5a50387c6f980b2e2f94f062487a1826da) + +Signed-off-by: Miroslav Rezanina +--- + block.c | 8 ++++---- + block/blkdebug.c | 7 +++---- + block/blkverify.c | 8 ++++---- + block/null.c | 3 +-- + block/nvme.c | 3 +-- + block/quorum.c | 4 ++-- + include/qapi/qmp/qnull.h | 3 +-- + include/qapi/qmp/qobject.h | 9 ++++++++- + monitor.c | 20 +++++++------------- + qapi/qobject-input-visitor.c | 6 ++---- + qapi/qobject-output-visitor.c | 7 +++---- + qobject/qdict.c | 33 +++++++++++---------------------- + 12 files changed, 47 insertions(+), 64 deletions(-) + +diff --git a/block.c b/block.c +index 55a7984..676e57f 100644 +--- a/block.c ++++ b/block.c +@@ -5134,8 +5134,8 @@ static bool append_open_options(QDict *d, BlockDriverState *bs) + continue; + } + +- qobject_ref(qdict_entry_value(entry)); +- qdict_put_obj(d, qdict_entry_key(entry), qdict_entry_value(entry)); ++ qdict_put_obj(d, qdict_entry_key(entry), ++ qobject_ref(qdict_entry_value(entry))); + found_any = true; + } + +@@ -5207,8 +5207,8 @@ void bdrv_refresh_filename(BlockDriverState *bs) + * suffices without querying the (exact_)filename of this BDS. */ + if (bs->file->bs->full_open_options) { + qdict_put_str(opts, "driver", drv->format_name); +- qobject_ref(bs->file->bs->full_open_options); +- qdict_put(opts, "file", bs->file->bs->full_open_options); ++ qdict_put(opts, "file", ++ qobject_ref(bs->file->bs->full_open_options)); + + bs->full_open_options = opts; + } else { +diff --git a/block/blkdebug.c b/block/blkdebug.c +index 689703d..053372c 100644 +--- a/block/blkdebug.c ++++ b/block/blkdebug.c +@@ -845,13 +845,12 @@ static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options) + opts = qdict_new(); + qdict_put_str(opts, "driver", "blkdebug"); + +- qobject_ref(bs->file->bs->full_open_options); +- qdict_put(opts, "image", bs->file->bs->full_open_options); ++ qdict_put(opts, "image", qobject_ref(bs->file->bs->full_open_options)); + + for (e = qdict_first(options); e; e = qdict_next(options, e)) { + if (strcmp(qdict_entry_key(e), "x-image")) { +- qobject_ref(qdict_entry_value(e)); +- qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e)); ++ qdict_put_obj(opts, qdict_entry_key(e), ++ qobject_ref(qdict_entry_value(e))); + } + } + +diff --git a/block/blkverify.c b/block/blkverify.c +index 3cffcb1..754cc9e 100644 +--- a/block/blkverify.c ++++ b/block/blkverify.c +@@ -291,10 +291,10 @@ static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options) + QDict *opts = qdict_new(); + qdict_put_str(opts, "driver", "blkverify"); + +- qobject_ref(bs->file->bs->full_open_options); +- qdict_put(opts, "raw", bs->file->bs->full_open_options); +- qobject_ref(s->test_file->bs->full_open_options); +- qdict_put(opts, "test", s->test_file->bs->full_open_options); ++ qdict_put(opts, "raw", ++ qobject_ref(bs->file->bs->full_open_options)); ++ qdict_put(opts, "test", ++ qobject_ref(s->test_file->bs->full_open_options)); + + bs->full_open_options = opts; + } +diff --git a/block/null.c b/block/null.c +index 700a2d0..3944550 100644 +--- a/block/null.c ++++ b/block/null.c +@@ -244,7 +244,6 @@ static int coroutine_fn null_co_block_status(BlockDriverState *bs, + + static void null_refresh_filename(BlockDriverState *bs, QDict *opts) + { +- qobject_ref(opts); + qdict_del(opts, "filename"); + + if (!qdict_size(opts)) { +@@ -253,7 +252,7 @@ static void null_refresh_filename(BlockDriverState *bs, QDict *opts) + } + + qdict_put_str(opts, "driver", bs->drv->format_name); +- bs->full_open_options = opts; ++ bs->full_open_options = qobject_ref(opts); + } + + static BlockDriver bdrv_null_co = { +diff --git a/block/nvme.c b/block/nvme.c +index e192da9..6f71122 100644 +--- a/block/nvme.c ++++ b/block/nvme.c +@@ -1073,7 +1073,6 @@ static int nvme_reopen_prepare(BDRVReopenState *reopen_state, + + static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts) + { +- qobject_ref(opts); + qdict_del(opts, "filename"); + + if (!qdict_size(opts)) { +@@ -1082,7 +1081,7 @@ static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts) + } + + qdict_put_str(opts, "driver", bs->drv->format_name); +- bs->full_open_options = opts; ++ bs->full_open_options = qobject_ref(opts); + } + + static void nvme_refresh_limits(BlockDriverState *bs, Error **errp) +diff --git a/block/quorum.c b/block/quorum.c +index 862cea3..a5051da 100644 +--- a/block/quorum.c ++++ b/block/quorum.c +@@ -1082,8 +1082,8 @@ static void quorum_refresh_filename(BlockDriverState *bs, QDict *options) + + children = qlist_new(); + for (i = 0; i < s->num_children; i++) { +- qobject_ref(s->children[i]->bs->full_open_options); +- qlist_append(children, s->children[i]->bs->full_open_options); ++ qlist_append(children, ++ qobject_ref(s->children[i]->bs->full_open_options)); + } + + opts = qdict_new(); +diff --git a/include/qapi/qmp/qnull.h b/include/qapi/qmp/qnull.h +index 75b29c6..c142688 100644 +--- a/include/qapi/qmp/qnull.h ++++ b/include/qapi/qmp/qnull.h +@@ -23,8 +23,7 @@ extern QNull qnull_; + + static inline QNull *qnull(void) + { +- qobject_ref(&qnull_); +- return &qnull_; ++ return qobject_ref(&qnull_); + } + + bool qnull_is_equal(const QObject *x, const QObject *y); +diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h +index e20006f..fcfd549 100644 +--- a/include/qapi/qmp/qobject.h ++++ b/include/qapi/qmp/qobject.h +@@ -103,8 +103,15 @@ static inline void qobject_unref_impl(QObject *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) qobject_ref_impl(QOBJECT(obj)) ++#define qobject_ref(obj) ({ \ ++ typeof(obj) _o = (obj); \ ++ qobject_ref_impl(QOBJECT(_o)); \ ++ _o; \ ++}) + + /** + * qobject_unref(): Decrement QObject's reference count, deallocate +diff --git a/monitor.c b/monitor.c +index 32f72f2..d948dbd 100644 +--- a/monitor.c ++++ b/monitor.c +@@ -494,9 +494,8 @@ static void monitor_json_emitter(Monitor *mon, QObject *data) + * caller won't free the data (which will be finally freed in + * responder thread). + */ +- qobject_ref(data); + qemu_mutex_lock(&mon->qmp.qmp_queue_lock); +- g_queue_push_tail(mon->qmp.qmp_responses, data); ++ g_queue_push_tail(mon->qmp.qmp_responses, qobject_ref(data)); + qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); + qemu_bh_schedule(mon_global.qmp_respond_bh); + } else { +@@ -614,8 +613,7 @@ monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) + * replacing a prior stored event if any. + */ + qobject_unref(evstate->qdict); +- evstate->qdict = qdict; +- qobject_ref(evstate->qdict); ++ evstate->qdict = qobject_ref(qdict); + } else { + /* + * Last send was (at least) evconf->rate ns ago. +@@ -629,8 +627,7 @@ monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) + + evstate = g_new(MonitorQAPIEventState, 1); + evstate->event = event; +- evstate->data = data; +- qobject_ref(evstate->data); ++ evstate->data = qobject_ref(data); + evstate->qdict = NULL; + evstate->timer = timer_new_ns(event_clock_type, + monitor_qapi_event_handler, +@@ -4066,9 +4063,7 @@ static void monitor_qmp_respond(Monitor *mon, QObject *rsp, + + if (rsp) { + if (id) { +- /* This is for the qdict below. */ +- qobject_ref(id); +- qdict_put_obj(qobject_to(QDict, rsp), "id", id); ++ qdict_put_obj(qobject_to(QDict, rsp), "id", qobject_ref(id)); + } + + monitor_json_emitter(mon, rsp); +@@ -4208,15 +4203,14 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) + goto err; + } + +- qobject_ref(id); +- qdict_del(qdict, "id"); +- + req_obj = g_new0(QMPRequest, 1); + req_obj->mon = mon; +- req_obj->id = id; ++ req_obj->id = qobject_ref(id); + req_obj->req = req; + req_obj->need_resume = false; + ++ qdict_del(qdict, "id"); ++ + if (qmp_is_oob(qdict)) { + /* Out-Of-Band (OOB) requests are executed directly in parser. */ + trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(req_obj->id) +diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c +index 7a290c4..da57f4c 100644 +--- a/qapi/qobject-input-visitor.c ++++ b/qapi/qobject-input-visitor.c +@@ -588,8 +588,7 @@ static void qobject_input_type_any(Visitor *v, const char *name, QObject **obj, + return; + } + +- qobject_ref(qobj); +- *obj = qobj; ++ *obj = qobject_ref(qobj); + } + + static void qobject_input_type_null(Visitor *v, const char *name, +@@ -677,8 +676,7 @@ static QObjectInputVisitor *qobject_input_visitor_base_new(QObject *obj) + v->visitor.optional = qobject_input_optional; + v->visitor.free = qobject_input_free; + +- v->root = obj; +- qobject_ref(obj); ++ v->root = qobject_ref(obj); + + return v; + } +diff --git a/qapi/qobject-output-visitor.c b/qapi/qobject-output-visitor.c +index 3a933b4..89ffd8a 100644 +--- a/qapi/qobject-output-visitor.c ++++ b/qapi/qobject-output-visitor.c +@@ -188,8 +188,8 @@ static void qobject_output_type_any(Visitor *v, const char *name, + QObject **obj, Error **errp) + { + QObjectOutputVisitor *qov = to_qov(v); +- qobject_ref(*obj); +- qobject_output_add_obj(qov, name, *obj); ++ ++ qobject_output_add_obj(qov, name, qobject_ref(*obj)); + } + + static void qobject_output_type_null(Visitor *v, const char *name, +@@ -210,8 +210,7 @@ static void qobject_output_complete(Visitor *v, void *opaque) + assert(qov->root && QSLIST_EMPTY(&qov->stack)); + assert(opaque == qov->result); + +- qobject_ref(qov->root); +- *qov->result = qov->root; ++ *qov->result = qobject_ref(qov->root); + qov->result = NULL; + } + +diff --git a/qobject/qdict.c b/qobject/qdict.c +index 2e9bd53..22800ee 100644 +--- a/qobject/qdict.c ++++ b/qobject/qdict.c +@@ -373,8 +373,7 @@ QDict *qdict_clone_shallow(const QDict *src) + + for (i = 0; i < QDICT_BUCKET_MAX; i++) { + QLIST_FOREACH(entry, &src->table[i], next) { +- qobject_ref(entry->value); +- qdict_put_obj(dest, entry->key, entry->value); ++ qdict_put_obj(dest, entry->key, qobject_ref(entry->value)); + } + } + +@@ -480,8 +479,7 @@ void qdict_copy_default(QDict *dst, QDict *src, const char *key) + + val = qdict_get(src, key); + if (val) { +- qobject_ref(val); +- qdict_put_obj(dst, key, val); ++ qdict_put_obj(dst, key, qobject_ref(val)); + } + } + +@@ -526,8 +524,7 @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) + qdict_flatten_qlist(qobject_to(QList, value), target, new_key); + } else { + /* All other types are moved to the target unchanged. */ +- qobject_ref(value); +- qdict_put_obj(target, new_key, value); ++ qdict_put_obj(target, new_key, qobject_ref(value)); + } + + g_free(new_key); +@@ -566,8 +563,7 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) + delete = true; + } else if (prefix) { + /* All other objects are moved to the target unchanged. */ +- qobject_ref(value); +- qdict_put_obj(target, new_key, value); ++ qdict_put_obj(target, new_key, qobject_ref(value)); + delete = true; + } + +@@ -610,8 +606,7 @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) + while (entry != NULL) { + next = qdict_next(src, entry); + if (strstart(entry->key, start, &p)) { +- qobject_ref(entry->value); +- qdict_put_obj(*dst, p, entry->value); ++ qdict_put_obj(*dst, p, qobject_ref(entry->value)); + qdict_del(src, entry->key); + } + entry = next; +@@ -894,16 +889,14 @@ QObject *qdict_crumple(const QDict *src, Error **errp) + qdict_put_obj(two_level, prefix, QOBJECT(child_dict)); + } + +- qobject_ref(ent->value); +- qdict_put_obj(child_dict, suffix, ent->value); ++ qdict_put_obj(child_dict, suffix, qobject_ref(ent->value)); + } else { + if (child) { + error_setg(errp, "Key %s prefix is already set as a dict", + prefix); + goto error; + } +- qobject_ref(ent->value); +- qdict_put_obj(two_level, prefix, ent->value); ++ qdict_put_obj(two_level, prefix, qobject_ref(ent->value)); + } + + g_free(prefix); +@@ -924,8 +917,7 @@ QObject *qdict_crumple(const QDict *src, Error **errp) + + qdict_put_obj(multi_level, ent->key, child); + } else { +- qobject_ref(ent->value); +- qdict_put_obj(multi_level, ent->key, ent->value); ++ qdict_put_obj(multi_level, ent->key, qobject_ref(ent->value)); + } + } + qobject_unref(two_level); +@@ -951,8 +943,7 @@ QObject *qdict_crumple(const QDict *src, Error **errp) + goto error; + } + +- qobject_ref(child); +- qlist_append_obj(qobject_to(QList, dst), child); ++ qlist_append_obj(qobject_to(QList, dst), qobject_ref(child)); + } + qobject_unref(multi_level); + multi_level = NULL; +@@ -1055,8 +1046,7 @@ void qdict_join(QDict *dest, QDict *src, bool overwrite) + next = qdict_next(src, entry); + + if (overwrite || !qdict_haskey(dest, entry->key)) { +- qobject_ref(entry->value); +- qdict_put_obj(dest, entry->key, entry->value); ++ qdict_put_obj(dest, entry->key, qobject_ref(entry->value)); + qdict_del(src, entry->key); + } + +@@ -1088,8 +1078,7 @@ bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp) + } + + qobj = qdict_get(qdict, renames->from); +- qobject_ref(qobj); +- qdict_put_obj(qdict, renames->to, qobj); ++ qdict_put_obj(qdict, renames->to, qobject_ref(qobj)); + qdict_del(qdict, renames->from); + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qobject-Move-block-specific-qdict-code-to-block-qdic.patch b/SOURCES/kvm-qobject-Move-block-specific-qdict-code-to-block-qdic.patch new file mode 100644 index 0000000..4f0255a --- /dev/null +++ b/SOURCES/kvm-qobject-Move-block-specific-qdict-code-to-block-qdic.patch @@ -0,0 +1,2728 @@ +From 4b3bbc27a6554471969347a1f0ddd70cd80e2b0b Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:16 +0200 +Subject: [PATCH 09/54] qobject: Move block-specific qdict code to + block-qdict.c + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-10-armbru@redhat.com> +Patchwork-id: 80722 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 09/23] qobject: Move block-specific qdict code to block-qdict.c +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Pure code motion, except for two brace placements and a comment +tweaked to appease checkpatch. + +Signed-off-by: Markus Armbruster +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit 0bcc8e5bd8d6fd6e5cb6462054f7cfa45b282f9a) +Signed-off-by: Miroslav Rezanina +--- + MAINTAINERS | 2 + + qobject/Makefile.objs | 1 + + qobject/block-qdict.c | 640 ++++++++++++++++++++++++++++++++++++++++++++ + qobject/qdict.c | 629 -------------------------------------------- + tests/Makefile.include | 4 + + tests/check-block-qdict.c | 655 ++++++++++++++++++++++++++++++++++++++++++++++ + tests/check-qdict.c | 642 --------------------------------------------- + 7 files changed, 1302 insertions(+), 1271 deletions(-) + create mode 100644 qobject/block-qdict.c + create mode 100644 tests/check-block-qdict.c + +diff --git a/MAINTAINERS b/MAINTAINERS +index 24b7016..ad5edc8 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -1339,6 +1339,8 @@ F: qemu-img* + F: qemu-io* + F: tests/qemu-iotests/ + F: util/qemu-progress.c ++F: qobject/block-qdict.c ++F: test/check-block-qdict.c + T: git git://repo.or.cz/qemu/kevin.git block + + Block I/O path +diff --git a/qobject/Makefile.objs b/qobject/Makefile.objs +index 002d258..7b12c9c 100644 +--- a/qobject/Makefile.objs ++++ b/qobject/Makefile.objs +@@ -1,2 +1,3 @@ + util-obj-y = qnull.o qnum.o qstring.o qdict.o qlist.o qbool.o qlit.o + util-obj-y += qjson.o qobject.o json-lexer.o json-streamer.o json-parser.o ++util-obj-y += block-qdict.o +diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c +new file mode 100644 +index 0000000..fb92010 +--- /dev/null ++++ b/qobject/block-qdict.c +@@ -0,0 +1,640 @@ ++/* ++ * 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/qlist.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; ++ 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); ++ new_key = g_strdup_printf("%s.%i", prefix, i); ++ ++ if (qobject_type(value) == QTYPE_QDICT) { ++ qdict_flatten_qdict(qobject_to(QDict, value), target, new_key); ++ } else if (qobject_type(value) == QTYPE_QLIST) { ++ qdict_flatten_qlist(qobject_to(QList, value), target, new_key); ++ } else { ++ /* All other types are moved to the target unchanged. */ ++ 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; ++ char *new_key; ++ bool delete; ++ ++ entry = qdict_first(qdict); ++ ++ while (entry != NULL) { ++ ++ next = qdict_next(qdict, entry); ++ value = qdict_entry_value(entry); ++ new_key = NULL; ++ delete = false; ++ ++ if (prefix) { ++ new_key = g_strdup_printf("%s.%s", prefix, entry->key); ++ } ++ ++ if (qobject_type(value) == QTYPE_QDICT) { ++ /* Entries of QDicts are processed recursively, the QDict object ++ * itself disappears. */ ++ qdict_flatten_qdict(qobject_to(QDict, value), target, ++ new_key ? new_key : entry->key); ++ delete = true; ++ } else if (qobject_type(value) == QTYPE_QLIST) { ++ qdict_flatten_qlist(qobject_to(QList, value), target, ++ new_key ? new_key : entry->key); ++ delete = true; ++ } else if (prefix) { ++ /* All other objects are moved to the target unchanged. */ ++ qdict_put_obj(target, new_key, qobject_ref(value)); ++ delete = true; ++ } ++ ++ g_free(new_key); ++ ++ if (delete) { ++ qdict_del(qdict, entry->key); ++ ++ /* Restart loop after modifying the iterated QDict */ ++ entry = qdict_first(qdict); ++ continue; ++ } ++ ++ entry = next; ++ } ++} ++ ++/** ++ * qdict_flatten(): For each nested 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 ++ * 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 */ ++void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) ++ ++{ ++ const QDictEntry *entry, *next; ++ const char *p; ++ ++ *dst = qdict_new(); ++ entry = qdict_first(src); ++ ++ while (entry != NULL) { ++ next = qdict_next(src, entry); ++ if (strstart(entry->key, start, &p)) { ++ 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); ++ } else { ++ qobject_ref(subqobj); ++ qdict_del(src, indexstr); ++ } ++ ++ qlist_append_obj(*dst, subqobj ?: QOBJECT(subqdict)); ++ } ++} ++ ++/** ++ * 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)) { ++ ++ if (qemu_strtoi64(ent->key, NULL, 10, &val) == 0) { ++ if (is_list == -1) { ++ is_list = 1; ++ } else if (!is_list) { ++ error_setg(errp, ++ "Cannot mix list and non-list keys"); ++ return -1; ++ } ++ len++; ++ if (val > max) { ++ max = val; ++ } ++ } else { ++ if (is_list == -1) { ++ is_list = 0; ++ } else if (is_list) { ++ error_setg(errp, ++ "Cannot mix list and non-list keys"); ++ return -1; ++ } ++ } ++ } ++ ++ 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, 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; ++ 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)) { ++ if (qobject_type(ent->value) == QTYPE_QDICT || ++ qobject_type(ent->value) == QTYPE_QLIST) { ++ error_setg(errp, "Value %s is not a scalar", ++ ent->key); ++ goto error; ++ } ++ ++ qdict_split_flat_key(ent->key, &prefix, &suffix); ++ ++ child = qdict_get(two_level, prefix); ++ if (suffix) { ++ QDict *child_dict = qobject_to(QDict, child); ++ if (!child_dict) { ++ if (child) { ++ error_setg(errp, "Key %s prefix is already set as a scalar", ++ prefix); ++ goto error; ++ } ++ ++ child_dict = qdict_new(); ++ qdict_put_obj(two_level, prefix, QOBJECT(child_dict)); ++ } ++ ++ qdict_put_obj(child_dict, suffix, qobject_ref(ent->value)); ++ } else { ++ if (child) { ++ error_setg(errp, "Key %s prefix is already set as a dict", ++ prefix); ++ goto error; ++ } ++ 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)) { ++ QDict *dict = qobject_to(QDict, ent->value); ++ if (dict) { ++ child = qdict_crumple(dict, 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_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; ++} +diff --git a/qobject/qdict.c b/qobject/qdict.c +index 0554c64..3d8c2f7 100644 +--- a/qobject/qdict.c ++++ b/qobject/qdict.c +@@ -11,17 +11,11 @@ + */ + + #include "qemu/osdep.h" +-#include "block/qdict.h" + #include "qapi/qmp/qnum.h" + #include "qapi/qmp/qdict.h" + #include "qapi/qmp/qbool.h" +-#include "qapi/qmp/qlist.h" + #include "qapi/qmp/qnull.h" + #include "qapi/qmp/qstring.h" +-#include "qapi/error.h" +-#include "qemu/queue.h" +-#include "qemu-common.h" +-#include "qemu/cutils.h" + + /** + * qdict_new(): Create a new QDict +@@ -464,626 +458,3 @@ void qdict_destroy_obj(QObject *obj) + + g_free(qdict); + } +- +-/** +- * 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; +- 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); +- new_key = g_strdup_printf("%s.%i", prefix, i); +- +- if (qobject_type(value) == QTYPE_QDICT) { +- qdict_flatten_qdict(qobject_to(QDict, value), target, new_key); +- } else if (qobject_type(value) == QTYPE_QLIST) { +- qdict_flatten_qlist(qobject_to(QList, value), target, new_key); +- } else { +- /* All other types are moved to the target unchanged. */ +- 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; +- char *new_key; +- bool delete; +- +- entry = qdict_first(qdict); +- +- while (entry != NULL) { +- +- next = qdict_next(qdict, entry); +- value = qdict_entry_value(entry); +- new_key = NULL; +- delete = false; +- +- if (prefix) { +- new_key = g_strdup_printf("%s.%s", prefix, entry->key); +- } +- +- if (qobject_type(value) == QTYPE_QDICT) { +- /* Entries of QDicts are processed recursively, the QDict object +- * itself disappears. */ +- qdict_flatten_qdict(qobject_to(QDict, value), target, +- new_key ? new_key : entry->key); +- delete = true; +- } else if (qobject_type(value) == QTYPE_QLIST) { +- qdict_flatten_qlist(qobject_to(QList, value), target, +- new_key ? new_key : entry->key); +- delete = true; +- } else if (prefix) { +- /* All other objects are moved to the target unchanged. */ +- qdict_put_obj(target, new_key, qobject_ref(value)); +- delete = true; +- } +- +- g_free(new_key); +- +- if (delete) { +- qdict_del(qdict, entry->key); +- +- /* Restart loop after modifying the iterated QDict */ +- entry = qdict_first(qdict); +- continue; +- } +- +- entry = next; +- } +-} +- +-/** +- * qdict_flatten(): For each nested 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 +- * 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 */ +-void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) +- +-{ +- const QDictEntry *entry, *next; +- const char *p; +- +- *dst = qdict_new(); +- entry = qdict_first(src); +- +- while (entry != NULL) { +- next = qdict_next(src, entry); +- if (strstart(entry->key, start, &p)) { +- 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); +- } else { +- qobject_ref(subqobj); +- qdict_del(src, indexstr); +- } +- +- qlist_append_obj(*dst, subqobj ?: QOBJECT(subqdict)); +- } +-} +- +-/** +- * 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)) { +- +- if (qemu_strtoi64(ent->key, NULL, 10, &val) == 0) { +- if (is_list == -1) { +- is_list = 1; +- } else if (!is_list) { +- error_setg(errp, +- "Cannot mix list and non-list keys"); +- return -1; +- } +- len++; +- if (val > max) { +- max = val; +- } +- } else { +- if (is_list == -1) { +- is_list = 0; +- } else if (is_list) { +- error_setg(errp, +- "Cannot mix list and non-list keys"); +- return -1; +- } +- } +- } +- +- 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, 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; +- 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)) { +- if (qobject_type(ent->value) == QTYPE_QDICT || +- qobject_type(ent->value) == QTYPE_QLIST) { +- error_setg(errp, "Value %s is not a scalar", +- ent->key); +- goto error; +- } +- +- qdict_split_flat_key(ent->key, &prefix, &suffix); +- +- child = qdict_get(two_level, prefix); +- if (suffix) { +- QDict *child_dict = qobject_to(QDict, child); +- if (!child_dict) { +- if (child) { +- error_setg(errp, "Key %s prefix is already set as a scalar", +- prefix); +- goto error; +- } +- +- child_dict = qdict_new(); +- qdict_put_obj(two_level, prefix, QOBJECT(child_dict)); +- } +- +- qdict_put_obj(child_dict, suffix, qobject_ref(ent->value)); +- } else { +- if (child) { +- error_setg(errp, "Key %s prefix is already set as a dict", +- prefix); +- goto error; +- } +- 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)) { +- QDict *dict = qobject_to(QDict, ent->value); +- if (dict) { +- child = qdict_crumple(dict, 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_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; +-} +diff --git a/tests/Makefile.include b/tests/Makefile.include +index 5cb1902..9dd63e5 100644 +--- a/tests/Makefile.include ++++ b/tests/Makefile.include +@@ -40,6 +40,8 @@ SYSEMU_TARGET_LIST := $(subst -softmmu.mak,,$(notdir \ + + check-unit-y = tests/check-qdict$(EXESUF) + gcov-files-check-qdict-y = qobject/qdict.c ++check-unit-y = tests/check-block-qdict$(EXESUF) ++gcov-files-check-block-qdict-y = qobject/block-qdict.c + check-unit-y += tests/test-char$(EXESUF) + gcov-files-check-qdict-y = chardev/char.c + check-unit-y += tests/check-qnum$(EXESUF) +@@ -559,6 +561,7 @@ GENERATED_FILES += tests/test-qapi-types.h tests/test-qapi-visit.h \ + test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \ + tests/check-qlist.o tests/check-qnull.o tests/check-qobject.o \ + tests/check-qjson.o tests/check-qlit.o \ ++ tests/check-block-qtest.o \ + tests/test-coroutine.o tests/test-string-output-visitor.o \ + tests/test-string-input-visitor.o tests/test-qobject-output-visitor.o \ + tests/test-clone-visitor.o \ +@@ -589,6 +592,7 @@ test-block-obj-y = $(block-obj-y) $(test-io-obj-y) tests/iothread.o + tests/check-qnum$(EXESUF): tests/check-qnum.o $(test-util-obj-y) + tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y) + tests/check-qdict$(EXESUF): tests/check-qdict.o $(test-util-obj-y) ++tests/check-block-qdict$(EXESUF): tests/check-block-qdict.o $(test-util-obj-y) + tests/check-qlist$(EXESUF): tests/check-qlist.o $(test-util-obj-y) + tests/check-qnull$(EXESUF): tests/check-qnull.o $(test-util-obj-y) + tests/check-qobject$(EXESUF): tests/check-qobject.o $(test-util-obj-y) +diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c +new file mode 100644 +index 0000000..5b9f4d5 +--- /dev/null ++++ b/tests/check-block-qdict.c +@@ -0,0 +1,655 @@ ++/* ++ * Unit-tests for Block layer QDict extras ++ * ++ * 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/qlist.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/error.h" ++ ++static void qdict_defaults_test(void) ++{ ++ QDict *dict, *copy; ++ ++ dict = qdict_new(); ++ copy = qdict_new(); ++ ++ qdict_set_default_str(dict, "foo", "abc"); ++ qdict_set_default_str(dict, "foo", "def"); ++ g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "abc"); ++ qdict_set_default_str(dict, "bar", "ghi"); ++ ++ qdict_copy_default(copy, dict, "foo"); ++ g_assert_cmpstr(qdict_get_str(copy, "foo"), ==, "abc"); ++ qdict_set_default_str(copy, "bar", "xyz"); ++ qdict_copy_default(copy, dict, "bar"); ++ g_assert_cmpstr(qdict_get_str(copy, "bar"), ==, "xyz"); ++ ++ qobject_unref(copy); ++ qobject_unref(dict); ++} ++ ++static void qdict_flatten_test(void) ++{ ++ QList *list1 = qlist_new(); ++ QList *list2 = qlist_new(); ++ QDict *dict1 = qdict_new(); ++ QDict *dict2 = qdict_new(); ++ QDict *dict3 = qdict_new(); ++ ++ /* ++ * Test the flattening of ++ * ++ * { ++ * "e": [ ++ * 42, ++ * [ ++ * 23, ++ * 66, ++ * { ++ * "a": 0, ++ * "b": 1 ++ * } ++ * ] ++ * ], ++ * "f": { ++ * "c": 2, ++ * "d": 3, ++ * }, ++ * "g": 4 ++ * } ++ * ++ * to ++ * ++ * { ++ * "e.0": 42, ++ * "e.1.0": 23, ++ * "e.1.1": 66, ++ * "e.1.2.a": 0, ++ * "e.1.2.b": 1, ++ * "f.c": 2, ++ * "f.d": 3, ++ * "g": 4 ++ * } ++ */ ++ ++ qdict_put_int(dict1, "a", 0); ++ qdict_put_int(dict1, "b", 1); ++ ++ qlist_append_int(list1, 23); ++ qlist_append_int(list1, 66); ++ qlist_append(list1, dict1); ++ qlist_append_int(list2, 42); ++ qlist_append(list2, list1); ++ ++ qdict_put_int(dict2, "c", 2); ++ qdict_put_int(dict2, "d", 3); ++ qdict_put(dict3, "e", list2); ++ qdict_put(dict3, "f", dict2); ++ qdict_put_int(dict3, "g", 4); ++ ++ qdict_flatten(dict3); ++ ++ g_assert(qdict_get_int(dict3, "e.0") == 42); ++ g_assert(qdict_get_int(dict3, "e.1.0") == 23); ++ g_assert(qdict_get_int(dict3, "e.1.1") == 66); ++ g_assert(qdict_get_int(dict3, "e.1.2.a") == 0); ++ g_assert(qdict_get_int(dict3, "e.1.2.b") == 1); ++ g_assert(qdict_get_int(dict3, "f.c") == 2); ++ g_assert(qdict_get_int(dict3, "f.d") == 3); ++ g_assert(qdict_get_int(dict3, "g") == 4); ++ ++ g_assert(qdict_size(dict3) == 8); ++ ++ qobject_unref(dict3); ++} ++ ++static void qdict_array_split_test(void) ++{ ++ QDict *test_dict = qdict_new(); ++ QDict *dict1, *dict2; ++ QNum *int1; ++ QList *test_list; ++ ++ /* ++ * Test the split of ++ * ++ * { ++ * "1.x": 0, ++ * "4.y": 1, ++ * "0.a": 42, ++ * "o.o": 7, ++ * "0.b": 23, ++ * "2": 66 ++ * } ++ * ++ * to ++ * ++ * [ ++ * { ++ * "a": 42, ++ * "b": 23 ++ * }, ++ * { ++ * "x": 0 ++ * }, ++ * 66 ++ * ] ++ * ++ * and ++ * ++ * { ++ * "4.y": 1, ++ * "o.o": 7 ++ * } ++ * ++ * (remaining in the old QDict) ++ * ++ * This example is given in the comment of qdict_array_split(). ++ */ ++ ++ qdict_put_int(test_dict, "1.x", 0); ++ qdict_put_int(test_dict, "4.y", 1); ++ qdict_put_int(test_dict, "0.a", 42); ++ qdict_put_int(test_dict, "o.o", 7); ++ qdict_put_int(test_dict, "0.b", 23); ++ qdict_put_int(test_dict, "2", 66); ++ ++ qdict_array_split(test_dict, &test_list); ++ ++ dict1 = qobject_to(QDict, qlist_pop(test_list)); ++ dict2 = qobject_to(QDict, qlist_pop(test_list)); ++ int1 = qobject_to(QNum, qlist_pop(test_list)); ++ ++ g_assert(dict1); ++ g_assert(dict2); ++ g_assert(int1); ++ g_assert(qlist_empty(test_list)); ++ ++ qobject_unref(test_list); ++ ++ g_assert(qdict_get_int(dict1, "a") == 42); ++ g_assert(qdict_get_int(dict1, "b") == 23); ++ ++ g_assert(qdict_size(dict1) == 2); ++ ++ qobject_unref(dict1); ++ ++ g_assert(qdict_get_int(dict2, "x") == 0); ++ ++ g_assert(qdict_size(dict2) == 1); ++ ++ qobject_unref(dict2); ++ ++ g_assert_cmpint(qnum_get_int(int1), ==, 66); ++ ++ qobject_unref(int1); ++ ++ g_assert(qdict_get_int(test_dict, "4.y") == 1); ++ g_assert(qdict_get_int(test_dict, "o.o") == 7); ++ ++ g_assert(qdict_size(test_dict) == 2); ++ ++ qobject_unref(test_dict); ++ ++ /* ++ * Test the split of ++ * ++ * { ++ * "0": 42, ++ * "1": 23, ++ * "1.x": 84 ++ * } ++ * ++ * to ++ * ++ * [ ++ * 42 ++ * ] ++ * ++ * and ++ * ++ * { ++ * "1": 23, ++ * "1.x": 84 ++ * } ++ * ++ * That is, test whether splitting stops if there is both an entry with key ++ * of "%u" and other entries with keys prefixed "%u." for the same index. ++ */ ++ ++ test_dict = qdict_new(); ++ ++ qdict_put_int(test_dict, "0", 42); ++ qdict_put_int(test_dict, "1", 23); ++ qdict_put_int(test_dict, "1.x", 84); ++ ++ qdict_array_split(test_dict, &test_list); ++ ++ int1 = qobject_to(QNum, qlist_pop(test_list)); ++ ++ g_assert(int1); ++ g_assert(qlist_empty(test_list)); ++ ++ qobject_unref(test_list); ++ ++ g_assert_cmpint(qnum_get_int(int1), ==, 42); ++ ++ qobject_unref(int1); ++ ++ g_assert(qdict_get_int(test_dict, "1") == 23); ++ g_assert(qdict_get_int(test_dict, "1.x") == 84); ++ ++ g_assert(qdict_size(test_dict) == 2); ++ ++ qobject_unref(test_dict); ++} ++ ++static void qdict_array_entries_test(void) ++{ ++ QDict *dict = qdict_new(); ++ ++ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); ++ ++ qdict_put_int(dict, "bar", 0); ++ qdict_put_int(dict, "baz.0", 0); ++ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); ++ ++ qdict_put_int(dict, "foo.1", 0); ++ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); ++ qdict_put_int(dict, "foo.0", 0); ++ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 2); ++ qdict_put_int(dict, "foo.bar", 0); ++ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); ++ qdict_del(dict, "foo.bar"); ++ ++ qdict_put_int(dict, "foo.2.a", 0); ++ qdict_put_int(dict, "foo.2.b", 0); ++ qdict_put_int(dict, "foo.2.c", 0); ++ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 3); ++ g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); ++ ++ qobject_unref(dict); ++ ++ dict = qdict_new(); ++ qdict_put_int(dict, "1", 0); ++ g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); ++ qdict_put_int(dict, "0", 0); ++ g_assert_cmpint(qdict_array_entries(dict, ""), ==, 2); ++ qdict_put_int(dict, "bar", 0); ++ g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); ++ qdict_del(dict, "bar"); ++ ++ qdict_put_int(dict, "2.a", 0); ++ qdict_put_int(dict, "2.b", 0); ++ qdict_put_int(dict, "2.c", 0); ++ g_assert_cmpint(qdict_array_entries(dict, ""), ==, 3); ++ ++ qobject_unref(dict); ++} ++ ++static void qdict_join_test(void) ++{ ++ QDict *dict1, *dict2; ++ bool overwrite = false; ++ int i; ++ ++ dict1 = qdict_new(); ++ dict2 = qdict_new(); ++ ++ /* Test everything once without overwrite and once with */ ++ do { ++ /* Test empty dicts */ ++ qdict_join(dict1, dict2, overwrite); ++ ++ g_assert(qdict_size(dict1) == 0); ++ g_assert(qdict_size(dict2) == 0); ++ ++ /* First iteration: Test movement */ ++ /* Second iteration: Test empty source and non-empty destination */ ++ qdict_put_int(dict2, "foo", 42); ++ ++ for (i = 0; i < 2; i++) { ++ qdict_join(dict1, dict2, overwrite); ++ ++ g_assert(qdict_size(dict1) == 1); ++ g_assert(qdict_size(dict2) == 0); ++ ++ g_assert(qdict_get_int(dict1, "foo") == 42); ++ } ++ ++ /* Test non-empty source and destination without conflict */ ++ qdict_put_int(dict2, "bar", 23); ++ ++ qdict_join(dict1, dict2, overwrite); ++ ++ g_assert(qdict_size(dict1) == 2); ++ g_assert(qdict_size(dict2) == 0); ++ ++ g_assert(qdict_get_int(dict1, "foo") == 42); ++ g_assert(qdict_get_int(dict1, "bar") == 23); ++ ++ /* Test conflict */ ++ qdict_put_int(dict2, "foo", 84); ++ ++ qdict_join(dict1, dict2, overwrite); ++ ++ g_assert(qdict_size(dict1) == 2); ++ g_assert(qdict_size(dict2) == !overwrite); ++ ++ g_assert(qdict_get_int(dict1, "foo") == (overwrite ? 84 : 42)); ++ g_assert(qdict_get_int(dict1, "bar") == 23); ++ ++ if (!overwrite) { ++ g_assert(qdict_get_int(dict2, "foo") == 84); ++ } ++ ++ /* Check the references */ ++ g_assert(qdict_get(dict1, "foo")->base.refcnt == 1); ++ g_assert(qdict_get(dict1, "bar")->base.refcnt == 1); ++ ++ if (!overwrite) { ++ g_assert(qdict_get(dict2, "foo")->base.refcnt == 1); ++ } ++ ++ /* Clean up */ ++ qdict_del(dict1, "foo"); ++ qdict_del(dict1, "bar"); ++ ++ if (!overwrite) { ++ qdict_del(dict2, "foo"); ++ } ++ } while (overwrite ^= true); ++ ++ qobject_unref(dict1); ++ qobject_unref(dict2); ++} ++ ++static void qdict_crumple_test_recursive(void) ++{ ++ QDict *src, *dst, *rule, *vnc, *acl, *listen; ++ QList *rules; ++ ++ src = qdict_new(); ++ qdict_put_str(src, "vnc.listen.addr", "127.0.0.1"); ++ qdict_put_str(src, "vnc.listen.port", "5901"); ++ qdict_put_str(src, "vnc.acl.rules.0.match", "fred"); ++ qdict_put_str(src, "vnc.acl.rules.0.policy", "allow"); ++ qdict_put_str(src, "vnc.acl.rules.1.match", "bob"); ++ qdict_put_str(src, "vnc.acl.rules.1.policy", "deny"); ++ qdict_put_str(src, "vnc.acl.default", "deny"); ++ qdict_put_str(src, "vnc.acl..name", "acl0"); ++ qdict_put_str(src, "vnc.acl.rule..name", "acl0"); ++ ++ dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); ++ g_assert(dst); ++ g_assert_cmpint(qdict_size(dst), ==, 1); ++ ++ vnc = qdict_get_qdict(dst, "vnc"); ++ g_assert(vnc); ++ g_assert_cmpint(qdict_size(vnc), ==, 3); ++ ++ listen = qdict_get_qdict(vnc, "listen"); ++ g_assert(listen); ++ g_assert_cmpint(qdict_size(listen), ==, 2); ++ g_assert_cmpstr("127.0.0.1", ==, qdict_get_str(listen, "addr")); ++ g_assert_cmpstr("5901", ==, qdict_get_str(listen, "port")); ++ ++ acl = qdict_get_qdict(vnc, "acl"); ++ g_assert(acl); ++ g_assert_cmpint(qdict_size(acl), ==, 3); ++ ++ rules = qdict_get_qlist(acl, "rules"); ++ g_assert(rules); ++ g_assert_cmpint(qlist_size(rules), ==, 2); ++ ++ rule = qobject_to(QDict, qlist_pop(rules)); ++ g_assert(rule); ++ g_assert_cmpint(qdict_size(rule), ==, 2); ++ g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match")); ++ g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy")); ++ qobject_unref(rule); ++ ++ rule = qobject_to(QDict, qlist_pop(rules)); ++ g_assert(rule); ++ g_assert_cmpint(qdict_size(rule), ==, 2); ++ g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match")); ++ g_assert_cmpstr("deny", ==, qdict_get_str(rule, "policy")); ++ qobject_unref(rule); ++ ++ /* With recursive crumpling, we should see all names unescaped */ ++ g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name")); ++ g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name")); ++ ++ qobject_unref(src); ++ qobject_unref(dst); ++} ++ ++static void qdict_crumple_test_empty(void) ++{ ++ QDict *src, *dst; ++ ++ src = qdict_new(); ++ ++ dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); ++ ++ g_assert_cmpint(qdict_size(dst), ==, 0); ++ ++ qobject_unref(src); ++ qobject_unref(dst); ++} ++ ++static int qdict_count_entries(QDict *dict) ++{ ++ const QDictEntry *e; ++ int count = 0; ++ ++ for (e = qdict_first(dict); e; e = qdict_next(dict, e)) { ++ count++; ++ } ++ ++ return count; ++} ++ ++static void qdict_rename_keys_test(void) ++{ ++ QDict *dict = qdict_new(); ++ QDict *copy; ++ QDictRenames *renames; ++ Error *local_err = NULL; ++ ++ qdict_put_str(dict, "abc", "foo"); ++ qdict_put_str(dict, "abcdef", "bar"); ++ qdict_put_int(dict, "number", 42); ++ qdict_put_bool(dict, "flag", true); ++ qdict_put_null(dict, "nothing"); ++ ++ /* Empty rename list */ ++ renames = (QDictRenames[]) { ++ { NULL, "this can be anything" } ++ }; ++ copy = qdict_clone_shallow(dict); ++ qdict_rename_keys(copy, renames, &error_abort); ++ ++ g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); ++ g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); ++ g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); ++ g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); ++ g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); ++ g_assert_cmpint(qdict_count_entries(copy), ==, 5); ++ ++ qobject_unref(copy); ++ ++ /* Simple rename of all entries */ ++ renames = (QDictRenames[]) { ++ { "abc", "str1" }, ++ { "abcdef", "str2" }, ++ { "number", "int" }, ++ { "flag", "bool" }, ++ { "nothing", "null" }, ++ { NULL , NULL } ++ }; ++ copy = qdict_clone_shallow(dict); ++ qdict_rename_keys(copy, renames, &error_abort); ++ ++ g_assert(!qdict_haskey(copy, "abc")); ++ g_assert(!qdict_haskey(copy, "abcdef")); ++ g_assert(!qdict_haskey(copy, "number")); ++ g_assert(!qdict_haskey(copy, "flag")); ++ g_assert(!qdict_haskey(copy, "nothing")); ++ ++ g_assert_cmpstr(qdict_get_str(copy, "str1"), ==, "foo"); ++ g_assert_cmpstr(qdict_get_str(copy, "str2"), ==, "bar"); ++ g_assert_cmpint(qdict_get_int(copy, "int"), ==, 42); ++ g_assert_cmpint(qdict_get_bool(copy, "bool"), ==, true); ++ g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL); ++ g_assert_cmpint(qdict_count_entries(copy), ==, 5); ++ ++ qobject_unref(copy); ++ ++ /* Renames are processed top to bottom */ ++ renames = (QDictRenames[]) { ++ { "abc", "tmp" }, ++ { "abcdef", "abc" }, ++ { "number", "abcdef" }, ++ { "flag", "number" }, ++ { "nothing", "flag" }, ++ { "tmp", "nothing" }, ++ { NULL , NULL } ++ }; ++ copy = qdict_clone_shallow(dict); ++ qdict_rename_keys(copy, renames, &error_abort); ++ ++ g_assert_cmpstr(qdict_get_str(copy, "nothing"), ==, "foo"); ++ g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "bar"); ++ g_assert_cmpint(qdict_get_int(copy, "abcdef"), ==, 42); ++ g_assert_cmpint(qdict_get_bool(copy, "number"), ==, true); ++ g_assert(qobject_type(qdict_get(copy, "flag")) == QTYPE_QNULL); ++ g_assert(!qdict_haskey(copy, "tmp")); ++ g_assert_cmpint(qdict_count_entries(copy), ==, 5); ++ ++ qobject_unref(copy); ++ ++ /* Conflicting rename */ ++ renames = (QDictRenames[]) { ++ { "abcdef", "abc" }, ++ { NULL , NULL } ++ }; ++ copy = qdict_clone_shallow(dict); ++ qdict_rename_keys(copy, renames, &local_err); ++ ++ g_assert(local_err != NULL); ++ error_free(local_err); ++ local_err = NULL; ++ ++ g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); ++ g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); ++ g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); ++ g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); ++ g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); ++ g_assert_cmpint(qdict_count_entries(copy), ==, 5); ++ ++ qobject_unref(copy); ++ ++ /* Renames in an empty dict */ ++ renames = (QDictRenames[]) { ++ { "abcdef", "abc" }, ++ { NULL , NULL } ++ }; ++ ++ qobject_unref(dict); ++ dict = qdict_new(); ++ ++ qdict_rename_keys(dict, renames, &error_abort); ++ g_assert(qdict_first(dict) == NULL); ++ ++ qobject_unref(dict); ++} ++ ++static void qdict_crumple_test_bad_inputs(void) ++{ ++ QDict *src; ++ Error *error = NULL; ++ ++ src = qdict_new(); ++ /* rule.0 can't be both a string and a dict */ ++ qdict_put_str(src, "rule.0", "fred"); ++ qdict_put_str(src, "rule.0.policy", "allow"); ++ ++ g_assert(qdict_crumple(src, &error) == NULL); ++ g_assert(error != NULL); ++ error_free(error); ++ error = NULL; ++ qobject_unref(src); ++ ++ src = qdict_new(); ++ /* rule can't be both a list and a dict */ ++ qdict_put_str(src, "rule.0", "fred"); ++ qdict_put_str(src, "rule.a", "allow"); ++ ++ g_assert(qdict_crumple(src, &error) == NULL); ++ g_assert(error != NULL); ++ error_free(error); ++ error = NULL; ++ qobject_unref(src); ++ ++ src = qdict_new(); ++ /* The input should be flat, ie no dicts or lists */ ++ qdict_put(src, "rule.a", qdict_new()); ++ qdict_put_str(src, "rule.b", "allow"); ++ ++ g_assert(qdict_crumple(src, &error) == NULL); ++ g_assert(error != NULL); ++ error_free(error); ++ error = NULL; ++ qobject_unref(src); ++ ++ src = qdict_new(); ++ /* List indexes must not have gaps */ ++ qdict_put_str(src, "rule.0", "deny"); ++ qdict_put_str(src, "rule.3", "allow"); ++ ++ g_assert(qdict_crumple(src, &error) == NULL); ++ g_assert(error != NULL); ++ error_free(error); ++ error = NULL; ++ qobject_unref(src); ++ ++ src = qdict_new(); ++ /* List indexes must be in %zu format */ ++ qdict_put_str(src, "rule.0", "deny"); ++ qdict_put_str(src, "rule.+1", "allow"); ++ ++ g_assert(qdict_crumple(src, &error) == NULL); ++ g_assert(error != NULL); ++ error_free(error); ++ error = NULL; ++ qobject_unref(src); ++} ++ ++int main(int argc, char **argv) ++{ ++ g_test_init(&argc, &argv, NULL); ++ ++ g_test_add_func("/public/defaults", qdict_defaults_test); ++ g_test_add_func("/public/flatten", qdict_flatten_test); ++ g_test_add_func("/public/array_split", qdict_array_split_test); ++ g_test_add_func("/public/array_entries", qdict_array_entries_test); ++ g_test_add_func("/public/join", qdict_join_test); ++ g_test_add_func("/public/crumple/recursive", ++ qdict_crumple_test_recursive); ++ g_test_add_func("/public/crumple/empty", ++ qdict_crumple_test_empty); ++ g_test_add_func("/public/crumple/bad_inputs", ++ qdict_crumple_test_bad_inputs); ++ ++ g_test_add_func("/public/rename_keys", qdict_rename_keys_test); ++ ++ return g_test_run(); ++} +diff --git a/tests/check-qdict.c b/tests/check-qdict.c +index 93e2112..86e9fe7 100644 +--- a/tests/check-qdict.c ++++ b/tests/check-qdict.c +@@ -11,13 +11,7 @@ + */ + + #include "qemu/osdep.h" +-#include "block/qdict.h" + #include "qapi/qmp/qdict.h" +-#include "qapi/qmp/qlist.h" +-#include "qapi/qmp/qnum.h" +-#include "qapi/qmp/qstring.h" +-#include "qapi/error.h" +-#include "qemu-common.h" + + /* + * Public Interface test-cases +@@ -157,28 +151,6 @@ static void qdict_get_try_str_test(void) + qobject_unref(tests_dict); + } + +-static void qdict_defaults_test(void) +-{ +- QDict *dict, *copy; +- +- dict = qdict_new(); +- copy = qdict_new(); +- +- qdict_set_default_str(dict, "foo", "abc"); +- qdict_set_default_str(dict, "foo", "def"); +- g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "abc"); +- qdict_set_default_str(dict, "bar", "ghi"); +- +- qdict_copy_default(copy, dict, "foo"); +- g_assert_cmpstr(qdict_get_str(copy, "foo"), ==, "abc"); +- qdict_set_default_str(copy, "bar", "xyz"); +- qdict_copy_default(copy, dict, "bar"); +- g_assert_cmpstr(qdict_get_str(copy, "bar"), ==, "xyz"); +- +- qobject_unref(copy); +- qobject_unref(dict); +-} +- + static void qdict_haskey_not_test(void) + { + QDict *tests_dict = qdict_new(); +@@ -254,606 +226,6 @@ static void qdict_iterapi_test(void) + qobject_unref(tests_dict); + } + +-static void qdict_flatten_test(void) +-{ +- QList *list1 = qlist_new(); +- QList *list2 = qlist_new(); +- QDict *dict1 = qdict_new(); +- QDict *dict2 = qdict_new(); +- QDict *dict3 = qdict_new(); +- +- /* +- * Test the flattening of +- * +- * { +- * "e": [ +- * 42, +- * [ +- * 23, +- * 66, +- * { +- * "a": 0, +- * "b": 1 +- * } +- * ] +- * ], +- * "f": { +- * "c": 2, +- * "d": 3, +- * }, +- * "g": 4 +- * } +- * +- * to +- * +- * { +- * "e.0": 42, +- * "e.1.0": 23, +- * "e.1.1": 66, +- * "e.1.2.a": 0, +- * "e.1.2.b": 1, +- * "f.c": 2, +- * "f.d": 3, +- * "g": 4 +- * } +- */ +- +- qdict_put_int(dict1, "a", 0); +- qdict_put_int(dict1, "b", 1); +- +- qlist_append_int(list1, 23); +- qlist_append_int(list1, 66); +- qlist_append(list1, dict1); +- qlist_append_int(list2, 42); +- qlist_append(list2, list1); +- +- qdict_put_int(dict2, "c", 2); +- qdict_put_int(dict2, "d", 3); +- qdict_put(dict3, "e", list2); +- qdict_put(dict3, "f", dict2); +- qdict_put_int(dict3, "g", 4); +- +- qdict_flatten(dict3); +- +- g_assert(qdict_get_int(dict3, "e.0") == 42); +- g_assert(qdict_get_int(dict3, "e.1.0") == 23); +- g_assert(qdict_get_int(dict3, "e.1.1") == 66); +- g_assert(qdict_get_int(dict3, "e.1.2.a") == 0); +- g_assert(qdict_get_int(dict3, "e.1.2.b") == 1); +- g_assert(qdict_get_int(dict3, "f.c") == 2); +- g_assert(qdict_get_int(dict3, "f.d") == 3); +- g_assert(qdict_get_int(dict3, "g") == 4); +- +- g_assert(qdict_size(dict3) == 8); +- +- qobject_unref(dict3); +-} +- +-static void qdict_array_split_test(void) +-{ +- QDict *test_dict = qdict_new(); +- QDict *dict1, *dict2; +- QNum *int1; +- QList *test_list; +- +- /* +- * Test the split of +- * +- * { +- * "1.x": 0, +- * "4.y": 1, +- * "0.a": 42, +- * "o.o": 7, +- * "0.b": 23, +- * "2": 66 +- * } +- * +- * to +- * +- * [ +- * { +- * "a": 42, +- * "b": 23 +- * }, +- * { +- * "x": 0 +- * }, +- * 66 +- * ] +- * +- * and +- * +- * { +- * "4.y": 1, +- * "o.o": 7 +- * } +- * +- * (remaining in the old QDict) +- * +- * This example is given in the comment of qdict_array_split(). +- */ +- +- qdict_put_int(test_dict, "1.x", 0); +- qdict_put_int(test_dict, "4.y", 1); +- qdict_put_int(test_dict, "0.a", 42); +- qdict_put_int(test_dict, "o.o", 7); +- qdict_put_int(test_dict, "0.b", 23); +- qdict_put_int(test_dict, "2", 66); +- +- qdict_array_split(test_dict, &test_list); +- +- dict1 = qobject_to(QDict, qlist_pop(test_list)); +- dict2 = qobject_to(QDict, qlist_pop(test_list)); +- int1 = qobject_to(QNum, qlist_pop(test_list)); +- +- g_assert(dict1); +- g_assert(dict2); +- g_assert(int1); +- g_assert(qlist_empty(test_list)); +- +- qobject_unref(test_list); +- +- g_assert(qdict_get_int(dict1, "a") == 42); +- g_assert(qdict_get_int(dict1, "b") == 23); +- +- g_assert(qdict_size(dict1) == 2); +- +- qobject_unref(dict1); +- +- g_assert(qdict_get_int(dict2, "x") == 0); +- +- g_assert(qdict_size(dict2) == 1); +- +- qobject_unref(dict2); +- +- g_assert_cmpint(qnum_get_int(int1), ==, 66); +- +- qobject_unref(int1); +- +- g_assert(qdict_get_int(test_dict, "4.y") == 1); +- g_assert(qdict_get_int(test_dict, "o.o") == 7); +- +- g_assert(qdict_size(test_dict) == 2); +- +- qobject_unref(test_dict); +- +- /* +- * Test the split of +- * +- * { +- * "0": 42, +- * "1": 23, +- * "1.x": 84 +- * } +- * +- * to +- * +- * [ +- * 42 +- * ] +- * +- * and +- * +- * { +- * "1": 23, +- * "1.x": 84 +- * } +- * +- * That is, test whether splitting stops if there is both an entry with key +- * of "%u" and other entries with keys prefixed "%u." for the same index. +- */ +- +- test_dict = qdict_new(); +- +- qdict_put_int(test_dict, "0", 42); +- qdict_put_int(test_dict, "1", 23); +- qdict_put_int(test_dict, "1.x", 84); +- +- qdict_array_split(test_dict, &test_list); +- +- int1 = qobject_to(QNum, qlist_pop(test_list)); +- +- g_assert(int1); +- g_assert(qlist_empty(test_list)); +- +- qobject_unref(test_list); +- +- g_assert_cmpint(qnum_get_int(int1), ==, 42); +- +- qobject_unref(int1); +- +- g_assert(qdict_get_int(test_dict, "1") == 23); +- g_assert(qdict_get_int(test_dict, "1.x") == 84); +- +- g_assert(qdict_size(test_dict) == 2); +- +- qobject_unref(test_dict); +-} +- +-static void qdict_array_entries_test(void) +-{ +- QDict *dict = qdict_new(); +- +- g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); +- +- qdict_put_int(dict, "bar", 0); +- qdict_put_int(dict, "baz.0", 0); +- g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); +- +- qdict_put_int(dict, "foo.1", 0); +- g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); +- qdict_put_int(dict, "foo.0", 0); +- g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 2); +- qdict_put_int(dict, "foo.bar", 0); +- g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); +- qdict_del(dict, "foo.bar"); +- +- qdict_put_int(dict, "foo.2.a", 0); +- qdict_put_int(dict, "foo.2.b", 0); +- qdict_put_int(dict, "foo.2.c", 0); +- g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 3); +- g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); +- +- qobject_unref(dict); +- +- dict = qdict_new(); +- qdict_put_int(dict, "1", 0); +- g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); +- qdict_put_int(dict, "0", 0); +- g_assert_cmpint(qdict_array_entries(dict, ""), ==, 2); +- qdict_put_int(dict, "bar", 0); +- g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); +- qdict_del(dict, "bar"); +- +- qdict_put_int(dict, "2.a", 0); +- qdict_put_int(dict, "2.b", 0); +- qdict_put_int(dict, "2.c", 0); +- g_assert_cmpint(qdict_array_entries(dict, ""), ==, 3); +- +- qobject_unref(dict); +-} +- +-static void qdict_join_test(void) +-{ +- QDict *dict1, *dict2; +- bool overwrite = false; +- int i; +- +- dict1 = qdict_new(); +- dict2 = qdict_new(); +- +- /* Test everything once without overwrite and once with */ +- do +- { +- /* Test empty dicts */ +- qdict_join(dict1, dict2, overwrite); +- +- g_assert(qdict_size(dict1) == 0); +- g_assert(qdict_size(dict2) == 0); +- +- /* First iteration: Test movement */ +- /* Second iteration: Test empty source and non-empty destination */ +- qdict_put_int(dict2, "foo", 42); +- +- for (i = 0; i < 2; i++) { +- qdict_join(dict1, dict2, overwrite); +- +- g_assert(qdict_size(dict1) == 1); +- g_assert(qdict_size(dict2) == 0); +- +- g_assert(qdict_get_int(dict1, "foo") == 42); +- } +- +- /* Test non-empty source and destination without conflict */ +- qdict_put_int(dict2, "bar", 23); +- +- qdict_join(dict1, dict2, overwrite); +- +- g_assert(qdict_size(dict1) == 2); +- g_assert(qdict_size(dict2) == 0); +- +- g_assert(qdict_get_int(dict1, "foo") == 42); +- g_assert(qdict_get_int(dict1, "bar") == 23); +- +- /* Test conflict */ +- qdict_put_int(dict2, "foo", 84); +- +- qdict_join(dict1, dict2, overwrite); +- +- g_assert(qdict_size(dict1) == 2); +- g_assert(qdict_size(dict2) == !overwrite); +- +- g_assert(qdict_get_int(dict1, "foo") == (overwrite ? 84 : 42)); +- g_assert(qdict_get_int(dict1, "bar") == 23); +- +- if (!overwrite) { +- g_assert(qdict_get_int(dict2, "foo") == 84); +- } +- +- /* Check the references */ +- g_assert(qdict_get(dict1, "foo")->base.refcnt == 1); +- g_assert(qdict_get(dict1, "bar")->base.refcnt == 1); +- +- if (!overwrite) { +- g_assert(qdict_get(dict2, "foo")->base.refcnt == 1); +- } +- +- /* Clean up */ +- qdict_del(dict1, "foo"); +- qdict_del(dict1, "bar"); +- +- if (!overwrite) { +- qdict_del(dict2, "foo"); +- } +- } +- while (overwrite ^= true); +- +- qobject_unref(dict1); +- qobject_unref(dict2); +-} +- +-static void qdict_crumple_test_recursive(void) +-{ +- QDict *src, *dst, *rule, *vnc, *acl, *listen; +- QList *rules; +- +- src = qdict_new(); +- qdict_put_str(src, "vnc.listen.addr", "127.0.0.1"); +- qdict_put_str(src, "vnc.listen.port", "5901"); +- qdict_put_str(src, "vnc.acl.rules.0.match", "fred"); +- qdict_put_str(src, "vnc.acl.rules.0.policy", "allow"); +- qdict_put_str(src, "vnc.acl.rules.1.match", "bob"); +- qdict_put_str(src, "vnc.acl.rules.1.policy", "deny"); +- qdict_put_str(src, "vnc.acl.default", "deny"); +- qdict_put_str(src, "vnc.acl..name", "acl0"); +- qdict_put_str(src, "vnc.acl.rule..name", "acl0"); +- +- dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); +- g_assert(dst); +- g_assert_cmpint(qdict_size(dst), ==, 1); +- +- vnc = qdict_get_qdict(dst, "vnc"); +- g_assert(vnc); +- g_assert_cmpint(qdict_size(vnc), ==, 3); +- +- listen = qdict_get_qdict(vnc, "listen"); +- g_assert(listen); +- g_assert_cmpint(qdict_size(listen), ==, 2); +- g_assert_cmpstr("127.0.0.1", ==, qdict_get_str(listen, "addr")); +- g_assert_cmpstr("5901", ==, qdict_get_str(listen, "port")); +- +- acl = qdict_get_qdict(vnc, "acl"); +- g_assert(acl); +- g_assert_cmpint(qdict_size(acl), ==, 3); +- +- rules = qdict_get_qlist(acl, "rules"); +- g_assert(rules); +- g_assert_cmpint(qlist_size(rules), ==, 2); +- +- rule = qobject_to(QDict, qlist_pop(rules)); +- g_assert(rule); +- g_assert_cmpint(qdict_size(rule), ==, 2); +- g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match")); +- g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy")); +- qobject_unref(rule); +- +- rule = qobject_to(QDict, qlist_pop(rules)); +- g_assert(rule); +- g_assert_cmpint(qdict_size(rule), ==, 2); +- g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match")); +- g_assert_cmpstr("deny", ==, qdict_get_str(rule, "policy")); +- qobject_unref(rule); +- +- /* With recursive crumpling, we should see all names unescaped */ +- g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name")); +- g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name")); +- +- qobject_unref(src); +- qobject_unref(dst); +-} +- +-static void qdict_crumple_test_empty(void) +-{ +- QDict *src, *dst; +- +- src = qdict_new(); +- +- dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); +- +- g_assert_cmpint(qdict_size(dst), ==, 0); +- +- qobject_unref(src); +- qobject_unref(dst); +-} +- +-static int qdict_count_entries(QDict *dict) +-{ +- const QDictEntry *e; +- int count = 0; +- +- for (e = qdict_first(dict); e; e = qdict_next(dict, e)) { +- count++; +- } +- +- return count; +-} +- +-static void qdict_rename_keys_test(void) +-{ +- QDict *dict = qdict_new(); +- QDict *copy; +- QDictRenames *renames; +- Error *local_err = NULL; +- +- qdict_put_str(dict, "abc", "foo"); +- qdict_put_str(dict, "abcdef", "bar"); +- qdict_put_int(dict, "number", 42); +- qdict_put_bool(dict, "flag", true); +- qdict_put_null(dict, "nothing"); +- +- /* Empty rename list */ +- renames = (QDictRenames[]) { +- { NULL, "this can be anything" } +- }; +- copy = qdict_clone_shallow(dict); +- qdict_rename_keys(copy, renames, &error_abort); +- +- g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); +- g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); +- g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); +- g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); +- g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); +- g_assert_cmpint(qdict_count_entries(copy), ==, 5); +- +- qobject_unref(copy); +- +- /* Simple rename of all entries */ +- renames = (QDictRenames[]) { +- { "abc", "str1" }, +- { "abcdef", "str2" }, +- { "number", "int" }, +- { "flag", "bool" }, +- { "nothing", "null" }, +- { NULL , NULL } +- }; +- copy = qdict_clone_shallow(dict); +- qdict_rename_keys(copy, renames, &error_abort); +- +- g_assert(!qdict_haskey(copy, "abc")); +- g_assert(!qdict_haskey(copy, "abcdef")); +- g_assert(!qdict_haskey(copy, "number")); +- g_assert(!qdict_haskey(copy, "flag")); +- g_assert(!qdict_haskey(copy, "nothing")); +- +- g_assert_cmpstr(qdict_get_str(copy, "str1"), ==, "foo"); +- g_assert_cmpstr(qdict_get_str(copy, "str2"), ==, "bar"); +- g_assert_cmpint(qdict_get_int(copy, "int"), ==, 42); +- g_assert_cmpint(qdict_get_bool(copy, "bool"), ==, true); +- g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL); +- g_assert_cmpint(qdict_count_entries(copy), ==, 5); +- +- qobject_unref(copy); +- +- /* Renames are processed top to bottom */ +- renames = (QDictRenames[]) { +- { "abc", "tmp" }, +- { "abcdef", "abc" }, +- { "number", "abcdef" }, +- { "flag", "number" }, +- { "nothing", "flag" }, +- { "tmp", "nothing" }, +- { NULL , NULL } +- }; +- copy = qdict_clone_shallow(dict); +- qdict_rename_keys(copy, renames, &error_abort); +- +- g_assert_cmpstr(qdict_get_str(copy, "nothing"), ==, "foo"); +- g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "bar"); +- g_assert_cmpint(qdict_get_int(copy, "abcdef"), ==, 42); +- g_assert_cmpint(qdict_get_bool(copy, "number"), ==, true); +- g_assert(qobject_type(qdict_get(copy, "flag")) == QTYPE_QNULL); +- g_assert(!qdict_haskey(copy, "tmp")); +- g_assert_cmpint(qdict_count_entries(copy), ==, 5); +- +- qobject_unref(copy); +- +- /* Conflicting rename */ +- renames = (QDictRenames[]) { +- { "abcdef", "abc" }, +- { NULL , NULL } +- }; +- copy = qdict_clone_shallow(dict); +- qdict_rename_keys(copy, renames, &local_err); +- +- g_assert(local_err != NULL); +- error_free(local_err); +- local_err = NULL; +- +- g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); +- g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); +- g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); +- g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); +- g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); +- g_assert_cmpint(qdict_count_entries(copy), ==, 5); +- +- qobject_unref(copy); +- +- /* Renames in an empty dict */ +- renames = (QDictRenames[]) { +- { "abcdef", "abc" }, +- { NULL , NULL } +- }; +- +- qobject_unref(dict); +- dict = qdict_new(); +- +- qdict_rename_keys(dict, renames, &error_abort); +- g_assert(qdict_first(dict) == NULL); +- +- qobject_unref(dict); +-} +- +-static void qdict_crumple_test_bad_inputs(void) +-{ +- QDict *src; +- Error *error = NULL; +- +- src = qdict_new(); +- /* rule.0 can't be both a string and a dict */ +- qdict_put_str(src, "rule.0", "fred"); +- qdict_put_str(src, "rule.0.policy", "allow"); +- +- g_assert(qdict_crumple(src, &error) == NULL); +- g_assert(error != NULL); +- error_free(error); +- error = NULL; +- qobject_unref(src); +- +- src = qdict_new(); +- /* rule can't be both a list and a dict */ +- qdict_put_str(src, "rule.0", "fred"); +- qdict_put_str(src, "rule.a", "allow"); +- +- g_assert(qdict_crumple(src, &error) == NULL); +- g_assert(error != NULL); +- error_free(error); +- error = NULL; +- qobject_unref(src); +- +- src = qdict_new(); +- /* The input should be flat, ie no dicts or lists */ +- qdict_put(src, "rule.a", qdict_new()); +- qdict_put_str(src, "rule.b", "allow"); +- +- g_assert(qdict_crumple(src, &error) == NULL); +- g_assert(error != NULL); +- error_free(error); +- error = NULL; +- qobject_unref(src); +- +- src = qdict_new(); +- /* List indexes must not have gaps */ +- qdict_put_str(src, "rule.0", "deny"); +- qdict_put_str(src, "rule.3", "allow"); +- +- g_assert(qdict_crumple(src, &error) == NULL); +- g_assert(error != NULL); +- error_free(error); +- error = NULL; +- qobject_unref(src); +- +- src = qdict_new(); +- /* List indexes must be in %zu format */ +- qdict_put_str(src, "rule.0", "deny"); +- qdict_put_str(src, "rule.+1", "allow"); +- +- g_assert(qdict_crumple(src, &error) == NULL); +- g_assert(error != NULL); +- error_free(error); +- error = NULL; +- qobject_unref(src); +-} +- + /* + * Errors test-cases + */ +@@ -987,29 +359,15 @@ int main(int argc, char **argv) + g_test_add_func("/public/get_try_int", qdict_get_try_int_test); + g_test_add_func("/public/get_str", qdict_get_str_test); + g_test_add_func("/public/get_try_str", qdict_get_try_str_test); +- g_test_add_func("/public/defaults", qdict_defaults_test); + g_test_add_func("/public/haskey_not", qdict_haskey_not_test); + g_test_add_func("/public/haskey", qdict_haskey_test); + g_test_add_func("/public/del", qdict_del_test); + g_test_add_func("/public/to_qdict", qobject_to_qdict_test); + g_test_add_func("/public/iterapi", qdict_iterapi_test); +- g_test_add_func("/public/flatten", qdict_flatten_test); +- g_test_add_func("/public/array_split", qdict_array_split_test); +- g_test_add_func("/public/array_entries", qdict_array_entries_test); +- g_test_add_func("/public/join", qdict_join_test); + + g_test_add_func("/errors/put_exists", qdict_put_exists_test); + g_test_add_func("/errors/get_not_exists", qdict_get_not_exists_test); + +- g_test_add_func("/public/crumple/recursive", +- qdict_crumple_test_recursive); +- g_test_add_func("/public/crumple/empty", +- qdict_crumple_test_empty); +- g_test_add_func("/public/crumple/bad_inputs", +- qdict_crumple_test_bad_inputs); +- +- g_test_add_func("/public/rename_keys", qdict_rename_keys_test); +- + /* The Big one */ + if (g_test_slow()) { + g_test_add_func("/stress/test", qdict_stress_test); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qobject-Replace-qobject_incref-QINCREF-qobject_decre.patch b/SOURCES/kvm-qobject-Replace-qobject_incref-QINCREF-qobject_decre.patch new file mode 100644 index 0000000..fe406c4 --- /dev/null +++ b/SOURCES/kvm-qobject-Replace-qobject_incref-QINCREF-qobject_decre.patch @@ -0,0 +1,5252 @@ +From 7760613392819cfcfca3f2569909fb2fa794204f Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:11 +0200 +Subject: [PATCH 04/54] qobject: Replace qobject_incref/QINCREF + qobject_decref/QDECREF +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-5-armbru@redhat.com> +Patchwork-id: 80739 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 04/23] qobject: Replace qobject_incref/QINCREF qobject_decref/QDECREF +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +From: Marc-André Lureau + +Now that we can safely call QOBJECT() on QObject * as well as its +subtypes, we can have macros qobject_ref() / qobject_unref() that work +everywhere instead of having to use QINCREF() / QDECREF() for QObject +and qobject_incref() / qobject_decref() for its subtypes. + +The replacement is mechanical, except I broke a long line, and added a +cast in monitor_qmp_cleanup_req_queue_locked(). Unlike +qobject_decref(), qobject_unref() doesn't accept void *. + +Note that the new macros evaluate their argument exactly once, thus no +need to shout them. + +Signed-off-by: Marc-André Lureau +Reviewed-by: Eric Blake +Message-Id: <20180419150145.24795-4-marcandre.lureau@redhat.com> +Reviewed-by: Markus Armbruster +[Rebased, semantic conflict resolved, commit message improved] +Signed-off-by: Markus Armbruster +(cherry picked from commit cb3e7f08aeaab0ab13e629ce8496dca150a449ba) +[Trivial conflict in tests/migration-test.c resolved] + +Signed-off-by: Miroslav Rezanina +--- + block.c | 78 ++++++++++++++-------------- + block/blkdebug.c | 4 +- + block/blkverify.c | 4 +- + block/crypto.c | 4 +- + block/gluster.c | 4 +- + block/iscsi.c | 2 +- + block/nbd.c | 4 +- + block/nfs.c | 4 +- + block/null.c | 2 +- + block/nvme.c | 2 +- + block/parallels.c | 4 +- + block/qapi.c | 2 +- + block/qcow.c | 8 +-- + block/qcow2.c | 8 +-- + block/qed.c | 4 +- + block/quorum.c | 2 +- + block/rbd.c | 14 ++--- + block/sheepdog.c | 12 ++--- + block/snapshot.c | 4 +- + block/ssh.c | 4 +- + block/vdi.c | 2 +- + block/vhdx.c | 4 +- + block/vpc.c | 4 +- + block/vvfat.c | 2 +- + block/vxhs.c | 2 +- + blockdev.c | 16 +++--- + docs/devel/qapi-code-gen.txt | 2 +- + hw/i386/acpi-build.c | 12 ++--- + hw/ppc/spapr_drc.c | 2 +- + hw/usb/xen-usb.c | 4 +- + include/qapi/qmp/qnull.h | 2 +- + include/qapi/qmp/qobject.h | 40 +++++++-------- + migration/migration.c | 4 +- + migration/qjson.c | 2 +- + monitor.c | 50 +++++++++--------- + qapi/qapi-dealloc-visitor.c | 4 +- + qapi/qmp-dispatch.c | 6 +-- + qapi/qobject-input-visitor.c | 8 +-- + qapi/qobject-output-visitor.c | 8 +-- + qemu-img.c | 18 +++---- + qemu-io.c | 6 +-- + qga/main.c | 12 ++--- + qmp.c | 4 +- + qobject/json-parser.c | 10 ++-- + qobject/qdict.c | 38 +++++++------- + qobject/qjson.c | 2 +- + qobject/qlist.c | 4 +- + qom/object.c | 16 +++--- + qom/object_interfaces.c | 2 +- + scripts/coccinelle/qobject.cocci | 8 +-- + scripts/qapi/events.py | 2 +- + target/ppc/translate_init.c | 2 +- + target/s390x/cpu_models.c | 2 +- + tests/ahci-test.c | 6 +-- + tests/check-qdict.c | 100 ++++++++++++++++++------------------ + tests/check-qjson.c | 84 +++++++++++++++--------------- + tests/check-qlist.c | 8 +-- + tests/check-qlit.c | 10 ++-- + tests/check-qnull.c | 10 ++-- + tests/check-qnum.c | 28 +++++----- + tests/check-qobject.c | 2 +- + tests/check-qstring.c | 10 ++-- + tests/cpu-plug-test.c | 4 +- + tests/device-introspect-test.c | 24 ++++----- + tests/drive_del-test.c | 4 +- + tests/libqos/libqos.c | 8 +-- + tests/libqos/pci-pc.c | 2 +- + tests/libqtest.c | 24 ++++----- + tests/machine-none-test.c | 2 +- + tests/migration-test.c | 24 ++++----- + tests/numa-test.c | 16 +++--- + tests/pvpanic-test.c | 2 +- + tests/q35-test.c | 2 +- + tests/qmp-test.c | 38 +++++++------- + tests/qom-test.c | 8 +-- + tests/tco-test.c | 12 ++--- + tests/test-char.c | 2 +- + tests/test-keyval.c | 82 ++++++++++++++--------------- + tests/test-netfilter.c | 26 +++++----- + tests/test-qemu-opts.c | 14 ++--- + tests/test-qga.c | 76 +++++++++++++-------------- + tests/test-qmp-cmds.c | 24 ++++----- + tests/test-qmp-event.c | 2 +- + tests/test-qobject-input-visitor.c | 10 ++-- + tests/test-qobject-output-visitor.c | 18 +++---- + tests/test-visitor-serialization.c | 6 +-- + tests/test-x86-cpuid-compat.c | 14 ++--- + tests/tmp105-test.c | 4 +- + tests/vhost-user-test.c | 6 +-- + tests/virtio-net-test.c | 6 +-- + tests/vmgenid-test.c | 2 +- + tests/wdt_ib700-test.c | 14 ++--- + util/keyval.c | 12 ++--- + util/qemu-config.c | 4 +- + 94 files changed, 608 insertions(+), 612 deletions(-) + +diff --git a/block.c b/block.c +index a2caadf..55a7984 100644 +--- a/block.c ++++ b/block.c +@@ -1227,9 +1227,9 @@ BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, + + ret = bdrv_open_driver(bs, drv, node_name, bs->options, flags, errp); + if (ret < 0) { +- QDECREF(bs->explicit_options); ++ qobject_unref(bs->explicit_options); + bs->explicit_options = NULL; +- QDECREF(bs->options); ++ qobject_unref(bs->options); + bs->options = NULL; + bdrv_unref(bs); + return NULL; +@@ -1460,7 +1460,7 @@ static QDict *parse_json_filename(const char *filename, Error **errp) + + options = qobject_to(QDict, options_obj); + if (!options) { +- qobject_decref(options_obj); ++ qobject_unref(options_obj); + error_setg(errp, "Invalid JSON object given"); + return NULL; + } +@@ -1490,7 +1490,7 @@ static void parse_json_protocol(QDict *options, const char **pfilename, + /* Options given in the filename have lower priority than options + * specified directly */ + qdict_join(options, json_options, false); +- QDECREF(json_options); ++ qobject_unref(json_options); + *pfilename = NULL; + } + +@@ -2273,7 +2273,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, + if (reference || qdict_haskey(options, "file.filename")) { + backing_filename[0] = '\0'; + } else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) { +- QDECREF(options); ++ qobject_unref(options); + goto free_exit; + } else { + bdrv_get_full_backing_filename(bs, backing_filename, PATH_MAX, +@@ -2281,7 +2281,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, + if (local_err) { + ret = -EINVAL; + error_propagate(errp, local_err); +- QDECREF(options); ++ qobject_unref(options); + goto free_exit; + } + } +@@ -2289,7 +2289,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, + if (!bs->drv || !bs->drv->supports_backing) { + ret = -EINVAL; + error_setg(errp, "Driver doesn't support backing files"); +- QDECREF(options); ++ qobject_unref(options); + goto free_exit; + } + +@@ -2323,7 +2323,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, + + free_exit: + g_free(backing_filename); +- QDECREF(tmp_parent_options); ++ qobject_unref(tmp_parent_options); + return ret; + } + +@@ -2356,7 +2356,7 @@ bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key, + error_setg(errp, "A block device must be specified for \"%s\"", + bdref_key); + } +- QDECREF(image_options); ++ qobject_unref(image_options); + goto done; + } + +@@ -2449,7 +2449,7 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp) + obj = NULL; + + fail: +- qobject_decref(obj); ++ qobject_unref(obj); + visit_free(v); + return bs; + } +@@ -2519,7 +2519,7 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, + } + + out: +- QDECREF(snapshot_options); ++ qobject_unref(snapshot_options); + g_free(tmp_filename); + return bs_snapshot; + } +@@ -2530,7 +2530,7 @@ out: + * 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 QINCREF() before calling bdrv_open. ++ * 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. +@@ -2561,7 +2561,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, + + if (reference) { + bool options_non_empty = options ? qdict_size(options) : false; +- QDECREF(options); ++ qobject_unref(options); + + if (filename || options_non_empty) { + error_setg(errp, "Cannot reference an existing block device with " +@@ -2752,7 +2752,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, + + bdrv_parent_cb_change_media(bs, true); + +- QDECREF(options); ++ qobject_unref(options); + + /* For snapshot=on, create a temporary qcow2 overlay. bs points to the + * temporary snapshot afterwards. */ +@@ -2776,10 +2776,10 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, + + fail: + blk_unref(file); +- QDECREF(snapshot_options); +- QDECREF(bs->explicit_options); +- QDECREF(bs->options); +- QDECREF(options); ++ 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); +@@ -2788,8 +2788,8 @@ fail: + + close_and_fail: + bdrv_unref(bs); +- QDECREF(snapshot_options); +- QDECREF(options); ++ qobject_unref(snapshot_options); ++ qobject_unref(options); + error_propagate(errp, local_err); + return NULL; + } +@@ -2884,7 +2884,7 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, + old_options = qdict_clone_shallow(bs->explicit_options); + } + bdrv_join_options(bs, options, old_options); +- QDECREF(old_options); ++ qobject_unref(old_options); + + explicit_options = qdict_clone_shallow(options); + +@@ -2899,13 +2899,13 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, + qemu_opts_absorb_qdict(opts, options_copy, NULL); + update_flags_from_options(&flags, opts); + qemu_opts_del(opts); +- QDECREF(options_copy); ++ qobject_unref(options_copy); + } + + /* 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); +- QDECREF(old_options); ++ qobject_unref(old_options); + + /* bdrv_open_inherit() sets and clears some additional flags internally */ + flags &= ~BDRV_O_PROTOCOL; +@@ -2917,8 +2917,8 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, + bs_entry = g_new0(BlockReopenQueueEntry, 1); + QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry); + } else { +- QDECREF(bs_entry->state.options); +- QDECREF(bs_entry->state.explicit_options); ++ qobject_unref(bs_entry->state.options); ++ qobject_unref(bs_entry->state.explicit_options); + } + + bs_entry->state.bs = bs; +@@ -3008,9 +3008,9 @@ cleanup: + if (ret && bs_entry->prepared) { + bdrv_reopen_abort(&bs_entry->state); + } else if (ret) { +- QDECREF(bs_entry->state.explicit_options); ++ qobject_unref(bs_entry->state.explicit_options); + } +- QDECREF(bs_entry->state.options); ++ qobject_unref(bs_entry->state.options); + g_free(bs_entry); + } + g_free(bs_queue); +@@ -3253,7 +3253,7 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state) + } + + /* set BDS specific flags now */ +- QDECREF(bs->explicit_options); ++ qobject_unref(bs->explicit_options); + + bs->explicit_options = reopen_state->explicit_options; + bs->open_flags = reopen_state->flags; +@@ -3296,7 +3296,7 @@ void bdrv_reopen_abort(BDRVReopenState *reopen_state) + drv->bdrv_reopen_abort(reopen_state); + } + +- QDECREF(reopen_state->explicit_options); ++ qobject_unref(reopen_state->explicit_options); + + bdrv_abort_perm_update(reopen_state->bs); + } +@@ -3343,11 +3343,11 @@ static void bdrv_close(BlockDriverState *bs) + bs->total_sectors = 0; + bs->encrypted = false; + bs->sg = false; +- QDECREF(bs->options); +- QDECREF(bs->explicit_options); ++ qobject_unref(bs->options); ++ qobject_unref(bs->explicit_options); + bs->options = NULL; + bs->explicit_options = NULL; +- QDECREF(bs->full_open_options); ++ qobject_unref(bs->full_open_options); + bs->full_open_options = NULL; + + bdrv_release_named_dirty_bitmaps(bs); +@@ -5134,7 +5134,7 @@ static bool append_open_options(QDict *d, BlockDriverState *bs) + continue; + } + +- qobject_incref(qdict_entry_value(entry)); ++ qobject_ref(qdict_entry_value(entry)); + qdict_put_obj(d, qdict_entry_key(entry), qdict_entry_value(entry)); + found_any = true; + } +@@ -5174,21 +5174,21 @@ void bdrv_refresh_filename(BlockDriverState *bs) + * information before refreshing it */ + bs->exact_filename[0] = '\0'; + if (bs->full_open_options) { +- QDECREF(bs->full_open_options); ++ qobject_unref(bs->full_open_options); + bs->full_open_options = NULL; + } + + opts = qdict_new(); + append_open_options(opts, bs); + drv->bdrv_refresh_filename(bs, opts); +- QDECREF(opts); ++ qobject_unref(opts); + } else if (bs->file) { + /* Try to reconstruct valid information from the underlying file */ + bool has_open_options; + + bs->exact_filename[0] = '\0'; + if (bs->full_open_options) { +- QDECREF(bs->full_open_options); ++ qobject_unref(bs->full_open_options); + bs->full_open_options = NULL; + } + +@@ -5207,12 +5207,12 @@ void bdrv_refresh_filename(BlockDriverState *bs) + * suffices without querying the (exact_)filename of this BDS. */ + if (bs->file->bs->full_open_options) { + qdict_put_str(opts, "driver", drv->format_name); +- QINCREF(bs->file->bs->full_open_options); ++ qobject_ref(bs->file->bs->full_open_options); + qdict_put(opts, "file", bs->file->bs->full_open_options); + + bs->full_open_options = opts; + } else { +- QDECREF(opts); ++ qobject_unref(opts); + } + } else if (!bs->full_open_options && qdict_size(bs->options)) { + /* There is no underlying file BDS (at least referenced by BDS.file), +@@ -5246,7 +5246,7 @@ void bdrv_refresh_filename(BlockDriverState *bs) + QString *json = qobject_to_json(QOBJECT(bs->full_open_options)); + snprintf(bs->filename, sizeof(bs->filename), "json:%s", + qstring_get_str(json)); +- QDECREF(json); ++ qobject_unref(json); + } + } + +diff --git a/block/blkdebug.c b/block/blkdebug.c +index 5897124..689703d 100644 +--- a/block/blkdebug.c ++++ b/block/blkdebug.c +@@ -845,12 +845,12 @@ static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options) + opts = qdict_new(); + qdict_put_str(opts, "driver", "blkdebug"); + +- QINCREF(bs->file->bs->full_open_options); ++ qobject_ref(bs->file->bs->full_open_options); + qdict_put(opts, "image", bs->file->bs->full_open_options); + + for (e = qdict_first(options); e; e = qdict_next(options, e)) { + if (strcmp(qdict_entry_key(e), "x-image")) { +- qobject_incref(qdict_entry_value(e)); ++ qobject_ref(qdict_entry_value(e)); + qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e)); + } + } +diff --git a/block/blkverify.c b/block/blkverify.c +index 331365b..3cffcb1 100644 +--- a/block/blkverify.c ++++ b/block/blkverify.c +@@ -291,9 +291,9 @@ static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options) + QDict *opts = qdict_new(); + qdict_put_str(opts, "driver", "blkverify"); + +- QINCREF(bs->file->bs->full_open_options); ++ qobject_ref(bs->file->bs->full_open_options); + qdict_put(opts, "raw", bs->file->bs->full_open_options); +- QINCREF(s->test_file->bs->full_open_options); ++ qobject_ref(s->test_file->bs->full_open_options); + qdict_put(opts, "test", s->test_file->bs->full_open_options); + + bs->full_open_options = opts; +diff --git a/block/crypto.c b/block/crypto.c +index bc6c7e3..7e7ad2d 100644 +--- a/block/crypto.c ++++ b/block/crypto.c +@@ -305,7 +305,7 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, + + ret = 0; + cleanup: +- QDECREF(cryptoopts); ++ qobject_unref(cryptoopts); + qapi_free_QCryptoBlockOpenOptions(open_opts); + return ret; + } +@@ -635,7 +635,7 @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename, + fail: + bdrv_unref(bs); + qapi_free_QCryptoBlockCreateOptions(create_opts); +- QDECREF(cryptoopts); ++ qobject_unref(cryptoopts); + return ret; + } + +diff --git a/block/gluster.c b/block/gluster.c +index 4adc1a8..55be566 100644 +--- a/block/gluster.c ++++ b/block/gluster.c +@@ -650,7 +650,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, + } + gsconf = NULL; + +- QDECREF(backing_options); ++ qobject_unref(backing_options); + backing_options = NULL; + g_free(str); + str = NULL; +@@ -663,7 +663,7 @@ out: + qapi_free_SocketAddress(gsconf); + qemu_opts_del(opts); + g_free(str); +- QDECREF(backing_options); ++ qobject_unref(backing_options); + errno = EINVAL; + return -errno; + } +diff --git a/block/iscsi.c b/block/iscsi.c +index f5aecfc..d19ae0e 100644 +--- a/block/iscsi.c ++++ b/block/iscsi.c +@@ -2143,7 +2143,7 @@ static int coroutine_fn iscsi_co_create_opts(const char *filename, QemuOpts *opt + } else { + ret = iscsi_open(bs, bs_options, 0, NULL); + } +- QDECREF(bs_options); ++ qobject_unref(bs_options); + + if (ret != 0) { + goto out; +diff --git a/block/nbd.c b/block/nbd.c +index 1e2b3ba..3e1693c 100644 +--- a/block/nbd.c ++++ b/block/nbd.c +@@ -293,8 +293,8 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, + } + + done: +- QDECREF(addr); +- qobject_decref(crumpled_addr); ++ qobject_unref(addr); ++ qobject_unref(crumpled_addr); + visit_free(iv); + return saddr; + } +diff --git a/block/nfs.c b/block/nfs.c +index 2577df4..66fddf1 100644 +--- a/block/nfs.c ++++ b/block/nfs.c +@@ -567,7 +567,7 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options, + v = qobject_input_visitor_new_keyval(crumpled); + visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err); + visit_free(v); +- qobject_decref(crumpled); ++ qobject_unref(crumpled); + + if (local_err) { + return NULL; +@@ -683,7 +683,7 @@ static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts, + + ret = 0; + out: +- QDECREF(options); ++ qobject_unref(options); + qapi_free_BlockdevCreateOptions(create_options); + return ret; + } +diff --git a/block/null.c b/block/null.c +index 806a863..700a2d0 100644 +--- a/block/null.c ++++ b/block/null.c +@@ -244,7 +244,7 @@ static int coroutine_fn null_co_block_status(BlockDriverState *bs, + + static void null_refresh_filename(BlockDriverState *bs, QDict *opts) + { +- QINCREF(opts); ++ qobject_ref(opts); + qdict_del(opts, "filename"); + + if (!qdict_size(opts)) { +diff --git a/block/nvme.c b/block/nvme.c +index c4f3a7b..e192da9 100644 +--- a/block/nvme.c ++++ b/block/nvme.c +@@ -1073,7 +1073,7 @@ static int nvme_reopen_prepare(BDRVReopenState *reopen_state, + + static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts) + { +- QINCREF(opts); ++ qobject_ref(opts); + qdict_del(opts, "filename"); + + if (!qdict_size(opts)) { +diff --git a/block/parallels.c b/block/parallels.c +index 799215e..045810d 100644 +--- a/block/parallels.c ++++ b/block/parallels.c +@@ -651,7 +651,7 @@ static int coroutine_fn parallels_co_create_opts(const char *filename, + qdict_put_str(qdict, "file", bs->node_name); + + qobj = qdict_crumple(qdict, errp); +- QDECREF(qdict); ++ qobject_unref(qdict); + qdict = qobject_to(QDict, qobj); + if (qdict == NULL) { + ret = -EINVAL; +@@ -682,7 +682,7 @@ static int coroutine_fn parallels_co_create_opts(const char *filename, + ret = 0; + + done: +- QDECREF(qdict); ++ qobject_unref(qdict); + bdrv_unref(bs); + qapi_free_BlockdevCreateOptions(create_options); + return ret; +diff --git a/block/qapi.c b/block/qapi.c +index 04c6fc6..e12968f 100644 +--- a/block/qapi.c ++++ b/block/qapi.c +@@ -773,7 +773,7 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f, + visit_complete(v, &obj); + data = qdict_get(qobject_to(QDict, obj), "data"); + dump_qobject(func_fprintf, f, 1, data); +- qobject_decref(obj); ++ qobject_unref(obj); + visit_free(v); + } + +diff --git a/block/qcow.c b/block/qcow.c +index f928916..4b2f7db 100644 +--- a/block/qcow.c ++++ b/block/qcow.c +@@ -315,7 +315,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, + goto fail; + } + +- QDECREF(encryptopts); ++ qobject_unref(encryptopts); + qapi_free_QCryptoBlockOpenOptions(crypto_opts); + qemu_co_mutex_init(&s->lock); + return 0; +@@ -326,7 +326,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, + g_free(s->cluster_cache); + g_free(s->cluster_data); + qcrypto_block_free(s->crypto); +- QDECREF(encryptopts); ++ qobject_unref(encryptopts); + qapi_free_QCryptoBlockOpenOptions(crypto_opts); + return ret; + } +@@ -995,7 +995,7 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, + qdict_put_str(qdict, "file", bs->node_name); + + qobj = qdict_crumple(qdict, errp); +- QDECREF(qdict); ++ qobject_unref(qdict); + qdict = qobject_to(QDict, qobj); + if (qdict == NULL) { + ret = -EINVAL; +@@ -1025,7 +1025,7 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, + + ret = 0; + fail: +- QDECREF(qdict); ++ qobject_unref(qdict); + bdrv_unref(bs); + qapi_free_BlockdevCreateOptions(create_options); + return ret; +diff --git a/block/qcow2.c b/block/qcow2.c +index ef68772..2f36e63 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -1063,7 +1063,7 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, + + ret = 0; + fail: +- QDECREF(encryptopts); ++ qobject_unref(encryptopts); + qemu_opts_del(opts); + opts = NULL; + return ret; +@@ -2183,7 +2183,7 @@ static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs, + qemu_co_mutex_lock(&s->lock); + ret = qcow2_do_open(bs, options, flags, &local_err); + qemu_co_mutex_unlock(&s->lock); +- QDECREF(options); ++ qobject_unref(options); + if (local_err) { + error_propagate(errp, local_err); + error_prepend(errp, "Could not reopen qcow2 layer: "); +@@ -3139,7 +3139,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt + + /* Now get the QAPI type BlockdevCreateOptions */ + qobj = qdict_crumple(qdict, errp); +- QDECREF(qdict); ++ qobject_unref(qdict); + qdict = qobject_to(QDict, qobj); + if (qdict == NULL) { + ret = -EINVAL; +@@ -3168,7 +3168,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt + + ret = 0; + finish: +- QDECREF(qdict); ++ qobject_unref(qdict); + bdrv_unref(bs); + qapi_free_BlockdevCreateOptions(create_options); + return ret; +diff --git a/block/qed.c b/block/qed.c +index 35ff505..1db8eaf 100644 +--- a/block/qed.c ++++ b/block/qed.c +@@ -763,7 +763,7 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, + qdict_put_str(qdict, "file", bs->node_name); + + qobj = qdict_crumple(qdict, errp); +- QDECREF(qdict); ++ qobject_unref(qdict); + qdict = qobject_to(QDict, qobj); + if (qdict == NULL) { + ret = -EINVAL; +@@ -789,7 +789,7 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, + ret = bdrv_qed_co_create(create_options, errp); + + fail: +- QDECREF(qdict); ++ qobject_unref(qdict); + bdrv_unref(bs); + qapi_free_BlockdevCreateOptions(create_options); + return ret; +diff --git a/block/quorum.c b/block/quorum.c +index cfe484a..862cea3 100644 +--- a/block/quorum.c ++++ b/block/quorum.c +@@ -1082,7 +1082,7 @@ static void quorum_refresh_filename(BlockDriverState *bs, QDict *options) + + children = qlist_new(); + for (i = 0; i < s->num_children; i++) { +- QINCREF(s->children[i]->bs->full_open_options); ++ qobject_ref(s->children[i]->bs->full_open_options); + qlist_append(children, s->children[i]->bs->full_open_options); + } + +diff --git a/block/rbd.c b/block/rbd.c +index c9359d0..a14b42f 100644 +--- a/block/rbd.c ++++ b/block/rbd.c +@@ -226,7 +226,7 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options, + + done: + g_free(buf); +- QDECREF(keypairs); ++ qobject_unref(keypairs); + return; + } + +@@ -275,17 +275,17 @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json, + key = qstring_get_str(name); + + ret = rados_conf_set(cluster, key, qstring_get_str(value)); +- QDECREF(value); ++ qobject_unref(value); + if (ret < 0) { + error_setg_errno(errp, -ret, "invalid conf option %s", key); +- QDECREF(name); ++ qobject_unref(name); + ret = -EINVAL; + break; + } +- QDECREF(name); ++ qobject_unref(name); + } + +- QDECREF(keypairs); ++ qobject_unref(keypairs); + return ret; + } + +@@ -449,7 +449,7 @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename, + } + + exit: +- QDECREF(options); ++ qobject_unref(options); + qapi_free_BlockdevCreateOptions(create_options); + return ret; + } +@@ -664,7 +664,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, + v = qobject_input_visitor_new_keyval(crumpled); + visit_type_BlockdevOptionsRbd(v, NULL, &opts, &local_err); + visit_free(v); +- qobject_decref(crumpled); ++ qobject_unref(crumpled); + + if (local_err) { + error_propagate(errp, local_err); +diff --git a/block/sheepdog.c b/block/sheepdog.c +index 387f59c..07529f4 100644 +--- a/block/sheepdog.c ++++ b/block/sheepdog.c +@@ -567,8 +567,8 @@ static SocketAddress *sd_server_config(QDict *options, Error **errp) + + done: + visit_free(iv); +- qobject_decref(crumpled_server); +- QDECREF(server); ++ qobject_unref(crumpled_server); ++ qobject_unref(server); + return saddr; + } + +@@ -1883,7 +1883,7 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size, + + if (local_err) { + error_propagate(errp, local_err); +- qobject_decref(obj); ++ qobject_unref(obj); + return -EINVAL; + } + +@@ -1901,7 +1901,7 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size, + ret = sd_prealloc(bs, 0, size, errp); + fail: + bdrv_unref(bs); +- QDECREF(qdict); ++ qobject_unref(qdict); + return ret; + } + +@@ -2226,7 +2226,7 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, + v = qobject_input_visitor_new_keyval(crumpled); + visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); + visit_free(v); +- qobject_decref(crumpled); ++ qobject_unref(crumpled); + + if (local_err) { + error_propagate(errp, local_err); +@@ -2252,7 +2252,7 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, + ret = sd_co_create(create_options, errp); + fail: + qapi_free_BlockdevCreateOptions(create_options); +- QDECREF(qdict); ++ qobject_unref(qdict); + return ret; + } + +diff --git a/block/snapshot.c b/block/snapshot.c +index eacc1f1..2953d96 100644 +--- a/block/snapshot.c ++++ b/block/snapshot.c +@@ -214,7 +214,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs, + bdrv_ref(file); + + qdict_extract_subqdict(options, &file_options, "file."); +- QDECREF(file_options); ++ qobject_unref(file_options); + qdict_put_str(options, "file", bdrv_get_node_name(file)); + + drv->bdrv_close(bs); +@@ -223,7 +223,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs, + + ret = bdrv_snapshot_goto(file, snapshot_id, errp); + open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err); +- QDECREF(options); ++ qobject_unref(options); + if (open_ret < 0) { + bdrv_unref(file); + bs->drv = NULL; +diff --git a/block/ssh.c b/block/ssh.c +index ab3acf0..412a1bf 100644 +--- a/block/ssh.c ++++ b/block/ssh.c +@@ -638,7 +638,7 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp) + v = qobject_input_visitor_new(crumpled); + visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err); + visit_free(v); +- qobject_decref(crumpled); ++ qobject_unref(crumpled); + + if (local_err) { + error_propagate(errp, local_err); +@@ -917,7 +917,7 @@ static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts, + ret = ssh_co_create(create_options, errp); + + out: +- QDECREF(uri_options); ++ qobject_unref(uri_options); + qapi_free_BlockdevCreateOptions(create_options); + return ret; + } +diff --git a/block/vdi.c b/block/vdi.c +index 4a2d1ff..96a22b8 100644 +--- a/block/vdi.c ++++ b/block/vdi.c +@@ -951,7 +951,7 @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts, + /* Create the vdi image (format layer) */ + ret = vdi_co_do_create(create_options, block_size, errp); + done: +- QDECREF(qdict); ++ qobject_unref(qdict); + qapi_free_BlockdevCreateOptions(create_options); + bdrv_unref(bs_file); + return ret; +diff --git a/block/vhdx.c b/block/vhdx.c +index 6ac0424..c3a4220 100644 +--- a/block/vhdx.c ++++ b/block/vhdx.c +@@ -2003,7 +2003,7 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, + qdict_put_str(qdict, "file", bs->node_name); + + qobj = qdict_crumple(qdict, errp); +- QDECREF(qdict); ++ qobject_unref(qdict); + qdict = qobject_to(QDict, qobj); + if (qdict == NULL) { + ret = -EINVAL; +@@ -2049,7 +2049,7 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, + ret = vhdx_co_create(create_options, errp); + + fail: +- QDECREF(qdict); ++ qobject_unref(qdict); + bdrv_unref(bs); + qapi_free_BlockdevCreateOptions(create_options); + return ret; +diff --git a/block/vpc.c b/block/vpc.c +index 44f99a4..0ebfcd3 100644 +--- a/block/vpc.c ++++ b/block/vpc.c +@@ -1119,7 +1119,7 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, + qdict_put_str(qdict, "file", bs->node_name); + + qobj = qdict_crumple(qdict, errp); +- QDECREF(qdict); ++ qobject_unref(qdict); + qdict = qobject_to(QDict, qobj); + if (qdict == NULL) { + ret = -EINVAL; +@@ -1157,7 +1157,7 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, + ret = vpc_co_create(create_options, errp); + + fail: +- QDECREF(qdict); ++ qobject_unref(qdict); + bdrv_unref(bs); + qapi_free_BlockdevCreateOptions(create_options); + return ret; +diff --git a/block/vvfat.c b/block/vvfat.c +index 1569783..662dca0 100644 +--- a/block/vvfat.c ++++ b/block/vvfat.c +@@ -3179,7 +3179,7 @@ static int enable_write_target(BlockDriverState *bs, Error **errp) + qdict_put_str(options, "write-target.driver", "qcow"); + s->qcow = bdrv_open_child(s->qcow_filename, options, "write-target", bs, + &child_vvfat_qcow, false, errp); +- QDECREF(options); ++ qobject_unref(options); + if (!s->qcow) { + ret = -EINVAL; + goto err; +diff --git a/block/vxhs.c b/block/vxhs.c +index 68edb51..96e83d9 100644 +--- a/block/vxhs.c ++++ b/block/vxhs.c +@@ -497,7 +497,7 @@ static int vxhs_open(BlockDriverState *bs, QDict *options, + + out: + g_free(of_vsa_addr); +- QDECREF(backing_options); ++ qobject_unref(backing_options); + qemu_opts_del(tcp_opts); + qemu_opts_del(opts); + g_free(cacert); +diff --git a/blockdev.c b/blockdev.c +index 60b37dc..129e444 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -617,7 +617,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, + blk_rs->read_only = read_only; + blk_rs->detect_zeroes = detect_zeroes; + +- QDECREF(bs_opts); ++ qobject_unref(bs_opts); + } else { + if (file && !*file) { + file = NULL; +@@ -673,16 +673,16 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, + + err_no_bs_opts: + qemu_opts_del(opts); +- QDECREF(interval_dict); +- QDECREF(interval_list); ++ qobject_unref(interval_dict); ++ qobject_unref(interval_list); + return blk; + + early_err: + qemu_opts_del(opts); +- QDECREF(interval_dict); +- QDECREF(interval_list); ++ qobject_unref(interval_dict); ++ qobject_unref(interval_list); + err_no_opts: +- QDECREF(bs_opts); ++ qobject_unref(bs_opts); + return NULL; + } + +@@ -1171,7 +1171,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) + + fail: + qemu_opts_del(legacy_opts); +- QDECREF(bs_opts); ++ qobject_unref(bs_opts); + return dinfo; + } + +@@ -4068,7 +4068,7 @@ void hmp_drive_add_node(Monitor *mon, const char *optstr) + qdict = qemu_opts_to_qdict(opts, NULL); + + if (!qdict_get_try_str(qdict, "node-name")) { +- QDECREF(qdict); ++ qobject_unref(qdict); + error_report("'node-name' needs to be specified"); + goto out; + } +diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt +index a569d24..b9b6eab 100644 +--- a/docs/devel/qapi-code-gen.txt ++++ b/docs/devel/qapi-code-gen.txt +@@ -1340,7 +1340,7 @@ Example: + emit(EXAMPLE_QAPI_EVENT_MY_EVENT, qmp, &err); + + error_propagate(errp, err); +- QDECREF(qmp); ++ qobject_unref(qmp); + } + + const QEnumLookup example_QAPIEvent_lookup = { +diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c +index 976d151..b309a97 100644 +--- a/hw/i386/acpi-build.c ++++ b/hw/i386/acpi-build.c +@@ -201,21 +201,21 @@ static void acpi_get_pm_info(AcpiPmInfo *pm) + } else { + pm->s3_disabled = false; + } +- qobject_decref(o); ++ qobject_unref(o); + o = object_property_get_qobject(obj, ACPI_PM_PROP_S4_DISABLED, NULL); + if (o) { + pm->s4_disabled = qnum_get_uint(qobject_to(QNum, o)); + } else { + pm->s4_disabled = false; + } +- qobject_decref(o); ++ qobject_unref(o); + o = object_property_get_qobject(obj, ACPI_PM_PROP_S4_VAL, NULL); + if (o) { + pm->s4_val = qnum_get_uint(qobject_to(QNum, o)); + } else { + pm->s4_val = false; + } +- qobject_decref(o); ++ qobject_unref(o); + + pm->pcihp_bridge_en = + object_property_get_bool(obj, "acpi-pci-hotplug-with-bridge-support", +@@ -573,7 +573,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, + } + } + aml_append(parent_scope, method); +- qobject_decref(bsel); ++ qobject_unref(bsel); + } + + /** +@@ -2617,12 +2617,12 @@ static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg) + return false; + } + mcfg->mcfg_base = qnum_get_uint(qobject_to(QNum, o)); +- qobject_decref(o); ++ qobject_unref(o); + + o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_SIZE, NULL); + assert(o); + mcfg->mcfg_size = qnum_get_uint(qobject_to(QNum, o)); +- qobject_decref(o); ++ qobject_unref(o); + return true; + } + +diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c +index aa25113..8a045d6 100644 +--- a/hw/ppc/spapr_drc.c ++++ b/hw/ppc/spapr_drc.c +@@ -305,7 +305,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, + + if (!drc->fdt) { + visit_type_null(v, NULL, &null, errp); +- QDECREF(null); ++ qobject_unref(null); + return; + } + +diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c +index 3beeb0d..b3a90c0 100644 +--- a/hw/usb/xen-usb.c ++++ b/hw/usb/xen-usb.c +@@ -763,7 +763,7 @@ static void usbback_portid_add(struct usbback_info *usbif, unsigned port, + if (!usbif->ports[port - 1].dev) { + goto err; + } +- QDECREF(qdict); ++ qobject_unref(qdict); + speed = usbif->ports[port - 1].dev->speed; + switch (speed) { + case USB_SPEED_LOW: +@@ -796,7 +796,7 @@ static void usbback_portid_add(struct usbback_info *usbif, unsigned port, + return; + + err: +- QDECREF(qdict); ++ qobject_unref(qdict); + xen_pv_printf(&usbif->xendev, 0, "device %s could not be opened\n", busid); + } + +diff --git a/include/qapi/qmp/qnull.h b/include/qapi/qmp/qnull.h +index e8ea2c3..75b29c6 100644 +--- a/include/qapi/qmp/qnull.h ++++ b/include/qapi/qmp/qnull.h +@@ -23,7 +23,7 @@ extern QNull qnull_; + + static inline QNull *qnull(void) + { +- QINCREF(&qnull_); ++ qobject_ref(&qnull_); + return &qnull_; + } + +diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h +index a713c01..e20006f 100644 +--- a/include/qapi/qmp/qobject.h ++++ b/include/qapi/qmp/qobject.h +@@ -15,17 +15,17 @@ + * ------------------------------------ + * + * - 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 QDECREF() on the reference +- * when you are done. ++ * 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 QINCREF() to make the reference strong. ++ * 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 +- * QDECREF() when the reference is no longer needed. In other words, ++ * 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. + */ +@@ -50,14 +50,6 @@ struct QObject { + _obj ? container_of(&(_obj)->base, QObject, base) : NULL; \ + }) + +-/* High-level interface for qobject_incref() */ +-#define QINCREF(obj) \ +- qobject_incref(QOBJECT(obj)) +- +-/* High-level interface for qobject_decref() */ +-#define QDECREF(obj) \ +- qobject_decref(obj ? QOBJECT(obj) : NULL) +- + /* Required for qobject_to() */ + #define QTYPE_CAST_TO_QNull QTYPE_QNULL + #define QTYPE_CAST_TO_QNum QTYPE_QNUM +@@ -80,10 +72,7 @@ static inline void qobject_init(QObject *obj, QType type) + obj->base.type = type; + } + +-/** +- * qobject_incref(): Increment QObject's reference count +- */ +-static inline void qobject_incref(QObject *obj) ++static inline void qobject_ref_impl(QObject *obj) + { + if (obj) { + obj->base.refcnt++; +@@ -104,11 +93,7 @@ bool qobject_is_equal(const QObject *x, const QObject *y); + */ + void qobject_destroy(QObject *obj); + +-/** +- * qobject_decref(): Decrement QObject's reference count, deallocate +- * when it reaches zero +- */ +-static inline void qobject_decref(QObject *obj) ++static inline void qobject_unref_impl(QObject *obj) + { + assert(!obj || obj->base.refcnt); + if (obj && --obj->base.refcnt == 0) { +@@ -117,6 +102,17 @@ static inline void qobject_decref(QObject *obj) + } + + /** ++ * qobject_ref(): Increment QObject's reference count ++ */ ++#define qobject_ref(obj) qobject_ref_impl(QOBJECT(obj)) ++ ++/** ++ * 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) +diff --git a/migration/migration.c b/migration/migration.c +index ceb1697..ef4bb42 100644 +--- a/migration/migration.c ++++ b/migration/migration.c +@@ -1008,14 +1008,14 @@ void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp) + /* TODO Rewrite "" to null instead */ + if (params->has_tls_creds + && params->tls_creds->type == QTYPE_QNULL) { +- QDECREF(params->tls_creds->u.n); ++ qobject_unref(params->tls_creds->u.n); + params->tls_creds->type = QTYPE_QSTRING; + params->tls_creds->u.s = strdup(""); + } + /* TODO Rewrite "" to null instead */ + if (params->has_tls_hostname + && params->tls_hostname->type == QTYPE_QNULL) { +- QDECREF(params->tls_hostname->u.n); ++ qobject_unref(params->tls_hostname->u.n); + params->tls_hostname->type = QTYPE_QSTRING; + params->tls_hostname->u.s = strdup(""); + } +diff --git a/migration/qjson.c b/migration/qjson.c +index 9d7f6eb..e9889bd 100644 +--- a/migration/qjson.c ++++ b/migration/qjson.c +@@ -109,6 +109,6 @@ void qjson_finish(QJSON *json) + + void qjson_destroy(QJSON *json) + { +- QDECREF(json->str); ++ qobject_unref(json->str); + g_free(json); + } +diff --git a/monitor.c b/monitor.c +index 4f595ae..32f72f2 100644 +--- a/monitor.c ++++ b/monitor.c +@@ -329,8 +329,8 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, + + static void qmp_request_free(QMPRequest *req) + { +- qobject_decref(req->id); +- qobject_decref(req->req); ++ qobject_unref(req->id); ++ qobject_unref(req->req); + g_free(req); + } + +@@ -346,7 +346,7 @@ static void monitor_qmp_cleanup_req_queue_locked(Monitor *mon) + static void monitor_qmp_cleanup_resp_queue_locked(Monitor *mon) + { + while (!g_queue_is_empty(mon->qmp.qmp_responses)) { +- qobject_decref(g_queue_pop_head(mon->qmp.qmp_responses)); ++ qobject_unref((QObject *)g_queue_pop_head(mon->qmp.qmp_responses)); + } + } + +@@ -391,14 +391,14 @@ static void monitor_flush_locked(Monitor *mon) + rc = qemu_chr_fe_write(&mon->chr, (const uint8_t *) buf, len); + if ((rc < 0 && errno != EAGAIN) || (rc == len)) { + /* all flushed or error */ +- QDECREF(mon->outbuf); ++ qobject_unref(mon->outbuf); + mon->outbuf = qstring_new(); + return; + } + if (rc > 0) { + /* partial write */ + QString *tmp = qstring_from_str(buf + rc); +- QDECREF(mon->outbuf); ++ qobject_unref(mon->outbuf); + mon->outbuf = tmp; + } + if (mon->out_watch == 0) { +@@ -482,7 +482,7 @@ static void monitor_json_emitter_raw(Monitor *mon, + qstring_append_chr(json, '\n'); + monitor_puts(mon, qstring_get_str(json)); + +- QDECREF(json); ++ qobject_unref(json); + } + + static void monitor_json_emitter(Monitor *mon, QObject *data) +@@ -494,9 +494,9 @@ static void monitor_json_emitter(Monitor *mon, QObject *data) + * caller won't free the data (which will be finally freed in + * responder thread). + */ +- qobject_incref(data); ++ qobject_ref(data); + qemu_mutex_lock(&mon->qmp.qmp_queue_lock); +- g_queue_push_tail(mon->qmp.qmp_responses, (void *)data); ++ g_queue_push_tail(mon->qmp.qmp_responses, data); + qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); + qemu_bh_schedule(mon_global.qmp_respond_bh); + } else { +@@ -546,7 +546,7 @@ static void monitor_qmp_bh_responder(void *opaque) + break; + } + monitor_json_emitter_raw(response.mon, response.data); +- qobject_decref(response.data); ++ qobject_unref(response.data); + } + } + +@@ -613,9 +613,9 @@ monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) + * last send. Store event for sending when timer fires, + * replacing a prior stored event if any. + */ +- QDECREF(evstate->qdict); ++ qobject_unref(evstate->qdict); + evstate->qdict = qdict; +- QINCREF(evstate->qdict); ++ qobject_ref(evstate->qdict); + } else { + /* + * Last send was (at least) evconf->rate ns ago. +@@ -630,7 +630,7 @@ monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) + evstate = g_new(MonitorQAPIEventState, 1); + evstate->event = event; + evstate->data = data; +- QINCREF(evstate->data); ++ qobject_ref(evstate->data); + evstate->qdict = NULL; + evstate->timer = timer_new_ns(event_clock_type, + monitor_qapi_event_handler, +@@ -660,12 +660,12 @@ static void monitor_qapi_event_handler(void *opaque) + int64_t now = qemu_clock_get_ns(event_clock_type); + + monitor_qapi_event_emit(evstate->event, evstate->qdict); +- QDECREF(evstate->qdict); ++ qobject_unref(evstate->qdict); + evstate->qdict = NULL; + timer_mod_ns(evstate->timer, now + evconf->rate); + } else { + g_hash_table_remove(monitor_qapi_event_state, evstate); +- QDECREF(evstate->data); ++ qobject_unref(evstate->data); + timer_free(evstate->timer); + g_free(evstate); + } +@@ -747,7 +747,7 @@ static void monitor_data_destroy(Monitor *mon) + json_message_parser_destroy(&mon->qmp.parser); + } + readline_free(mon->rs); +- QDECREF(mon->outbuf); ++ qobject_unref(mon->outbuf); + qemu_mutex_destroy(&mon->out_lock); + qemu_mutex_destroy(&mon->qmp.qmp_queue_lock); + monitor_qmp_cleanup_req_queue_locked(mon); +@@ -3380,7 +3380,7 @@ static QDict *monitor_parse_arguments(Monitor *mon, + return qdict; + + fail: +- QDECREF(qdict); ++ qobject_unref(qdict); + g_free(key); + return NULL; + } +@@ -3405,7 +3405,7 @@ static void handle_hmp_command(Monitor *mon, const char *cmdline) + } + + cmd->cmd(mon, qdict); +- QDECREF(qdict); ++ qobject_unref(qdict); + } + + static void cmd_completion(Monitor *mon, const char *name, const char *list) +@@ -4067,15 +4067,15 @@ static void monitor_qmp_respond(Monitor *mon, QObject *rsp, + if (rsp) { + if (id) { + /* This is for the qdict below. */ +- qobject_incref(id); ++ qobject_ref(id); + qdict_put_obj(qobject_to(QDict, rsp), "id", id); + } + + monitor_json_emitter(mon, rsp); + } + +- qobject_decref(id); +- qobject_decref(rsp); ++ qobject_unref(id); ++ qobject_unref(rsp); + } + + /* +@@ -4098,7 +4098,7 @@ static void monitor_qmp_dispatch_one(QMPRequest *req_obj) + if (trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) { + QString *req_json = qobject_to_json(req); + trace_handle_qmp_command(mon, qstring_get_str(req_json)); +- QDECREF(req_json); ++ qobject_unref(req_json); + } + + old_mon = cur_mon; +@@ -4116,7 +4116,7 @@ static void monitor_qmp_dispatch_one(QMPRequest *req_obj) + monitor_resume(mon); + } + +- qobject_decref(req); ++ qobject_unref(req); + } + + /* +@@ -4208,7 +4208,7 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) + goto err; + } + +- qobject_incref(id); ++ qobject_ref(id); + qdict_del(qdict, "id"); + + req_obj = g_new0(QMPRequest, 1); +@@ -4263,7 +4263,7 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) + + err: + monitor_qmp_respond(mon, NULL, err, NULL); +- qobject_decref(req); ++ qobject_unref(req); + } + + static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size) +@@ -4382,7 +4382,7 @@ static void monitor_qmp_event(void *opaque, int event) + monitor_qmp_caps_reset(mon); + data = get_qmp_greeting(mon); + monitor_json_emitter(mon, data); +- qobject_decref(data); ++ qobject_unref(data); + mon_refcount++; + break; + case CHR_EVENT_CLOSED: +diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c +index fd23803..6b24afd 100644 +--- a/qapi/qapi-dealloc-visitor.c ++++ b/qapi/qapi-dealloc-visitor.c +@@ -99,7 +99,7 @@ static void qapi_dealloc_type_anything(Visitor *v, const char *name, + QObject **obj, Error **errp) + { + if (obj) { +- qobject_decref(*obj); ++ qobject_unref(*obj); + } + } + +@@ -107,7 +107,7 @@ static void qapi_dealloc_type_null(Visitor *v, const char *name, + QNull **obj, Error **errp) + { + if (obj) { +- QDECREF(*obj); ++ qobject_unref(*obj); + } + } + +diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c +index dd05907..f9377b2 100644 +--- a/qapi/qmp-dispatch.c ++++ b/qapi/qmp-dispatch.c +@@ -105,7 +105,7 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, + args = qdict_new(); + } else { + args = qdict_get_qdict(dict, "arguments"); +- QINCREF(args); ++ qobject_ref(args); + } + + cmd->fn(args, &ret, &local_err); +@@ -117,7 +117,7 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, + ret = QOBJECT(qdict_new()); + } + +- QDECREF(args); ++ qobject_unref(args); + + return ret; + } +@@ -166,7 +166,7 @@ QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request) + } else if (ret) { + qdict_put_obj(rsp, "return", ret); + } else { +- QDECREF(rsp); ++ qobject_unref(rsp); + return NULL; + } + +diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c +index a7569d5..7a290c4 100644 +--- a/qapi/qobject-input-visitor.c ++++ b/qapi/qobject-input-visitor.c +@@ -588,7 +588,7 @@ static void qobject_input_type_any(Visitor *v, const char *name, QObject **obj, + return; + } + +- qobject_incref(qobj); ++ qobject_ref(qobj); + *obj = qobj; + } + +@@ -652,7 +652,7 @@ static void qobject_input_free(Visitor *v) + qobject_input_stack_object_free(tos); + } + +- qobject_decref(qiv->root); ++ qobject_unref(qiv->root); + if (qiv->errname) { + g_string_free(qiv->errname, TRUE); + } +@@ -678,7 +678,7 @@ static QObjectInputVisitor *qobject_input_visitor_base_new(QObject *obj) + v->visitor.free = qobject_input_free; + + v->root = obj; +- qobject_incref(obj); ++ qobject_ref(obj); + + return v; + } +@@ -744,7 +744,7 @@ Visitor *qobject_input_visitor_new_str(const char *str, + } + v = qobject_input_visitor_new_keyval(QOBJECT(args)); + } +- QDECREF(args); ++ qobject_unref(args); + + return v; + } +diff --git a/qapi/qobject-output-visitor.c b/qapi/qobject-output-visitor.c +index 877e37e..3a933b4 100644 +--- a/qapi/qobject-output-visitor.c ++++ b/qapi/qobject-output-visitor.c +@@ -188,7 +188,7 @@ static void qobject_output_type_any(Visitor *v, const char *name, + QObject **obj, Error **errp) + { + QObjectOutputVisitor *qov = to_qov(v); +- qobject_incref(*obj); ++ qobject_ref(*obj); + qobject_output_add_obj(qov, name, *obj); + } + +@@ -201,7 +201,7 @@ static void qobject_output_type_null(Visitor *v, const char *name, + + /* Finish building, and return the root object. + * The root object is never null. The caller becomes the object's +- * owner, and should use qobject_decref() when done with it. */ ++ * owner, and should use qobject_unref() when done with it. */ + static void qobject_output_complete(Visitor *v, void *opaque) + { + QObjectOutputVisitor *qov = to_qov(v); +@@ -210,7 +210,7 @@ static void qobject_output_complete(Visitor *v, void *opaque) + assert(qov->root && QSLIST_EMPTY(&qov->stack)); + assert(opaque == qov->result); + +- qobject_incref(qov->root); ++ qobject_ref(qov->root); + *qov->result = qov->root; + qov->result = NULL; + } +@@ -226,7 +226,7 @@ static void qobject_output_free(Visitor *v) + g_free(e); + } + +- qobject_decref(qov->root); ++ qobject_unref(qov->root); + g_free(qov); + } + +diff --git a/qemu-img.c b/qemu-img.c +index 8320887..62b29e7 100644 +--- a/qemu-img.c ++++ b/qemu-img.c +@@ -279,7 +279,7 @@ static BlockBackend *img_open_opts(const char *optstr, + if (qdict_haskey(options, BDRV_OPT_FORCE_SHARE) + && !qdict_get_bool(options, BDRV_OPT_FORCE_SHARE)) { + error_report("--force-share/-U conflicts with image options"); +- QDECREF(options); ++ qobject_unref(options); + return NULL; + } + qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true); +@@ -561,9 +561,9 @@ static void dump_json_image_check(ImageCheck *check, bool quiet) + str = qobject_to_json_pretty(obj); + assert(str != NULL); + qprintf(quiet, "%s\n", qstring_get_str(str)); +- qobject_decref(obj); ++ qobject_unref(obj); + visit_free(v); +- QDECREF(str); ++ qobject_unref(str); + } + + static void dump_human_image_check(ImageCheck *check, bool quiet) +@@ -2384,9 +2384,9 @@ static void dump_json_image_info_list(ImageInfoList *list) + str = qobject_to_json_pretty(obj); + assert(str != NULL); + printf("%s\n", qstring_get_str(str)); +- qobject_decref(obj); ++ qobject_unref(obj); + visit_free(v); +- QDECREF(str); ++ qobject_unref(str); + } + + static void dump_json_image_info(ImageInfo *info) +@@ -2400,9 +2400,9 @@ static void dump_json_image_info(ImageInfo *info) + str = qobject_to_json_pretty(obj); + assert(str != NULL); + printf("%s\n", qstring_get_str(str)); +- qobject_decref(obj); ++ qobject_unref(obj); + visit_free(v); +- QDECREF(str); ++ qobject_unref(str); + } + + static void dump_human_image_info_list(ImageInfoList *list) +@@ -4488,9 +4488,9 @@ static void dump_json_block_measure_info(BlockMeasureInfo *info) + str = qobject_to_json_pretty(obj); + assert(str != NULL); + printf("%s\n", qstring_get_str(str)); +- qobject_decref(obj); ++ qobject_unref(obj); + visit_free(v); +- QDECREF(str); ++ qobject_unref(str); + } + + static int img_measure(int argc, char **argv) +diff --git a/qemu-io.c b/qemu-io.c +index e692c55..72fee0d 100644 +--- a/qemu-io.c ++++ b/qemu-io.c +@@ -86,7 +86,7 @@ static int openfile(char *name, int flags, bool writethrough, bool force_share, + + if (qemuio_blk) { + error_report("file open already, try 'help close'"); +- QDECREF(opts); ++ qobject_unref(opts); + return 1; + } + +@@ -97,7 +97,7 @@ static int openfile(char *name, int flags, bool writethrough, bool force_share, + if (qdict_haskey(opts, BDRV_OPT_FORCE_SHARE) + && !qdict_get_bool(opts, BDRV_OPT_FORCE_SHARE)) { + error_report("-U conflicts with image options"); +- QDECREF(opts); ++ qobject_unref(opts); + return 1; + } + qdict_put_bool(opts, BDRV_OPT_FORCE_SHARE, true); +@@ -243,7 +243,7 @@ static int open_f(BlockBackend *blk, int argc, char **argv) + } else if (optind == argc) { + openfile(NULL, flags, writethrough, force_share, opts); + } else { +- QDECREF(opts); ++ qobject_unref(opts); + qemuio_command_usage(&open_cmd); + } + return 0; +diff --git a/qga/main.c b/qga/main.c +index df1888e..1e1cec7 100644 +--- a/qga/main.c ++++ b/qga/main.c +@@ -563,7 +563,7 @@ static int send_response(GAState *s, QObject *payload) + response_qstr = qstring_new(); + qstring_append_chr(response_qstr, QGA_SENTINEL_BYTE); + qstring_append(response_qstr, qstring_get_str(payload_qstr)); +- QDECREF(payload_qstr); ++ qobject_unref(payload_qstr); + } else { + response_qstr = payload_qstr; + } +@@ -571,7 +571,7 @@ static int send_response(GAState *s, QObject *payload) + qstring_append_chr(response_qstr, '\n'); + buf = qstring_get_str(response_qstr); + status = ga_channel_write_all(s->channel, buf, strlen(buf)); +- QDECREF(response_qstr); ++ qobject_unref(response_qstr); + if (status != G_IO_STATUS_NORMAL) { + return -EIO; + } +@@ -592,7 +592,7 @@ static void process_command(GAState *s, QDict *req) + if (ret < 0) { + g_warning("error sending response: %s", strerror(-ret)); + } +- qobject_decref(rsp); ++ qobject_unref(rsp); + } + } + +@@ -609,7 +609,7 @@ static void process_event(JSONMessageParser *parser, GQueue *tokens) + g_debug("process_event: called"); + qdict = qobject_to(QDict, json_parser_parse_err(tokens, NULL, &err)); + if (err || !qdict) { +- QDECREF(qdict); ++ qobject_unref(qdict); + qdict = qdict_new(); + if (!err) { + g_warning("failed to parse event: unknown error"); +@@ -626,7 +626,7 @@ static void process_event(JSONMessageParser *parser, GQueue *tokens) + process_command(s, qdict); + } else { + if (!qdict_haskey(qdict, "error")) { +- QDECREF(qdict); ++ qobject_unref(qdict); + qdict = qdict_new(); + g_warning("unrecognized payload format"); + error_setg(&err, QERR_UNSUPPORTED); +@@ -639,7 +639,7 @@ static void process_event(JSONMessageParser *parser, GQueue *tokens) + } + } + +- QDECREF(qdict); ++ qobject_unref(qdict); + } + + /* false return signals GAChannel to close the current client connection */ +diff --git a/qmp.c b/qmp.c +index f722616..9e95b88 100644 +--- a/qmp.c ++++ b/qmp.c +@@ -710,7 +710,7 @@ void qmp_object_add(const char *type, const char *id, + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "props", "dict"); + return; + } +- QINCREF(pdict); ++ qobject_ref(pdict); + } else { + pdict = qdict_new(); + } +@@ -721,7 +721,7 @@ void qmp_object_add(const char *type, const char *id, + if (obj) { + object_unref(obj); + } +- QDECREF(pdict); ++ qobject_unref(pdict); + } + + void qmp_object_del(const char *id, Error **errp) +diff --git a/qobject/json-parser.c b/qobject/json-parser.c +index 769b960..a5aa790 100644 +--- a/qobject/json-parser.c ++++ b/qobject/json-parser.c +@@ -222,7 +222,7 @@ static QString *qstring_from_escaped_str(JSONParserContext *ctxt, + return str; + + out: +- QDECREF(str); ++ qobject_unref(str); + return NULL; + } + +@@ -311,12 +311,12 @@ static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap) + + qdict_put_obj(dict, qstring_get_str(key), value); + +- QDECREF(key); ++ qobject_unref(key); + + return 0; + + out: +- QDECREF(key); ++ qobject_unref(key); + + return -1; + } +@@ -371,7 +371,7 @@ static QObject *parse_object(JSONParserContext *ctxt, va_list *ap) + return QOBJECT(dict); + + out: +- QDECREF(dict); ++ qobject_unref(dict); + return NULL; + } + +@@ -435,7 +435,7 @@ static QObject *parse_array(JSONParserContext *ctxt, va_list *ap) + return QOBJECT(list); + + out: +- QDECREF(list); ++ qobject_unref(list); + return NULL; + } + +diff --git a/qobject/qdict.c b/qobject/qdict.c +index d1997a0..2e9bd53 100644 +--- a/qobject/qdict.c ++++ b/qobject/qdict.c +@@ -123,7 +123,7 @@ void qdict_put_obj(QDict *qdict, const char *key, QObject *value) + entry = qdict_find(qdict, key, bucket); + if (entry) { + /* replace key's value */ +- qobject_decref(entry->value); ++ qobject_unref(entry->value); + entry->value = value; + } else { + /* allocate a new entry */ +@@ -373,7 +373,7 @@ QDict *qdict_clone_shallow(const QDict *src) + + for (i = 0; i < QDICT_BUCKET_MAX; i++) { + QLIST_FOREACH(entry, &src->table[i], next) { +- qobject_incref(entry->value); ++ qobject_ref(entry->value); + qdict_put_obj(dest, entry->key, entry->value); + } + } +@@ -390,7 +390,7 @@ static void qentry_destroy(QDictEntry *e) + assert(e->key != NULL); + assert(e->value != NULL); + +- qobject_decref(e->value); ++ qobject_unref(e->value); + g_free(e->key); + g_free(e); + } +@@ -480,7 +480,7 @@ void qdict_copy_default(QDict *dst, QDict *src, const char *key) + + val = qdict_get(src, key); + if (val) { +- qobject_incref(val); ++ qobject_ref(val); + qdict_put_obj(dst, key, val); + } + } +@@ -526,7 +526,7 @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) + qdict_flatten_qlist(qobject_to(QList, value), target, new_key); + } else { + /* All other types are moved to the target unchanged. */ +- qobject_incref(value); ++ qobject_ref(value); + qdict_put_obj(target, new_key, value); + } + +@@ -566,7 +566,7 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) + delete = true; + } else if (prefix) { + /* All other objects are moved to the target unchanged. */ +- qobject_incref(value); ++ qobject_ref(value); + qdict_put_obj(target, new_key, value); + delete = true; + } +@@ -610,7 +610,7 @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) + while (entry != NULL) { + next = qdict_next(src, entry); + if (strstart(entry->key, start, &p)) { +- qobject_incref(entry->value); ++ qobject_ref(entry->value); + qdict_put_obj(*dst, p, entry->value); + qdict_del(src, entry->key); + } +@@ -684,7 +684,7 @@ void qdict_array_split(QDict *src, QList **dst) + qdict_extract_subqdict(src, &subqdict, prefix); + assert(qdict_size(subqdict) > 0); + } else { +- qobject_incref(subqobj); ++ qobject_ref(subqobj); + qdict_del(src, indexstr); + } + +@@ -894,7 +894,7 @@ QObject *qdict_crumple(const QDict *src, Error **errp) + qdict_put_obj(two_level, prefix, QOBJECT(child_dict)); + } + +- qobject_incref(ent->value); ++ qobject_ref(ent->value); + qdict_put_obj(child_dict, suffix, ent->value); + } else { + if (child) { +@@ -902,7 +902,7 @@ QObject *qdict_crumple(const QDict *src, Error **errp) + prefix); + goto error; + } +- qobject_incref(ent->value); ++ qobject_ref(ent->value); + qdict_put_obj(two_level, prefix, ent->value); + } + +@@ -924,11 +924,11 @@ QObject *qdict_crumple(const QDict *src, Error **errp) + + qdict_put_obj(multi_level, ent->key, child); + } else { +- qobject_incref(ent->value); ++ qobject_ref(ent->value); + qdict_put_obj(multi_level, ent->key, ent->value); + } + } +- QDECREF(two_level); ++ qobject_unref(two_level); + two_level = NULL; + + /* Step 3: detect if we need to turn our dict into list */ +@@ -951,10 +951,10 @@ QObject *qdict_crumple(const QDict *src, Error **errp) + goto error; + } + +- qobject_incref(child); ++ qobject_ref(child); + qlist_append_obj(qobject_to(QList, dst), child); + } +- QDECREF(multi_level); ++ qobject_unref(multi_level); + multi_level = NULL; + } else { + dst = QOBJECT(multi_level); +@@ -964,9 +964,9 @@ QObject *qdict_crumple(const QDict *src, Error **errp) + + error: + g_free(prefix); +- QDECREF(multi_level); +- QDECREF(two_level); +- qobject_decref(dst); ++ qobject_unref(multi_level); ++ qobject_unref(two_level); ++ qobject_unref(dst); + return NULL; + } + +@@ -1055,7 +1055,7 @@ void qdict_join(QDict *dest, QDict *src, bool overwrite) + next = qdict_next(src, entry); + + if (overwrite || !qdict_haskey(dest, entry->key)) { +- qobject_incref(entry->value); ++ qobject_ref(entry->value); + qdict_put_obj(dest, entry->key, entry->value); + qdict_del(src, entry->key); + } +@@ -1088,7 +1088,7 @@ bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp) + } + + qobj = qdict_get(qdict, renames->from); +- qobject_incref(qobj); ++ qobject_ref(qobj); + qdict_put_obj(qdict, renames->to, qobj); + qdict_del(qdict, renames->from); + } +diff --git a/qobject/qjson.c b/qobject/qjson.c +index 655d38a..9816a65 100644 +--- a/qobject/qjson.c ++++ b/qobject/qjson.c +@@ -104,7 +104,7 @@ static void to_json_dict_iter(const char *key, QObject *obj, void *opaque) + + qkey = qstring_from_str(key); + to_json(QOBJECT(qkey), s->str, s->pretty, s->indent); +- QDECREF(qkey); ++ qobject_unref(qkey); + + qstring_append(s->str, ": "); + to_json(obj, s->str, s->pretty, s->indent); +diff --git a/qobject/qlist.c b/qobject/qlist.c +index 954fe98..37c1c16 100644 +--- a/qobject/qlist.c ++++ b/qobject/qlist.c +@@ -39,7 +39,7 @@ static void qlist_copy_elem(QObject *obj, void *opaque) + { + QList *dst = opaque; + +- qobject_incref(obj); ++ qobject_ref(obj); + qlist_append_obj(dst, obj); + } + +@@ -196,7 +196,7 @@ void qlist_destroy_obj(QObject *obj) + + QTAILQ_FOREACH_SAFE(entry, &qlist->head, next, next_entry) { + QTAILQ_REMOVE(&qlist->head, entry, next); +- qobject_decref(entry->value); ++ qobject_unref(entry->value); + g_free(entry); + } + +diff --git a/qom/object.c b/qom/object.c +index 4677951..76a89af 100644 +--- a/qom/object.c ++++ b/qom/object.c +@@ -1129,7 +1129,7 @@ void object_property_set_str(Object *obj, const char *value, + QString *qstr = qstring_from_str(value); + object_property_set_qobject(obj, QOBJECT(qstr), name, errp); + +- QDECREF(qstr); ++ qobject_unref(qstr); + } + + char *object_property_get_str(Object *obj, const char *name, +@@ -1147,7 +1147,7 @@ char *object_property_get_str(Object *obj, const char *name, + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "string"); + } + +- qobject_decref(ret); ++ qobject_unref(ret); + return retval; + } + +@@ -1187,7 +1187,7 @@ void object_property_set_bool(Object *obj, bool value, + QBool *qbool = qbool_from_bool(value); + object_property_set_qobject(obj, QOBJECT(qbool), name, errp); + +- QDECREF(qbool); ++ qobject_unref(qbool); + } + + bool object_property_get_bool(Object *obj, const char *name, +@@ -1208,7 +1208,7 @@ bool object_property_get_bool(Object *obj, const char *name, + retval = qbool_get_bool(qbool); + } + +- qobject_decref(ret); ++ qobject_unref(ret); + return retval; + } + +@@ -1218,7 +1218,7 @@ void object_property_set_int(Object *obj, int64_t value, + QNum *qnum = qnum_from_int(value); + object_property_set_qobject(obj, QOBJECT(qnum), name, errp); + +- QDECREF(qnum); ++ qobject_unref(qnum); + } + + int64_t object_property_get_int(Object *obj, const char *name, +@@ -1238,7 +1238,7 @@ int64_t object_property_get_int(Object *obj, const char *name, + retval = -1; + } + +- qobject_decref(ret); ++ qobject_unref(ret); + return retval; + } + +@@ -1248,7 +1248,7 @@ void object_property_set_uint(Object *obj, uint64_t value, + QNum *qnum = qnum_from_uint(value); + + object_property_set_qobject(obj, QOBJECT(qnum), name, errp); +- QDECREF(qnum); ++ qobject_unref(qnum); + } + + uint64_t object_property_get_uint(Object *obj, const char *name, +@@ -1267,7 +1267,7 @@ uint64_t object_property_get_uint(Object *obj, const char *name, + retval = 0; + } + +- qobject_decref(ret); ++ qobject_unref(ret); + return retval; + } + +diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c +index 2f76e1f..980ffc2 100644 +--- a/qom/object_interfaces.c ++++ b/qom/object_interfaces.c +@@ -140,7 +140,7 @@ Object *user_creatable_add_opts(QemuOpts *opts, Error **errp) + qemu_opts_set_id(opts, (char *) id); + qemu_opt_set(opts, "qom-type", type, &error_abort); + g_free(type); +- QDECREF(pdict); ++ qobject_unref(pdict); + return obj; + } + +diff --git a/scripts/coccinelle/qobject.cocci b/scripts/coccinelle/qobject.cocci +index 47bcafe..9fee9c0 100644 +--- a/scripts/coccinelle/qobject.cocci ++++ b/scripts/coccinelle/qobject.cocci +@@ -3,11 +3,11 @@ + expression Obj, Key, E; + @@ + ( +-- qobject_incref(QOBJECT(E)); +-+ QINCREF(E); ++- qobject_ref(QOBJECT(E)); +++ qobject_ref(E); + | +-- qobject_decref(QOBJECT(E)); +-+ QDECREF(E); ++- qobject_unref(QOBJECT(E)); +++ qobject_unref(E); + | + - qdict_put_obj(Obj, Key, QOBJECT(E)); + + qdict_put(Obj, Key, E); +diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py +index 3dc523c..4426861 100644 +--- a/scripts/qapi/events.py ++++ b/scripts/qapi/events.py +@@ -142,7 +142,7 @@ out: + ''') + ret += mcgen(''' + error_propagate(errp, err); +- QDECREF(qmp); ++ qobject_unref(qmp); + } + ''') + return ret +diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c +index 391b94b..17b06c7 100644 +--- a/target/ppc/translate_init.c ++++ b/target/ppc/translate_init.c +@@ -8351,7 +8351,7 @@ static void getset_compat_deprecated(Object *obj, Visitor *v, const char *name, + "use max-cpu-compat machine property instead"); + } + visit_type_null(v, name, &null, NULL); +- QDECREF(null); ++ qobject_unref(null); + } + + static const PropertyInfo ppc_compat_deprecated_propinfo = { +diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c +index c4016e0..70b23bf 100644 +--- a/target/s390x/cpu_models.c ++++ b/target/s390x/cpu_models.c +@@ -554,7 +554,7 @@ static void cpu_info_from_model(CpuModelInfo *info, const S390CPUModel *model, + } + + if (!qdict_size(qdict)) { +- QDECREF(qdict); ++ qobject_unref(qdict); + } else { + info->props = QOBJECT(qdict); + info->has_props = true; +diff --git a/tests/ahci-test.c b/tests/ahci-test.c +index fb3cd84..1a7b761 100644 +--- a/tests/ahci-test.c ++++ b/tests/ahci-test.c +@@ -1566,7 +1566,7 @@ static void atapi_wait_tray(bool open) + } else { + g_assert(!qdict_get_bool(data, "tray-open")); + } +- QDECREF(rsp); ++ qobject_unref(rsp); + } + + static void test_atapi_tray(void) +@@ -1596,7 +1596,7 @@ static void test_atapi_tray(void) + "'arguments': {'id': 'cd0'}}"); + atapi_wait_tray(true); + rsp = qmp_receive(); +- QDECREF(rsp); ++ qobject_unref(rsp); + + qmp_discard_response("{'execute': 'blockdev-remove-medium', " + "'arguments': {'id': 'cd0'}}"); +@@ -1623,7 +1623,7 @@ static void test_atapi_tray(void) + "'arguments': {'id': 'cd0'}}"); + atapi_wait_tray(false); + rsp = qmp_receive(); +- QDECREF(rsp); ++ qobject_unref(rsp); + + /* Now, to convince ATAPI we understand the media has changed... */ + ahci_atapi_test_ready(ahci, port, false, SENSE_NOT_READY); +diff --git a/tests/check-qdict.c b/tests/check-qdict.c +index 07bb8f4..eba5d35 100644 +--- a/tests/check-qdict.c ++++ b/tests/check-qdict.c +@@ -34,7 +34,7 @@ static void qdict_new_test(void) + g_assert(qdict->base.refcnt == 1); + g_assert(qobject_type(QOBJECT(qdict)) == QTYPE_QDICT); + +- QDECREF(qdict); ++ qobject_unref(qdict); + } + + static void qdict_put_obj_test(void) +@@ -54,7 +54,7 @@ static void qdict_put_obj_test(void) + qn = qobject_to(QNum, ent->value); + g_assert_cmpint(qnum_get_int(qn), ==, num); + +- QDECREF(qdict); ++ qobject_unref(qdict); + } + + static void qdict_destroy_simple_test(void) +@@ -65,7 +65,7 @@ static void qdict_destroy_simple_test(void) + qdict_put_int(qdict, "num", 0); + qdict_put_str(qdict, "str", "foo"); + +- QDECREF(qdict); ++ qobject_unref(qdict); + } + + static void qdict_get_test(void) +@@ -84,7 +84,7 @@ static void qdict_get_test(void) + qn = qobject_to(QNum, obj); + g_assert_cmpint(qnum_get_int(qn), ==, value); + +- QDECREF(tests_dict); ++ qobject_unref(tests_dict); + } + + static void qdict_get_int_test(void) +@@ -99,7 +99,7 @@ static void qdict_get_int_test(void) + ret = qdict_get_int(tests_dict, key); + g_assert(ret == value); + +- QDECREF(tests_dict); ++ qobject_unref(tests_dict); + } + + static void qdict_get_try_int_test(void) +@@ -121,7 +121,7 @@ static void qdict_get_try_int_test(void) + ret = qdict_get_try_int(tests_dict, "string", -42); + g_assert_cmpuint(ret, ==, -42); + +- QDECREF(tests_dict); ++ qobject_unref(tests_dict); + } + + static void qdict_get_str_test(void) +@@ -137,7 +137,7 @@ static void qdict_get_str_test(void) + g_assert(p != NULL); + g_assert(strcmp(p, str) == 0); + +- QDECREF(tests_dict); ++ qobject_unref(tests_dict); + } + + static void qdict_get_try_str_test(void) +@@ -153,7 +153,7 @@ static void qdict_get_try_str_test(void) + g_assert(p != NULL); + g_assert(strcmp(p, str) == 0); + +- QDECREF(tests_dict); ++ qobject_unref(tests_dict); + } + + static void qdict_defaults_test(void) +@@ -174,8 +174,8 @@ static void qdict_defaults_test(void) + qdict_copy_default(copy, dict, "bar"); + g_assert_cmpstr(qdict_get_str(copy, "bar"), ==, "xyz"); + +- QDECREF(copy); +- QDECREF(dict); ++ qobject_unref(copy); ++ qobject_unref(dict); + } + + static void qdict_haskey_not_test(void) +@@ -183,7 +183,7 @@ static void qdict_haskey_not_test(void) + QDict *tests_dict = qdict_new(); + g_assert(qdict_haskey(tests_dict, "test") == 0); + +- QDECREF(tests_dict); ++ qobject_unref(tests_dict); + } + + static void qdict_haskey_test(void) +@@ -194,7 +194,7 @@ static void qdict_haskey_test(void) + qdict_put_int(tests_dict, key, 0); + g_assert(qdict_haskey(tests_dict, key) == 1); + +- QDECREF(tests_dict); ++ qobject_unref(tests_dict); + } + + static void qdict_del_test(void) +@@ -210,7 +210,7 @@ static void qdict_del_test(void) + g_assert(qdict_size(tests_dict) == 0); + g_assert(qdict_haskey(tests_dict, key) == 0); + +- QDECREF(tests_dict); ++ qobject_unref(tests_dict); + } + + static void qobject_to_qdict_test(void) +@@ -218,7 +218,7 @@ static void qobject_to_qdict_test(void) + QDict *tests_dict = qdict_new(); + g_assert(qobject_to(QDict, QOBJECT(tests_dict)) == tests_dict); + +- QDECREF(tests_dict); ++ qobject_unref(tests_dict); + } + + static void qdict_iterapi_test(void) +@@ -250,7 +250,7 @@ static void qdict_iterapi_test(void) + + g_assert(count == qdict_size(tests_dict)); + +- QDECREF(tests_dict); ++ qobject_unref(tests_dict); + } + + static void qdict_flatten_test(void) +@@ -325,7 +325,7 @@ static void qdict_flatten_test(void) + + g_assert(qdict_size(dict3) == 8); + +- QDECREF(dict3); ++ qobject_unref(dict3); + } + + static void qdict_array_split_test(void) +@@ -390,31 +390,31 @@ static void qdict_array_split_test(void) + g_assert(int1); + g_assert(qlist_empty(test_list)); + +- QDECREF(test_list); ++ qobject_unref(test_list); + + g_assert(qdict_get_int(dict1, "a") == 42); + g_assert(qdict_get_int(dict1, "b") == 23); + + g_assert(qdict_size(dict1) == 2); + +- QDECREF(dict1); ++ qobject_unref(dict1); + + g_assert(qdict_get_int(dict2, "x") == 0); + + g_assert(qdict_size(dict2) == 1); + +- QDECREF(dict2); ++ qobject_unref(dict2); + + g_assert_cmpint(qnum_get_int(int1), ==, 66); + +- QDECREF(int1); ++ qobject_unref(int1); + + g_assert(qdict_get_int(test_dict, "4.y") == 1); + g_assert(qdict_get_int(test_dict, "o.o") == 7); + + g_assert(qdict_size(test_dict) == 2); + +- QDECREF(test_dict); ++ qobject_unref(test_dict); + + /* + * Test the split of +@@ -455,18 +455,18 @@ static void qdict_array_split_test(void) + g_assert(int1); + g_assert(qlist_empty(test_list)); + +- QDECREF(test_list); ++ qobject_unref(test_list); + + g_assert_cmpint(qnum_get_int(int1), ==, 42); + +- QDECREF(int1); ++ qobject_unref(int1); + + g_assert(qdict_get_int(test_dict, "1") == 23); + g_assert(qdict_get_int(test_dict, "1.x") == 84); + + g_assert(qdict_size(test_dict) == 2); + +- QDECREF(test_dict); ++ qobject_unref(test_dict); + } + + static void qdict_array_entries_test(void) +@@ -493,7 +493,7 @@ static void qdict_array_entries_test(void) + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 3); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); + +- QDECREF(dict); ++ qobject_unref(dict); + + dict = qdict_new(); + qdict_put_int(dict, "1", 0); +@@ -509,7 +509,7 @@ static void qdict_array_entries_test(void) + qdict_put_int(dict, "2.c", 0); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, 3); + +- QDECREF(dict); ++ qobject_unref(dict); + } + + static void qdict_join_test(void) +@@ -587,8 +587,8 @@ static void qdict_join_test(void) + } + while (overwrite ^= true); + +- QDECREF(dict1); +- QDECREF(dict2); ++ qobject_unref(dict1); ++ qobject_unref(dict2); + } + + static void qdict_crumple_test_recursive(void) +@@ -634,21 +634,21 @@ static void qdict_crumple_test_recursive(void) + g_assert_cmpint(qdict_size(rule), ==, 2); + g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match")); + g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy")); +- QDECREF(rule); ++ qobject_unref(rule); + + rule = qobject_to(QDict, qlist_pop(rules)); + g_assert(rule); + g_assert_cmpint(qdict_size(rule), ==, 2); + g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match")); + g_assert_cmpstr("deny", ==, qdict_get_str(rule, "policy")); +- QDECREF(rule); ++ qobject_unref(rule); + + /* With recursive crumpling, we should see all names unescaped */ + g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name")); + g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name")); + +- QDECREF(src); +- QDECREF(dst); ++ qobject_unref(src); ++ qobject_unref(dst); + } + + static void qdict_crumple_test_empty(void) +@@ -661,8 +661,8 @@ static void qdict_crumple_test_empty(void) + + g_assert_cmpint(qdict_size(dst), ==, 0); + +- QDECREF(src); +- QDECREF(dst); ++ qobject_unref(src); ++ qobject_unref(dst); + } + + static int qdict_count_entries(QDict *dict) +@@ -704,7 +704,7 @@ static void qdict_rename_keys_test(void) + g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + +- QDECREF(copy); ++ qobject_unref(copy); + + /* Simple rename of all entries */ + renames = (QDictRenames[]) { +@@ -731,7 +731,7 @@ static void qdict_rename_keys_test(void) + g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + +- QDECREF(copy); ++ qobject_unref(copy); + + /* Renames are processed top to bottom */ + renames = (QDictRenames[]) { +@@ -754,7 +754,7 @@ static void qdict_rename_keys_test(void) + g_assert(!qdict_haskey(copy, "tmp")); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + +- QDECREF(copy); ++ qobject_unref(copy); + + /* Conflicting rename */ + renames = (QDictRenames[]) { +@@ -775,7 +775,7 @@ static void qdict_rename_keys_test(void) + g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + +- QDECREF(copy); ++ qobject_unref(copy); + + /* Renames in an empty dict */ + renames = (QDictRenames[]) { +@@ -783,13 +783,13 @@ static void qdict_rename_keys_test(void) + { NULL , NULL } + }; + +- QDECREF(dict); ++ qobject_unref(dict); + dict = qdict_new(); + + qdict_rename_keys(dict, renames, &error_abort); + g_assert(qdict_first(dict) == NULL); + +- QDECREF(dict); ++ qobject_unref(dict); + } + + static void qdict_crumple_test_bad_inputs(void) +@@ -806,7 +806,7 @@ static void qdict_crumple_test_bad_inputs(void) + g_assert(error != NULL); + error_free(error); + error = NULL; +- QDECREF(src); ++ qobject_unref(src); + + src = qdict_new(); + /* rule can't be both a list and a dict */ +@@ -817,7 +817,7 @@ static void qdict_crumple_test_bad_inputs(void) + g_assert(error != NULL); + error_free(error); + error = NULL; +- QDECREF(src); ++ qobject_unref(src); + + src = qdict_new(); + /* The input should be flat, ie no dicts or lists */ +@@ -828,7 +828,7 @@ static void qdict_crumple_test_bad_inputs(void) + g_assert(error != NULL); + error_free(error); + error = NULL; +- QDECREF(src); ++ qobject_unref(src); + + src = qdict_new(); + /* List indexes must not have gaps */ +@@ -839,7 +839,7 @@ static void qdict_crumple_test_bad_inputs(void) + g_assert(error != NULL); + error_free(error); + error = NULL; +- QDECREF(src); ++ qobject_unref(src); + + src = qdict_new(); + /* List indexes must be in %zu format */ +@@ -850,7 +850,7 @@ static void qdict_crumple_test_bad_inputs(void) + g_assert(error != NULL); + error_free(error); + error = NULL; +- QDECREF(src); ++ qobject_unref(src); + } + + /* +@@ -871,7 +871,7 @@ static void qdict_put_exists_test(void) + + g_assert(qdict_size(tests_dict) == 1); + +- QDECREF(tests_dict); ++ qobject_unref(tests_dict); + } + + static void qdict_get_not_exists_test(void) +@@ -879,7 +879,7 @@ static void qdict_get_not_exists_test(void) + QDict *tests_dict = qdict_new(); + g_assert(qdict_get(tests_dict, "foo") == NULL); + +- QDECREF(tests_dict); ++ qobject_unref(tests_dict); + } + + /* +@@ -951,7 +951,7 @@ static void qdict_stress_test(void) + + g_assert(strcmp(str1, str2) == 0); + +- QDECREF(value); ++ qobject_unref(value); + } + + // Delete everything +@@ -962,14 +962,14 @@ static void qdict_stress_test(void) + break; + + qdict_del(qdict, key); +- QDECREF(value); ++ qobject_unref(value); + + g_assert(qdict_haskey(qdict, key) == 0); + } + fclose(test_file); + + g_assert(qdict_size(qdict) == 0); +- QDECREF(qdict); ++ qobject_unref(qdict); + } + + int main(int argc, char **argv) +diff --git a/tests/check-qjson.c b/tests/check-qjson.c +index 997f4d3..da582df 100644 +--- a/tests/check-qjson.c ++++ b/tests/check-qjson.c +@@ -67,10 +67,10 @@ static void escaped_string(void) + if (test_cases[i].skip == 0) { + str = qobject_to_json(obj); + g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].encoded); +- qobject_decref(obj); ++ qobject_unref(obj); + } + +- QDECREF(str); ++ qobject_unref(str); + } + } + +@@ -99,9 +99,9 @@ static void simple_string(void) + str = qobject_to_json(obj); + g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0); + +- qobject_decref(obj); ++ qobject_unref(obj); + +- QDECREF(str); ++ qobject_unref(str); + } + } + +@@ -127,7 +127,7 @@ static void single_quote_string(void) + g_assert(str); + g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); + +- QDECREF(str); ++ qobject_unref(str); + } + } + +@@ -823,7 +823,7 @@ static void utf8_string(void) + } else { + g_assert(!obj); + } +- qobject_decref(obj); ++ qobject_unref(obj); + + obj = QOBJECT(qstring_from_str(utf8_in)); + str = qobject_to_json(obj); +@@ -833,8 +833,8 @@ static void utf8_string(void) + } else { + g_assert(!str); + } +- QDECREF(str); +- qobject_decref(obj); ++ qobject_unref(str); ++ qobject_unref(obj); + + /* + * Disabled, because qobject_from_json() is buggy, and I can't +@@ -869,7 +869,7 @@ static void vararg_string(void) + g_assert(str); + g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); + +- QDECREF(str); ++ qobject_unref(str); + } + } + +@@ -904,10 +904,10 @@ static void simple_number(void) + + str = qobject_to_json(QOBJECT(qnum)); + g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0); +- QDECREF(str); ++ qobject_unref(str); + } + +- QDECREF(qnum); ++ qobject_unref(qnum); + } + } + +@@ -928,8 +928,8 @@ static void large_number(void) + + str = qobject_to_json(QOBJECT(qnum)); + g_assert_cmpstr(qstring_get_str(str), ==, maxu64); +- QDECREF(str); +- QDECREF(qnum); ++ qobject_unref(str); ++ qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_json(gtu64, &error_abort)); + g_assert(qnum); +@@ -939,8 +939,8 @@ static void large_number(void) + + str = qobject_to_json(QOBJECT(qnum)); + g_assert_cmpstr(qstring_get_str(str), ==, gtu64); +- QDECREF(str); +- QDECREF(qnum); ++ qobject_unref(str); ++ qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_json(lti64, &error_abort)); + g_assert(qnum); +@@ -950,8 +950,8 @@ static void large_number(void) + + str = qobject_to_json(QOBJECT(qnum)); + g_assert_cmpstr(qstring_get_str(str), ==, "-9223372036854775808"); +- QDECREF(str); +- QDECREF(qnum); ++ qobject_unref(str); ++ qobject_unref(qnum); + } + + static void float_number(void) +@@ -983,10 +983,10 @@ static void float_number(void) + + str = qobject_to_json(obj); + g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0); +- QDECREF(str); ++ qobject_unref(str); + } + +- QDECREF(qnum); ++ qobject_unref(qnum); + } + } + +@@ -1001,16 +1001,16 @@ static void vararg_number(void) + qnum = qobject_to(QNum, qobject_from_jsonf("%d", value)); + g_assert(qnum_get_try_int(qnum, &val)); + g_assert_cmpint(val, ==, value); +- QDECREF(qnum); ++ qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf("%lld", value_ll)); + g_assert(qnum_get_try_int(qnum, &val)); + g_assert_cmpint(val, ==, value_ll); +- QDECREF(qnum); ++ qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf("%f", valuef)); + g_assert(qnum_get_double(qnum) == valuef); +- QDECREF(qnum); ++ qobject_unref(qnum); + } + + static void keyword_literal(void) +@@ -1027,9 +1027,9 @@ static void keyword_literal(void) + + str = qobject_to_json(obj); + g_assert(strcmp(qstring_get_str(str), "true") == 0); +- QDECREF(str); ++ qobject_unref(str); + +- QDECREF(qbool); ++ qobject_unref(qbool); + + obj = qobject_from_json("false", &error_abort); + qbool = qobject_to(QBool, obj); +@@ -1038,20 +1038,20 @@ static void keyword_literal(void) + + str = qobject_to_json(obj); + g_assert(strcmp(qstring_get_str(str), "false") == 0); +- QDECREF(str); ++ qobject_unref(str); + +- QDECREF(qbool); ++ qobject_unref(qbool); + + qbool = qobject_to(QBool, qobject_from_jsonf("%i", false)); + g_assert(qbool); + g_assert(qbool_get_bool(qbool) == false); +- QDECREF(qbool); ++ qobject_unref(qbool); + + /* Test that non-zero values other than 1 get collapsed to true */ + qbool = qobject_to(QBool, qobject_from_jsonf("%i", 2)); + g_assert(qbool); + g_assert(qbool_get_bool(qbool) == true); +- QDECREF(qbool); ++ qobject_unref(qbool); + + obj = qobject_from_json("null", &error_abort); + g_assert(obj != NULL); +@@ -1060,8 +1060,8 @@ static void keyword_literal(void) + null = qnull(); + g_assert(QOBJECT(null) == obj); + +- qobject_decref(obj); +- QDECREF(null); ++ qobject_unref(obj); ++ qobject_unref(null); + } + + static void simple_dict(void) +@@ -1101,12 +1101,12 @@ static void simple_dict(void) + g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); + + str = qobject_to_json(obj); +- qobject_decref(obj); ++ qobject_unref(obj); + + obj = qobject_from_json(qstring_get_str(str), &error_abort); + g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); +- qobject_decref(obj); +- QDECREF(str); ++ qobject_unref(obj); ++ qobject_unref(str); + } + } + +@@ -1158,7 +1158,7 @@ static void large_dict(void) + obj = qobject_from_json(gstr->str, &error_abort); + g_assert(obj != NULL); + +- qobject_decref(obj); ++ qobject_unref(obj); + g_string_free(gstr, true); + } + +@@ -1210,12 +1210,12 @@ static void simple_list(void) + g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); + + str = qobject_to_json(obj); +- qobject_decref(obj); ++ qobject_unref(obj); + + obj = qobject_from_json(qstring_get_str(str), &error_abort); + g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); +- qobject_decref(obj); +- QDECREF(str); ++ qobject_unref(obj); ++ qobject_unref(str); + } + } + +@@ -1272,13 +1272,13 @@ static void simple_whitespace(void) + g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); + + str = qobject_to_json(obj); +- qobject_decref(obj); ++ qobject_unref(obj); + + obj = qobject_from_json(qstring_get_str(str), &error_abort); + g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); + +- qobject_decref(obj); +- QDECREF(str); ++ qobject_unref(obj); ++ qobject_unref(str); + } + } + +@@ -1301,7 +1301,7 @@ static void simple_varargs(void) + obj = qobject_from_jsonf("[%d, 2, %p]", 1, embedded_obj); + g_assert(qlit_equal_qobject(&decoded, obj)); + +- qobject_decref(obj); ++ qobject_unref(obj); + } + + static void empty_input(void) +@@ -1410,7 +1410,7 @@ static void limits_nesting(void) + + obj = qobject_from_json(make_nest(buf, max_nesting), &error_abort); + g_assert(obj != NULL); +- qobject_decref(obj); ++ qobject_unref(obj); + + obj = qobject_from_json(make_nest(buf, max_nesting + 1), &err); + error_free_or_abort(&err); +diff --git a/tests/check-qlist.c b/tests/check-qlist.c +index a1c69ed..ece83e2 100644 +--- a/tests/check-qlist.c ++++ b/tests/check-qlist.c +@@ -29,7 +29,7 @@ static void qlist_new_test(void) + g_assert(qlist->base.refcnt == 1); + g_assert(qobject_type(QOBJECT(qlist)) == QTYPE_QLIST); + +- QDECREF(qlist); ++ qobject_unref(qlist); + } + + static void qlist_append_test(void) +@@ -47,7 +47,7 @@ static void qlist_append_test(void) + g_assert(entry != NULL); + g_assert(entry->value == QOBJECT(qi)); + +- QDECREF(qlist); ++ qobject_unref(qlist); + } + + static void qobject_to_qlist_test(void) +@@ -58,7 +58,7 @@ static void qobject_to_qlist_test(void) + + g_assert(qobject_to(QList, QOBJECT(qlist)) == qlist); + +- QDECREF(qlist); ++ qobject_unref(qlist); + } + + static int iter_called; +@@ -96,7 +96,7 @@ static void qlist_iter_test(void) + + g_assert(iter_called == iter_max); + +- QDECREF(qlist); ++ qobject_unref(qlist); + } + + int main(int argc, char **argv) +diff --git a/tests/check-qlit.c b/tests/check-qlit.c +index 96bbb06..bd6798d 100644 +--- a/tests/check-qlit.c ++++ b/tests/check-qlit.c +@@ -62,7 +62,7 @@ static void qlit_equal_qobject_test(void) + qdict_put(qobject_to(QDict, qobj), "bee", qlist_new()); + g_assert(!qlit_equal_qobject(&qlit, qobj)); + +- qobject_decref(qobj); ++ qobject_unref(qobj); + } + + static void qobject_from_qlit_test(void) +@@ -79,15 +79,15 @@ static void qobject_from_qlit_test(void) + bee = qdict_get_qlist(qdict, "bee"); + obj = qlist_pop(bee); + g_assert_cmpint(qnum_get_int(qobject_to(QNum, obj)), ==, 43); +- qobject_decref(obj); ++ qobject_unref(obj); + obj = qlist_pop(bee); + g_assert_cmpint(qnum_get_int(qobject_to(QNum, obj)), ==, 44); +- qobject_decref(obj); ++ qobject_unref(obj); + obj = qlist_pop(bee); + g_assert(qbool_get_bool(qobject_to(QBool, obj))); +- qobject_decref(obj); ++ qobject_unref(obj); + +- qobject_decref(qobj); ++ qobject_unref(qobj); + } + + int main(int argc, char **argv) +diff --git a/tests/check-qnull.c b/tests/check-qnull.c +index afa4400..ebf21db 100644 +--- a/tests/check-qnull.c ++++ b/tests/check-qnull.c +@@ -30,7 +30,7 @@ static void qnull_ref_test(void) + g_assert(obj == QOBJECT(&qnull_)); + g_assert(qnull_.base.refcnt == 2); + g_assert(qobject_type(obj) == QTYPE_QNULL); +- qobject_decref(obj); ++ qobject_unref(obj); + g_assert(qnull_.base.refcnt == 1); + } + +@@ -49,10 +49,10 @@ static void qnull_visit_test(void) + g_assert(qnull_.base.refcnt == 1); + obj = QOBJECT(qnull()); + v = qobject_input_visitor_new(obj); +- qobject_decref(obj); ++ qobject_unref(obj); + visit_type_null(v, NULL, &null, &error_abort); + g_assert(obj == QOBJECT(&qnull_)); +- QDECREF(null); ++ qobject_unref(null); + visit_free(v); + + null = NULL; +@@ -60,8 +60,8 @@ static void qnull_visit_test(void) + visit_type_null(v, NULL, &null, &error_abort); + visit_complete(v, &obj); + g_assert(obj == QOBJECT(&qnull_)); +- QDECREF(null); +- qobject_decref(obj); ++ qobject_unref(null); ++ qobject_unref(obj); + visit_free(v); + + g_assert(qnull_.base.refcnt == 1); +diff --git a/tests/check-qnum.c b/tests/check-qnum.c +index 9187da7..4105015 100644 +--- a/tests/check-qnum.c ++++ b/tests/check-qnum.c +@@ -35,7 +35,7 @@ static void qnum_from_int_test(void) + g_assert_cmpint(qn->base.refcnt, ==, 1); + g_assert_cmpint(qobject_type(QOBJECT(qn)), ==, QTYPE_QNUM); + +- QDECREF(qn); ++ qobject_unref(qn); + } + + static void qnum_from_uint_test(void) +@@ -50,7 +50,7 @@ static void qnum_from_uint_test(void) + g_assert(qn->base.refcnt == 1); + g_assert(qobject_type(QOBJECT(qn)) == QTYPE_QNUM); + +- QDECREF(qn); ++ qobject_unref(qn); + } + + static void qnum_from_double_test(void) +@@ -65,7 +65,7 @@ static void qnum_from_double_test(void) + g_assert_cmpint(qn->base.refcnt, ==, 1); + g_assert_cmpint(qobject_type(QOBJECT(qn)), ==, QTYPE_QNUM); + +- QDECREF(qn); ++ qobject_unref(qn); + } + + static void qnum_from_int64_test(void) +@@ -76,7 +76,7 @@ static void qnum_from_int64_test(void) + qn = qnum_from_int(value); + g_assert_cmpint((int64_t) qn->u.i64, ==, value); + +- QDECREF(qn); ++ qobject_unref(qn); + } + + static void qnum_get_int_test(void) +@@ -87,7 +87,7 @@ static void qnum_get_int_test(void) + qn = qnum_from_int(value); + g_assert_cmpint(qnum_get_int(qn), ==, value); + +- QDECREF(qn); ++ qobject_unref(qn); + } + + static void qnum_get_uint_test(void) +@@ -100,25 +100,25 @@ static void qnum_get_uint_test(void) + qn = qnum_from_uint(value); + g_assert(qnum_get_try_uint(qn, &val)); + g_assert_cmpuint(val, ==, value); +- QDECREF(qn); ++ qobject_unref(qn); + + qn = qnum_from_int(value); + g_assert(qnum_get_try_uint(qn, &val)); + g_assert_cmpuint(val, ==, value); +- QDECREF(qn); ++ qobject_unref(qn); + + /* invalid cases */ + qn = qnum_from_int(-1); + g_assert(!qnum_get_try_uint(qn, &val)); +- QDECREF(qn); ++ qobject_unref(qn); + + qn = qnum_from_uint(-1ULL); + g_assert(!qnum_get_try_int(qn, &ival)); +- QDECREF(qn); ++ qobject_unref(qn); + + qn = qnum_from_double(0.42); + g_assert(!qnum_get_try_uint(qn, &val)); +- QDECREF(qn); ++ qobject_unref(qn); + } + + static void qobject_to_qnum_test(void) +@@ -127,11 +127,11 @@ static void qobject_to_qnum_test(void) + + qn = qnum_from_int(0); + g_assert(qobject_to(QNum, QOBJECT(qn)) == qn); +- QDECREF(qn); ++ qobject_unref(qn); + + qn = qnum_from_double(0); + g_assert(qobject_to(QNum, QOBJECT(qn)) == qn); +- QDECREF(qn); ++ qobject_unref(qn); + } + + static void qnum_to_string_test(void) +@@ -143,13 +143,13 @@ static void qnum_to_string_test(void) + tmp = qnum_to_string(qn); + g_assert_cmpstr(tmp, ==, "123456"); + g_free(tmp); +- QDECREF(qn); ++ qobject_unref(qn); + + qn = qnum_from_double(0.42); + tmp = qnum_to_string(qn); + g_assert_cmpstr(tmp, ==, "0.42"); + g_free(tmp); +- QDECREF(qn); ++ qobject_unref(qn); + } + + int main(int argc, char **argv) +diff --git a/tests/check-qobject.c b/tests/check-qobject.c +index 7629b80..5cb08fc 100644 +--- a/tests/check-qobject.c ++++ b/tests/check-qobject.c +@@ -80,7 +80,7 @@ static void do_free_all(int _, ...) + + va_start(ap, _); + while ((obj = va_arg(ap, QObject *)) != NULL) { +- qobject_decref(obj); ++ qobject_unref(obj); + } + va_end(ap); + } +diff --git a/tests/check-qstring.c b/tests/check-qstring.c +index 9c4dd3f..f11a7a8 100644 +--- a/tests/check-qstring.c ++++ b/tests/check-qstring.c +@@ -31,7 +31,7 @@ static void qstring_from_str_test(void) + g_assert(strcmp(str, qstring->string) == 0); + g_assert(qobject_type(QOBJECT(qstring)) == QTYPE_QSTRING); + +- QDECREF(qstring); ++ qobject_unref(qstring); + } + + static void qstring_get_str_test(void) +@@ -44,7 +44,7 @@ static void qstring_get_str_test(void) + ret_str = qstring_get_str(qstring); + g_assert(strcmp(ret_str, str) == 0); + +- QDECREF(qstring); ++ qobject_unref(qstring); + } + + static void qstring_append_chr_test(void) +@@ -59,7 +59,7 @@ static void qstring_append_chr_test(void) + qstring_append_chr(qstring, str[i]); + + g_assert(strcmp(str, qstring_get_str(qstring)) == 0); +- QDECREF(qstring); ++ qobject_unref(qstring); + } + + static void qstring_from_substr_test(void) +@@ -70,7 +70,7 @@ static void qstring_from_substr_test(void) + g_assert(qs != NULL); + g_assert(strcmp(qstring_get_str(qs), "tualiza") == 0); + +- QDECREF(qs); ++ qobject_unref(qs); + } + + +@@ -81,7 +81,7 @@ static void qobject_to_qstring_test(void) + qstring = qstring_from_str("foo"); + g_assert(qobject_to(QString, QOBJECT(qstring)) == qstring); + +- QDECREF(qstring); ++ qobject_unref(qstring); + } + + int main(int argc, char **argv) +diff --git a/tests/cpu-plug-test.c b/tests/cpu-plug-test.c +index 112869b..48b8d09 100644 +--- a/tests/cpu-plug-test.c ++++ b/tests/cpu-plug-test.c +@@ -42,7 +42,7 @@ static void test_plug_with_cpu_add(gconstpointer data) + " 'arguments': { 'id': %d } }", i); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); +- QDECREF(response); ++ qobject_unref(response); + } + + qtest_end(); +@@ -66,7 +66,7 @@ static void test_plug_without_cpu_add(gconstpointer data) + s->sockets * s->cores * s->threads); + g_assert(response); + g_assert(qdict_haskey(response, "error")); +- QDECREF(response); ++ qobject_unref(response); + + qtest_end(); + g_free(args); +diff --git a/tests/device-introspect-test.c b/tests/device-introspect-test.c +index a01321a..0b4f221 100644 +--- a/tests/device-introspect-test.c ++++ b/tests/device-introspect-test.c +@@ -40,8 +40,8 @@ static QList *qom_list_types(const char *implements, bool abstract) + " 'arguments': %p }", args); + g_assert(qdict_haskey(resp, "return")); + ret = qdict_get_qlist(resp, "return"); +- QINCREF(ret); +- QDECREF(resp); ++ qobject_ref(ret); ++ qobject_unref(resp); + return ret; + } + +@@ -54,7 +54,7 @@ static QDict *qom_type_index(QList *types) + QLIST_FOREACH_ENTRY(types, e) { + QDict *d = qobject_to(QDict, qlist_entry_obj(e)); + const char *name = qdict_get_str(d, "name"); +- QINCREF(d); ++ qobject_ref(d); + qdict_put(index, name, d); + } + return index; +@@ -108,7 +108,7 @@ static void test_one_device(const char *type) + resp = qmp("{'execute': 'device-list-properties'," + " 'arguments': {'typename': %s}}", + type); +- QDECREF(resp); ++ qobject_unref(resp); + + help = hmp("device_add \"%s,help\"", type); + g_free(help); +@@ -129,7 +129,7 @@ static void test_device_intro_list(void) + qtest_start(common_args); + + types = device_type_list(true); +- QDECREF(types); ++ qobject_unref(types); + + help = hmp("device_add help"); + g_free(help); +@@ -157,8 +157,8 @@ static void test_qom_list_parents(const char *parent) + g_assert(qom_has_parent(index, name, parent)); + } + +- QDECREF(types); +- QDECREF(index); ++ qobject_unref(types); ++ qobject_unref(index); + } + + static void test_qom_list_fields(void) +@@ -187,8 +187,8 @@ static void test_qom_list_fields(void) + test_qom_list_parents("device"); + test_qom_list_parents("sys-bus-device"); + +- QDECREF(all_types); +- QDECREF(non_abstract); ++ qobject_unref(all_types); ++ qobject_unref(non_abstract); + qtest_end(); + } + +@@ -222,7 +222,7 @@ static void test_device_intro_concrete(void) + test_one_device(type); + } + +- QDECREF(types); ++ qobject_unref(types); + qtest_end(); + } + +@@ -255,8 +255,8 @@ static void test_abstract_interfaces(void) + g_assert(qdict_haskey(d, "abstract") && qdict_get_bool(d, "abstract")); + } + +- QDECREF(all_types); +- QDECREF(index); ++ qobject_unref(all_types); ++ qobject_unref(index); + qtest_end(); + } + +diff --git a/tests/drive_del-test.c b/tests/drive_del-test.c +index 313030a..852fefc 100644 +--- a/tests/drive_del-test.c ++++ b/tests/drive_del-test.c +@@ -41,7 +41,7 @@ static void device_del(void) + response = qmp_receive(); + g_assert(response); + g_assert(qdict_haskey(response, "return")); +- QDECREF(response); ++ qobject_unref(response); + } + + static void test_drive_without_dev(void) +@@ -78,7 +78,7 @@ static void test_after_failed_device_add(void) + g_assert(response); + error = qdict_get_qdict(response, "error"); + g_assert_cmpstr(qdict_get_try_str(error, "class"), ==, "GenericError"); +- QDECREF(response); ++ qobject_unref(response); + + /* Delete the drive */ + drive_del(); +diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c +index 5124e98..013ca68 100644 +--- a/tests/libqos/libqos.c ++++ b/tests/libqos/libqos.c +@@ -100,14 +100,14 @@ void migrate(QOSState *from, QOSState *to, const char *uri) + sub = qdict_get_qdict(rsp, "return"); + g_assert(qdict_haskey(sub, "running")); + running = qdict_get_bool(sub, "running"); +- QDECREF(rsp); ++ qobject_unref(rsp); + + /* Issue the migrate command. */ + rsp = qtest_qmp(from->qts, + "{ 'execute': 'migrate', 'arguments': { 'uri': %s }}", + uri); + g_assert(qdict_haskey(rsp, "return")); +- QDECREF(rsp); ++ qobject_unref(rsp); + + /* Wait for STOP event, but only if we were running: */ + if (running) { +@@ -132,12 +132,12 @@ void migrate(QOSState *from, QOSState *to, const char *uri) + + /* "setup", "active", "completed", "failed", "cancelled" */ + if (strcmp(st, "completed") == 0) { +- QDECREF(rsp); ++ qobject_unref(rsp); + break; + } + + if ((strcmp(st, "setup") == 0) || (strcmp(st, "active") == 0)) { +- QDECREF(rsp); ++ qobject_unref(rsp); + g_usleep(5000); + continue; + } +diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c +index a2daf61..a780330 100644 +--- a/tests/libqos/pci-pc.c ++++ b/tests/libqos/pci-pc.c +@@ -170,7 +170,7 @@ void qpci_unplug_acpi_device_test(const char *id, uint8_t slot) + g_free(cmd); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); +- QDECREF(response); ++ qobject_unref(response); + + outb(ACPI_PCIHP_ADDR + PCI_EJ_BASE, 1 << slot); + +diff --git a/tests/libqtest.c b/tests/libqtest.c +index 6f33a37..43fb97e 100644 +--- a/tests/libqtest.c ++++ b/tests/libqtest.c +@@ -517,8 +517,8 @@ void qmp_fd_sendv(int fd, const char *fmt, va_list ap) + /* Send QMP request */ + socket_send(fd, str, qstring_get_length(qstr)); + +- QDECREF(qstr); +- qobject_decref(qobj); ++ qobject_unref(qstr); ++ qobject_unref(qobj); + } + } + +@@ -585,7 +585,7 @@ void qtest_async_qmp(QTestState *s, const char *fmt, ...) + void qtest_qmpv_discard_response(QTestState *s, const char *fmt, va_list ap) + { + QDict *response = qtest_qmpv(s, fmt, ap); +- QDECREF(response); ++ qobject_unref(response); + } + + void qtest_qmp_discard_response(QTestState *s, const char *fmt, ...) +@@ -596,7 +596,7 @@ void qtest_qmp_discard_response(QTestState *s, const char *fmt, ...) + va_start(ap, fmt); + response = qtest_qmpv(s, fmt, ap); + va_end(ap); +- QDECREF(response); ++ qobject_unref(response); + } + + QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event) +@@ -609,7 +609,7 @@ QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event) + (strcmp(qdict_get_str(response, "event"), event) == 0)) { + return response; + } +- QDECREF(response); ++ qobject_unref(response); + } + } + +@@ -618,7 +618,7 @@ void qtest_qmp_eventwait(QTestState *s, const char *event) + QDict *response; + + response = qtest_qmp_eventwait_ref(s, event); +- QDECREF(response); ++ qobject_unref(response); + } + + char *qtest_hmpv(QTestState *s, const char *fmt, va_list ap) +@@ -634,12 +634,12 @@ char *qtest_hmpv(QTestState *s, const char *fmt, va_list ap) + ret = g_strdup(qdict_get_try_str(resp, "return")); + while (ret == NULL && qdict_get_try_str(resp, "event")) { + /* Ignore asynchronous QMP events */ +- QDECREF(resp); ++ qobject_unref(resp); + resp = qtest_qmp_receive(s); + ret = g_strdup(qdict_get_try_str(resp, "return")); + } + g_assert(ret); +- QDECREF(resp); ++ qobject_unref(resp); + g_free(cmd); + return ret; + } +@@ -1021,7 +1021,7 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine)) + } + + qtest_end(); +- QDECREF(response); ++ qobject_unref(response); + } + + /* +@@ -1050,7 +1050,7 @@ void qtest_qmp_device_add(const char *driver, const char *id, const char *fmt, + g_assert(response); + g_assert(!qdict_haskey(response, "event")); /* We don't expect any events */ + g_assert(!qdict_haskey(response, "error")); +- QDECREF(response); ++ qobject_unref(response); + } + + /* +@@ -1095,6 +1095,6 @@ void qtest_qmp_device_del(const char *id) + g_assert(event); + g_assert_cmpstr(qdict_get_str(event, "event"), ==, "DEVICE_DELETED"); + +- QDECREF(response1); +- QDECREF(response2); ++ qobject_unref(response1); ++ qobject_unref(response2); + } +diff --git a/tests/machine-none-test.c b/tests/machine-none-test.c +index efdd4be..f286557 100644 +--- a/tests/machine-none-test.c ++++ b/tests/machine-none-test.c +@@ -88,7 +88,7 @@ static void test_machine_cpu_cli(void) + + response = qmp("{ 'execute': 'quit' }"); + g_assert(qdict_haskey(response, "return")); +- QDECREF(response); ++ qobject_unref(response); + + qtest_quit(global_qtest); + } +diff --git a/tests/migration-test.c b/tests/migration-test.c +index 422bf1a..0acd715 100644 +--- a/tests/migration-test.c ++++ b/tests/migration-test.c +@@ -193,7 +193,7 @@ static QDict *wait_command(QTestState *who, const char *command) + if (!strcmp(event_string, "STOP")) { + got_stop = true; + } +- QDECREF(response); ++ qobject_unref(response); + response = qtest_qmp_receive(who); + } + return response; +@@ -219,7 +219,7 @@ static uint64_t get_migration_pass(QTestState *who) + rsp_ram = qdict_get_qdict(rsp_return, "ram"); + result = qdict_get_try_int(rsp_ram, "dirty-sync-count", 0); + } +- QDECREF(rsp); ++ qobject_unref(rsp); + return result; + } + +@@ -235,7 +235,7 @@ static void wait_for_migration_complete(QTestState *who) + status = qdict_get_str(rsp_return, "status"); + completed = strcmp(status, "completed") == 0; + g_assert_cmpstr(status, !=, "failed"); +- QDECREF(rsp); ++ qobject_unref(rsp); + if (completed) { + return; + } +@@ -322,7 +322,7 @@ static void migrate_check_parameter(QTestState *who, const char *parameter, + qdict_get_try_int(rsp_return, parameter, -1)); + g_assert_cmpstr(result, ==, value); + g_free(result); +- QDECREF(rsp); ++ qobject_unref(rsp); + } + + static void migrate_set_parameter(QTestState *who, const char *parameter, +@@ -337,7 +337,7 @@ static void migrate_set_parameter(QTestState *who, const char *parameter, + rsp = qtest_qmp(who, cmd); + g_free(cmd); + g_assert(qdict_haskey(rsp, "return")); +- QDECREF(rsp); ++ qobject_unref(rsp); + migrate_check_parameter(who, parameter, value); + } + +@@ -355,7 +355,7 @@ static void migrate_set_capability(QTestState *who, const char *capability, + rsp = qtest_qmp(who, cmd); + g_free(cmd); + g_assert(qdict_haskey(rsp, "return")); +- QDECREF(rsp); ++ qobject_unref(rsp); + } + + static void migrate(QTestState *who, const char *uri) +@@ -369,7 +369,7 @@ static void migrate(QTestState *who, const char *uri) + rsp = qtest_qmp(who, cmd); + g_free(cmd); + g_assert(qdict_haskey(rsp, "return")); +- QDECREF(rsp); ++ qobject_unref(rsp); + } + + static void migrate_start_postcopy(QTestState *who) +@@ -378,7 +378,7 @@ static void migrate_start_postcopy(QTestState *who) + + rsp = wait_command(who, "{ 'execute': 'migrate-start-postcopy' }"); + g_assert(qdict_haskey(rsp, "return")); +- QDECREF(rsp); ++ qobject_unref(rsp); + } + + static void test_migrate_start(QTestState **from, QTestState **to, +@@ -491,7 +491,7 @@ static void deprecated_set_downtime(QTestState *who, const double value) + rsp = qtest_qmp(who, cmd); + g_free(cmd); + g_assert(qdict_haskey(rsp, "return")); +- QDECREF(rsp); ++ qobject_unref(rsp); + result_int = value * 1000L; + expected = g_strdup_printf("%" PRId64, result_int); + migrate_check_parameter(who, "downtime-limit", expected); +@@ -508,7 +508,7 @@ static void deprecated_set_speed(QTestState *who, const char *value) + rsp = qtest_qmp(who, cmd); + g_free(cmd); + g_assert(qdict_haskey(rsp, "return")); +- QDECREF(rsp); ++ qobject_unref(rsp); + migrate_check_parameter(who, "max-bandwidth", value); + } + +@@ -581,7 +581,7 @@ static void test_baddest(void) + + g_assert(!strcmp(status, "setup") || !(strcmp(status, "failed"))); + failed = !strcmp(status, "failed"); +- QDECREF(rsp); ++ qobject_unref(rsp); + } while (!failed); + + /* Is the machine currently running? */ +@@ -590,7 +590,7 @@ static void test_baddest(void) + rsp_return = qdict_get_qdict(rsp, "return"); + g_assert(qdict_haskey(rsp_return, "running")); + g_assert(qdict_get_bool(rsp_return, "running")); +- QDECREF(rsp); ++ qobject_unref(rsp); + + test_migrate_end(from, to, false); + } +diff --git a/tests/numa-test.c b/tests/numa-test.c +index 0f861d8..169213f 100644 +--- a/tests/numa-test.c ++++ b/tests/numa-test.c +@@ -111,10 +111,10 @@ static void test_query_cpus(const void *data) + } else { + g_assert_cmpint(node, ==, 1); + } +- qobject_decref(e); ++ qobject_unref(e); + } + +- QDECREF(resp); ++ qobject_unref(resp); + qtest_end(); + g_free(cli); + } +@@ -164,10 +164,10 @@ static void pc_numa_cpu(const void *data) + } else { + g_assert(false); + } +- qobject_decref(e); ++ qobject_unref(e); + } + +- QDECREF(resp); ++ qobject_unref(resp); + qtest_end(); + g_free(cli); + } +@@ -209,10 +209,10 @@ static void spapr_numa_cpu(const void *data) + } else { + g_assert(false); + } +- qobject_decref(e); ++ qobject_unref(e); + } + +- QDECREF(resp); ++ qobject_unref(resp); + qtest_end(); + g_free(cli); + } +@@ -252,10 +252,10 @@ static void aarch64_numa_cpu(const void *data) + } else { + g_assert(false); + } +- qobject_decref(e); ++ qobject_unref(e); + } + +- QDECREF(resp); ++ qobject_unref(resp); + qtest_end(); + g_free(cli); + } +diff --git a/tests/pvpanic-test.c b/tests/pvpanic-test.c +index ebdf32c..7461a72 100644 +--- a/tests/pvpanic-test.c ++++ b/tests/pvpanic-test.c +@@ -28,7 +28,7 @@ static void test_panic(void) + data = qdict_get_qdict(response, "data"); + g_assert(qdict_haskey(data, "action")); + g_assert_cmpstr(qdict_get_str(data, "action"), ==, "pause"); +- QDECREF(response); ++ qobject_unref(response); + } + + int main(int argc, char **argv) +diff --git a/tests/q35-test.c b/tests/q35-test.c +index 3eaedf4..7ea7acc 100644 +--- a/tests/q35-test.c ++++ b/tests/q35-test.c +@@ -109,7 +109,7 @@ static void test_smram_lock(void) + response = qmp("{'execute': 'system_reset', 'arguments': {} }"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); +- QDECREF(response); ++ qobject_unref(response); + + /* check open is settable again */ + smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, false); +diff --git a/tests/qmp-test.c b/tests/qmp-test.c +index f34428e..3a5c42e 100644 +--- a/tests/qmp-test.c ++++ b/tests/qmp-test.c +@@ -52,27 +52,27 @@ static void test_malformed(QTestState *qts) + /* Not even a dictionary */ + resp = qtest_qmp(qts, "null"); + g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); +- QDECREF(resp); ++ qobject_unref(resp); + + /* No "execute" key */ + resp = qtest_qmp(qts, "{}"); + g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); +- QDECREF(resp); ++ qobject_unref(resp); + + /* "execute" isn't a string */ + resp = qtest_qmp(qts, "{ 'execute': true }"); + g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); +- QDECREF(resp); ++ qobject_unref(resp); + + /* "arguments" isn't a dictionary */ + resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'arguments': [] }"); + g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); +- QDECREF(resp); ++ qobject_unref(resp); + + /* extra key */ + resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'extra': true }"); + g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); +- QDECREF(resp); ++ qobject_unref(resp); + } + + static void test_qmp_protocol(void) +@@ -90,12 +90,12 @@ static void test_qmp_protocol(void) + test_version(qdict_get(q, "version")); + capabilities = qdict_get_qlist(q, "capabilities"); + g_assert(capabilities && qlist_empty(capabilities)); +- QDECREF(resp); ++ qobject_unref(resp); + + /* Test valid command before handshake */ + resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); + g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound"); +- QDECREF(resp); ++ qobject_unref(resp); + + /* Test malformed commands before handshake */ + test_malformed(qts); +@@ -104,17 +104,17 @@ static void test_qmp_protocol(void) + resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }"); + ret = qdict_get_qdict(resp, "return"); + g_assert(ret && !qdict_size(ret)); +- QDECREF(resp); ++ qobject_unref(resp); + + /* Test repeated handshake */ + resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }"); + g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound"); +- QDECREF(resp); ++ qobject_unref(resp); + + /* Test valid command */ + resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); + test_version(qdict_get(resp, "return")); +- QDECREF(resp); ++ qobject_unref(resp); + + /* Test malformed commands */ + test_malformed(qts); +@@ -124,13 +124,13 @@ static void test_qmp_protocol(void) + ret = qdict_get_qdict(resp, "return"); + g_assert(ret); + g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, "cookie#1"); +- QDECREF(resp); ++ qobject_unref(resp); + + /* Test command failure with 'id' */ + resp = qtest_qmp(qts, "{ 'execute': 'human-monitor-command', 'id': 2 }"); + g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); + g_assert_cmpint(qdict_get_int(resp, "id"), ==, 2); +- QDECREF(resp); ++ qobject_unref(resp); + + qtest_quit(qts); + } +@@ -159,21 +159,21 @@ static void test_qmp_oob(void) + qstr = qobject_to(QString, entry->value); + g_assert(qstr); + g_assert_cmpstr(qstring_get_str(qstr), ==, "oob"); +- QDECREF(resp); ++ qobject_unref(resp); + + /* Try a fake capability, it should fail. */ + resp = qtest_qmp(qts, + "{ 'execute': 'qmp_capabilities', " + " 'arguments': { 'enable': [ 'cap-does-not-exist' ] } }"); + g_assert(qdict_haskey(resp, "error")); +- QDECREF(resp); ++ qobject_unref(resp); + + /* Now, enable OOB in current QMP session, it should succeed. */ + resp = qtest_qmp(qts, + "{ 'execute': 'qmp_capabilities', " + " 'arguments': { 'enable': [ 'oob' ] } }"); + g_assert(qdict_haskey(resp, "return")); +- QDECREF(resp); ++ qobject_unref(resp); + + /* + * Try any command that does not support OOB but with OOB flag. We +@@ -183,7 +183,7 @@ static void test_qmp_oob(void) + "{ 'execute': 'query-cpus'," + " 'control': { 'run-oob': true } }"); + g_assert(qdict_haskey(resp, "error")); +- QDECREF(resp); ++ qobject_unref(resp); + + /* + * First send the "x-oob-test" command with lock=true and +@@ -210,7 +210,7 @@ static void test_qmp_oob(void) + !g_strcmp0(cmd_id, "unlock-cmd")) { + acks++; + } +- QDECREF(resp); ++ qobject_unref(resp); + } + + qtest_quit(qts); +@@ -274,7 +274,7 @@ static void test_query(const void *data) + -1, &error_abort), + ==, expected_error_class); + } +- QDECREF(resp); ++ qobject_unref(resp); + + qtest_end(); + } +@@ -324,7 +324,7 @@ static void qmp_schema_init(QmpSchema *schema) + visit_type_SchemaInfoList(qiv, NULL, &schema->list, &error_abort); + visit_free(qiv); + +- QDECREF(resp); ++ qobject_unref(resp); + qtest_end(); + + schema->hash = g_hash_table_new(g_str_hash, g_str_equal); +diff --git a/tests/qom-test.c b/tests/qom-test.c +index db0d3ab..5bf3921 100644 +--- a/tests/qom-test.c ++++ b/tests/qom-test.c +@@ -59,7 +59,7 @@ static void test_properties(const char *path, bool recurse) + g_assert(response); + + if (!recurse) { +- QDECREF(response); ++ qobject_unref(response); + return; + } + +@@ -84,10 +84,10 @@ static void test_properties(const char *path, bool recurse) + path, prop); + /* qom-get may fail but should not, e.g., segfault. */ + g_assert(tmp); +- QDECREF(tmp); ++ qobject_unref(tmp); + } + } +- QDECREF(response); ++ qobject_unref(response); + } + + static void test_machine(gconstpointer data) +@@ -103,7 +103,7 @@ static void test_machine(gconstpointer data) + + response = qmp("{ 'execute': 'quit' }"); + g_assert(qdict_haskey(response, "return")); +- QDECREF(response); ++ qobject_unref(response); + + qtest_end(); + g_free(args); +diff --git a/tests/tco-test.c b/tests/tco-test.c +index aee17af..9945fb8 100644 +--- a/tests/tco-test.c ++++ b/tests/tco-test.c +@@ -241,8 +241,8 @@ static QDict *get_watchdog_action(void) + QDict *data; + + data = qdict_get_qdict(ev, "data"); +- QINCREF(data); +- QDECREF(ev); ++ qobject_ref(data); ++ qobject_unref(ev); + return data; + } + +@@ -265,7 +265,7 @@ static void test_tco_second_timeout_pause(void) + clock_step(ticks * TCO_TICK_NSEC * 2); + ad = get_watchdog_action(); + g_assert(!strcmp(qdict_get_str(ad, "action"), "pause")); +- QDECREF(ad); ++ qobject_unref(ad); + + stop_tco(&td); + test_end(&td); +@@ -290,7 +290,7 @@ static void test_tco_second_timeout_reset(void) + clock_step(ticks * TCO_TICK_NSEC * 2); + ad = get_watchdog_action(); + g_assert(!strcmp(qdict_get_str(ad, "action"), "reset")); +- QDECREF(ad); ++ qobject_unref(ad); + + stop_tco(&td); + test_end(&td); +@@ -315,7 +315,7 @@ static void test_tco_second_timeout_shutdown(void) + clock_step(ticks * TCO_TICK_NSEC * 2); + ad = get_watchdog_action(); + g_assert(!strcmp(qdict_get_str(ad, "action"), "shutdown")); +- QDECREF(ad); ++ qobject_unref(ad); + + stop_tco(&td); + test_end(&td); +@@ -340,7 +340,7 @@ static void test_tco_second_timeout_none(void) + clock_step(ticks * TCO_TICK_NSEC * 2); + ad = get_watchdog_action(); + g_assert(!strcmp(qdict_get_str(ad, "action"), "none")); +- QDECREF(ad); ++ qobject_unref(ad); + + stop_tco(&td); + test_end(&td); +diff --git a/tests/test-char.c b/tests/test-char.c +index 306c728..1880d36 100644 +--- a/tests/test-char.c ++++ b/tests/test-char.c +@@ -322,7 +322,7 @@ static void char_socket_test_common(Chardev *chr) + qdict = qobject_to(QDict, addr); + port = qdict_get_str(qdict, "port"); + tmp = g_strdup_printf("tcp:127.0.0.1:%s", port); +- QDECREF(qdict); ++ qobject_unref(qdict); + + qemu_chr_fe_init(&be, chr, &error_abort); + qemu_chr_fe_set_handlers(&be, socket_can_read, socket_read, +diff --git a/tests/test-keyval.c b/tests/test-keyval.c +index 029f052..63cb146 100644 +--- a/tests/test-keyval.c ++++ b/tests/test-keyval.c +@@ -30,7 +30,7 @@ static void test_keyval_parse(void) + /* Nothing */ + qdict = keyval_parse("", NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 0); +- QDECREF(qdict); ++ qobject_unref(qdict); + + /* Empty key (qemu_opts_parse() accepts this) */ + qdict = keyval_parse("=val", NULL, &err); +@@ -70,7 +70,7 @@ static void test_keyval_parse(void) + qdict = keyval_parse(params + 2, NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 1); + g_assert_cmpstr(qdict_get_try_str(qdict, long_key + 1), ==, "v"); +- QDECREF(qdict); ++ qobject_unref(qdict); + + /* Long key fragment */ + qdict = keyval_parse(params, NULL, &error_abort); +@@ -79,7 +79,7 @@ static void test_keyval_parse(void) + g_assert(sub_qdict); + g_assert_cmpuint(qdict_size(sub_qdict), ==, 1); + g_assert_cmpstr(qdict_get_try_str(sub_qdict, long_key + 1), ==, "v"); +- QDECREF(qdict); ++ qobject_unref(qdict); + g_free(params); + + /* Crap after valid key */ +@@ -92,13 +92,13 @@ static void test_keyval_parse(void) + g_assert_cmpuint(qdict_size(qdict), ==, 2); + g_assert_cmpstr(qdict_get_try_str(qdict, "a"), ==, "3"); + g_assert_cmpstr(qdict_get_try_str(qdict, "b"), ==, "2,x"); +- QDECREF(qdict); ++ qobject_unref(qdict); + + /* Even when it doesn't in qemu_opts_parse() */ + qdict = keyval_parse("id=foo,id=bar", NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 1); + g_assert_cmpstr(qdict_get_try_str(qdict, "id"), ==, "bar"); +- QDECREF(qdict); ++ qobject_unref(qdict); + + /* Dotted keys */ + qdict = keyval_parse("a.b.c=1,a.b.c=2,d=3", NULL, &error_abort); +@@ -111,7 +111,7 @@ static void test_keyval_parse(void) + g_assert_cmpuint(qdict_size(sub_qdict), ==, 1); + g_assert_cmpstr(qdict_get_try_str(sub_qdict, "c"), ==, "2"); + g_assert_cmpstr(qdict_get_try_str(qdict, "d"), ==, "3"); +- QDECREF(qdict); ++ qobject_unref(qdict); + + /* Inconsistent dotted keys */ + qdict = keyval_parse("a.b=1,a=2", NULL, &err); +@@ -125,7 +125,7 @@ static void test_keyval_parse(void) + qdict = keyval_parse("x=y,", NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 1); + g_assert_cmpstr(qdict_get_try_str(qdict, "x"), ==, "y"); +- QDECREF(qdict); ++ qobject_unref(qdict); + + /* Except when it isn't */ + qdict = keyval_parse(",", NULL, &err); +@@ -136,13 +136,13 @@ static void test_keyval_parse(void) + qdict = keyval_parse("x=,,id=bar", NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 1); + g_assert_cmpstr(qdict_get_try_str(qdict, "x"), ==, ",id=bar"); +- QDECREF(qdict); ++ qobject_unref(qdict); + + /* Anti-social ID is left to caller (qemu_opts_parse() rejects it) */ + qdict = keyval_parse("id=666", NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 1); + g_assert_cmpstr(qdict_get_try_str(qdict, "id"), ==, "666"); +- QDECREF(qdict); ++ qobject_unref(qdict); + + /* Implied value not supported (unlike qemu_opts_parse()) */ + qdict = keyval_parse("an,noaus,noaus=", NULL, &err); +@@ -160,7 +160,7 @@ static void test_keyval_parse(void) + g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), ==, "an"); + g_assert_cmpstr(qdict_get_try_str(qdict, "aus"), ==, "off"); + g_assert_cmpstr(qdict_get_try_str(qdict, "noaus"), ==, ""); +- QDECREF(qdict); ++ qobject_unref(qdict); + + /* Implied dotted key */ + qdict = keyval_parse("val", "eins.zwei", &error_abort); +@@ -169,7 +169,7 @@ static void test_keyval_parse(void) + g_assert(sub_qdict); + g_assert_cmpuint(qdict_size(sub_qdict), ==, 1); + g_assert_cmpstr(qdict_get_try_str(sub_qdict, "zwei"), ==, "val"); +- QDECREF(qdict); ++ qobject_unref(qdict); + + /* Implied key with empty value (qemu_opts_parse() accepts this) */ + qdict = keyval_parse(",", "implied", &err); +@@ -198,7 +198,7 @@ static void check_list012(QList *qlist) + qstr = qobject_to(QString, qlist_pop(qlist)); + g_assert(qstr); + g_assert_cmpstr(qstring_get_str(qstr), ==, expected[i]); +- QDECREF(qstr); ++ qobject_unref(qstr); + } + g_assert(qlist_empty(qlist)); + } +@@ -218,14 +218,14 @@ static void test_keyval_parse_list(void) + NULL, &error_abort); + g_assert_cmpint(qdict_size(qdict), ==, 1); + check_list012(qdict_get_qlist(qdict, "list")); +- QDECREF(qdict); ++ qobject_unref(qdict); + + /* Multiple indexes, last one wins */ + qdict = keyval_parse("list.1=goner,list.0=null,list.01=eins,list.2=zwei", + NULL, &error_abort); + g_assert_cmpint(qdict_size(qdict), ==, 1); + check_list012(qdict_get_qlist(qdict, "list")); +- QDECREF(qdict); ++ qobject_unref(qdict); + + /* List at deeper nesting */ + qdict = keyval_parse("a.list.1=eins,a.list.00=null,a.list.2=zwei", +@@ -234,7 +234,7 @@ static void test_keyval_parse_list(void) + sub_qdict = qdict_get_qdict(qdict, "a"); + g_assert_cmpint(qdict_size(sub_qdict), ==, 1); + check_list012(qdict_get_qlist(sub_qdict, "list")); +- QDECREF(qdict); ++ qobject_unref(qdict); + + /* Inconsistent dotted keys: both list and dictionary */ + qdict = keyval_parse("a.b.c=1,a.b.0=2", NULL, &err); +@@ -262,7 +262,7 @@ static void test_keyval_visit_bool(void) + + qdict = keyval_parse("bool1=on,bool2=off", NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_bool(v, "bool1", &b, &error_abort); + g_assert(b); +@@ -274,7 +274,7 @@ static void test_keyval_visit_bool(void) + + qdict = keyval_parse("bool1=offer", NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_bool(v, "bool1", &b, &err); + error_free_or_abort(&err); +@@ -292,7 +292,7 @@ static void test_keyval_visit_number(void) + /* Lower limit zero */ + qdict = keyval_parse("number1=0", NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, "number1", &u, &error_abort); + g_assert_cmpuint(u, ==, 0); +@@ -304,7 +304,7 @@ static void test_keyval_visit_number(void) + qdict = keyval_parse("number1=18446744073709551615,number2=-1", + NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, "number1", &u, &error_abort); + g_assert_cmphex(u, ==, UINT64_MAX); +@@ -318,7 +318,7 @@ static void test_keyval_visit_number(void) + qdict = keyval_parse("number1=18446744073709551616", + NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, "number1", &u, &err); + error_free_or_abort(&err); +@@ -329,7 +329,7 @@ static void test_keyval_visit_number(void) + qdict = keyval_parse("number1=-18446744073709551616", + NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, "number1", &u, &err); + error_free_or_abort(&err); +@@ -340,7 +340,7 @@ static void test_keyval_visit_number(void) + qdict = keyval_parse("number1=0x2a,number2=052", + NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, "number1", &u, &error_abort); + g_assert_cmpuint(u, ==, 42); +@@ -354,7 +354,7 @@ static void test_keyval_visit_number(void) + qdict = keyval_parse("number1=3.14,number2=08", + NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, "number1", &u, &err); + error_free_or_abort(&err); +@@ -374,7 +374,7 @@ static void test_keyval_visit_size(void) + /* Lower limit zero */ + qdict = keyval_parse("sz1=0", NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &error_abort); + g_assert_cmpuint(sz, ==, 0); +@@ -390,7 +390,7 @@ static void test_keyval_visit_size(void) + "sz3=9007199254740993", + NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &error_abort); + g_assert_cmphex(sz, ==, 0x1fffffffffffff); +@@ -407,7 +407,7 @@ static void test_keyval_visit_size(void) + "sz2=9223372036854775295", /* 7ffffffffffffdff */ + NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &error_abort); + g_assert_cmphex(sz, ==, 0x7ffffffffffffc00); +@@ -422,7 +422,7 @@ static void test_keyval_visit_size(void) + "sz2=18446744073709550591", /* fffffffffffffbff */ + NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &error_abort); + g_assert_cmphex(sz, ==, 0xfffffffffffff800); +@@ -437,7 +437,7 @@ static void test_keyval_visit_size(void) + "sz2=18446744073709550592", /* fffffffffffffc00 */ + NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &err); + error_free_or_abort(&err); +@@ -450,7 +450,7 @@ static void test_keyval_visit_size(void) + qdict = keyval_parse("sz1=8b,sz2=1.5k,sz3=2M,sz4=0.1G,sz5=16777215T", + NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &error_abort); + g_assert_cmpuint(sz, ==, 8); +@@ -469,7 +469,7 @@ static void test_keyval_visit_size(void) + /* Beyond limit with suffix */ + qdict = keyval_parse("sz1=16777216T", NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &err); + error_free_or_abort(&err); +@@ -479,7 +479,7 @@ static void test_keyval_visit_size(void) + /* Trailing crap */ + qdict = keyval_parse("sz1=16E,sz2=16Gi", NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &err); + error_free_or_abort(&err); +@@ -498,7 +498,7 @@ static void test_keyval_visit_dict(void) + + qdict = keyval_parse("a.b.c=1,a.b.c=2,d=3", NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_start_struct(v, "a", NULL, 0, &error_abort); + visit_start_struct(v, "b", NULL, 0, &error_abort); +@@ -516,7 +516,7 @@ static void test_keyval_visit_dict(void) + + qdict = keyval_parse("a.b=", NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_start_struct(v, "a", NULL, 0, &error_abort); + visit_type_int(v, "c", &i, &err); /* a.c missing */ +@@ -539,7 +539,7 @@ static void test_keyval_visit_list(void) + qdict = keyval_parse("a.0=,a.1=I,a.2.0=II", NULL, &error_abort); + /* TODO empty list */ + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_start_list(v, "a", NULL, 0, &error_abort); + visit_type_str(v, NULL, &s, &error_abort); +@@ -562,7 +562,7 @@ static void test_keyval_visit_list(void) + + qdict = keyval_parse("a.0=,b.0.0=head", NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_start_list(v, "a", NULL, 0, &error_abort); + visit_check_list(v, &err); /* a[0] unexpected */ +@@ -591,7 +591,7 @@ static void test_keyval_visit_optional(void) + + qdict = keyval_parse("a.b=1", NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_optional(v, "b", &present); + g_assert(!present); /* b missing */ +@@ -627,7 +627,7 @@ static void test_keyval_visit_alternate(void) + */ + qdict = keyval_parse("a=1,b=2,c=on", NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_AltStrObj(v, "a", &aso, &error_abort); + g_assert_cmpint(aso->type, ==, QTYPE_QSTRING); +@@ -651,19 +651,19 @@ static void test_keyval_visit_any(void) + + qdict = keyval_parse("a.0=null,a.1=1", NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); +- QDECREF(qdict); ++ qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_any(v, "a", &any, &error_abort); + qlist = qobject_to(QList, any); + g_assert(qlist); + qstr = qobject_to(QString, qlist_pop(qlist)); + g_assert_cmpstr(qstring_get_str(qstr), ==, "null"); +- QDECREF(qstr); ++ qobject_unref(qstr); + qstr = qobject_to(QString, qlist_pop(qlist)); + g_assert_cmpstr(qstring_get_str(qstr), ==, "1"); + g_assert(qlist_empty(qlist)); +- QDECREF(qstr); +- qobject_decref(any); ++ qobject_unref(qstr); ++ qobject_unref(any); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); +diff --git a/tests/test-netfilter.c b/tests/test-netfilter.c +index 95f7839..e47075d 100644 +--- a/tests/test-netfilter.c ++++ b/tests/test-netfilter.c +@@ -29,7 +29,7 @@ static void add_one_netfilter(void) + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); +- QDECREF(response); ++ qobject_unref(response); + + response = qmp("{'execute': 'object-del'," + " 'arguments': {" +@@ -37,7 +37,7 @@ static void add_one_netfilter(void) + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); +- QDECREF(response); ++ qobject_unref(response); + } + + /* add a netfilter to a netdev and then remove the netdev */ +@@ -57,7 +57,7 @@ static void remove_netdev_with_one_netfilter(void) + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); +- QDECREF(response); ++ qobject_unref(response); + + response = qmp("{'execute': 'netdev_del'," + " 'arguments': {" +@@ -65,7 +65,7 @@ static void remove_netdev_with_one_netfilter(void) + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); +- QDECREF(response); ++ qobject_unref(response); + + /* add back the netdev */ + response = qmp("{'execute': 'netdev_add'," +@@ -75,7 +75,7 @@ static void remove_netdev_with_one_netfilter(void) + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); +- QDECREF(response); ++ qobject_unref(response); + } + + /* add multi(2) netfilters to a netdev and then remove them */ +@@ -95,7 +95,7 @@ static void add_multi_netfilter(void) + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); +- QDECREF(response); ++ qobject_unref(response); + + response = qmp("{'execute': 'object-add'," + " 'arguments': {" +@@ -109,7 +109,7 @@ static void add_multi_netfilter(void) + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); +- QDECREF(response); ++ qobject_unref(response); + + response = qmp("{'execute': 'object-del'," + " 'arguments': {" +@@ -117,7 +117,7 @@ static void add_multi_netfilter(void) + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); +- QDECREF(response); ++ qobject_unref(response); + + response = qmp("{'execute': 'object-del'," + " 'arguments': {" +@@ -125,7 +125,7 @@ static void add_multi_netfilter(void) + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); +- QDECREF(response); ++ qobject_unref(response); + } + + /* add multi(2) netfilters to a netdev and then remove the netdev */ +@@ -145,7 +145,7 @@ static void remove_netdev_with_multi_netfilter(void) + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); +- QDECREF(response); ++ qobject_unref(response); + + response = qmp("{'execute': 'object-add'," + " 'arguments': {" +@@ -159,7 +159,7 @@ static void remove_netdev_with_multi_netfilter(void) + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); +- QDECREF(response); ++ qobject_unref(response); + + response = qmp("{'execute': 'netdev_del'," + " 'arguments': {" +@@ -167,7 +167,7 @@ static void remove_netdev_with_multi_netfilter(void) + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); +- QDECREF(response); ++ qobject_unref(response); + + /* add back the netdev */ + response = qmp("{'execute': 'netdev_add'," +@@ -177,7 +177,7 @@ static void remove_netdev_with_multi_netfilter(void) + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); +- QDECREF(response); ++ qobject_unref(response); + } + + int main(int argc, char **argv) +diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c +index 2c422ab..77dd72b 100644 +--- a/tests/test-qemu-opts.c ++++ b/tests/test-qemu-opts.c +@@ -887,7 +887,7 @@ static void test_opts_to_qdict_basic(void) + g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); + g_assert_false(qdict_haskey(dict, "number2")); + +- QDECREF(dict); ++ qobject_unref(dict); + qemu_opts_del(opts); + } + +@@ -914,7 +914,7 @@ static void test_opts_to_qdict_filtered(void) + g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); + g_assert_false(qdict_haskey(dict, "number2")); + g_assert_false(qdict_haskey(dict, "bool1")); +- QDECREF(dict); ++ qobject_unref(dict); + + dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, false); + g_assert(dict != NULL); +@@ -924,7 +924,7 @@ static void test_opts_to_qdict_filtered(void) + g_assert_false(qdict_haskey(dict, "str3")); + g_assert_false(qdict_haskey(dict, "number1")); + g_assert_false(qdict_haskey(dict, "number2")); +- QDECREF(dict); ++ qobject_unref(dict); + + /* Now delete converted options from opts */ + dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_01, true); +@@ -935,7 +935,7 @@ static void test_opts_to_qdict_filtered(void) + g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); + g_assert_false(qdict_haskey(dict, "number2")); + g_assert_false(qdict_haskey(dict, "bool1")); +- QDECREF(dict); ++ qobject_unref(dict); + + dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, true); + g_assert(dict != NULL); +@@ -945,7 +945,7 @@ static void test_opts_to_qdict_filtered(void) + g_assert_false(qdict_haskey(dict, "str3")); + g_assert_false(qdict_haskey(dict, "number1")); + g_assert_false(qdict_haskey(dict, "number2")); +- QDECREF(dict); ++ qobject_unref(dict); + + g_assert_true(QTAILQ_EMPTY(&opts->head)); + +@@ -978,13 +978,13 @@ static void test_opts_to_qdict_duplicates(void) + dict = qemu_opts_to_qdict(opts, NULL); + g_assert(dict != NULL); + g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b"); +- QDECREF(dict); ++ qobject_unref(dict); + + /* The last one still wins if entries are deleted, and both are deleted */ + dict = qemu_opts_to_qdict_filtered(opts, NULL, NULL, true); + g_assert(dict != NULL); + g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b"); +- QDECREF(dict); ++ qobject_unref(dict); + + g_assert_true(QTAILQ_EMPTY(&opts->head)); + +diff --git a/tests/test-qga.c b/tests/test-qga.c +index e6ab788..18e63cb 100644 +--- a/tests/test-qga.c ++++ b/tests/test-qga.c +@@ -180,7 +180,7 @@ static void test_qga_sync_delimited(gconstpointer fix) + v = qdict_get_int(ret, "return"); + g_assert_cmpint(r, ==, v); + +- QDECREF(ret); ++ qobject_unref(ret); + } + + static void test_qga_sync(gconstpointer fix) +@@ -212,7 +212,7 @@ static void test_qga_sync(gconstpointer fix) + v = qdict_get_int(ret, "return"); + g_assert_cmpint(r, ==, v); + +- QDECREF(ret); ++ qobject_unref(ret); + } + + static void test_qga_ping(gconstpointer fix) +@@ -224,7 +224,7 @@ static void test_qga_ping(gconstpointer fix) + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + +- QDECREF(ret); ++ qobject_unref(ret); + } + + static void test_qga_invalid_args(gconstpointer fix) +@@ -244,7 +244,7 @@ static void test_qga_invalid_args(gconstpointer fix) + g_assert_cmpstr(class, ==, "GenericError"); + g_assert_cmpstr(desc, ==, "Parameter 'foo' is unexpected"); + +- QDECREF(ret); ++ qobject_unref(ret); + } + + static void test_qga_invalid_cmd(gconstpointer fix) +@@ -263,7 +263,7 @@ static void test_qga_invalid_cmd(gconstpointer fix) + g_assert_cmpstr(class, ==, "CommandNotFound"); + g_assert_cmpint(strlen(desc), >, 0); + +- QDECREF(ret); ++ qobject_unref(ret); + } + + static void test_qga_info(gconstpointer fix) +@@ -280,7 +280,7 @@ static void test_qga_info(gconstpointer fix) + version = qdict_get_try_str(val, "version"); + g_assert_cmpstr(version, ==, QEMU_VERSION); + +- QDECREF(ret); ++ qobject_unref(ret); + } + + static void test_qga_get_vcpus(gconstpointer fix) +@@ -300,7 +300,7 @@ static void test_qga_get_vcpus(gconstpointer fix) + g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online")); + g_assert(qdict_haskey(qobject_to(QDict, entry->value), "logical-id")); + +- QDECREF(ret); ++ qobject_unref(ret); + } + + static void test_qga_get_fsinfo(gconstpointer fix) +@@ -324,7 +324,7 @@ static void test_qga_get_fsinfo(gconstpointer fix) + g_assert(qdict_haskey(qobject_to(QDict, entry->value), "disk")); + } + +- QDECREF(ret); ++ qobject_unref(ret); + } + + static void test_qga_get_memory_block_info(gconstpointer fix) +@@ -344,7 +344,7 @@ static void test_qga_get_memory_block_info(gconstpointer fix) + g_assert_cmpint(size, >, 0); + } + +- QDECREF(ret); ++ qobject_unref(ret); + } + + static void test_qga_get_memory_blocks(gconstpointer fix) +@@ -369,7 +369,7 @@ static void test_qga_get_memory_blocks(gconstpointer fix) + } + } + +- QDECREF(ret); ++ qobject_unref(ret); + } + + static void test_qga_network_get_interfaces(gconstpointer fix) +@@ -388,7 +388,7 @@ static void test_qga_network_get_interfaces(gconstpointer fix) + entry = qlist_first(list); + g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name")); + +- QDECREF(ret); ++ qobject_unref(ret); + } + + static void test_qga_file_ops(gconstpointer fix) +@@ -410,7 +410,7 @@ static void test_qga_file_ops(gconstpointer fix) + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + id = qdict_get_int(ret, "return"); +- QDECREF(ret); ++ qobject_unref(ret); + + enc = g_base64_encode(helloworld, sizeof(helloworld)); + /* write */ +@@ -426,7 +426,7 @@ static void test_qga_file_ops(gconstpointer fix) + eof = qdict_get_bool(val, "eof"); + g_assert_cmpint(count, ==, sizeof(helloworld)); + g_assert_cmpint(eof, ==, 0); +- QDECREF(ret); ++ qobject_unref(ret); + g_free(cmd); + + /* flush */ +@@ -434,7 +434,7 @@ static void test_qga_file_ops(gconstpointer fix) + " 'arguments': {'handle': %" PRId64 "} }", + id); + ret = qmp_fd(fixture->fd, cmd); +- QDECREF(ret); ++ qobject_unref(ret); + g_free(cmd); + + /* close */ +@@ -442,7 +442,7 @@ static void test_qga_file_ops(gconstpointer fix) + " 'arguments': {'handle': %" PRId64 "} }", + id); + ret = qmp_fd(fixture->fd, cmd); +- QDECREF(ret); ++ qobject_unref(ret); + g_free(cmd); + + /* check content */ +@@ -462,7 +462,7 @@ static void test_qga_file_ops(gconstpointer fix) + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + id = qdict_get_int(ret, "return"); +- QDECREF(ret); ++ qobject_unref(ret); + + /* read */ + cmd = g_strdup_printf("{'execute': 'guest-file-read'," +@@ -477,7 +477,7 @@ static void test_qga_file_ops(gconstpointer fix) + g_assert(eof); + g_assert_cmpstr(b64, ==, enc); + +- QDECREF(ret); ++ qobject_unref(ret); + g_free(cmd); + g_free(enc); + +@@ -493,7 +493,7 @@ static void test_qga_file_ops(gconstpointer fix) + g_assert_cmpint(count, ==, 0); + g_assert(eof); + g_assert_cmpstr(b64, ==, ""); +- QDECREF(ret); ++ qobject_unref(ret); + g_free(cmd); + + /* seek */ +@@ -508,7 +508,7 @@ static void test_qga_file_ops(gconstpointer fix) + eof = qdict_get_bool(val, "eof"); + g_assert_cmpint(count, ==, 6); + g_assert(!eof); +- QDECREF(ret); ++ qobject_unref(ret); + g_free(cmd); + + /* partial read */ +@@ -527,7 +527,7 @@ static void test_qga_file_ops(gconstpointer fix) + g_assert_cmpmem(dec, count, helloworld + 6, sizeof(helloworld) - 6); + g_free(dec); + +- QDECREF(ret); ++ qobject_unref(ret); + g_free(cmd); + + /* close */ +@@ -535,7 +535,7 @@ static void test_qga_file_ops(gconstpointer fix) + " 'arguments': {'handle': %" PRId64 "} }", + id); + ret = qmp_fd(fixture->fd, cmd); +- QDECREF(ret); ++ qobject_unref(ret); + g_free(cmd); + } + +@@ -555,7 +555,7 @@ static void test_qga_file_write_read(gconstpointer fix) + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + id = qdict_get_int(ret, "return"); +- QDECREF(ret); ++ qobject_unref(ret); + + enc = g_base64_encode(helloworld, sizeof(helloworld)); + /* write */ +@@ -571,7 +571,7 @@ static void test_qga_file_write_read(gconstpointer fix) + eof = qdict_get_bool(val, "eof"); + g_assert_cmpint(count, ==, sizeof(helloworld)); + g_assert_cmpint(eof, ==, 0); +- QDECREF(ret); ++ qobject_unref(ret); + g_free(cmd); + + /* read (check implicit flush) */ +@@ -586,7 +586,7 @@ static void test_qga_file_write_read(gconstpointer fix) + g_assert_cmpint(count, ==, 0); + g_assert(eof); + g_assert_cmpstr(b64, ==, ""); +- QDECREF(ret); ++ qobject_unref(ret); + g_free(cmd); + + /* seek to 0 */ +@@ -601,7 +601,7 @@ static void test_qga_file_write_read(gconstpointer fix) + eof = qdict_get_bool(val, "eof"); + g_assert_cmpint(count, ==, 0); + g_assert(!eof); +- QDECREF(ret); ++ qobject_unref(ret); + g_free(cmd); + + /* read */ +@@ -616,7 +616,7 @@ static void test_qga_file_write_read(gconstpointer fix) + g_assert_cmpint(count, ==, sizeof(helloworld)); + g_assert(eof); + g_assert_cmpstr(b64, ==, enc); +- QDECREF(ret); ++ qobject_unref(ret); + g_free(cmd); + g_free(enc); + +@@ -625,7 +625,7 @@ static void test_qga_file_write_read(gconstpointer fix) + " 'arguments': {'handle': %" PRId64 "} }", + id); + ret = qmp_fd(fixture->fd, cmd); +- QDECREF(ret); ++ qobject_unref(ret); + g_free(cmd); + } + +@@ -642,7 +642,7 @@ static void test_qga_get_time(gconstpointer fix) + time = qdict_get_int(ret, "return"); + g_assert_cmpint(time, >, 0); + +- QDECREF(ret); ++ qobject_unref(ret); + } + + static void test_qga_blacklist(gconstpointer data) +@@ -661,7 +661,7 @@ static void test_qga_blacklist(gconstpointer data) + desc = qdict_get_try_str(error, "desc"); + g_assert_cmpstr(class, ==, "GenericError"); + g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); +- QDECREF(ret); ++ qobject_unref(ret); + + ret = qmp_fd(fix.fd, "{'execute': 'guest-get-time'}"); + g_assert_nonnull(ret); +@@ -670,12 +670,12 @@ static void test_qga_blacklist(gconstpointer data) + desc = qdict_get_try_str(error, "desc"); + g_assert_cmpstr(class, ==, "GenericError"); + g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); +- QDECREF(ret); ++ qobject_unref(ret); + + /* check something work */ + ret = qmp_fd(fix.fd, "{'execute': 'guest-get-fsinfo'}"); + qmp_assert_no_error(ret); +- QDECREF(ret); ++ qobject_unref(ret); + + fixture_tear_down(&fix, NULL); + } +@@ -772,7 +772,7 @@ static void test_qga_fsfreeze_status(gconstpointer fix) + status = qdict_get_try_str(ret, "return"); + g_assert_cmpstr(status, ==, "thawed"); + +- QDECREF(ret); ++ qobject_unref(ret); + } + + static void test_qga_guest_exec(gconstpointer fix) +@@ -795,7 +795,7 @@ static void test_qga_guest_exec(gconstpointer fix) + val = qdict_get_qdict(ret, "return"); + pid = qdict_get_int(val, "pid"); + g_assert_cmpint(pid, >, 0); +- QDECREF(ret); ++ qobject_unref(ret); + + /* wait for completion */ + now = g_get_monotonic_time(); +@@ -807,7 +807,7 @@ static void test_qga_guest_exec(gconstpointer fix) + val = qdict_get_qdict(ret, "return"); + exited = qdict_get_bool(val, "exited"); + if (!exited) { +- QDECREF(ret); ++ qobject_unref(ret); + } + } while (!exited && + g_get_monotonic_time() < now + 5 * G_TIME_SPAN_SECOND); +@@ -822,7 +822,7 @@ static void test_qga_guest_exec(gconstpointer fix) + g_assert_cmpint(len, ==, 12); + g_assert_cmpstr((char *)decoded, ==, "\" test_str \""); + g_free(decoded); +- QDECREF(ret); ++ qobject_unref(ret); + } + + static void test_qga_guest_exec_invalid(gconstpointer fix) +@@ -841,7 +841,7 @@ static void test_qga_guest_exec_invalid(gconstpointer fix) + desc = qdict_get_str(error, "desc"); + g_assert_cmpstr(class, ==, "GenericError"); + g_assert_cmpint(strlen(desc), >, 0); +- QDECREF(ret); ++ qobject_unref(ret); + + /* invalid pid */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec-status'," +@@ -853,7 +853,7 @@ static void test_qga_guest_exec_invalid(gconstpointer fix) + desc = qdict_get_str(error, "desc"); + g_assert_cmpstr(class, ==, "GenericError"); + g_assert_cmpint(strlen(desc), >, 0); +- QDECREF(ret); ++ qobject_unref(ret); + } + + static void test_qga_guest_get_osinfo(gconstpointer data) +@@ -905,7 +905,7 @@ static void test_qga_guest_get_osinfo(gconstpointer data) + g_assert_nonnull(str); + g_assert_cmpstr(str, ==, "unit-test"); + +- QDECREF(ret); ++ qobject_unref(ret); + g_free(env[0]); + fixture_tear_down(&fixture, NULL); + } +diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c +index db690cc..e0ed461 100644 +--- a/tests/test-qmp-cmds.c ++++ b/tests/test-qmp-cmds.c +@@ -106,8 +106,8 @@ static void test_dispatch_cmd(void) + assert(resp != NULL); + assert(!qdict_haskey(qobject_to(QDict, resp), "error")); + +- qobject_decref(resp); +- QDECREF(req); ++ qobject_unref(resp); ++ qobject_unref(req); + } + + /* test commands that return an error due to invalid parameters */ +@@ -123,8 +123,8 @@ static void test_dispatch_cmd_failure(void) + assert(resp != NULL); + assert(qdict_haskey(qobject_to(QDict, resp), "error")); + +- qobject_decref(resp); +- QDECREF(req); ++ qobject_unref(resp); ++ qobject_unref(req); + + /* check that with extra arguments it throws an error */ + req = qdict_new(); +@@ -137,8 +137,8 @@ static void test_dispatch_cmd_failure(void) + assert(resp != NULL); + assert(qdict_haskey(qobject_to(QDict, resp), "error")); + +- qobject_decref(resp); +- QDECREF(req); ++ qobject_unref(resp); ++ qobject_unref(req); + } + + static QObject *test_qmp_dispatch(QDict *req) +@@ -153,8 +153,8 @@ static QObject *test_qmp_dispatch(QDict *req) + assert(resp && !qdict_haskey(resp, "error")); + ret = qdict_get(resp, "return"); + assert(ret); +- qobject_incref(ret); +- qobject_decref(resp_obj); ++ qobject_ref(ret); ++ qobject_unref(resp_obj); + return ret; + } + +@@ -195,7 +195,7 @@ static void test_dispatch_cmd_io(void) + assert(qdict_get_int(ret_dict_dict2_userdef, "integer") == 422); + assert(!strcmp(qdict_get_str(ret_dict_dict2_userdef, "string"), "hello2")); + assert(!strcmp(qdict_get_str(ret_dict_dict2, "string"), "blah4")); +- QDECREF(ret); ++ qobject_unref(ret); + + qdict_put_int(args3, "a", 66); + qdict_put(req, "arguments", args3); +@@ -204,9 +204,9 @@ static void test_dispatch_cmd_io(void) + ret3 = qobject_to(QNum, test_qmp_dispatch(req)); + g_assert(qnum_get_try_int(ret3, &val)); + g_assert_cmpint(val, ==, 66); +- QDECREF(ret3); ++ qobject_unref(ret3); + +- QDECREF(req); ++ qobject_unref(req); + } + + /* test generated dealloc functions for generated types */ +@@ -257,7 +257,7 @@ static void test_dealloc_partial(void) + v = qobject_input_visitor_new(QOBJECT(ud2_dict)); + visit_type_UserDefTwo(v, NULL, &ud2, &err); + visit_free(v); +- QDECREF(ud2_dict); ++ qobject_unref(ud2_dict); + } + + /* verify that visit_type_XXX() cleans up properly on error */ +diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c +index bb10366..3a7c227 100644 +--- a/tests/test-qmp-event.c ++++ b/tests/test-qmp-event.c +@@ -133,7 +133,7 @@ static void event_prepare(TestEventData *data, + static void event_teardown(TestEventData *data, + const void *unused) + { +- QDECREF(data->expect); ++ qobject_unref(data->expect); + test_event_data = NULL; + + g_mutex_unlock(&test_event_lock); +diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c +index 6dc59c6..0f4d234 100644 +--- a/tests/test-qobject-input-visitor.c ++++ b/tests/test-qobject-input-visitor.c +@@ -35,7 +35,7 @@ typedef struct TestInputVisitorData { + static void visitor_input_teardown(TestInputVisitorData *data, + const void *unused) + { +- qobject_decref(data->obj); ++ qobject_unref(data->obj); + data->obj = NULL; + + if (data->qiv) { +@@ -483,7 +483,7 @@ static void test_visitor_in_any(TestInputVisitorData *data, + g_assert(qnum); + g_assert(qnum_get_try_int(qnum, &val)); + g_assert_cmpint(val, ==, -42); +- qobject_decref(res); ++ qobject_unref(res); + + v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }"); + visit_type_any(v, NULL, &res, &error_abort); +@@ -505,7 +505,7 @@ static void test_visitor_in_any(TestInputVisitorData *data, + qstring = qobject_to(QString, qobj); + g_assert(qstring); + g_assert_cmpstr(qstring_get_str(qstring), ==, "foo"); +- qobject_decref(res); ++ qobject_unref(res); + } + + static void test_visitor_in_null(TestInputVisitorData *data, +@@ -530,7 +530,7 @@ static void test_visitor_in_null(TestInputVisitorData *data, + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_null(v, "a", &null, &error_abort); + g_assert(qobject_type(QOBJECT(null)) == QTYPE_QNULL); +- QDECREF(null); ++ qobject_unref(null); + visit_type_null(v, "b", &null, &err); + error_free_or_abort(&err); + g_assert(!null); +@@ -1262,7 +1262,7 @@ static void do_test_visitor_in_qmp_introspect(TestInputVisitorData *data, + g_assert(schema); + + qapi_free_SchemaInfoList(schema); +- qobject_decref(obj); ++ qobject_unref(obj); + visit_free(v); + } + +diff --git a/tests/test-qobject-output-visitor.c b/tests/test-qobject-output-visitor.c +index ecf21c0..be63585 100644 +--- a/tests/test-qobject-output-visitor.c ++++ b/tests/test-qobject-output-visitor.c +@@ -40,7 +40,7 @@ static void visitor_output_teardown(TestOutputVisitorData *data, + { + visit_free(data->ov); + data->ov = NULL; +- qobject_decref(data->obj); ++ qobject_unref(data->obj); + data->obj = NULL; + } + +@@ -346,7 +346,7 @@ static void test_visitor_out_any(TestOutputVisitorData *data, + g_assert(qnum); + g_assert(qnum_get_try_int(qnum, &val)); + g_assert_cmpint(val, ==, -42); +- qobject_decref(qobj); ++ qobject_unref(qobj); + + visitor_reset(data); + qdict = qdict_new(); +@@ -355,7 +355,7 @@ static void test_visitor_out_any(TestOutputVisitorData *data, + qdict_put_str(qdict, "string", "foo"); + qobj = QOBJECT(qdict); + visit_type_any(data->ov, NULL, &qobj, &error_abort); +- qobject_decref(qobj); ++ qobject_unref(qobj); + qdict = qobject_to(QDict, visitor_get(data)); + g_assert(qdict); + qnum = qobject_to(QNum, qdict_get(qdict, "integer")); +@@ -630,7 +630,7 @@ static void check_native_list(QObject *qobj, + qvalue = qobject_to(QNum, tmp); + g_assert(qnum_get_try_uint(qvalue, &val)); + g_assert_cmpint(val, ==, i); +- qobject_decref(qlist_pop(qlist)); ++ qobject_unref(qlist_pop(qlist)); + } + break; + +@@ -654,7 +654,7 @@ static void check_native_list(QObject *qobj, + qvalue = qobject_to(QNum, tmp); + g_assert(qnum_get_try_int(qvalue, &val)); + g_assert_cmpint(val, ==, i); +- qobject_decref(qlist_pop(qlist)); ++ qobject_unref(qlist_pop(qlist)); + } + break; + case USER_DEF_NATIVE_LIST_UNION_KIND_BOOLEAN: +@@ -665,7 +665,7 @@ static void check_native_list(QObject *qobj, + g_assert(tmp); + qvalue = qobject_to(QBool, tmp); + g_assert_cmpint(qbool_get_bool(qvalue), ==, i % 3 == 0); +- qobject_decref(qlist_pop(qlist)); ++ qobject_unref(qlist_pop(qlist)); + } + break; + case USER_DEF_NATIVE_LIST_UNION_KIND_STRING: +@@ -678,7 +678,7 @@ static void check_native_list(QObject *qobj, + qvalue = qobject_to(QString, tmp); + sprintf(str, "%d", i); + g_assert_cmpstr(qstring_get_str(qvalue), ==, str); +- qobject_decref(qlist_pop(qlist)); ++ qobject_unref(qlist_pop(qlist)); + } + break; + case USER_DEF_NATIVE_LIST_UNION_KIND_NUMBER: +@@ -695,7 +695,7 @@ static void check_native_list(QObject *qobj, + g_string_printf(double_actual, "%.6f", qnum_get_double(qvalue)); + g_assert_cmpstr(double_actual->str, ==, double_expected->str); + +- qobject_decref(qlist_pop(qlist)); ++ qobject_unref(qlist_pop(qlist)); + g_string_free(double_expected, true); + g_string_free(double_actual, true); + } +@@ -703,7 +703,7 @@ static void check_native_list(QObject *qobj, + default: + g_assert_not_reached(); + } +- QDECREF(qlist); ++ qobject_unref(qlist); + } + + static void test_native_list(TestOutputVisitorData *data, +diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c +index d18d90d..1c5a8b9 100644 +--- a/tests/test-visitor-serialization.c ++++ b/tests/test-visitor-serialization.c +@@ -1036,10 +1036,10 @@ static void qmp_deserialize(void **native_out, void *datap, + output_json = qobject_to_json(obj_orig); + obj = qobject_from_json(qstring_get_str(output_json), &error_abort); + +- QDECREF(output_json); ++ qobject_unref(output_json); + d->qiv = qobject_input_visitor_new(obj); +- qobject_decref(obj_orig); +- qobject_decref(obj); ++ qobject_unref(obj_orig); ++ qobject_unref(obj); + visit(d->qiv, native_out, errp); + } + +diff --git a/tests/test-x86-cpuid-compat.c b/tests/test-x86-cpuid-compat.c +index 9e4a508..c1ee197 100644 +--- a/tests/test-x86-cpuid-compat.c ++++ b/tests/test-x86-cpuid-compat.c +@@ -19,7 +19,7 @@ static char *get_cpu0_qom_path(void) + + cpu0 = qobject_to(QDict, qlist_peek(ret)); + path = g_strdup(qdict_get_str(cpu0, "qom_path")); +- QDECREF(resp); ++ qobject_unref(resp); + return path; + } + +@@ -30,8 +30,8 @@ static QObject *qom_get(const char *path, const char *prop) + " 'property': %s } }", + path, prop); + QObject *ret = qdict_get(resp, "return"); +- qobject_incref(ret); +- QDECREF(resp); ++ qobject_ref(ret); ++ qobject_unref(resp); + return ret; + } + +@@ -41,7 +41,7 @@ static bool qom_get_bool(const char *path, const char *prop) + QBool *value = qobject_to(QBool, qom_get(path, prop)); + bool b = qbool_get_bool(value); + +- QDECREF(value); ++ qobject_unref(value); + return b; + } + #endif +@@ -66,7 +66,7 @@ static void test_cpuid_prop(const void *data) + g_assert_cmpint(val, ==, args->expected_value); + qtest_end(); + +- QDECREF(value); ++ qobject_unref(value); + g_free(path); + } + +@@ -142,8 +142,8 @@ static void test_feature_flag(const void *data) + + g_assert(!!(value & (1U << args->bitnr)) == args->expected_value); + +- QDECREF(present); +- QDECREF(filtered); ++ qobject_unref(present); ++ qobject_unref(filtered); + g_free(path); + } + +diff --git a/tests/tmp105-test.c b/tests/tmp105-test.c +index 66c7a01..d093cff 100644 +--- a/tests/tmp105-test.c ++++ b/tests/tmp105-test.c +@@ -74,7 +74,7 @@ static int qmp_tmp105_get_temperature(const char *id) + "'property': 'temperature' } }", id); + g_assert(qdict_haskey(response, "return")); + ret = qdict_get_int(response, "return"); +- QDECREF(response); ++ qobject_unref(response); + return ret; + } + +@@ -85,7 +85,7 @@ static void qmp_tmp105_set_temperature(const char *id, int value) + response = qmp("{ 'execute': 'qom-set', 'arguments': { 'path': %s, " + "'property': 'temperature', 'value': %d } }", id, value); + g_assert(qdict_haskey(response, "return")); +- QDECREF(response); ++ qobject_unref(response); + } + + #define TMP105_PRECISION (1000/16) +diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c +index 61d9972..bbc8091 100644 +--- a/tests/vhost-user-test.c ++++ b/tests/vhost-user-test.c +@@ -727,7 +727,7 @@ static void test_migrate(void) + rsp = qmp("{ 'execute': 'migrate_set_speed'," + "'arguments': { 'value': 10 } }"); + g_assert(qdict_haskey(rsp, "return")); +- QDECREF(rsp); ++ qobject_unref(rsp); + + cmd = g_strdup_printf("{ 'execute': 'migrate'," + "'arguments': { 'uri': '%s' } }", +@@ -735,7 +735,7 @@ static void test_migrate(void) + rsp = qmp(cmd); + g_free(cmd); + g_assert(qdict_haskey(rsp, "return")); +- QDECREF(rsp); ++ qobject_unref(rsp); + + wait_for_log_fd(s); + +@@ -751,7 +751,7 @@ static void test_migrate(void) + rsp = qmp("{ 'execute': 'migrate_set_speed'," + "'arguments': { 'value': 0 } }"); + g_assert(qdict_haskey(rsp, "return")); +- QDECREF(rsp); ++ qobject_unref(rsp); + + qmp_eventwait("STOP"); + +diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c +index 0a3c5dd..b285a26 100644 +--- a/tests/virtio-net-test.c ++++ b/tests/virtio-net-test.c +@@ -173,7 +173,7 @@ static void rx_stop_cont_test(QVirtioDevice *dev, + qvirtqueue_kick(dev, vq, free_head); + + rsp = qmp("{ 'execute' : 'stop'}"); +- QDECREF(rsp); ++ qobject_unref(rsp); + + ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test)); + g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len)); +@@ -182,9 +182,9 @@ static void rx_stop_cont_test(QVirtioDevice *dev, + * ensure the packet data gets queued in QEMU, before we do 'cont'. + */ + rsp = qmp("{ 'execute' : 'query-status'}"); +- QDECREF(rsp); ++ qobject_unref(rsp); + rsp = qmp("{ 'execute' : 'cont'}"); +- QDECREF(rsp); ++ qobject_unref(rsp); + + qvirtio_wait_used_elem(dev, vq, free_head, NULL, QVIRTIO_NET_TIMEOUT_US); + memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test)); +diff --git a/tests/vmgenid-test.c b/tests/vmgenid-test.c +index 2ec274e..8d915c6 100644 +--- a/tests/vmgenid-test.c ++++ b/tests/vmgenid-test.c +@@ -125,7 +125,7 @@ static void read_guid_from_monitor(QemuUUID *guid) + guid_str = qdict_get_str(rsp_ret, "guid"); + g_assert(qemu_uuid_parse(guid_str, guid) == 0); + } +- QDECREF(rsp); ++ qobject_unref(rsp); + } + + static char disk[] = "tests/vmgenid-test-disk-XXXXXX"; +diff --git a/tests/wdt_ib700-test.c b/tests/wdt_ib700-test.c +index 3b5bbcf..797288d 100644 +--- a/tests/wdt_ib700-test.c ++++ b/tests/wdt_ib700-test.c +@@ -16,7 +16,7 @@ static void qmp_check_no_event(QTestState *s) + { + QDict *resp = qtest_qmp(s, "{'execute':'query-status'}"); + g_assert(qdict_haskey(resp, "return")); +- QDECREF(resp); ++ qobject_unref(resp); + } + + static QDict *ib700_program_and_wait(QTestState *s) +@@ -48,8 +48,8 @@ static QDict *ib700_program_and_wait(QTestState *s) + qtest_clock_step(s, 2 * NANOSECONDS_PER_SECOND); + event = qtest_qmp_eventwait_ref(s, "WATCHDOG"); + data = qdict_get_qdict(event, "data"); +- QINCREF(data); +- QDECREF(event); ++ qobject_ref(data); ++ qobject_unref(event); + return data; + } + +@@ -62,7 +62,7 @@ static void ib700_pause(void) + qtest_irq_intercept_in(s, "ioapic"); + d = ib700_program_and_wait(s); + g_assert(!strcmp(qdict_get_str(d, "action"), "pause")); +- QDECREF(d); ++ qobject_unref(d); + qtest_qmp_eventwait(s, "STOP"); + qtest_quit(s); + } +@@ -75,7 +75,7 @@ static void ib700_reset(void) + qtest_irq_intercept_in(s, "ioapic"); + d = ib700_program_and_wait(s); + g_assert(!strcmp(qdict_get_str(d, "action"), "reset")); +- QDECREF(d); ++ qobject_unref(d); + qtest_qmp_eventwait(s, "RESET"); + qtest_quit(s); + } +@@ -89,7 +89,7 @@ static void ib700_shutdown(void) + qtest_irq_intercept_in(s, "ioapic"); + d = ib700_program_and_wait(s); + g_assert(!strcmp(qdict_get_str(d, "action"), "reset")); +- QDECREF(d); ++ qobject_unref(d); + qtest_qmp_eventwait(s, "SHUTDOWN"); + qtest_quit(s); + } +@@ -102,7 +102,7 @@ static void ib700_none(void) + qtest_irq_intercept_in(s, "ioapic"); + d = ib700_program_and_wait(s); + g_assert(!strcmp(qdict_get_str(d, "action"), "none")); +- QDECREF(d); ++ qobject_unref(d); + qtest_quit(s); + } + +diff --git a/util/keyval.c b/util/keyval.c +index 1c7351a..13def4a 100644 +--- a/util/keyval.c ++++ b/util/keyval.c +@@ -126,7 +126,7 @@ static int key_to_index(const char *key, const char **end) + * Else, fail because we have conflicting needs on how to map + * @key_in_cur. + * In any case, take over the reference to @value, i.e. if the caller +- * wants to hold on to a reference, it needs to QINCREF(). ++ * wants to hold on to a reference, it needs to qobject_ref(). + * Use @key up to @key_cursor to identify the key in error messages. + * On success, return the mapped value. + * On failure, store an error through @errp and return NULL. +@@ -143,7 +143,7 @@ static QObject *keyval_parse_put(QDict *cur, + if (qobject_type(old) != (value ? QTYPE_QSTRING : QTYPE_QDICT)) { + error_setg(errp, "Parameters '%.*s.*' used inconsistently", + (int)(key_cursor - key), key); +- QDECREF(value); ++ qobject_unref(value); + return NULL; + } + if (!value) { +@@ -375,10 +375,10 @@ static QObject *keyval_listify(QDict *cur, GSList *key_of_cur, Error **errp) + error_setg(errp, "Parameter '%s%d' missing", key, i); + g_free(key); + g_free(elt); +- QDECREF(list); ++ qobject_unref(list); + return NULL; + } +- qobject_incref(elt[i]); ++ qobject_ref(elt[i]); + qlist_append_obj(list, elt[i]); + } + +@@ -404,7 +404,7 @@ QDict *keyval_parse(const char *params, const char *implied_key, + while (*s) { + s = keyval_parse_one(qdict, s, implied_key, errp); + if (!s) { +- QDECREF(qdict); ++ qobject_unref(qdict); + return NULL; + } + implied_key = NULL; +@@ -412,7 +412,7 @@ QDict *keyval_parse(const char *params, const char *implied_key, + + listified = keyval_listify(qdict, NULL, errp); + if (!listified) { +- QDECREF(qdict); ++ qobject_unref(qdict); + return NULL; + } + assert(listified == QOBJECT(qdict)); +diff --git a/util/qemu-config.c b/util/qemu-config.c +index 20f7d14..14d8402 100644 +--- a/util/qemu-config.c ++++ b/util/qemu-config.c +@@ -562,8 +562,8 @@ static void config_parse_qdict_section(QDict *options, QemuOptsList *opts, + } + + out: +- QDECREF(subqdict); +- QDECREF(list); ++ qobject_unref(subqdict); ++ qobject_unref(list); + } + + void qemu_config_parse_qdict(QDict *options, QemuOptsList **lists, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qobject-Use-qobject_to-instead-of-type-cast.patch b/SOURCES/kvm-qobject-Use-qobject_to-instead-of-type-cast.patch new file mode 100644 index 0000000..aedd1a1 --- /dev/null +++ b/SOURCES/kvm-qobject-Use-qobject_to-instead-of-type-cast.patch @@ -0,0 +1,55 @@ +From 5dcc5026b76ccfe45ed4a4ed56c8c949ead76e98 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:08 +0200 +Subject: [PATCH 01/54] qobject: Use qobject_to() instead of type cast +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-2-armbru@redhat.com> +Patchwork-id: 80732 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 01/23] qobject: Use qobject_to() instead of type cast +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +The proper way to convert from (abstract) QObject to a (concrete) +subtype is qobject_to(). Look for offenders that type cast instead: + + $ git-grep '(Q[A-Z][a-z]* \*)' + hmp.c: qmp_device_add((QDict *)qdict, NULL, &err); + include/qapi/qmp/qobject.h: return (QObject *)obj; + qobject/qobject.c:static void (*qdestroy[QTYPE__MAX])(QObject *) = { + tests/check-qdict.c: dst = (QDict *)qdict_crumple(src, &error_abort); + +The first two cast away const, the third isn't a type cast. Fix the +fourth. + +Signed-off-by: Markus Armbruster +Message-Id: <20180426152805.8469-1-armbru@redhat.com> +Reviewed-by: Eric Blake +Reviewed-by: Marc-André Lureau +(cherry picked from commit 46cfbf13b06d551072ed17fbfca67c103edf814f) +Signed-off-by: Miroslav Rezanina +--- + tests/check-qdict.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/check-qdict.c b/tests/check-qdict.c +index 2e73c2f..08d4303 100644 +--- a/tests/check-qdict.c ++++ b/tests/check-qdict.c +@@ -657,7 +657,7 @@ static void qdict_crumple_test_empty(void) + + src = qdict_new(); + +- dst = (QDict *)qdict_crumple(src, &error_abort); ++ dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); + + g_assert_cmpint(qdict_size(dst), ==, 0); + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qobject-use-a-QObjectBase_-struct.patch b/SOURCES/kvm-qobject-use-a-QObjectBase_-struct.patch new file mode 100644 index 0000000..fced9cb --- /dev/null +++ b/SOURCES/kvm-qobject-use-a-QObjectBase_-struct.patch @@ -0,0 +1,254 @@ +From a05d912ac29700014b1d6a894240a62f883e4409 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:10 +0200 +Subject: [PATCH 03/54] qobject: use a QObjectBase_ struct +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-4-armbru@redhat.com> +Patchwork-id: 80742 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 03/23] qobject: use a QObjectBase_ struct +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +From: Marc-André Lureau + +By moving the base fields to a QObjectBase_, QObject can be a type +which also has a 'base' field. This allows writing a generic QOBJECT() +macro that will work with any QObject type, including QObject +itself. The container_of() macro ensures that the object to cast has a +QObjectBase_ base field, giving some type safety guarantees. QObject +must have no members but QObjectBase_ base, or else QOBJECT() breaks. + +QObjectBase_ is not a typedef and uses a trailing underscore to make +it obvious it is not for normal use and to avoid potential abuse. + +Signed-off-by: Marc-André Lureau +Reviewed-by: Eric Blake +Message-Id: <20180419150145.24795-3-marcandre.lureau@redhat.com> +Reviewed-by: Markus Armbruster +Signed-off-by: Markus Armbruster +(cherry picked from commit 3d3eacaeccaab718ea0e2ddaa578bfae9e311c59) +Signed-off-by: Miroslav Rezanina +--- + include/qapi/qmp/qbool.h | 2 +- + include/qapi/qmp/qdict.h | 2 +- + include/qapi/qmp/qlist.h | 2 +- + include/qapi/qmp/qnull.h | 2 +- + include/qapi/qmp/qnum.h | 2 +- + include/qapi/qmp/qobject.h | 31 ++++++++++++++++++++----------- + include/qapi/qmp/qstring.h | 2 +- + qobject/qobject.c | 12 ++++++------ + tests/check-qdict.c | 6 +++--- + 9 files changed, 35 insertions(+), 26 deletions(-) + +diff --git a/include/qapi/qmp/qbool.h b/include/qapi/qmp/qbool.h +index b9a44a1..5f61e38 100644 +--- a/include/qapi/qmp/qbool.h ++++ b/include/qapi/qmp/qbool.h +@@ -17,7 +17,7 @@ + #include "qapi/qmp/qobject.h" + + struct QBool { +- QObject base; ++ struct QObjectBase_ base; + bool value; + }; + +diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h +index 2cc3e90..921a28d 100644 +--- a/include/qapi/qmp/qdict.h ++++ b/include/qapi/qmp/qdict.h +@@ -25,7 +25,7 @@ typedef struct QDictEntry { + } QDictEntry; + + struct QDict { +- QObject base; ++ struct QObjectBase_ base; + size_t size; + QLIST_HEAD(,QDictEntry) table[QDICT_BUCKET_MAX]; + }; +diff --git a/include/qapi/qmp/qlist.h b/include/qapi/qmp/qlist.h +index 5c673ac..8d2c32c 100644 +--- a/include/qapi/qmp/qlist.h ++++ b/include/qapi/qmp/qlist.h +@@ -22,7 +22,7 @@ typedef struct QListEntry { + } QListEntry; + + struct QList { +- QObject base; ++ struct QObjectBase_ base; + QTAILQ_HEAD(,QListEntry) head; + }; + +diff --git a/include/qapi/qmp/qnull.h b/include/qapi/qmp/qnull.h +index c992ee2..e8ea2c3 100644 +--- a/include/qapi/qmp/qnull.h ++++ b/include/qapi/qmp/qnull.h +@@ -16,7 +16,7 @@ + #include "qapi/qmp/qobject.h" + + struct QNull { +- QObject base; ++ struct QObjectBase_ base; + }; + + extern QNull qnull_; +diff --git a/include/qapi/qmp/qnum.h b/include/qapi/qmp/qnum.h +index 3e47475..45bf02a 100644 +--- a/include/qapi/qmp/qnum.h ++++ b/include/qapi/qmp/qnum.h +@@ -45,7 +45,7 @@ typedef enum { + * convert under the hood. + */ + struct QNum { +- QObject base; ++ struct QObjectBase_ base; + QNumKind kind; + union { + int64_t i64; +diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h +index 5206ff9..a713c01 100644 +--- a/include/qapi/qmp/qobject.h ++++ b/include/qapi/qmp/qobject.h +@@ -34,13 +34,21 @@ + + #include "qapi/qapi-builtin-types.h" + +-struct QObject { ++/* Not for use outside include/qapi/qmp/ */ ++struct QObjectBase_ { + QType type; + size_t refcnt; + }; + +-/* Get the 'base' part of an object */ +-#define QOBJECT(obj) (&(obj)->base) ++/* this struct must have no other members than base */ ++struct QObject { ++ struct QObjectBase_ base; ++}; ++ ++#define QOBJECT(obj) ({ \ ++ typeof(obj) _obj = (obj); \ ++ _obj ? container_of(&(_obj)->base, QObject, base) : NULL; \ ++}) + + /* High-level interface for qobject_incref() */ + #define QINCREF(obj) \ +@@ -68,8 +76,8 @@ QEMU_BUILD_BUG_MSG(QTYPE__MAX != 7, + static inline void qobject_init(QObject *obj, QType type) + { + assert(QTYPE_NONE < type && type < QTYPE__MAX); +- obj->refcnt = 1; +- obj->type = type; ++ obj->base.refcnt = 1; ++ obj->base.type = type; + } + + /** +@@ -77,8 +85,9 @@ static inline void qobject_init(QObject *obj, QType type) + */ + static inline void qobject_incref(QObject *obj) + { +- if (obj) +- obj->refcnt++; ++ if (obj) { ++ obj->base.refcnt++; ++ } + } + + /** +@@ -101,8 +110,8 @@ void qobject_destroy(QObject *obj); + */ + static inline void qobject_decref(QObject *obj) + { +- assert(!obj || obj->refcnt); +- if (obj && --obj->refcnt == 0) { ++ assert(!obj || obj->base.refcnt); ++ if (obj && --obj->base.refcnt == 0) { + qobject_destroy(obj); + } + } +@@ -112,8 +121,8 @@ static inline void qobject_decref(QObject *obj) + */ + static inline QType qobject_type(const QObject *obj) + { +- assert(QTYPE_NONE < obj->type && obj->type < QTYPE__MAX); +- return obj->type; ++ assert(QTYPE_NONE < obj->base.type && obj->base.type < QTYPE__MAX); ++ return obj->base.type; + } + + /** +diff --git a/include/qapi/qmp/qstring.h b/include/qapi/qmp/qstring.h +index 30ae260..b3b3d44 100644 +--- a/include/qapi/qmp/qstring.h ++++ b/include/qapi/qmp/qstring.h +@@ -16,7 +16,7 @@ + #include "qapi/qmp/qobject.h" + + struct QString { +- QObject base; ++ struct QObjectBase_ base; + char *string; + size_t length; + size_t capacity; +diff --git a/qobject/qobject.c b/qobject/qobject.c +index 87649c5..cf4b7e2 100644 +--- a/qobject/qobject.c ++++ b/qobject/qobject.c +@@ -37,9 +37,9 @@ static void (*qdestroy[QTYPE__MAX])(QObject *) = { + + void qobject_destroy(QObject *obj) + { +- assert(!obj->refcnt); +- assert(QTYPE_QNULL < obj->type && obj->type < QTYPE__MAX); +- qdestroy[obj->type](obj); ++ assert(!obj->base.refcnt); ++ assert(QTYPE_QNULL < obj->base.type && obj->base.type < QTYPE__MAX); ++ qdestroy[obj->base.type](obj); + } + + +@@ -62,11 +62,11 @@ bool qobject_is_equal(const QObject *x, const QObject *y) + return true; + } + +- if (!x || !y || x->type != y->type) { ++ if (!x || !y || x->base.type != y->base.type) { + return false; + } + +- assert(QTYPE_NONE < x->type && x->type < QTYPE__MAX); ++ assert(QTYPE_NONE < x->base.type && x->base.type < QTYPE__MAX); + +- return qis_equal[x->type](x, y); ++ return qis_equal[x->base.type](x, y); + } +diff --git a/tests/check-qdict.c b/tests/check-qdict.c +index 08d4303..07bb8f4 100644 +--- a/tests/check-qdict.c ++++ b/tests/check-qdict.c +@@ -570,11 +570,11 @@ static void qdict_join_test(void) + } + + /* Check the references */ +- g_assert(qdict_get(dict1, "foo")->refcnt == 1); +- g_assert(qdict_get(dict1, "bar")->refcnt == 1); ++ g_assert(qdict_get(dict1, "foo")->base.refcnt == 1); ++ g_assert(qdict_get(dict1, "bar")->base.refcnt == 1); + + if (!overwrite) { +- g_assert(qdict_get(dict2, "foo")->refcnt == 1); ++ g_assert(qdict_get(dict2, "foo")->base.refcnt == 1); + } + + /* Clean up */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qom-Clean-up-error-reporting-in-user_creatable_add_o.patch b/SOURCES/kvm-qom-Clean-up-error-reporting-in-user_creatable_add_o.patch new file mode 100644 index 0000000..72cf31c --- /dev/null +++ b/SOURCES/kvm-qom-Clean-up-error-reporting-in-user_creatable_add_o.patch @@ -0,0 +1,138 @@ +From 2070eac10c75c262e8f803b3654f512c55c261c1 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 27 Mar 2019 17:22:34 +0100 +Subject: [PATCH 095/163] qom: Clean up error reporting in + user_creatable_add_opts_foreach() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190327172308.31077-22-jsnow@redhat.com> +Patchwork-id: 85216 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 21/55] qom: Clean up error reporting in user_creatable_add_opts_foreach() +Bugzilla: 1691009 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Markus Armbruster + +Calling error_report() in a function that takes an Error ** argument +is suspicious. user_creatable_add_opts_foreach() does that, and then +fails without setting an error. Its caller main(), via +qemu_opts_foreach(), is fine with it, but clean it up anyway. + +Cc: Daniel P. Berrangé +Signed-off-by: Markus Armbruster +Reviewed-by: Eric Blake +Reviewed-by: Marc-André Lureau +Message-Id: <20181017082702.5581-20-armbru@redhat.com> +(cherry picked from commit 7e1e0c11127bde81cff260fc6859690435c509d6) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + qemu-io.c | 8 +++----- + qemu-nbd.c | 8 +++----- + qom/object_interfaces.c | 4 +--- + vl.c | 16 ++++++---------- + 4 files changed, 13 insertions(+), 23 deletions(-) + +diff --git a/qemu-io.c b/qemu-io.c +index 13829f5..6df7731 100644 +--- a/qemu-io.c ++++ b/qemu-io.c +@@ -620,11 +620,9 @@ int main(int argc, char **argv) + exit(1); + } + +- if (qemu_opts_foreach(&qemu_object_opts, +- user_creatable_add_opts_foreach, +- NULL, NULL)) { +- exit(1); +- } ++ qemu_opts_foreach(&qemu_object_opts, ++ user_creatable_add_opts_foreach, ++ NULL, &error_fatal); + + if (!trace_init_backends()) { + exit(1); +diff --git a/qemu-nbd.c b/qemu-nbd.c +index 0c92f62..51b55f2 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -781,11 +781,9 @@ int main(int argc, char **argv) + exit(EXIT_FAILURE); + } + +- if (qemu_opts_foreach(&qemu_object_opts, +- user_creatable_add_opts_foreach, +- NULL, NULL)) { +- exit(EXIT_FAILURE); +- } ++ qemu_opts_foreach(&qemu_object_opts, ++ user_creatable_add_opts_foreach, ++ NULL, &error_fatal); + + if (!trace_init_backends()) { + exit(1); +diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c +index 980ffc2..7491c69 100644 +--- a/qom/object_interfaces.c ++++ b/qom/object_interfaces.c +@@ -149,7 +149,6 @@ int user_creatable_add_opts_foreach(void *opaque, QemuOpts *opts, Error **errp) + { + bool (*type_predicate)(const char *) = opaque; + Object *obj = NULL; +- Error *err = NULL; + const char *type; + + type = qemu_opt_get(opts, "qom-type"); +@@ -158,9 +157,8 @@ int user_creatable_add_opts_foreach(void *opaque, QemuOpts *opts, Error **errp) + return 0; + } + +- obj = user_creatable_add_opts(opts, &err); ++ obj = user_creatable_add_opts(opts, errp); + if (!obj) { +- error_report_err(err); + return -1; + } + object_unref(obj); +diff --git a/vl.c b/vl.c +index dfe261d..92a98ab 100644 +--- a/vl.c ++++ b/vl.c +@@ -4407,11 +4407,9 @@ int main(int argc, char **argv, char **envp) + page_size_init(); + socket_init(); + +- if (qemu_opts_foreach(qemu_find_opts("object"), +- user_creatable_add_opts_foreach, +- object_create_initial, NULL)) { +- exit(1); +- } ++ qemu_opts_foreach(qemu_find_opts("object"), ++ user_creatable_add_opts_foreach, ++ object_create_initial, &error_fatal); + + if (qemu_opts_foreach(qemu_find_opts("chardev"), + chardev_init_func, NULL, NULL)) { +@@ -4537,11 +4535,9 @@ int main(int argc, char **argv, char **envp) + exit(1); + } + +- if (qemu_opts_foreach(qemu_find_opts("object"), +- user_creatable_add_opts_foreach, +- object_create_delayed, NULL)) { +- exit(1); +- } ++ qemu_opts_foreach(qemu_find_opts("object"), ++ user_creatable_add_opts_foreach, ++ object_create_delayed, &error_fatal); + + if (tpm_init() < 0) { + exit(1); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qxl-check-release-info-object.patch b/SOURCES/kvm-qxl-check-release-info-object.patch new file mode 100644 index 0000000..a8173a0 --- /dev/null +++ b/SOURCES/kvm-qxl-check-release-info-object.patch @@ -0,0 +1,50 @@ +From 2e9c7edfee60b61e3b6b0c0eba29cb4df6d47f85 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= +Date: Wed, 19 Jun 2019 16:05:49 +0200 +Subject: [PATCH 18/23] qxl: check release info object +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Philippe Mathieu-Daudé +Message-id: <20190619160549.15731-2-philmd@redhat.com> +Patchwork-id: 88734 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/1] qxl: check release info object +Bugzilla: 1712704 +RH-Acked-by: Gerd Hoffmann +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Stefan Hajnoczi + +From: Prasad J Pandit + +When releasing spice resources in release_resource() routine, +if release info object 'ext.info' is null, it leads to null +pointer dereference. Add check to avoid it. + +Reported-by: Bugs SysSec +Signed-off-by: Prasad J Pandit +Message-id: 20190425063534.32747-1-ppandit@redhat.com +Signed-off-by: Gerd Hoffmann +(cherry picked from commit d52680fc932efb8a2f334cc6993e705ed1e31e99) +Signed-off-by: Miroslav Rezanina +--- + hw/display/qxl.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/hw/display/qxl.c b/hw/display/qxl.c +index a71714c..f757d2e 100644 +--- a/hw/display/qxl.c ++++ b/hw/display/qxl.c +@@ -763,6 +763,9 @@ static void interface_release_resource(QXLInstance *sin, + QXLReleaseRing *ring; + uint64_t *item, id; + ++ if (!ext.info) { ++ return; ++ } + if (ext.group_id == MEMSLOT_GROUP_HOST) { + /* host group -> vga mode update request */ + QXLCommandExt *cmdext = (void *)(intptr_t)(ext.info->id); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-qxl-fix-local-renderer-crash.patch b/SOURCES/kvm-qxl-fix-local-renderer-crash.patch new file mode 100644 index 0000000..b0c90fc --- /dev/null +++ b/SOURCES/kvm-qxl-fix-local-renderer-crash.patch @@ -0,0 +1,52 @@ +From e79d4d03368f7346249033e0f5ad2732228852ca Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Wed, 13 Jun 2018 13:15:57 +0200 +Subject: [PATCH 02/89] qxl: fix local renderer crash +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Gerd Hoffmann +Message-id: <20180613131557.12255-2-kraxel@redhat.com> +Patchwork-id: 80662 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/1] qxl: fix local renderer crash +Bugzilla: 1567733 +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Marc-André Lureau +RH-Acked-by: Laszlo Ersek + +Make sure we only ask the spice local renderer for display updates in +case we have a valid primary surface. Without that spice is confused +and throws errors in case a display update request (triggered by +screendump for example) happens in parallel to a mode switch and hits +the race window where the old primary surface is gone and the new isn't +establisted yet. + +Cc: qemu-stable@nongnu.org +Fixes: https://bugzilla.redhat.com//show_bug.cgi?id=1567733 +Signed-off-by: Gerd Hoffmann +Reviewed-by: Marc-André Lureau +Message-id: 20180427115528.345-1-kraxel@redhat.com +(cherry picked from commit 5bd5c27c7d284d01477c5cc022ce22438c46bf9f) +Signed-off-by: Miroslav Rezanina +--- + hw/display/qxl-render.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c +index e7ac4f8..c62b9a5 100644 +--- a/hw/display/qxl-render.c ++++ b/hw/display/qxl-render.c +@@ -169,7 +169,8 @@ void qxl_render_update(PCIQXLDevice *qxl) + + qemu_mutex_lock(&qxl->ssd.lock); + +- if (!runstate_is_running() || !qxl->guest_primary.commands) { ++ if (!runstate_is_running() || !qxl->guest_primary.commands || ++ qxl->mode == QXL_MODE_UNDEFINED) { + qxl_render_update_area_unlocked(qxl); + qemu_mutex_unlock(&qxl->ssd.lock); + return; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-r2d-Fix-flash-memory-size-sector-size-width-device-I.patch b/SOURCES/kvm-r2d-Fix-flash-memory-size-sector-size-width-device-I.patch new file mode 100644 index 0000000..5f711b7 --- /dev/null +++ b/SOURCES/kvm-r2d-Fix-flash-memory-size-sector-size-width-device-I.patch @@ -0,0 +1,86 @@ +From e164d03a365ec6457a09acefdda7e6fe50e068af Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:00 +0200 +Subject: [PATCH 33/53] r2d: Fix flash memory size, sector size, width, device + ID +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-12-armbru@redhat.com> +Patchwork-id: 87987 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 11/31] r2d: Fix flash memory size, sector size, width, device ID +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +pflash_cfi02_register() takes a size in bytes, a block size in bytes +and a number of blocks. r2d_init() passes FLASH_SIZE, 16 * KiB, +FLASH_SIZE >> 16. Does not compute: size doesn't match block size * +number of blocks. The latter happens to win: FLASH_SIZE / 4, +i.e. 8MiB. + +The best information we have on the physical hardware lists a Cypress +S29PL127J60TFI130 128MiBit NOR flash addressable in words of 16 bits, +in sectors of 4 and 32 Kibiwords. We don't model multiple sector +sizes. + +Fix the flash size from 8 to 16MiB, and adjust the sector size from 16 +to 64KiB. Fix the width from 4 to 2. While there, supply the real +device IDs 0x0001, 0x227e, 0x2220, 0x2200 instead of zeros. + +Cc: Magnus Damm +Signed-off-by: Markus Armbruster +Reviewed-by: Philippe Mathieu-Daudé +Message-Id: <20190308094610.21210-10-armbru@redhat.com> +Tested-by: Philippe Mathieu-Daudé +(cherry picked from commit 8468713412b1eb0d24d605bf97d159a9b01d4b02) +[Trivial conflict in hw/sh4/r2d.c due to lack of commit ab3dd749241] + +Signed-off-by: Miroslav Rezanina +--- + hw/sh4/r2d.c | 16 ++++++++++++---- + 1 file changed, 12 insertions(+), 4 deletions(-) + +diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c +index 458ed83..befb582 100644 +--- a/hw/sh4/r2d.c ++++ b/hw/sh4/r2d.c +@@ -44,7 +44,7 @@ + #include "exec/address-spaces.h" + + #define FLASH_BASE 0x00000000 +-#define FLASH_SIZE 0x02000000 ++#define FLASH_SIZE (16 * MiB) + + #define SDRAM_BASE 0x0c000000 /* Physical location of SDRAM: Area 3 */ + #define SDRAM_SIZE 0x04000000 +@@ -288,12 +288,20 @@ static void r2d_init(MachineState *machine) + sysbus_mmio_map(busdev, 1, 0x1400080c); + mmio_ide_init_drives(dev, dinfo, NULL); + +- /* onboard flash memory */ ++ /* ++ * Onboard flash memory ++ * According to the old board user document in Japanese (under ++ * NDA) what is referred to as FROM (Area0) is connected via a ++ * 32-bit bus and CS0 to CN8. The docs mention a Cypress ++ * S29PL127J60TFI130 chipsset. Per the 'S29PL-J 002-00615 ++ * Rev. *E' datasheet, it is a 128Mbit NOR parallel flash ++ * addressable in words of 16bit. ++ */ + dinfo = drive_get(IF_PFLASH, 0, 0); + pflash_cfi02_register(0x0, NULL, "r2d.flash", FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, +- (16 * 1024), FLASH_SIZE >> 16, +- 1, 4, 0x0000, 0x0000, 0x0000, 0x0000, ++ 64 * KiB, FLASH_SIZE >> 16, ++ 1, 2, 0x0001, 0x227e, 0x2220, 0x2200, + 0x555, 0x2aa, 0); + + /* NIC: rtl8139 on-board, and 2 slots. */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-raw-Check-byte-range-uniformly.patch b/SOURCES/kvm-raw-Check-byte-range-uniformly.patch new file mode 100644 index 0000000..72c1689 --- /dev/null +++ b/SOURCES/kvm-raw-Check-byte-range-uniformly.patch @@ -0,0 +1,159 @@ +From f0714015bbe871d1f0c1fe9d8b8c7bd2afc2a0a0 Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Fri, 29 Jun 2018 06:11:42 +0200 +Subject: [PATCH 38/57] raw: Check byte range uniformly + +RH-Author: Fam Zheng +Message-id: <20180629061153.12687-3-famz@redhat.com> +Patchwork-id: 81152 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 02/13] raw: Check byte range uniformly +Bugzilla: 1482537 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Kevin Wolf + +We don't verify the request range against s->size in the I/O callbacks +except for raw_co_pwritev. This is inconsistent (especially for +raw_co_pwrite_zeroes and raw_co_pdiscard), so fix them, in the meanwhile +make the helper reusable by the coming new callbacks. + +Note that in most cases the block layer already verifies the request +byte range against our reported image length, before invoking the driver +callbacks. The exception is during image creating, after +blk_set_allow_write_beyond_eof(blk, true) is called. But in that case, +the requests are not directly from the user or guest. So there is no +visible behavior change in adding the check code. + +The int64_t -> uint64_t inconsistency, as shown by the type casting, is +pre-existing due to the interface. + +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Eric Blake +Signed-off-by: Fam Zheng +Message-id: 20180601092648.24614-3-famz@redhat.com +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit 384455385248762e74a080978f18f0c8f74757fe) +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + block/raw-format.c | 64 +++++++++++++++++++++++++++++++++--------------------- + 1 file changed, 39 insertions(+), 25 deletions(-) + +diff --git a/block/raw-format.c b/block/raw-format.c +index fe33693..b69a067 100644 +--- a/block/raw-format.c ++++ b/block/raw-format.c +@@ -167,16 +167,37 @@ static void raw_reopen_abort(BDRVReopenState *state) + state->opaque = NULL; + } + ++/* Check and adjust the offset, against 'offset' and 'size' options. */ ++static inline int raw_adjust_offset(BlockDriverState *bs, uint64_t *offset, ++ uint64_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 raw_co_preadv(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, QEMUIOVector *qiov, + int flags) + { +- BDRVRawState *s = bs->opaque; ++ int ret; + +- if (offset > UINT64_MAX - s->offset) { +- return -EINVAL; ++ ret = raw_adjust_offset(bs, &offset, bytes, false); ++ if (ret) { ++ return ret; + } +- offset += s->offset; + + BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); + return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); +@@ -186,23 +207,11 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, QEMUIOVector *qiov, + int flags) + { +- BDRVRawState *s = bs->opaque; + void *buf = NULL; + BlockDriver *drv; + QEMUIOVector local_qiov; + int ret; + +- if (s->has_size && (offset > s->size || bytes > (s->size - offset))) { +- /* There's not enough space for the data. Don't write anything and just +- * fail to prevent leaking out of the size specified in options. */ +- return -ENOSPC; +- } +- +- if (offset > UINT64_MAX - s->offset) { +- ret = -EINVAL; +- goto fail; +- } +- + 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 +@@ -237,7 +246,10 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset, + qiov = &local_qiov; + } + +- offset += s->offset; ++ ret = raw_adjust_offset(bs, &offset, bytes, true); ++ if (ret) { ++ goto fail; ++ } + + BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); + ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); +@@ -267,22 +279,24 @@ static int coroutine_fn raw_co_pwrite_zeroes(BlockDriverState *bs, + int64_t offset, int bytes, + BdrvRequestFlags flags) + { +- BDRVRawState *s = bs->opaque; +- if (offset > UINT64_MAX - s->offset) { +- return -EINVAL; ++ int ret; ++ ++ ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true); ++ if (ret) { ++ return ret; + } +- offset += s->offset; + return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); + } + + static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs, + int64_t offset, int bytes) + { +- BDRVRawState *s = bs->opaque; +- if (offset > UINT64_MAX - s->offset) { +- return -EINVAL; ++ int ret; ++ ++ ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true); ++ if (ret) { ++ return ret; + } +- offset += s->offset; + return bdrv_co_pdiscard(bs->file->bs, offset, bytes); + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-raw-Implement-copy-offloading.patch b/SOURCES/kvm-raw-Implement-copy-offloading.patch new file mode 100644 index 0000000..9062abf --- /dev/null +++ b/SOURCES/kvm-raw-Implement-copy-offloading.patch @@ -0,0 +1,80 @@ +From 5d79960f00eebe7a8a8bcd423f39c4e72f3366d1 Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Fri, 29 Jun 2018 06:11:43 +0200 +Subject: [PATCH 39/57] raw: Implement copy offloading + +RH-Author: Fam Zheng +Message-id: <20180629061153.12687-4-famz@redhat.com> +Patchwork-id: 81155 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 03/13] raw: Implement copy offloading +Bugzilla: 1482537 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Kevin Wolf + +Just pass down to ->file. + +Signed-off-by: Fam Zheng +Reviewed-by: Stefan Hajnoczi +Message-id: 20180601092648.24614-4-famz@redhat.com +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit 72d219e2f916adeec9845473d239571a267f3314) +Signed-off-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + block/raw-format.c | 32 ++++++++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +diff --git a/block/raw-format.c b/block/raw-format.c +index b69a067..f2e468d 100644 +--- a/block/raw-format.c ++++ b/block/raw-format.c +@@ -497,6 +497,36 @@ static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) + return bdrv_probe_geometry(bs->file->bs, geo); + } + ++static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs, ++ BdrvChild *src, uint64_t src_offset, ++ BdrvChild *dst, uint64_t dst_offset, ++ uint64_t bytes, BdrvRequestFlags 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, flags); ++} ++ ++static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, ++ BdrvChild *src, uint64_t src_offset, ++ BdrvChild *dst, uint64_t dst_offset, ++ uint64_t bytes, BdrvRequestFlags 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, ++ flags); ++} ++ + BlockDriver bdrv_raw = { + .format_name = "raw", + .instance_size = sizeof(BDRVRawState), +@@ -513,6 +543,8 @@ BlockDriver bdrv_raw = { + .bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes, + .bdrv_co_pdiscard = &raw_co_pdiscard, + .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_truncate = &raw_truncate, + .bdrv_getlength = &raw_getlength, + .has_variable_length = true, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-rbd-Close-image-in-qemu_rbd_open-error-path.patch b/SOURCES/kvm-rbd-Close-image-in-qemu_rbd_open-error-path.patch new file mode 100644 index 0000000..47f3de4 --- /dev/null +++ b/SOURCES/kvm-rbd-Close-image-in-qemu_rbd_open-error-path.patch @@ -0,0 +1,46 @@ +From 65f966b512842ad7654eb80a4b98cbfff7437751 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 23 Nov 2018 10:41:45 +0100 +Subject: [PATCH 04/34] rbd: Close image in qemu_rbd_open() error path + +RH-Author: Kevin Wolf +Message-id: <20181123104154.13541-4-kwolf@redhat.com> +Patchwork-id: 83113 +O-Subject: [RHEL-7.7/7.6.z qemu-kvm-rhev PATCH v2 03/12] rbd: Close image in qemu_rbd_open() error path +Bugzilla: 1623986 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina +RH-Acked-by: John Snow + +Commit e2b8247a322 introduced an error path in qemu_rbd_open() after +calling rbd_open(), but neglected to close the image again in this error +path. The error path should contain everything that the regular close +function qemu_rbd_close() contains. + +This adds the missing rbd_close() call. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +(cherry picked from commit a51b9c4862c29f427931f45ee1d39ac1663ba859) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/rbd.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/block/rbd.c b/block/rbd.c +index 8f81fbc..8ce68c8 100644 +--- a/block/rbd.c ++++ b/block/rbd.c +@@ -779,6 +779,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, + "automatically marking the image read-only."); + r = bdrv_set_read_only(bs, true, &local_err); + if (r < 0) { ++ rbd_close(s->image); + error_propagate(errp, local_err); + goto failed_open; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-rbd-Drop-deprecated-drive-parameter-filename.patch b/SOURCES/kvm-rbd-Drop-deprecated-drive-parameter-filename.patch new file mode 100644 index 0000000..05ce0ee --- /dev/null +++ b/SOURCES/kvm-rbd-Drop-deprecated-drive-parameter-filename.patch @@ -0,0 +1,59 @@ +From 61a2510cbf901b95759402df893e63be02b510d1 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:13 +0200 +Subject: [PATCH 06/54] rbd: Drop deprecated -drive parameter "filename" + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-7-armbru@redhat.com> +Patchwork-id: 80735 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 06/23] rbd: Drop deprecated -drive parameter "filename" +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Parameter "filename" is deprecated since commit 91589d9e5ca, v2.10.0. +Time to get rid of it. + +Signed-off-by: Markus Armbruster +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit bb9f762ff35459e34630d731a5a91c02602150d1) +Signed-off-by: Miroslav Rezanina +--- + block/rbd.c | 16 ---------------- + 1 file changed, 16 deletions(-) + +diff --git a/block/rbd.c b/block/rbd.c +index a14b42f..2842c0e 100644 +--- a/block/rbd.c ++++ b/block/rbd.c +@@ -625,25 +625,9 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, + QObject *crumpled = NULL; + const QDictEntry *e; + Error *local_err = NULL; +- const char *filename; + char *keypairs, *secretid; + int r; + +- /* If we are given a filename, parse the filename, with precedence given to +- * filename encoded options */ +- filename = qdict_get_try_str(options, "filename"); +- if (filename) { +- warn_report("'filename' option specified. " +- "This is an unsupported option, and may be deprecated " +- "in the future"); +- qemu_rbd_parse_filename(filename, options, &local_err); +- qdict_del(options, "filename"); +- if (local_err) { +- error_propagate(errp, local_err); +- return -EINVAL; +- } +- } +- + keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs")); + if (keypairs) { + qdict_del(options, "=keyvalue-pairs"); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-rbd-New-parameter-auth-client-required.patch b/SOURCES/kvm-rbd-New-parameter-auth-client-required.patch new file mode 100644 index 0000000..b95bd48 --- /dev/null +++ b/SOURCES/kvm-rbd-New-parameter-auth-client-required.patch @@ -0,0 +1,187 @@ +From 92e418ec44b35eedff728cc692a09bd001d0762e Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:29 +0200 +Subject: [PATCH 22/54] rbd: New parameter auth-client-required + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-23-armbru@redhat.com> +Patchwork-id: 80731 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 22/23] rbd: New parameter auth-client-required +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Parameter auth-client-required lets you configure authentication +methods. We tried to provide that in v2.9.0, but backed out due to +interface design doubts (commit 464444fcc16). + +This commit is similar to what we backed out, but simpler: we use a +list of enumeration values instead of a list of objects with a member +of enumeration type. + +Let's review our reasons for backing out the first try, as stated in +the commit message: + + * The implementation uses deprecated rados_conf_set() key + "auth_supported". No biggie. + +Fixed: we use "auth-client-required". + + * The implementation makes -drive silently ignore invalid parameters + "auth" and "auth-supported.*.X" where X isn't "auth". Fixable (in + fact I'm going to fix similar bugs around parameter server), so + again no biggie. + +That fix is commit 2836284db60. This commit doesn't bring the bugs +back. + + * BlockdevOptionsRbd member @password-secret applies only to + authentication method cephx. Should it be a variant member of + RbdAuthMethod? + +We've had time to ponder, and we decided to stick to the way Ceph +configuration works: the key configured separately, and silently +ignored if the authentication method doesn't use it. + + * BlockdevOptionsRbd member @user could apply to both methods cephx + and none, but I'm not sure it's actually used with none. If it + isn't, should it be a variant member of RbdAuthMethod? + +Likewise. + + * The client offers a *set* of authentication methods, not a list. + Should the methods be optional members of BlockdevOptionsRbd instead + of members of list @auth-supported? The latter begs the question + what multiple entries for the same method mean. Trivial question + now that RbdAuthMethod contains nothing but @type, but less so when + RbdAuthMethod acquires other members, such the ones discussed above. + +Again, we decided to stick to the way Ceph configuration works, except +we make auth-client-required a list of enumeration values instead of a +string containing keywords separated by delimiters. + + * How BlockdevOptionsRbd member @auth-supported interacts with + settings from a configuration file specified with @conf is + undocumented. I suspect it's untested, too. + +Not actually true, the documentation for @conf says "Values in the +configuration file will be overridden by options specified via QAPI", +and we've tested this. + +Signed-off-by: Markus Armbruster +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit a3699de4dde82bc76b33a83798a9da82c2336cce) +Signed-off-by: Miroslav Rezanina +--- + block/rbd.c | 42 ++++++++++++++++++++++++++++++++---------- + qapi/block-core.json | 13 +++++++++++++ + 2 files changed, 45 insertions(+), 10 deletions(-) + +diff --git a/block/rbd.c b/block/rbd.c +index c834d72..9c0903f 100644 +--- a/block/rbd.c ++++ b/block/rbd.c +@@ -233,20 +233,42 @@ done: + + + static int qemu_rbd_set_auth(rados_t cluster, const char *secretid, ++ BlockdevOptionsRbd *opts, + Error **errp) + { +- if (secretid == 0) { +- return 0; +- } ++ char *acr; ++ int r; ++ GString *accu; ++ RbdAuthModeList *auth; ++ ++ if (secretid) { ++ gchar *secret = qcrypto_secret_lookup_as_base64(secretid, ++ errp); ++ if (!secret) { ++ return -1; ++ } + +- gchar *secret = qcrypto_secret_lookup_as_base64(secretid, +- errp); +- if (!secret) { +- return -1; ++ rados_conf_set(cluster, "key", secret); ++ g_free(secret); + } + +- rados_conf_set(cluster, "key", secret); +- g_free(secret); ++ if (opts->has_auth_client_required) { ++ accu = g_string_new(""); ++ for (auth = opts->auth_client_required; auth; auth = auth->next) { ++ if (accu->str[0]) { ++ g_string_append_c(accu, ';'); ++ } ++ g_string_append(accu, RbdAuthMode_str(auth->value)); ++ } ++ acr = g_string_free(accu, FALSE); ++ r = rados_conf_set(cluster, "auth_client_required", acr); ++ g_free(acr); ++ if (r < 0) { ++ error_setg_errno(errp, -r, ++ "Could not set 'auth_client_required'"); ++ return r; ++ } ++ } + + return 0; + } +@@ -578,7 +600,7 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, + } + } + +- if (qemu_rbd_set_auth(*cluster, secretid, errp) < 0) { ++ if (qemu_rbd_set_auth(*cluster, secretid, opts, errp) < 0) { + r = -EIO; + goto failed_shutdown; + } +diff --git a/qapi/block-core.json b/qapi/block-core.json +index b38d5d6..28001fb 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -3170,6 +3170,14 @@ + + + ## ++# @RbdAuthMode: ++# ++# Since: 3.0 ++## ++{ 'enum': 'RbdAuthMode', ++ 'data': [ 'cephx', 'none' ] } ++ ++## + # @BlockdevOptionsRbd: + # + # @pool: Ceph pool name. +@@ -3184,6 +3192,10 @@ + # + # @user: Ceph id name. + # ++# @auth-client-required: Acceptable authentication modes. ++# This maps to Ceph configuration option ++# "auth_client_required". (Since 3.0) ++# + # @server: Monitor host address and port. This maps + # to the "mon_host" Ceph option. + # +@@ -3195,6 +3207,7 @@ + '*conf': 'str', + '*snapshot': 'str', + '*user': 'str', ++ '*auth-client-required': ['RbdAuthMode'], + '*server': ['InetSocketAddressBase'] } } + + ## +-- +1.8.3.1 + diff --git a/SOURCES/kvm-rbd-New-parameter-key-secret.patch b/SOURCES/kvm-rbd-New-parameter-key-secret.patch new file mode 100644 index 0000000..6f027c0 --- /dev/null +++ b/SOURCES/kvm-rbd-New-parameter-key-secret.patch @@ -0,0 +1,162 @@ +From c4b9fcd4bf4179a565e2f10d4c93ef6801007270 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Mon, 18 Jun 2018 08:43:30 +0200 +Subject: [PATCH 23/54] rbd: New parameter key-secret + +RH-Author: Markus Armbruster +Message-id: <20180618084330.30009-24-armbru@redhat.com> +Patchwork-id: 80727 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 23/23] rbd: New parameter key-secret +Bugzilla: 1557995 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +Legacy -drive supports "password-secret" parameter that isn't +available with -blockdev / blockdev-add. That's because we backed out +our first try to provide it there due to interface design doubts, in +commit 577d8c9a811, v2.9.0. + +This is the second try. It brings back the parameter, except it's +named "key-secret" now. + +Let's review our reasons for backing out the first try, as stated in +the commit message: + + * BlockdevOptionsRbd member @password-secret isn't actually a + password, it's a key generated by Ceph. + +Addressed by the rename. + + * We're not sure where member @password-secret belongs (see the + previous commit). + +See previous commit. + + * How @password-secret interacts with settings from a configuration + file specified with @conf is undocumented. + +Not actually true, the documentation for @conf says "Values in the +configuration file will be overridden by options specified via QAPI", +and we've tested this. + +Signed-off-by: Markus Armbruster +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit d083f954a95d37b460df0c2fbfe46ad7eb207b10) +[Conflict due to lack of commit e8e16d4baff "rbd: Switch to byte-based +callbacks" trivially resolved] + +Signed-off-by: Miroslav Rezanina +--- + block/rbd.c | 41 +++++++++++++++++++++++++---------------- + qapi/block-core.json | 6 ++++++ + 2 files changed, 31 insertions(+), 16 deletions(-) + +diff --git a/block/rbd.c b/block/rbd.c +index 9c0903f..3242bcd 100644 +--- a/block/rbd.c ++++ b/block/rbd.c +@@ -232,24 +232,25 @@ done: + } + + +-static int qemu_rbd_set_auth(rados_t cluster, const char *secretid, +- BlockdevOptionsRbd *opts, ++static int qemu_rbd_set_auth(rados_t cluster, BlockdevOptionsRbd *opts, + Error **errp) + { +- char *acr; ++ char *key, *acr; + int r; + GString *accu; + RbdAuthModeList *auth; + +- if (secretid) { +- gchar *secret = qcrypto_secret_lookup_as_base64(secretid, +- errp); +- if (!secret) { +- return -1; ++ if (opts->key_secret) { ++ key = qcrypto_secret_lookup_as_base64(opts->key_secret, errp); ++ if (!key) { ++ return -EIO; ++ } ++ r = rados_conf_set(cluster, "key", key); ++ g_free(key); ++ if (r < 0) { ++ error_setg_errno(errp, -r, "Could not set 'key'"); ++ return r; + } +- +- rados_conf_set(cluster, "key", secret); +- g_free(secret); + } + + if (opts->has_auth_client_required) { +@@ -360,9 +361,7 @@ static QemuOptsList runtime_opts = { + }, + }; + +-/* FIXME Deprecate and remove keypairs or make it available in QMP. +- * password_secret should eventually be configurable in opts->location. Support +- * for it in .bdrv_open will make it work here as well. */ ++/* FIXME Deprecate and remove keypairs or make it available in QMP. */ + static int qemu_rbd_do_create(BlockdevCreateOptions *options, + const char *keypairs, const char *password_secret, + Error **errp) +@@ -568,6 +567,16 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, + Error *local_err = NULL; + int r; + ++ if (secretid) { ++ if (opts->key_secret) { ++ error_setg(errp, ++ "Legacy 'password-secret' clashes with 'key-secret'"); ++ return -EINVAL; ++ } ++ opts->key_secret = g_strdup(secretid); ++ opts->has_key_secret = true; ++ } ++ + mon_host = qemu_rbd_mon_host(opts, &local_err); + if (local_err) { + error_propagate(errp, local_err); +@@ -600,8 +609,8 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, + } + } + +- if (qemu_rbd_set_auth(*cluster, secretid, opts, errp) < 0) { +- r = -EIO; ++ r = qemu_rbd_set_auth(*cluster, opts, errp); ++ if (r < 0) { + goto failed_shutdown; + } + +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 28001fb..46469be 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -3196,6 +3196,11 @@ + # This maps to Ceph configuration option + # "auth_client_required". (Since 3.0) + # ++# @key-secret: ID of a QCryptoSecret object providing a key ++# for cephx authentication. ++# This maps to Ceph configuration option ++# "key". (Since 3.0) ++# + # @server: Monitor host address and port. This maps + # to the "mon_host" Ceph option. + # +@@ -3208,6 +3213,7 @@ + '*snapshot': 'str', + '*user': 'str', + '*auth-client-required': ['RbdAuthMode'], ++ '*key-secret': 'str', + '*server': ['InetSocketAddressBase'] } } + + ## +-- +1.8.3.1 + diff --git a/SOURCES/kvm-redhat-define-HW_COMPAT_RHEL7_5.patch b/SOURCES/kvm-redhat-define-HW_COMPAT_RHEL7_5.patch new file mode 100644 index 0000000..030f15d --- /dev/null +++ b/SOURCES/kvm-redhat-define-HW_COMPAT_RHEL7_5.patch @@ -0,0 +1,61 @@ +From 49805edee0bec708c9f59e87bd17edb03acca9f6 Mon Sep 17 00:00:00 2001 +From: Laurent Vivier +Date: Fri, 1 Jun 2018 08:30:38 +0200 +Subject: [PATCH 3/8] redhat: define HW_COMPAT_RHEL7_5 + +RH-Author: Laurent Vivier +Message-id: <20180524141145.7532-2-lvivier@redhat.com> +Patchwork-id: 80471 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/2] redhat: define HW_COMPAT_RHEL7_5 +Bugzilla: 1557054 +RH-Acked-by: Eduardo Habkost +RH-Acked-by: Thomas Huth +RH-Acked-by: Laszlo Ersek + +Signed-off-by: Laurent Vivier +Signed-off-by: Miroslav Rezanina +--- + include/hw/compat.h | 29 +++++++++++++++++++++++++++++ + 1 file changed, 29 insertions(+) + +diff --git a/include/hw/compat.h b/include/hw/compat.h +index 666eed9..d711b5c5 100644 +--- a/include/hw/compat.h ++++ b/include/hw/compat.h +@@ -478,4 +478,33 @@ + .value = "false",\ + }, + ++/* like HW_COMPAT_2_10 and HW_COMPAT_2_11 ++ */ ++#define HW_COMPAT_RHEL7_5 \ ++ { /* HW_COMPAT_RHEL7_5 */ \ ++ .driver = "hpet",\ ++ .property = "hpet-offset-saved",\ ++ .value = "false",\ ++ },{ /* HW_COMPAT_RHEL7_5 */ \ ++ .driver = "virtio-blk-pci",\ ++ .property = "vectors",\ ++ .value = "2",\ ++ },{ /* HW_COMPAT_RHEL7_5 */ \ ++ .driver = "vhost-user-blk-pci",\ ++ .property = "vectors",\ ++ .value = "2",\ ++ },{ /* HW_COMPAT_RHEL7_5 */ \ ++ .driver = "e1000",\ ++ .property = "migrate_tso_props",\ ++ .value = "off",\ ++ },{ /* HW_COMPAT_RHEL7_5 */ \ ++ .driver = "virtio-mouse-device",\ ++ .property = "wheel-axis",\ ++ .value = "false",\ ++ },{ /* HW_COMPAT_RHEL7_5 */ \ ++ .driver = "virtio-tablet-device",\ ++ .property = "wheel-axis",\ ++ .value = "false",\ ++ }, ++ + #endif /* HW_COMPAT_H */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-redhat-define-pseries-rhel7.6.0-machine-types.patch b/SOURCES/kvm-redhat-define-pseries-rhel7.6.0-machine-types.patch new file mode 100644 index 0000000..c36213a --- /dev/null +++ b/SOURCES/kvm-redhat-define-pseries-rhel7.6.0-machine-types.patch @@ -0,0 +1,72 @@ +From 7b1ad037d92201aee894afd9cc70a852721f2d9d Mon Sep 17 00:00:00 2001 +From: Laurent Vivier +Date: Thu, 24 May 2018 14:11:45 +0200 +Subject: [PATCH 4/8] redhat: define pseries-rhel7.6.0 machine types + +RH-Author: Laurent Vivier +Message-id: <20180524141145.7532-3-lvivier@redhat.com> +Patchwork-id: 80470 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 2/2] redhat: define pseries-rhel7.6.0 machine types +Bugzilla: 1557054 +RH-Acked-by: Eduardo Habkost +RH-Acked-by: Thomas Huth +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Miroslav Rezanina + +Signed-off-by: Laurent Vivier +Signed-off-by: Miroslav Rezanina +--- + hw/ppc/spapr.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c +index 6f005a0..e2df370 100644 +--- a/hw/ppc/spapr.c ++++ b/hw/ppc/spapr.c +@@ -4354,19 +4354,41 @@ DEFINE_SPAPR_MACHINE(2_1, "2.1", false); + #endif + + /* ++ * pseries-rhel7.6.0 ++ */ ++ ++static void spapr_machine_rhel760_instance_options(MachineState *machine) ++{ ++} ++ ++static void spapr_machine_rhel760_class_options(MachineClass *mc) ++{ ++ /* Defaults for the latest behaviour inherited from the base class */ ++} ++ ++DEFINE_SPAPR_MACHINE(rhel760, "rhel7.6.0", true); ++ ++/* + * pseries-rhel7.5.0 ++ * like SPAPR_COMPAT_2_11 and SPAPR_COMPAT_2_10 ++ * SPAPR_CAP_HTM already enabled in 7.4 ++ * + */ ++#define SPAPR_COMPAT_RHEL7_5 \ ++ HW_COMPAT_RHEL7_5 \ + + static void spapr_machine_rhel750_instance_options(MachineState *machine) + { ++ spapr_machine_rhel760_instance_options(machine); + } + + static void spapr_machine_rhel750_class_options(MachineClass *mc) + { +- /* Defaults for the latest behaviour inherited from the base class */ ++ spapr_machine_rhel760_class_options(mc); ++ SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_RHEL7_5); + } + +-DEFINE_SPAPR_MACHINE(rhel750, "rhel7.5.0", true); ++DEFINE_SPAPR_MACHINE(rhel750, "rhel7.5.0", false); + + /* + * pseries-rhel7.5.0-sxxm +-- +1.8.3.1 + diff --git a/SOURCES/kvm-remove-duplicate-HW_COMPAT_RHEL7_5.patch b/SOURCES/kvm-remove-duplicate-HW_COMPAT_RHEL7_5.patch new file mode 100644 index 0000000..b649d61 --- /dev/null +++ b/SOURCES/kvm-remove-duplicate-HW_COMPAT_RHEL7_5.patch @@ -0,0 +1,66 @@ +From 9a021c064ee477faa45d396ab2c0174483711733 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Wed, 13 Jun 2018 10:40:25 +0200 +Subject: [PATCH 3/9] remove duplicate HW_COMPAT_RHEL7_5 + +RH-Author: Gerd Hoffmann +Message-id: <20180613104026.4395-2-kraxel@redhat.com> +Patchwork-id: 80654 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/2] remove duplicate HW_COMPAT_RHEL7_5 +Bugzilla: 1542080 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Daniel P. Berrange + +There are *two* HW_COMPAT_RHEL7_5 #defines in include/hw/compat.h. +Looks like a mismerge. Drop the second one. The first seems to be +the one we want keep as it documents which upstream release the compat +settings are coming from. + +Signed-off-by: Gerd Hoffmann +Signed-off-by: Miroslav Rezanina +--- + include/hw/compat.h | 29 ----------------------------- + 1 file changed, 29 deletions(-) + +diff --git a/include/hw/compat.h b/include/hw/compat.h +index d711b5c5..666eed9 100644 +--- a/include/hw/compat.h ++++ b/include/hw/compat.h +@@ -478,33 +478,4 @@ + .value = "false",\ + }, + +-/* like HW_COMPAT_2_10 and HW_COMPAT_2_11 +- */ +-#define HW_COMPAT_RHEL7_5 \ +- { /* HW_COMPAT_RHEL7_5 */ \ +- .driver = "hpet",\ +- .property = "hpet-offset-saved",\ +- .value = "false",\ +- },{ /* HW_COMPAT_RHEL7_5 */ \ +- .driver = "virtio-blk-pci",\ +- .property = "vectors",\ +- .value = "2",\ +- },{ /* HW_COMPAT_RHEL7_5 */ \ +- .driver = "vhost-user-blk-pci",\ +- .property = "vectors",\ +- .value = "2",\ +- },{ /* HW_COMPAT_RHEL7_5 */ \ +- .driver = "e1000",\ +- .property = "migrate_tso_props",\ +- .value = "off",\ +- },{ /* HW_COMPAT_RHEL7_5 */ \ +- .driver = "virtio-mouse-device",\ +- .property = "wheel-axis",\ +- .value = "false",\ +- },{ /* HW_COMPAT_RHEL7_5 */ \ +- .driver = "virtio-tablet-device",\ +- .property = "wheel-axis",\ +- .value = "false",\ +- }, +- + #endif /* HW_COMPAT_H */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-rhel-Set-host-phys-bits-limit-48-on-rhel-machine-typ.patch b/SOURCES/kvm-rhel-Set-host-phys-bits-limit-48-on-rhel-machine-typ.patch new file mode 100644 index 0000000..f89ae06 --- /dev/null +++ b/SOURCES/kvm-rhel-Set-host-phys-bits-limit-48-on-rhel-machine-typ.patch @@ -0,0 +1,53 @@ +From a402e015f9945358215e63b00d9efae44693ccd5 Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Thu, 11 Apr 2019 21:48:46 +0200 +Subject: [PATCH 160/163] rhel: Set host-phys-bits-limit=48 on rhel + machine-types + +RH-Author: Eduardo Habkost +Message-id: <20190411214846.8816-3-ehabkost@redhat.com> +Patchwork-id: 85609 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 2/2] rhel: Set host-phys-bits-limit=48 on rhel machine-types +Bugzilla: 1691519 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Pankaj Gupta +RH-Acked-by: Dr. David Alan Gilbert + +Upstream status: not applicable + +Currently we use the host physical address size by default on +VMs. This was a good default on most cases, but this is not the +case on host CPUs supporting 5-level EPT. On those cases, we +want VMs to use 4-level EPT by default. + +Ensure VMs will use 4-level EPT by default, by limiting physical +address bits to 48. + +Not applicable upstream because upstream doesn't set +host-phys-bits=on by default. + +Signed-off-by: Eduardo Habkost +Signed-off-by: Miroslav Rezanina +--- + include/hw/i386/pc.h | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h +index 88e5a92..1e9f252 100644 +--- a/include/hw/i386/pc.h ++++ b/include/hw/i386/pc.h +@@ -963,6 +963,11 @@ extern void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id); + .property = "host-phys-bits",\ + .value = "on",\ + },\ ++ { /* PC_RHEL_COMPAT */ \ ++ .driver = TYPE_X86_CPU,\ ++ .property = "host-phys-bits-limit",\ ++ .value = "48",\ ++ },\ + { /* PC_RHEL_COMPAT bz 1508330 */ \ + .driver = "vfio-pci",\ + .property = "x-no-geforce-quirks",\ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-rtl8139-fix-possible-out-of-bound-access.patch b/SOURCES/kvm-rtl8139-fix-possible-out-of-bound-access.patch new file mode 100644 index 0000000..a9a7252 --- /dev/null +++ b/SOURCES/kvm-rtl8139-fix-possible-out-of-bound-access.patch @@ -0,0 +1,78 @@ +From 72e7bb00aef5e81d870d44e3697d0a247a3bb3af Mon Sep 17 00:00:00 2001 +From: Xiao Wang +Date: Fri, 17 May 2019 07:29:35 +0200 +Subject: [PATCH 1/9] rtl8139: fix possible out of bound access + +RH-Author: Xiao Wang +Message-id: <1558078177-372-2-git-send-email-jasowang@redhat.com> +Patchwork-id: 88013 +O-Subject: [RHEL7.7 qemu-kvm-rhev PATCH 1/3] rtl8139: fix possible out of bound access +Bugzilla: 1636780 +RH-Acked-by: Thomas Huth +RH-Acked-by: Jens Freimann +RH-Acked-by: Maxime Coquelin +RH-Acked-by: Michael S. Tsirkin + +Bugzilla: 1636727 + +In rtl8139_do_receive(), we try to assign size_ to size which converts +from size_t to integer. This will cause troubles when size_ is greater +INT_MAX, this will lead a negative value in size and it can then pass +the check of size < MIN_BUF_SIZE which may lead out of bound access of +for both buf and buf1. + +Fixing by converting the type of size to size_t. + +CC: qemu-stable@nongnu.org +Reported-by: Daniel Shapira +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Jason Wang +(cherry picked from commit 1a326646fef38782e5542280040ec3ea23e4a730) +Signed-off-by: Miroslav Rezanina +--- + hw/net/rtl8139.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c +index bc37326..d343fb9 100644 +--- a/hw/net/rtl8139.c ++++ b/hw/net/rtl8139.c +@@ -817,7 +817,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t + RTL8139State *s = qemu_get_nic_opaque(nc); + PCIDevice *d = PCI_DEVICE(s); + /* size is the length of the buffer passed to the driver */ +- int size = size_; ++ size_t size = size_; + const uint8_t *dot1q_buf = NULL; + + uint32_t packet_header = 0; +@@ -826,7 +826,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t + static const uint8_t broadcast_macaddr[6] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +- DPRINTF(">>> received len=%d\n", size); ++ DPRINTF(">>> received len=%zu\n", size); + + /* test if board clock is stopped */ + if (!s->clock_enabled) +@@ -1035,7 +1035,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t + + if (size+4 > rx_space) + { +- DPRINTF("C+ Rx mode : descriptor %d size %d received %d + 4\n", ++ DPRINTF("C+ Rx mode : descriptor %d size %d received %zu + 4\n", + descriptor, rx_space, size); + + s->IntrStatus |= RxOverflow; +@@ -1148,7 +1148,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t + if (avail != 0 && RX_ALIGN(size + 8) >= avail) + { + DPRINTF("rx overflow: rx buffer length %d head 0x%04x " +- "read 0x%04x === available 0x%04x need 0x%04x\n", ++ "read 0x%04x === available 0x%04x need 0x%04zx\n", + s->RxBufferSize, s->RxBufAddr, s->RxBufPtr, avail, size + 8); + + s->IntrStatus |= RxOverflow; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-s390-ccw-force-diag-308-subcode-to-unsigned-long.patch b/SOURCES/kvm-s390-ccw-force-diag-308-subcode-to-unsigned-long.patch new file mode 100644 index 0000000..f2eea48 --- /dev/null +++ b/SOURCES/kvm-s390-ccw-force-diag-308-subcode-to-unsigned-long.patch @@ -0,0 +1,53 @@ +From 098630a1298aa56f6dfffaeecbddf2d0f39c55a8 Mon Sep 17 00:00:00 2001 +From: Thomas Huth +Date: Mon, 7 May 2018 07:58:03 +0200 +Subject: [PATCH 07/13] s390-ccw: force diag 308 subcode to unsigned long + +RH-Author: Thomas Huth +Message-id: <1525679888-9234-2-git-send-email-thuth@redhat.com> +Patchwork-id: 80049 +O-Subject: [RHEL-7.6 qemu-kvm-ma PATCH 1/6] s390-ccw: force diag 308 subcode to unsigned long +Bugzilla: 1523857 +RH-Acked-by: David Hildenbrand +RH-Acked-by: Cornelia Huck +RH-Acked-by: Laszlo Ersek + +From: Cornelia Huck + +We currently pass an integer as the subcode parameter. However, +the upper bits of the register containing the subcode need to +be 0, which is not guaranteed unless we explicitly specify the +subcode to be an unsigned long value. + +Fixes: d046c51dad3 ("pc-bios/s390-ccw: Get device address via diag 308/6") +Cc: qemu-stable@nongnu.org +Signed-off-by: Cornelia Huck +Acked-by: Christian Borntraeger +Tested-by: Thomas Huth +Signed-off-by: Thomas Huth +(cherry picked from commit 63d8b5ace31c1e1f3996fe4cd551d6d377594d5a) +Signed-off-by: Miroslav Rezanina +--- + pc-bios/s390-ccw/iplb.h | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h +index 5357a36..ded20c8 100644 +--- a/pc-bios/s390-ccw/iplb.h ++++ b/pc-bios/s390-ccw/iplb.h +@@ -101,10 +101,11 @@ static inline bool manage_iplb(IplParameterBlock *iplb, bool store) + { + register unsigned long addr asm("0") = (unsigned long) iplb; + register unsigned long rc asm("1") = 0; ++ unsigned long subcode = store ? 6 : 5; + + asm volatile ("diag %0,%2,0x308\n" + : "+d" (addr), "+d" (rc) +- : "d" (store ? 6 : 5) ++ : "d" (subcode) + : "memory", "cc"); + return rc == 0x01; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-s390x-add-RHEL-7.6-machine-type-for-ccw.patch b/SOURCES/kvm-s390x-add-RHEL-7.6-machine-type-for-ccw.patch new file mode 100644 index 0000000..b09f293 --- /dev/null +++ b/SOURCES/kvm-s390x-add-RHEL-7.6-machine-type-for-ccw.patch @@ -0,0 +1,139 @@ +From ea16161ecfee857c3d83841f806c5fa55837c483 Mon Sep 17 00:00:00 2001 +From: Cornelia Huck +Date: Thu, 17 May 2018 11:52:08 +0200 +Subject: [PATCH 2/8] s390x: add RHEL 7.6 machine type for ccw + +RH-Author: Cornelia Huck +Message-id: <20180517115208.20738-1-cohuck@redhat.com> +Patchwork-id: 80375 +O-Subject: [RHEL-7.6 qemu-kvm-ma PATCH v2] s390x: add RHEL 7.6 machine type for ccw +Bugzilla: 1562138 +RH-Acked-by: David Hildenbrand +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier + +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1562138 +Brew: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=16338248 +Branch: rhv7/master-2.12.0 +Upstream: n/a +Tested: Managedsave/restore between qemu-kvm-ma-2.10 and qemu-kvm-ma-2.12 + with 7.5.0 machine type; started guest with 7.6.0 machine type + +Follows the s390x-specific 2.10->2.12 compat handling (sclp masks, +qemu cpu model) and introduces generic compat defines for 7.5->7.6 +(even though not applicable on s390x). + +Signed-off-by: Cornelia Huck +Signed-off-by: Miroslav Rezanina +--- + hw/s390x/s390-virtio-ccw.c | 35 ++++++++++++++++++++++++++++++++--- + include/hw/compat.h | 28 ++++++++++++++++++++++++++++ + 2 files changed, 60 insertions(+), 3 deletions(-) + +diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c +index 3956ac3..64e2a3b 100644 +--- a/hw/s390x/s390-virtio-ccw.c ++++ b/hw/s390x/s390-virtio-ccw.c +@@ -671,6 +671,8 @@ bool css_migration_enabled(void) + } \ + type_init(ccw_machine_register_##suffix) + ++#if 0 /* Disabled for Red Hat Enterprise Linux */ ++ + #define CCW_COMPAT_2_11 \ + HW_COMPAT_2_11 \ + {\ +@@ -682,8 +684,6 @@ bool css_migration_enabled(void) + #define CCW_COMPAT_2_10 \ + HW_COMPAT_2_10 + +-#if 0 /* Disabled for Red Hat Enterprise Linux */ +- + #define CCW_COMPAT_2_9 \ + HW_COMPAT_2_9 \ + {\ +@@ -883,14 +883,43 @@ DEFINE_CCW_MACHINE(2_4, "2.4", false); + + #else + ++/* ++ * like CCW_COMPAT_2_11, but includes HW_COMPAT_RHEL7_5 (derived from ++ * HW_COMPAT_2_11 and HW_COMPAT_2_10) instead of HW_COMPAT_2_11 ++ */ ++#define CCW_COMPAT_RHEL7_5 \ ++ HW_COMPAT_RHEL7_5 \ ++ {\ ++ .driver = TYPE_SCLP_EVENT_FACILITY,\ ++ .property = "allow_all_mask_sizes",\ ++ .value = "off",\ ++ }, ++ ++static void ccw_machine_rhel760_instance_options(MachineState *machine) ++{ ++} ++ ++static void ccw_machine_rhel760_class_options(MachineClass *mc) ++{ ++} ++DEFINE_CCW_MACHINE(rhel760, "rhel7.6.0", true); ++ + static void ccw_machine_rhel750_instance_options(MachineState *machine) + { ++ static const S390FeatInit qemu_cpu_feat = { S390_FEAT_LIST_QEMU_V2_11 }; ++ ccw_machine_rhel760_instance_options(machine); ++ ++ /* before 2.12 we emulated the very first z900, and RHEL 7.5 is ++ based on 2.10 */ ++ s390_set_qemu_cpu_model(0x2064, 7, 1, qemu_cpu_feat); + } + + static void ccw_machine_rhel750_class_options(MachineClass *mc) + { ++ ccw_machine_rhel760_class_options(mc); ++ SET_MACHINE_COMPAT(mc, CCW_COMPAT_RHEL7_5); + } +-DEFINE_CCW_MACHINE(rhel750, "rhel7.5.0", true); ++DEFINE_CCW_MACHINE(rhel750, "rhel7.5.0", false); + + #endif + +diff --git a/include/hw/compat.h b/include/hw/compat.h +index 503b5c8..666eed9 100644 +--- a/include/hw/compat.h ++++ b/include/hw/compat.h +@@ -450,4 +450,32 @@ + .value = "off",\ + }, + ++/* The same as HW_COMPAT_2_11 + HW_COMPAT_2_10 */ ++#define HW_COMPAT_RHEL7_5 \ ++ { /* HW_COMPAT_RHEL7_5 from HW_COMPAT_2_11 */ \ ++ .driver = "hpet",\ ++ .property = "hpet-offset-saved",\ ++ .value = "false",\ ++ },{ /* HW_COMPAT_RHEL7_5 from HW_COMPAT_2_11 */ \ ++ .driver = "virtio-blk-pci",\ ++ .property = "vectors",\ ++ .value = "2",\ ++ },{ /* HW_COMPAT_RHEL7_5 from HW_COMPAT_2_11 */ \ ++ .driver = "vhost-user-blk-pci",\ ++ .property = "vectors",\ ++ .value = "2",\ ++ },{ /* HW_COMPAT_RHEL7_5 from HW_COMPAT_2_11 */ \ ++ .driver = "e1000",\ ++ .property = "migrate_tso_props",\ ++ .value = "off",\ ++ },{ /* HW_COMPAT_RHEL7_5 from HW_COMPAT_2_10 */ \ ++ .driver = "virtio-mouse-device",\ ++ .property = "wheel-axis",\ ++ .value = "false",\ ++ },{ /* HW_COMPAT_RHEL7_5 from HW_COMPAT_2_10 */ \ ++ .driver = "virtio-tablet-device",\ ++ .property = "wheel-axis",\ ++ .value = "false",\ ++ }, ++ + #endif /* HW_COMPAT_H */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-s390x-cpumodel-default-enable-bpb-and-ppa15-for-z196.patch b/SOURCES/kvm-s390x-cpumodel-default-enable-bpb-and-ppa15-for-z196.patch new file mode 100644 index 0000000..2ac5d99 --- /dev/null +++ b/SOURCES/kvm-s390x-cpumodel-default-enable-bpb-and-ppa15-for-z196.patch @@ -0,0 +1,62 @@ +From 11637f33d743a928b6e05f4475f3f840715f33c6 Mon Sep 17 00:00:00 2001 +From: Cornelia Huck +Date: Tue, 3 Jul 2018 10:55:10 +0200 +Subject: [PATCH 56/57] s390x/cpumodel: default enable bpb and ppa15 for z196 + and later + +RH-Author: Cornelia Huck +Message-id: <20180703105510.30555-1-cohuck@redhat.com> +Patchwork-id: 81191 +O-Subject: [RHEL-7.6 qemu-kvm-ma PATCH] s390x/cpumodel: default enable bpb and ppa15 for z196 and later +Bugzilla: 1595715 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Eduardo Habkost +RH-Acked-by: David Hildenbrand + +Most systems and host kernels provide the necessary building blocks for +bpb and ppa15. We can reverse the logic and default enable those +features, while still allowing to disable it via cpu model. + +So let us add bpb and ppa15 to z196 and later default CPU model for the +qemu rhel7.6.0 machine. (like -cpu z13). Older machine types (i.e. +s390-ccw-virtio-rhel7.5.0) will retain the old value and not provide those +bits in the default model. + +Signed-off-by: Cornelia Huck +Signed-off-by: Miroslav Rezanina +--- + hw/s390x/s390-virtio-ccw.c | 4 ++++ + target/s390x/gen-features.c | 2 ++ + 2 files changed, 6 insertions(+) + +diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c +index 64e2a3b..bf039a1 100644 +--- a/hw/s390x/s390-virtio-ccw.c ++++ b/hw/s390x/s390-virtio-ccw.c +@@ -912,6 +912,10 @@ static void ccw_machine_rhel750_instance_options(MachineState *machine) + /* before 2.12 we emulated the very first z900, and RHEL 7.5 is + based on 2.10 */ + s390_set_qemu_cpu_model(0x2064, 7, 1, qemu_cpu_feat); ++ ++ /* bpb and ppa15 were only in the full model in RHEL 7.5 */ ++ s390_cpudef_featoff_greater(11, 1, S390_FEAT_PPA15); ++ s390_cpudef_featoff_greater(11, 1, S390_FEAT_BPB); + } + + static void ccw_machine_rhel750_class_options(MachineClass *mc) +diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c +index 0cdbc15..6626b6f 100644 +--- a/target/s390x/gen-features.c ++++ b/target/s390x/gen-features.c +@@ -512,6 +512,8 @@ static uint16_t default_GEN11_GA1[] = { + S390_FEAT_IPTE_RANGE, + S390_FEAT_ACCESS_EXCEPTION_FS_INDICATION, + S390_FEAT_GROUP_MSA_EXT_4, ++ S390_FEAT_PPA15, ++ S390_FEAT_BPB, + }; + + #define default_GEN11_GA2 EmptyFeat +-- +1.8.3.1 + diff --git a/SOURCES/kvm-s390x-cpumodels-add-z14-Model-ZR1.patch b/SOURCES/kvm-s390x-cpumodels-add-z14-Model-ZR1.patch new file mode 100644 index 0000000..a9dd5f9 --- /dev/null +++ b/SOURCES/kvm-s390x-cpumodels-add-z14-Model-ZR1.patch @@ -0,0 +1,44 @@ +From f64b0a4ca1cb685491df47d525909c9a47ecb47e Mon Sep 17 00:00:00 2001 +From: Thomas Huth +Date: Wed, 20 Jun 2018 09:50:28 +0200 +Subject: [PATCH 50/54] s390x/cpumodels: add z14 Model ZR1 + +RH-Author: Thomas Huth +Message-id: <1529488228-5109-2-git-send-email-thuth@redhat.com> +Patchwork-id: 80892 +O-Subject: [RHEL-7.6 qemu-kvm-ma PATCH 1/1] s390x/cpumodels: add z14 Model ZR1 +Bugzilla: 1592327 +RH-Acked-by: David Hildenbrand +RH-Acked-by: Cornelia Huck +RH-Acked-by: Eduardo Habkost + +From: Christian Borntraeger + +Introduce the new z14 Model ZR1 cpu model. Mostly identical to z14, only +the cpu type differs (3906 vs. 3907) + +Signed-off-by: Christian Borntraeger +Message-Id: <20180613081819.147178-1-borntraeger@de.ibm.com> +Reviewed-by: David Hildenbrand +Signed-off-by: Cornelia Huck +(cherry picked from commit 23ad956bff98d949057156ea3f68a9763c2dda0e) +Signed-off-by: Miroslav Rezanina +--- + target/s390x/cpu_models.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c +index 70b23bf..0b5d271 100644 +--- a/target/s390x/cpu_models.c ++++ b/target/s390x/cpu_models.c +@@ -79,6 +79,7 @@ static S390CPUDef s390_cpu_defs[] = { + CPUDEF_INIT(0x2964, 13, 2, 47, 0x08000000U, "z13.2", "IBM z13 GA2"), + CPUDEF_INIT(0x2965, 13, 2, 47, 0x08000000U, "z13s", "IBM z13s GA1"), + CPUDEF_INIT(0x3906, 14, 1, 47, 0x08000000U, "z14", "IBM z14 GA1"), ++ CPUDEF_INIT(0x3907, 14, 1, 47, 0x08000000U, "z14ZR1", "IBM z14 Model ZR1 GA1"), + }; + + #define QEMU_MAX_CPU_TYPE 0x2827 +-- +1.8.3.1 + diff --git a/SOURCES/kvm-s390x-kvm-add-etoken-facility.patch b/SOURCES/kvm-s390x-kvm-add-etoken-facility.patch new file mode 100644 index 0000000..0cb1985 --- /dev/null +++ b/SOURCES/kvm-s390x-kvm-add-etoken-facility.patch @@ -0,0 +1,196 @@ +From d5c27d4667cc1fb8065c27dc7e7d0e5d29f34e29 Mon Sep 17 00:00:00 2001 +From: Thomas Huth +Date: Tue, 28 Aug 2018 17:03:12 +0200 +Subject: [PATCH 02/29] s390x/kvm: add etoken facility + +RH-Author: Thomas Huth +Message-id: <1535475792-21136-3-git-send-email-thuth@redhat.com> +Patchwork-id: 81948 +O-Subject: [RHEL-7.6 qemu-kvm-ma PATCH 2/2] s390x/kvm: add etoken facility +Bugzilla: 1622962 +RH-Acked-by: David Hildenbrand +RH-Acked-by: Cornelia Huck +RH-Acked-by: Jens Freimann + +From: Christian Borntraeger + +Provide the etoken facility. We need to handle cpu model, migration and +clear reset. + +Signed-off-by: Christian Borntraeger +Acked-by: Janosch Frank +Message-Id: <20180731090448.36662-3-borntraeger@de.ibm.com> +Reviewed-by: David Hildenbrand +Signed-off-by: Cornelia Huck +(cherry picked from commit 27e84d4ebd25b981ab27cb590fe06d1b0fcd06d2) +Signed-off-by: Miroslav Rezanina +--- + target/s390x/cpu.h | 3 +++ + target/s390x/cpu_features.c | 3 ++- + target/s390x/cpu_features_def.h | 3 ++- + target/s390x/gen-features.c | 3 ++- + target/s390x/kvm.c | 11 +++++++++++ + target/s390x/machine.c | 20 +++++++++++++++++++- + 6 files changed, 39 insertions(+), 4 deletions(-) + +diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h +index 3ee40f0..86d08fa 100644 +--- a/target/s390x/cpu.h ++++ b/target/s390x/cpu.h +@@ -2,6 +2,7 @@ + * S/390 virtual CPU header + * + * Copyright (c) 2009 Ulrich Hecht ++ * Copyright IBM Corp. 2012, 2018 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -68,6 +69,8 @@ struct CPUS390XState { + uint32_t aregs[16]; /* access registers */ + uint8_t riccb[64]; /* runtime instrumentation control */ + uint64_t gscb[4]; /* guarded storage control */ ++ uint64_t etoken; /* etoken */ ++ uint64_t etoken_extension; /* etoken extension */ + + /* Fields up to this point are not cleared by initial CPU reset */ + struct {} start_initial_reset_fields; +diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c +index 3b9e274..e05e6aa 100644 +--- a/target/s390x/cpu_features.c ++++ b/target/s390x/cpu_features.c +@@ -1,7 +1,7 @@ + /* + * CPU features/facilities for s390x + * +- * Copyright 2016 IBM Corp. ++ * Copyright IBM Corp. 2016, 2018 + * + * Author(s): David Hildenbrand + * +@@ -106,6 +106,7 @@ static const S390FeatDef s390_features[] = { + FEAT_INIT("irbm", S390_FEAT_TYPE_STFL, 145, "Insert-reference-bits-multiple facility"), + FEAT_INIT("msa8-base", S390_FEAT_TYPE_STFL, 146, "Message-security-assist-extension-8 facility (excluding subfunctions)"), + FEAT_INIT("cmmnt", S390_FEAT_TYPE_STFL, 147, "CMM: ESSA-enhancement (no translate) facility"), ++ FEAT_INIT("etoken", S390_FEAT_TYPE_STFL, 156, "Etoken facility"), + + /* SCLP SCCB Byte 80 - 98 (bit numbers relative to byte-80) */ + FEAT_INIT("gsls", S390_FEAT_TYPE_SCLP_CONF_CHAR, 40, "SIE: Guest-storage-limit-suppression facility"), +diff --git a/target/s390x/cpu_features_def.h b/target/s390x/cpu_features_def.h +index 7c5915c..ac2c947 100644 +--- a/target/s390x/cpu_features_def.h ++++ b/target/s390x/cpu_features_def.h +@@ -1,7 +1,7 @@ + /* + * CPU features/facilities for s390 + * +- * Copyright 2016 IBM Corp. ++ * Copyright IBM Corp. 2016, 2018 + * + * Author(s): Michael Mueller + * David Hildenbrand +@@ -93,6 +93,7 @@ typedef enum { + S390_FEAT_INSERT_REFERENCE_BITS_MULT, + S390_FEAT_MSA_EXT_8, + S390_FEAT_CMM_NT, ++ S390_FEAT_ETOKEN, + + /* Sclp Conf Char */ + S390_FEAT_SIE_GSLS, +diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c +index 6626b6f..5af042c 100644 +--- a/target/s390x/gen-features.c ++++ b/target/s390x/gen-features.c +@@ -1,7 +1,7 @@ + /* + * S390 feature list generator + * +- * Copyright 2016 IBM Corp. ++ * Copyright IBM Corp. 2016, 2018 + * + * Author(s): Michael Mueller + * David Hildenbrand +@@ -471,6 +471,7 @@ static uint16_t full_GEN14_GA1[] = { + S390_FEAT_GROUP_MSA_EXT_7, + S390_FEAT_GROUP_MSA_EXT_8, + S390_FEAT_CMM_NT, ++ S390_FEAT_ETOKEN, + S390_FEAT_HPMA2, + S390_FEAT_SIE_KSS, + S390_FEAT_GROUP_MULTIPLE_EPOCH_PTFF, +diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c +index fbccceb..ca8c32e 100644 +--- a/target/s390x/kvm.c ++++ b/target/s390x/kvm.c +@@ -494,6 +494,12 @@ int kvm_arch_put_registers(CPUState *cs, int level) + cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_BPBC; + } + ++ if (can_sync_regs(cs, KVM_SYNC_ETOKEN)) { ++ cs->kvm_run->s.regs.etoken = env->etoken; ++ cs->kvm_run->s.regs.etoken_extension = env->etoken_extension; ++ cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_ETOKEN; ++ } ++ + /* Finally the prefix */ + if (can_sync_regs(cs, KVM_SYNC_PREFIX)) { + cs->kvm_run->s.regs.prefix = env->psa; +@@ -608,6 +614,11 @@ int kvm_arch_get_registers(CPUState *cs) + env->bpbc = cs->kvm_run->s.regs.bpbc; + } + ++ if (can_sync_regs(cs, KVM_SYNC_ETOKEN)) { ++ env->etoken = cs->kvm_run->s.regs.etoken; ++ env->etoken_extension = cs->kvm_run->s.regs.etoken_extension; ++ } ++ + /* pfault parameters */ + if (can_sync_regs(cs, KVM_SYNC_PFAULT)) { + env->pfault_token = cs->kvm_run->s.regs.pft; +diff --git a/target/s390x/machine.c b/target/s390x/machine.c +index 84b4928..8421deb 100644 +--- a/target/s390x/machine.c ++++ b/target/s390x/machine.c +@@ -1,7 +1,7 @@ + /* + * S390x machine definitions and functions + * +- * Copyright IBM Corp. 2014 ++ * Copyright IBM Corp. 2014, 2018 + * + * Authors: + * Thomas Huth +@@ -210,6 +210,23 @@ const VMStateDescription vmstate_bpbc = { + } + }; + ++static bool etoken_needed(void *opaque) ++{ ++ return s390_has_feat(S390_FEAT_ETOKEN); ++} ++ ++const VMStateDescription vmstate_etoken = { ++ .name = "cpu/etoken", ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .needed = etoken_needed, ++ .fields = (VMStateField[]) { ++ VMSTATE_UINT64(env.etoken, S390CPU), ++ VMSTATE_UINT64(env.etoken_extension, S390CPU), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ + const VMStateDescription vmstate_s390_cpu = { + .name = "cpu", + .post_load = cpu_post_load, +@@ -245,6 +262,7 @@ const VMStateDescription vmstate_s390_cpu = { + &vmstate_exval, + &vmstate_gscb, + &vmstate_bpbc, ++ &vmstate_etoken, + NULL + }, + }; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-s390x-sclp-fix-maxram-calculation.patch b/SOURCES/kvm-s390x-sclp-fix-maxram-calculation.patch new file mode 100644 index 0000000..fa319c5 --- /dev/null +++ b/SOURCES/kvm-s390x-sclp-fix-maxram-calculation.patch @@ -0,0 +1,51 @@ +From 48815daa02f8d90bf1f4dc10e560cdc9ca61352b Mon Sep 17 00:00:00 2001 +From: Cornelia Huck +Date: Wed, 1 Aug 2018 11:09:14 +0200 +Subject: [PATCH 14/15] s390x/sclp: fix maxram calculation + +RH-Author: Cornelia Huck +Message-id: <20180801110914.17729-1-cohuck@redhat.com> +Patchwork-id: 81564 +O-Subject: [RHEL-7.6 qemu-kvm-ma PATCH] s390x/sclp: fix maxram calculation +Bugzilla: 1595740 +RH-Acked-by: David Hildenbrand +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +From: Christian Borntraeger + +We clamp down ram_size to match the sclp increment size. We do +not do the same for maxram_size, which means for large guests +with some sizes (e.g. -m 50000) maxram_size differs from ram_size. +This can break other code (e.g. CMMA migration) which uses maxram_size +to calculate the number of pages and then throws some errors. + +Fixes: 82fab5c5b90e468f3e9d54c ("s390x/sclp: remove memory hotplug support") +Signed-off-by: Christian Borntraeger +CC: qemu-stable@nongnu.org +CC: David Hildenbrand +Message-Id: <1532959766-53343-1-git-send-email-borntraeger@de.ibm.com> +Reviewed-by: David Hildenbrand +Signed-off-by: Cornelia Huck +(cherry picked from commit 408e5ace517ff18c9c7cd918fc93358162e6e26d) +Signed-off-by: Cornelia Huck +Signed-off-by: Miroslav Rezanina +--- + hw/s390x/sclp.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c +index 047d577..2abdb62 100644 +--- a/hw/s390x/sclp.c ++++ b/hw/s390x/sclp.c +@@ -319,6 +319,7 @@ static void sclp_memory_init(SCLPDevice *sclp) + initial_mem = initial_mem >> increment_size << increment_size; + + machine->ram_size = initial_mem; ++ machine->maxram_size = initial_mem; + /* let's propagate the changed ram size into the global variable. */ + ram_size = initial_mem; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-s390x.conf b/SOURCES/kvm-s390x.conf new file mode 100644 index 0000000..949f6d1 --- /dev/null +++ b/SOURCES/kvm-s390x.conf @@ -0,0 +1,7 @@ +# Setting "modprobe kvm nested=1" only enables Nested Virtualization until +# the next reboot or module reload. Uncomment the option below to enable +# the feature permanently. +# +# User changes in this file are preserved across upgrades. +# +#options kvm nested=1 diff --git a/SOURCES/kvm-sam460ex-Don-t-size-flash-memory-to-match-backing-im.patch b/SOURCES/kvm-sam460ex-Don-t-size-flash-memory-to-match-backing-im.patch new file mode 100644 index 0000000..1651288 --- /dev/null +++ b/SOURCES/kvm-sam460ex-Don-t-size-flash-memory-to-match-backing-im.patch @@ -0,0 +1,118 @@ +From 862385809b305a6b02bc9cf39c8fb055bf764838 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:50:57 +0200 +Subject: [PATCH 30/53] sam460ex: Don't size flash memory to match backing + image +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-9-armbru@redhat.com> +Patchwork-id: 87998 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 08/31] sam460ex: Don't size flash memory to match backing image +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +Machine "sam460ex" maps its flash memory at address 0xFFF00000. When +no image is supplied, its size is 1MiB (0x100000), and 512KiB of ROM +get mapped on top of its second half. Else, it's the size of the +image rounded up to the next multiple of 64KiB. + +The rounding is actually useless: pflash_cfi01_realize() fails with +"failed to read the initial flash content" unless it's a no-op. + +I have no idea what happens when the pflash's size exceeds 1MiB. +Useful outcomes seem unlikely. + +I guess memory at the end of the address space remains unmapped when +it's smaller than 1MiB. Again, useful outcomes seem unlikely. + +The physical hardware appears to have 512KiB of flash memory: +https://eu.mouser.com/datasheet/2/268/atmel_AT49BV040B-1180330.pdf + +For now, just set the flash memory size to 1MiB regardless of image +size, and document the mess. + +Cc: BALATON Zoltan +Signed-off-by: Markus Armbruster +Reviewed-by: BALATON Zoltan +Reviewed-by: Alex Bennée +Message-Id: <20190308094610.21210-7-armbru@redhat.com> +Reviewed-by: Philippe Mathieu-Daudé +(cherry picked from commit f30bc99559306eee9ef363bc11bf63a021aee707) +[Trivial conflict in hw/ppc/sam460ex.c due to lack of commit ab3dd749241] + +Signed-off-by: Miroslav Rezanina +--- + hw/ppc/sam460ex.c | 41 ++++++++++++++++++++++++++--------------- + 1 file changed, 26 insertions(+), 15 deletions(-) + +diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c +index dfff262..83c6e90 100644 +--- a/hw/ppc/sam460ex.c ++++ b/hw/ppc/sam460ex.c +@@ -212,32 +212,43 @@ static void generate_eeprom_serial(uint8_t *eeprom) + + static int sam460ex_load_uboot(void) + { ++ /* ++ * This first creates 1MiB of flash memory mapped at the end of ++ * the 32-bit address space (0xFFF00000..0xFFFFFFFF). ++ * ++ * If_PFLASH unit 0 is defined, the flash memory is initialized ++ * from that block backend. ++ * ++ * Else, it's initialized to zero. And then 512KiB of ROM get ++ * mapped on top of its second half (0xFFF80000..0xFFFFFFFF), ++ * initialized from u-boot-sam460-20100605.bin. ++ * ++ * This doesn't smell right. ++ * ++ * The physical hardware appears to have 512KiB flash memory. ++ * ++ * TODO Figure out what we really need here, and clean this up. ++ */ ++ + DriveInfo *dinfo; +- BlockBackend *blk = NULL; +- hwaddr base = FLASH_BASE | ((hwaddr)FLASH_BASE_H << 32); +- long bios_size = FLASH_SIZE; +- int fl_sectors; + + dinfo = drive_get(IF_PFLASH, 0, 0); +- if (dinfo) { +- blk = blk_by_legacy_dinfo(dinfo); +- bios_size = blk_getlength(blk); +- } +- fl_sectors = (bios_size + 65535) >> 16; +- +- if (!pflash_cfi01_register(base, NULL, "sam460ex.flash", bios_size, +- blk, (64 * 1024), fl_sectors, ++ if (!pflash_cfi01_register(FLASH_BASE | ((hwaddr)FLASH_BASE_H << 32), ++ NULL, "sam460ex.flash", FLASH_SIZE, ++ dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, ++ 64 * KiB, FLASH_SIZE / (64 * KiB), + 1, 0x89, 0x18, 0x0000, 0x0, 1)) { + error_report("qemu: Error registering flash memory."); + /* XXX: return an error instead? */ + exit(1); + } + +- if (!blk) { ++ if (!dinfo) { + /*error_report("No flash image given with the 'pflash' parameter," + " using default u-boot image");*/ +- base = UBOOT_LOAD_BASE | ((hwaddr)FLASH_BASE_H << 32); +- rom_add_file_fixed(UBOOT_FILENAME, base, -1); ++ rom_add_file_fixed(UBOOT_FILENAME, ++ UBOOT_LOAD_BASE | ((hwaddr)FLASH_BASE_H << 32), ++ -1); + } + + return 0; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-scripts-qemu.py-allow-adding-to-the-list-of-extra-ar.patch b/SOURCES/kvm-scripts-qemu.py-allow-adding-to-the-list-of-extra-ar.patch new file mode 100644 index 0000000..a6f01a6 --- /dev/null +++ b/SOURCES/kvm-scripts-qemu.py-allow-adding-to-the-list-of-extra-ar.patch @@ -0,0 +1,53 @@ +From 257036163501f7c59bd54ce6d050563780cd8a98 Mon Sep 17 00:00:00 2001 +From: Yash Mankad +Date: Tue, 17 Jul 2018 23:38:05 +0200 +Subject: [PATCH 48/89] scripts/qemu.py: allow adding to the list of extra + arguments +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Yash Mankad +Message-id: <4ba44e15a56b1119045859193f7e18d11d4a5ba4.1531870629.git.ymankad@redhat.com> +Patchwork-id: 81384 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 2/5] scripts/qemu.py: allow adding to the list of extra arguments +Bugzilla: +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow +RH-Acked-by: Eduardo Habkost + +From: Cleber Rosa + +Tests will often need to add extra arguments to QEMU command +line arguments. + +Signed-off-by: Cleber Rosa +Reviewed-by: Stefan Hajnoczi +Message-Id: <20180530184156.15634-3-crosa@redhat.com> +Reviewed-by: Philippe Mathieu-Daudé +Tested-by: Philippe Mathieu-Daudé +Signed-off-by: Eduardo Habkost +(cherry picked from commit 572a82438308216fbfc615c924b6e94e4b68dfaa) +Signed-off-by: Yash Mankad +Signed-off-by: Miroslav Rezanina +--- + scripts/qemu.py | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/scripts/qemu.py b/scripts/qemu.py +index 08a3e9a..7cd8193 100644 +--- a/scripts/qemu.py ++++ b/scripts/qemu.py +@@ -359,3 +359,9 @@ class QEMUMachine(object): + of the qemu process. + ''' + return self._iolog ++ ++ def add_args(self, *args): ++ ''' ++ Adds to the list of extra arguments to be given to the QEMU binary ++ ''' ++ self._args.extend(args) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-scripts-qemu.py-introduce-set_console-method.patch b/SOURCES/kvm-scripts-qemu.py-introduce-set_console-method.patch new file mode 100644 index 0000000..d0bce8c --- /dev/null +++ b/SOURCES/kvm-scripts-qemu.py-introduce-set_console-method.patch @@ -0,0 +1,197 @@ +From 7d56e73ebc9b816b987f26ec66101562bdd941b8 Mon Sep 17 00:00:00 2001 +From: Yash Mankad +Date: Tue, 17 Jul 2018 23:38:07 +0200 +Subject: [PATCH 50/89] scripts/qemu.py: introduce set_console() method +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Yash Mankad +Message-id: <3daef391b07eb8b66afabe66bee05749ab1a5e9a.1531870629.git.ymankad@redhat.com> +Patchwork-id: 81381 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 4/5] scripts/qemu.py: introduce set_console() method +Bugzilla: +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: John Snow +RH-Acked-by: Eduardo Habkost + +From: Cleber Rosa + +The set_console() method is intended to ease higher level use cases +that require a console device. + +The amount of intelligence is limited on purpose, requiring either the +device type explicitly, or the existence of a machine (pattern) +definition. + +Because of the console device type selection criteria (by machine +type), users should also be able to define that. It'll then be used +for both '-machine' and for the console device type selection. + +Users of the set_console() method will certainly be interested in +accessing the console device, and for that a console_socket property +has been added. + +Signed-off-by: Cleber Rosa +Message-Id: <20180530184156.15634-5-crosa@redhat.com> +Tested-by: Philippe Mathieu-Daudé +Reviewed-by: Stefan Hajnoczi +Signed-off-by: Eduardo Habkost +(cherry picked from commit 22dea9db2baf72a79782c748c57e2d87b06234d5) +Signed-off-by: Yash Mankad +Signed-off-by: Miroslav Rezanina +--- + scripts/qemu.py | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 96 insertions(+), 1 deletion(-) + +diff --git a/scripts/qemu.py b/scripts/qemu.py +index 7cd8193..f099ce7 100644 +--- a/scripts/qemu.py ++++ b/scripts/qemu.py +@@ -17,19 +17,41 @@ import logging + import os + import subprocess + import qmp.qmp ++import re + import shutil ++import socket + import tempfile + + + LOG = logging.getLogger(__name__) + + ++#: Maps machine types to the preferred console device types ++CONSOLE_DEV_TYPES = { ++ r'^clipper$': 'isa-serial', ++ r'^malta': 'isa-serial', ++ r'^(pc.*|q35.*|isapc)$': 'isa-serial', ++ r'^(40p|powernv|prep)$': 'isa-serial', ++ r'^pseries.*': 'spapr-vty', ++ r'^s390-ccw-virtio.*': 'sclpconsole', ++ } ++ ++ + class QEMUMachineError(Exception): + """ + Exception called when an error in QEMUMachine happens. + """ + + ++class QEMUMachineAddDeviceError(QEMUMachineError): ++ """ ++ Exception raised when a request to add a device can not be fulfilled ++ ++ The failures are caused by limitations, lack of information or conflicting ++ requests on the QEMUMachine methods. This exception does not represent ++ failures reported by the QEMU binary itself. ++ """ ++ + class MonitorResponseError(qmp.qmp.QMPError): + ''' + Represents erroneous QMP monitor reply +@@ -91,6 +113,10 @@ class QEMUMachine(object): + self._test_dir = test_dir + self._temp_dir = None + self._launched = False ++ self._machine = None ++ self._console_device_type = None ++ self._console_address = None ++ self._console_socket = None + + # just in case logging wasn't configured by the main script: + logging.basicConfig() +@@ -175,9 +201,19 @@ class QEMUMachine(object): + self._monitor_address[1]) + else: + moncdev = 'socket,id=mon,path=%s' % self._vm_monitor +- return ['-chardev', moncdev, ++ args = ['-chardev', moncdev, + '-mon', 'chardev=mon,mode=control', + '-display', 'none', '-vga', 'none'] ++ if self._machine is not None: ++ args.extend(['-machine', self._machine]) ++ if self._console_device_type is not None: ++ self._console_address = os.path.join(self._temp_dir, ++ self._name + "-console.sock") ++ chardev = ('socket,id=console,path=%s,server,nowait' % ++ self._console_address) ++ device = '%s,chardev=console' % self._console_device_type ++ args.extend(['-chardev', chardev, '-device', device]) ++ return args + + def _pre_launch(self): + self._temp_dir = tempfile.mkdtemp(dir=self._test_dir) +@@ -202,6 +238,10 @@ class QEMUMachine(object): + + self._qemu_log_path = None + ++ if self._console_socket is not None: ++ self._console_socket.close() ++ self._console_socket = None ++ + if self._temp_dir is not None: + shutil.rmtree(self._temp_dir) + self._temp_dir = None +@@ -365,3 +405,58 @@ class QEMUMachine(object): + Adds to the list of extra arguments to be given to the QEMU binary + ''' + self._args.extend(args) ++ ++ def set_machine(self, machine_type): ++ ''' ++ Sets the machine type ++ ++ If set, the machine type will be added to the base arguments ++ of the resulting QEMU command line. ++ ''' ++ self._machine = machine_type ++ ++ def set_console(self, device_type=None): ++ ''' ++ Sets the device type for a console device ++ ++ If set, the console device and a backing character device will ++ be added to the base arguments of the resulting QEMU command ++ line. ++ ++ This is a convenience method that will either use the provided ++ device type, of if not given, it will used the device type set ++ on CONSOLE_DEV_TYPES. ++ ++ The actual setting of command line arguments will be be done at ++ machine launch time, as it depends on the temporary directory ++ to be created. ++ ++ @param device_type: the device type, such as "isa-serial" ++ @raises: QEMUMachineAddDeviceError if the device type is not given ++ and can not be determined. ++ ''' ++ if device_type is None: ++ if self._machine is None: ++ raise QEMUMachineAddDeviceError("Can not add a console device:" ++ " QEMU instance without a " ++ "defined machine type") ++ for regex, device in CONSOLE_DEV_TYPES.items(): ++ if re.match(regex, self._machine): ++ device_type = device ++ break ++ if device_type is None: ++ raise QEMUMachineAddDeviceError("Can not add a console device:" ++ " no matching console device " ++ "type definition") ++ self._console_device_type = device_type ++ ++ @property ++ def console_socket(self): ++ """ ++ Returns a socket connected to the console ++ """ ++ if self._console_socket is None: ++ self._console_socket = socket.socket(socket.AF_UNIX, ++ socket.SOCK_STREAM) ++ self._console_socket.connect(self._console_address) ++ return self._console_socket +-- +1.8.3.1 + diff --git a/SOURCES/kvm-scsi-disk-Acquire-the-AioContext-in-scsi_-_realize.patch b/SOURCES/kvm-scsi-disk-Acquire-the-AioContext-in-scsi_-_realize.patch new file mode 100644 index 0000000..0f577ed --- /dev/null +++ b/SOURCES/kvm-scsi-disk-Acquire-the-AioContext-in-scsi_-_realize.patch @@ -0,0 +1,191 @@ +From e47b913ba52ead235fd3a03916d62ce49254c575 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 10 May 2019 13:28:24 +0200 +Subject: [PATCH 21/53] scsi-disk: Acquire the AioContext in scsi_*_realize() + +RH-Author: Markus Armbruster +Message-id: <20190510132825.29833-3-armbru@redhat.com> +Patchwork-id: 87265 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 2/3] scsi-disk: Acquire the AioContext in scsi_*_realize() +Bugzilla: 1673397 1673402 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Max Reitz +RH-Acked-by: Kevin Wolf + +From: Alberto Garcia + +This fixes a crash when attaching two disks with the same blockdev to +a SCSI device that is using iothreads. Test case included. + +Signed-off-by: Alberto Garcia +Signed-off-by: Kevin Wolf +(cherry picked from commit 3ff35ba391134e4e43ab96152deb38a62e62f858) +[Trivial conflict in hw/scsi/scsi-disk.c due to lack of commit +51f43d5792e resolved] +Signed-off-by: Markus Armbruster + +Signed-off-by: Miroslav Rezanina +--- + hw/scsi/scsi-disk.c | 23 ++++++++++++++++++++--- + tests/qemu-iotests/240 | 18 ++++++++++++++++++ + tests/qemu-iotests/240.out | 16 ++++++++++++++++ + 3 files changed, 54 insertions(+), 3 deletions(-) + +diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c +index 8033d7e..6cc5ccc 100644 +--- a/hw/scsi/scsi-disk.c ++++ b/hw/scsi/scsi-disk.c +@@ -2386,10 +2386,13 @@ static void scsi_realize(SCSIDevice *dev, Error **errp) + static void scsi_hd_realize(SCSIDevice *dev, Error **errp) + { + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); ++ AioContext *ctx = NULL; + /* can happen for devices without drive. The error message for missing + * backend will be issued in scsi_realize + */ + if (s->qdev.conf.blk) { ++ ctx = blk_get_aio_context(s->qdev.conf.blk); ++ aio_context_acquire(ctx); + blkconf_blocksizes(&s->qdev.conf); + } + s->qdev.blocksize = s->qdev.conf.logical_block_size; +@@ -2398,11 +2401,15 @@ static void scsi_hd_realize(SCSIDevice *dev, Error **errp) + s->product = g_strdup("QEMU HARDDISK"); + } + scsi_realize(&s->qdev, errp); ++ if (ctx) { ++ aio_context_release(ctx); ++ } + } + + static void scsi_cd_realize(SCSIDevice *dev, Error **errp) + { + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); ++ AioContext *ctx; + int ret; + + if (!dev->conf.blk) { +@@ -2413,6 +2420,8 @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp) + assert(ret == 0); + } + ++ ctx = blk_get_aio_context(dev->conf.blk); ++ aio_context_acquire(ctx); + s->qdev.blocksize = 2048; + s->qdev.type = TYPE_ROM; + s->features |= 1 << SCSI_DISK_F_REMOVABLE; +@@ -2420,6 +2429,7 @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp) + s->product = g_strdup("QEMU CD-ROM"); + } + scsi_realize(&s->qdev, errp); ++ aio_context_release(ctx); + } + + static void scsi_disk_realize(SCSIDevice *dev, Error **errp) +@@ -2558,6 +2568,7 @@ static int get_device_type(SCSIDiskState *s) + static void scsi_block_realize(SCSIDevice *dev, Error **errp) + { + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); ++ AioContext *ctx; + int sg_version; + int rc; + +@@ -2566,6 +2577,9 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp) + return; + } + ++ ctx = blk_get_aio_context(s->qdev.conf.blk); ++ aio_context_acquire(ctx); ++ + /* check we are using a driver managing SG_IO (version 3 and after) */ + rc = blk_ioctl(s->qdev.conf.blk, SG_GET_VERSION_NUM, &sg_version); + if (rc < 0) { +@@ -2573,18 +2587,18 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp) + if (rc != -EPERM) { + error_append_hint(errp, "Is this a SCSI device?\n"); + } +- return; ++ goto out; + } + if (sg_version < 30000) { + error_setg(errp, "scsi generic interface too old"); +- return; ++ goto out; + } + + /* get device type from INQUIRY data */ + rc = get_device_type(s); + if (rc < 0) { + error_setg(errp, "INQUIRY failed"); +- return; ++ goto out; + } + + /* Make a guess for the block size, we'll fix it when the guest sends. +@@ -2604,6 +2618,9 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp) + + scsi_realize(&s->qdev, errp); + scsi_generic_read_device_inquiry(&s->qdev); ++ ++out: ++ aio_context_release(ctx); + } + + typedef struct SCSIBlockReq { +diff --git a/tests/qemu-iotests/240 b/tests/qemu-iotests/240 +index ead7ee0..5d499c9 100755 +--- a/tests/qemu-iotests/240 ++++ b/tests/qemu-iotests/240 +@@ -83,6 +83,24 @@ run_qemu < +Date: Wed, 6 Feb 2019 15:58:29 +0100 +Subject: [PATCH 09/33] scsi-disk: Add device_id property + +RH-Author: Kevin Wolf +Message-id: <20190206155829.14641-3-kwolf@redhat.com> +Patchwork-id: 84254 +O-Subject: [RHEL-7.7/8.0-AV qemu-kvm-rhev PATCH 2/2] scsi-disk: Add device_id property +Bugzilla: 1673080 +RH-Acked-by: Max Reitz +RH-Acked-by: Thomas Huth +RH-Acked-by: Paolo Bonzini + +The new device_id property specifies which value to use for the vendor +specific designator in the Device Identification VPD page. + +In particular, this is necessary for libvirt to maintain guest ABI +compatibility when no serial number is given and a VM is switched from +-drive (where the BlockBackend name is used) to -blockdev (where the +vendor specific designator is left out by default). + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +(cherry picked from commit 7471a649fc3a391dd497297013fb2525ca9821ba) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + hw/scsi/scsi-disk.c | 24 ++++++++++++++++-------- + 1 file changed, 16 insertions(+), 8 deletions(-) + +diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c +index de6c7bc..8033d7e 100644 +--- a/hw/scsi/scsi-disk.c ++++ b/hw/scsi/scsi-disk.c +@@ -103,6 +103,7 @@ typedef struct SCSIDiskState + char *serial; + char *vendor; + char *product; ++ char *device_id; + bool tray_open; + bool tray_locked; + /* +@@ -638,13 +639,8 @@ static int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) + + case 0x83: /* Device identification page, mandatory */ + { +- const char *str = s->serial ?: blk_name(s->qdev.conf.blk); +- int max_len = s->serial ? 20 : 255 - 8; +- int id_len = strlen(str); ++ int id_len = s->device_id ? MIN(strlen(s->device_id), 255 - 8) : 0; + +- if (id_len > max_len) { +- id_len = max_len; +- } + DPRINTF("Inquiry EVPD[Device identification] " + "buffer size %zd\n", req->cmd.xfer); + +@@ -653,7 +649,7 @@ static int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) + outbuf[buflen++] = 0; /* not officially assigned */ + outbuf[buflen++] = 0; /* reserved */ + outbuf[buflen++] = id_len; /* length of data following */ +- memcpy(outbuf + buflen, str, id_len); ++ memcpy(outbuf + buflen, s->device_id, id_len); + buflen += id_len; + } + +@@ -2360,6 +2356,16 @@ static void scsi_realize(SCSIDevice *dev, Error **errp) + if (!s->vendor) { + s->vendor = g_strdup("QEMU"); + } ++ if (!s->device_id) { ++ if (s->serial) { ++ s->device_id = g_strdup_printf("%.20s", s->serial); ++ } else { ++ const char *str = blk_name(s->qdev.conf.blk); ++ if (str && *str) { ++ s->device_id = g_strdup(str); ++ } ++ } ++ } + + if (blk_is_sg(s->qdev.conf.blk)) { + error_setg(errp, "unwanted /dev/sg*"); +@@ -2895,7 +2901,9 @@ static const TypeInfo scsi_disk_base_info = { + DEFINE_PROP_STRING("ver", SCSIDiskState, version), \ + DEFINE_PROP_STRING("serial", SCSIDiskState, serial), \ + DEFINE_PROP_STRING("vendor", SCSIDiskState, vendor), \ +- DEFINE_PROP_STRING("product", SCSIDiskState, product) ++ DEFINE_PROP_STRING("product", SCSIDiskState, product), \ ++ DEFINE_PROP_STRING("device_id", SCSIDiskState, device_id) ++ + + static Property scsi_hd_properties[] = { + DEFINE_SCSI_DISK_PROPERTIES(), +-- +1.8.3.1 + diff --git a/SOURCES/kvm-scsi-disk-Block-Device-Characteristics-emulation-fix.patch b/SOURCES/kvm-scsi-disk-Block-Device-Characteristics-emulation-fix.patch new file mode 100644 index 0000000..ccc47da --- /dev/null +++ b/SOURCES/kvm-scsi-disk-Block-Device-Characteristics-emulation-fix.patch @@ -0,0 +1,83 @@ +From 691b2859207e4cee5e86f0cb86a7a81b9efa2bda Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Wed, 7 Nov 2018 18:00:03 +0100 +Subject: [PATCH 29/34] scsi-disk: Block Device Characteristics emulation fix +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Paolo Bonzini +Message-id: <20181107180007.22954-6-pbonzini@redhat.com> +Patchwork-id: 82945 +O-Subject: [RHEL7.6.z qemu-kvm-rhev PATCH 5/9] scsi-disk: Block Device Characteristics emulation fix +Bugzilla: 1566195 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Philippe Mathieu-Daudé + +From: Daniel Henrique Barboza + +The current BDC VPD page (page 0xb1) is too short. This can be +seen running sg_utils: + +$ sg_vpd --page=bdc /dev/sda +Block device characteristics VPD page (SBC): +Block device characteristics VPD page length too short=8 + +By the SCSI spec, the expected size of the SBC page is 0x40. +There is no telling how the guest will behave with a shorter +message - it can ignore it, or worse, make (wrong) +assumptions. + +This patch fixes the emulation by setting the size to 0x40. +This is the output of the previous sg_vpd command after +applying it: + +$ sg_vpd --page=bdc /dev/sda -v + inquiry cdb: 12 01 b1 00 fc 00 +Block device characteristics VPD page (SBC): + [PQual=0 Peripheral device type: disk] + Medium rotation rate is not reported + Product type: Not specified + WABEREQ=0 + WACEREQ=0 + Nominal form factor not reported + FUAB=0 + VBULS=0 + +To improve readability, this patch also adds the VBULS value +explictly and add comments on the existing fields we're +setting. + +Signed-off-by: Daniel Henrique Barboza +Acked-by: Paolo Bonzini +Signed-off-by: Kevin Wolf +(cherry picked from commit 740842c9656cd5dbc9ccf2ea0c3a74f0ba35144a) +Signed-off-by: Miroslav Rezanina +--- + hw/scsi/scsi-disk.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c +index b3d53ec..b8a27d6 100644 +--- a/hw/scsi/scsi-disk.c ++++ b/hw/scsi/scsi-disk.c +@@ -774,11 +774,12 @@ int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) + } + case 0xb1: /* block device characteristics */ + { +- buflen = 8; ++ buflen = 0x40; + outbuf[4] = (s->rotation_rate >> 8) & 0xff; + outbuf[5] = s->rotation_rate & 0xff; +- outbuf[6] = 0; +- outbuf[7] = 0; ++ outbuf[6] = 0; /* PRODUCT TYPE */ ++ outbuf[7] = 0; /* WABEREQ | WACEREQ | NOMINAL FORM FACTOR */ ++ outbuf[8] = 0; /* VBULS */ + break; + } + case 0xb2: /* thin provisioning */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-scsi-disk-Don-t-use-empty-string-as-device-id.patch b/SOURCES/kvm-scsi-disk-Don-t-use-empty-string-as-device-id.patch new file mode 100644 index 0000000..b734253 --- /dev/null +++ b/SOURCES/kvm-scsi-disk-Don-t-use-empty-string-as-device-id.patch @@ -0,0 +1,71 @@ +From 66d5fd223afbf662589611f3d6a22ff7bcc38bb0 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Wed, 6 Feb 2019 15:58:28 +0100 +Subject: [PATCH 08/33] scsi-disk: Don't use empty string as device id +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Kevin Wolf +Message-id: <20190206155829.14641-2-kwolf@redhat.com> +Patchwork-id: 84253 +O-Subject: [RHEL-7.7/8.0-AV qemu-kvm-rhev PATCH 1/2] scsi-disk: Don't use empty string as device id +Bugzilla: 1673080 +RH-Acked-by: Max Reitz +RH-Acked-by: Thomas Huth +RH-Acked-by: Paolo Bonzini + +scsi-disk includes in the Device Identification VPD page, depending on +configuration amongst others, a vendor specific designator that consists +either of the serial number if given or the BlockBackend name (which is +a host detail that better shouldn't have been leaked to the guest, but +now we have to maintain it for compatibility). + +With anonymous BlockBackends, i.e. scsi-disk devices constructed with +drive=, and no serial number explicitly specified, this ends +up as an empty string. If this happens to more than one disk, we have +accidentally signalled to the OS that this is a multipath setup, which +is obviously not what was intended. + +Instead of using an empty string for the vendor specific designator, +simply leave out that designator, which makes Linux detect such setups +as separate disks again. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +Reviewed-by: Philippe Mathieu-Daudé +(cherry picked from commit a8f58afcdb86e266e06c9dc41a71605e570244c3) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + hw/scsi/scsi-disk.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c +index a20ef91..de6c7bc 100644 +--- a/hw/scsi/scsi-disk.c ++++ b/hw/scsi/scsi-disk.c +@@ -648,12 +648,14 @@ static int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) + DPRINTF("Inquiry EVPD[Device identification] " + "buffer size %zd\n", req->cmd.xfer); + +- outbuf[buflen++] = 0x2; /* ASCII */ +- outbuf[buflen++] = 0; /* not officially assigned */ +- outbuf[buflen++] = 0; /* reserved */ +- outbuf[buflen++] = id_len; /* length of data following */ +- memcpy(outbuf + buflen, str, id_len); +- buflen += id_len; ++ if (id_len) { ++ outbuf[buflen++] = 0x2; /* ASCII */ ++ outbuf[buflen++] = 0; /* not officially assigned */ ++ outbuf[buflen++] = 0; /* reserved */ ++ outbuf[buflen++] = id_len; /* length of data following */ ++ memcpy(outbuf + buflen, str, id_len); ++ buflen += id_len; ++ } + + if (s->qdev.wwn) { + outbuf[buflen++] = 0x1; /* Binary */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-scsi-generic-avoid-invalid-access-to-struct-when-emu.patch b/SOURCES/kvm-scsi-generic-avoid-invalid-access-to-struct-when-emu.patch new file mode 100644 index 0000000..1210410 --- /dev/null +++ b/SOURCES/kvm-scsi-generic-avoid-invalid-access-to-struct-when-emu.patch @@ -0,0 +1,335 @@ +From fd72da696d48452bf9ffc4816e049f4ed18f6117 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Wed, 7 Nov 2018 18:00:06 +0100 +Subject: [PATCH 32/34] scsi-generic: avoid invalid access to struct when + emulating block limits +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Paolo Bonzini +Message-id: <20181107180007.22954-9-pbonzini@redhat.com> +Patchwork-id: 82946 +O-Subject: [RHEL7.6.z qemu-kvm-rhev PATCH 8/9] scsi-generic: avoid invalid access to struct when emulating block limits +Bugzilla: 1566195 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Philippe Mathieu-Daudé + +Emulation of the block limits VPD page called back into scsi-disk.c, +which however expected the request to be for a SCSIDiskState and +accessed a scsi-generic device outside the bounds of its struct +(namely to retrieve s->max_unmap_size and s->max_io_size). + +To avoid this, move the emulation code to a separate function that +takes a new SCSIBlockLimits struct and marshals it into the VPD +response format. + +Reported-by: Max Reitz +Reviewed-by: Max Reitz +Signed-off-by: Paolo Bonzini +(cherry picked from commit 3d4a8bf0eed68a781e06118e4d1df6e2f106a1f2) +Signed-off-by: Miroslav Rezanina +--- + hw/scsi/Makefile.objs | 2 +- + hw/scsi/emulation.c | 42 +++++++++++++++++++++ + hw/scsi/scsi-disk.c | 92 ++++++++++----------------------------------- + hw/scsi/scsi-generic.c | 35 ++++++++++++----- + include/hw/scsi/emulation.h | 16 ++++++++ + include/hw/scsi/scsi.h | 1 - + 6 files changed, 104 insertions(+), 84 deletions(-) + create mode 100644 hw/scsi/emulation.c + create mode 100644 include/hw/scsi/emulation.h + +diff --git a/hw/scsi/Makefile.objs b/hw/scsi/Makefile.objs +index b188f72..c4f249a 100644 +--- a/hw/scsi/Makefile.objs ++++ b/hw/scsi/Makefile.objs +@@ -1,4 +1,4 @@ +-common-obj-y += scsi-disk.o ++common-obj-y += scsi-disk.o emulation.o + common-obj-y += scsi-generic.o scsi-bus.o + common-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o + common-obj-$(CONFIG_MPTSAS_SCSI_PCI) += mptsas.o mptconfig.o mptendian.o +diff --git a/hw/scsi/emulation.c b/hw/scsi/emulation.c +new file mode 100644 +index 0000000..06d62f3 +--- /dev/null ++++ b/hw/scsi/emulation.c +@@ -0,0 +1,42 @@ ++#include "qemu/osdep.h" ++#include "qemu/units.h" ++#include "qemu/bswap.h" ++#include "hw/scsi/emulation.h" ++ ++int scsi_emulate_block_limits(uint8_t *outbuf, const SCSIBlockLimits *bl) ++{ ++ /* required VPD size with unmap support */ ++ memset(outbuf, 0, 0x3c); ++ ++ outbuf[0] = bl->wsnz; /* wsnz */ ++ ++ if (bl->max_io_sectors) { ++ /* optimal transfer length granularity. This field and the optimal ++ * transfer length can't be greater than maximum transfer length. ++ */ ++ stw_be_p(outbuf + 2, MIN(bl->min_io_size, bl->max_io_sectors)); ++ ++ /* maximum transfer length */ ++ stl_be_p(outbuf + 4, bl->max_io_sectors); ++ ++ /* optimal transfer length */ ++ stl_be_p(outbuf + 8, MIN(bl->opt_io_size, bl->max_io_sectors)); ++ } else { ++ stw_be_p(outbuf + 2, bl->min_io_size); ++ stl_be_p(outbuf + 8, bl->opt_io_size); ++ } ++ ++ /* max unmap LBA count */ ++ stl_be_p(outbuf + 16, bl->max_unmap_sectors); ++ ++ /* max unmap descriptors */ ++ stl_be_p(outbuf + 20, bl->max_unmap_descr); ++ ++ /* optimal unmap granularity; alignment is zero */ ++ stl_be_p(outbuf + 24, bl->unmap_sectors); ++ ++ /* max write same size, make it the same as maximum transfer length */ ++ stl_be_p(outbuf + 36, bl->max_io_sectors); ++ ++ return 0x3c; ++} +diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c +index b8a27d6..a20ef91 100644 +--- a/hw/scsi/scsi-disk.c ++++ b/hw/scsi/scsi-disk.c +@@ -32,6 +32,7 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0) + #include "qapi/error.h" + #include "qemu/error-report.h" + #include "hw/scsi/scsi.h" ++#include "hw/scsi/emulation.h" + #include "scsi/constants.h" + #include "sysemu/sysemu.h" + #include "sysemu/block-backend.h" +@@ -585,7 +586,7 @@ static uint8_t *scsi_get_buf(SCSIRequest *req) + return (uint8_t *)r->iov.iov_base; + } + +-int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) ++static int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) + { + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + uint8_t page_code = req->cmd.buf[2]; +@@ -687,89 +688,36 @@ int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) + } + case 0xb0: /* block limits */ + { +- unsigned int unmap_sectors = +- s->qdev.conf.discard_granularity / s->qdev.blocksize; +- unsigned int min_io_size = +- s->qdev.conf.min_io_size / s->qdev.blocksize; +- unsigned int opt_io_size = +- s->qdev.conf.opt_io_size / s->qdev.blocksize; +- unsigned int max_unmap_sectors = +- s->max_unmap_size / s->qdev.blocksize; +- unsigned int max_io_sectors = +- s->max_io_size / s->qdev.blocksize; ++ SCSIBlockLimits bl = {}; + + if (s->qdev.type == TYPE_ROM) { + DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n", + page_code); + return -1; + } ++ bl.wsnz = 1; ++ bl.unmap_sectors = ++ s->qdev.conf.discard_granularity / s->qdev.blocksize; ++ bl.min_io_size = ++ s->qdev.conf.min_io_size / s->qdev.blocksize; ++ bl.opt_io_size = ++ s->qdev.conf.opt_io_size / s->qdev.blocksize; ++ bl.max_unmap_sectors = ++ s->max_unmap_size / s->qdev.blocksize; ++ bl.max_io_sectors = ++ s->max_io_size / s->qdev.blocksize; ++ /* 255 descriptors fit in 4 KiB with an 8-byte header */ ++ bl.max_unmap_descr = 255; ++ + if (s->qdev.type == TYPE_DISK) { + int max_transfer_blk = blk_get_max_transfer(s->qdev.conf.blk); + int max_io_sectors_blk = + max_transfer_blk / s->qdev.blocksize; + +- max_io_sectors = +- MIN_NON_ZERO(max_io_sectors_blk, max_io_sectors); +- +- /* min_io_size and opt_io_size can't be greater than +- * max_io_sectors */ +- if (min_io_size) { +- min_io_size = MIN(min_io_size, max_io_sectors); +- } +- if (opt_io_size) { +- opt_io_size = MIN(opt_io_size, max_io_sectors); +- } ++ bl.max_io_sectors = ++ MIN_NON_ZERO(max_io_sectors_blk, bl.max_io_sectors); + } +- /* required VPD size with unmap support */ +- buflen = 0x40; +- memset(outbuf + 4, 0, buflen - 4); +- +- outbuf[4] = 0x1; /* wsnz */ +- +- /* optimal transfer length granularity */ +- outbuf[6] = (min_io_size >> 8) & 0xff; +- outbuf[7] = min_io_size & 0xff; +- +- /* maximum transfer length */ +- outbuf[8] = (max_io_sectors >> 24) & 0xff; +- outbuf[9] = (max_io_sectors >> 16) & 0xff; +- outbuf[10] = (max_io_sectors >> 8) & 0xff; +- outbuf[11] = max_io_sectors & 0xff; +- +- /* optimal transfer length */ +- outbuf[12] = (opt_io_size >> 24) & 0xff; +- outbuf[13] = (opt_io_size >> 16) & 0xff; +- outbuf[14] = (opt_io_size >> 8) & 0xff; +- outbuf[15] = opt_io_size & 0xff; +- +- /* max unmap LBA count, default is 1GB */ +- outbuf[20] = (max_unmap_sectors >> 24) & 0xff; +- outbuf[21] = (max_unmap_sectors >> 16) & 0xff; +- outbuf[22] = (max_unmap_sectors >> 8) & 0xff; +- outbuf[23] = max_unmap_sectors & 0xff; +- +- /* max unmap descriptors, 255 fit in 4 kb with an 8-byte header */ +- outbuf[24] = 0; +- outbuf[25] = 0; +- outbuf[26] = 0; +- outbuf[27] = 255; +- +- /* optimal unmap granularity */ +- outbuf[28] = (unmap_sectors >> 24) & 0xff; +- outbuf[29] = (unmap_sectors >> 16) & 0xff; +- outbuf[30] = (unmap_sectors >> 8) & 0xff; +- outbuf[31] = unmap_sectors & 0xff; +- +- /* max write same size */ +- outbuf[36] = 0; +- outbuf[37] = 0; +- outbuf[38] = 0; +- outbuf[39] = 0; +- +- outbuf[40] = (max_io_sectors >> 24) & 0xff; +- outbuf[41] = (max_io_sectors >> 16) & 0xff; +- outbuf[42] = (max_io_sectors >> 8) & 0xff; +- outbuf[43] = max_io_sectors & 0xff; ++ buflen += scsi_emulate_block_limits(outbuf + buflen, &bl); + break; + } + case 0xb1: /* block device characteristics */ +diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c +index 6d6db69..f218cef 100644 +--- a/hw/scsi/scsi-generic.c ++++ b/hw/scsi/scsi-generic.c +@@ -16,6 +16,7 @@ + #include "qemu-common.h" + #include "qemu/error-report.h" + #include "hw/scsi/scsi.h" ++#include "hw/scsi/emulation.h" + #include "sysemu/block-backend.h" + #include "sysemu/blockdev.h" + +@@ -182,7 +183,7 @@ static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s) + /* Also take care of the opt xfer len. */ + stl_be_p(&r->buf[12], + MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12]))); +- } else if (page == 0x00 && s->needs_vpd_bl_emulation) { ++ } else if (s->needs_vpd_bl_emulation && page == 0x00) { + /* + * Now we're capable of supplying the VPD Block Limits + * response if the hardware can't. Add it in the INQUIRY +@@ -210,9 +211,24 @@ static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s) + } + } + +-static int scsi_emulate_block_limits(SCSIGenericReq *r) ++static int scsi_generic_emulate_block_limits(SCSIGenericReq *r, SCSIDevice *s) + { +- r->buflen = scsi_disk_emulate_vpd_page(&r->req, r->buf); ++ int len; ++ uint8_t buf[64]; ++ ++ SCSIBlockLimits bl = { ++ .max_io_sectors = blk_get_max_transfer(s->conf.blk) / s->blocksize ++ }; ++ ++ memset(r->buf, 0, r->buflen); ++ stb_p(buf, s->type); ++ stb_p(buf + 1, 0xb0); ++ len = scsi_emulate_block_limits(buf + 4, &bl); ++ assert(len <= sizeof(buf) - 4); ++ stw_be_p(buf + 2, len); ++ ++ memcpy(r->buf, buf, MIN(r->buflen, len + 4)); ++ + r->io_header.sb_len_wr = 0; + + /* +@@ -254,13 +270,12 @@ static void scsi_read_complete(void * opaque, int ret) + * resulted in sense error but would need emulation. + * In this case, emulate a valid VPD response. + */ +- if (s->needs_vpd_bl_emulation) { +- int is_vpd_bl = r->req.cmd.buf[0] == INQUIRY && +- r->req.cmd.buf[1] & 0x01 && +- r->req.cmd.buf[2] == 0xb0; +- +- if (is_vpd_bl && sg_io_sense_from_errno(-ret, &r->io_header, &sense)) { +- len = scsi_emulate_block_limits(r); ++ if (s->needs_vpd_bl_emulation && ++ r->req.cmd.buf[0] == INQUIRY && ++ (r->req.cmd.buf[1] & 0x01) && ++ r->req.cmd.buf[2] == 0xb0) { ++ if (sg_io_sense_from_errno(-ret, &r->io_header, &sense)) { ++ len = scsi_generic_emulate_block_limits(r, s); + /* + * No need to let scsi_read_complete go on and handle an + * INQUIRY VPD BL request we created manually. +diff --git a/include/hw/scsi/emulation.h b/include/hw/scsi/emulation.h +new file mode 100644 +index 0000000..09fba1f +--- /dev/null ++++ b/include/hw/scsi/emulation.h +@@ -0,0 +1,16 @@ ++#ifndef HW_SCSI_EMULATION_H ++#define HW_SCSI_EMULATION_H 1 ++ ++typedef struct SCSIBlockLimits { ++ bool wsnz; ++ uint16_t min_io_size; ++ uint32_t max_unmap_descr; ++ uint32_t opt_io_size; ++ uint32_t max_unmap_sectors; ++ uint32_t unmap_sectors; ++ uint32_t max_io_sectors; ++} SCSIBlockLimits; ++ ++int scsi_emulate_block_limits(uint8_t *outbuf, const SCSIBlockLimits *bl); ++ ++#endif +diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h +index ee3a411..acef25f 100644 +--- a/include/hw/scsi/scsi.h ++++ b/include/hw/scsi/scsi.h +@@ -189,7 +189,6 @@ void scsi_device_report_change(SCSIDevice *dev, SCSISense sense); + void scsi_device_unit_attention_reported(SCSIDevice *dev); + void scsi_generic_read_device_inquiry(SCSIDevice *dev); + int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed); +-int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf); + int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size, + uint8_t *buf, uint8_t buf_size); + SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-scsi-generic-avoid-out-of-bounds-access-to-VPD-page-.patch b/SOURCES/kvm-scsi-generic-avoid-out-of-bounds-access-to-VPD-page-.patch new file mode 100644 index 0000000..120b8a0 --- /dev/null +++ b/SOURCES/kvm-scsi-generic-avoid-out-of-bounds-access-to-VPD-page-.patch @@ -0,0 +1,49 @@ +From 7cf8326f7f34372f1646475f20a2617726aff797 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Wed, 7 Nov 2018 18:00:05 +0100 +Subject: [PATCH 31/34] scsi-generic: avoid out-of-bounds access to VPD page + list +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Paolo Bonzini +Message-id: <20181107180007.22954-8-pbonzini@redhat.com> +Patchwork-id: 82939 +O-Subject: [RHEL7.6.z qemu-kvm-rhev PATCH 7/9] scsi-generic: avoid out-of-bounds access to VPD page list +Bugzilla: 1566195 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Philippe Mathieu-Daudé + +A device can report an excessive number of VPD pages when asked for a +list; this can cause an out-of-bounds access to buf in +scsi_generic_set_vpd_bl_emulation. It should not happen, but +it is technically not incorrect so handle it: do not check any byte +past the allocation length that was sent to the INQUIRY command. + +Reported-by: Max Reitz +Reviewed-by: Max Reitz +Signed-off-by: Paolo Bonzini +(cherry picked from commit 57dbb58d800f62b9e56d946660dba4e8dbd20204) +Signed-off-by: Miroslav Rezanina +--- + hw/scsi/scsi-generic.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c +index 98c6a34..6d6db69 100644 +--- a/hw/scsi/scsi-generic.c ++++ b/hw/scsi/scsi-generic.c +@@ -539,7 +539,7 @@ static void scsi_generic_set_vpd_bl_emulation(SCSIDevice *s) + } + + page_len = buf[3]; +- for (i = 4; i < page_len + 4; i++) { ++ for (i = 4; i < MIN(sizeof(buf), page_len + 4); i++) { + if (buf[i] == 0xb0) { + s->needs_vpd_bl_emulation = false; + return; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-scsi-generic-avoid-possible-out-of-bounds-access-to-.patch b/SOURCES/kvm-scsi-generic-avoid-possible-out-of-bounds-access-to-.patch new file mode 100644 index 0000000..f48d5dd --- /dev/null +++ b/SOURCES/kvm-scsi-generic-avoid-possible-out-of-bounds-access-to-.patch @@ -0,0 +1,88 @@ +From afb03b05279fbb6e405ca1815e419f8ec3650a2c Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 18:42:16 +0100 +Subject: [PATCH 10/33] scsi-generic: avoid possible out-of-bounds access to + r->buf + +RH-Author: John Snow +Message-id: <20190206184216.15861-2-jsnow@redhat.com> +Patchwork-id: 84258 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/1] scsi-generic: avoid possible out-of-bounds access to r->buf +Bugzilla: 1668999 +CVE: CVE-2019-6501/20190111 +RH-Acked-by: Max Reitz +RH-Acked-by: Kevin Wolf +RH-Acked-by: Paolo Bonzini + +From: Paolo Bonzini + +Whenever the allocation length of a SCSI request is shorter than the size of the +VPD page list, page_idx is used blindly to index into r->buf. Even though +the stores in the insertion sort are protected against overflows, the same is not +true of the reads and the final store of 0xb0. + +This basically does the same thing as commit 57dbb58d80 ("scsi-generic: avoid +out-of-bounds access to VPD page list", 2018-11-06), except that here the +allocation length can be chosen by the guest. Note that according to the SCSI +standard, the contents of the PAGE LENGTH field are not altered based +on the allocation length. + +The code was introduced by commit 6c219fc8a1 ("scsi-generic: keep VPD +page list sorted", 2018-11-06) but the overflow was already possible before. + +Reported-by: Kevin Wolf +Reviewed-by: Kevin Wolf +Signed-off-by: Paolo Bonzini +Message-id: 20190111164506.15971-1-pbonzini@redhat.com +Fixes: a71c775b24ebc664129eb1d9b4c360590353efd5 +Signed-off-by: Paolo Bonzini +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + hw/scsi/scsi-generic.c | 18 ++++++++++-------- + 1 file changed, 10 insertions(+), 8 deletions(-) + +diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c +index 4ac53e4..e21adf9 100644 +--- a/hw/scsi/scsi-generic.c ++++ b/hw/scsi/scsi-generic.c +@@ -183,7 +183,7 @@ static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s) + /* Also take care of the opt xfer len. */ + stl_be_p(&r->buf[12], + MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12]))); +- } else if (s->needs_vpd_bl_emulation && page == 0x00) { ++ } else if (s->needs_vpd_bl_emulation && page == 0x00 && r->buflen >= 4) { + /* + * Now we're capable of supplying the VPD Block Limits + * response if the hardware can't. Add it in the INQUIRY +@@ -194,18 +194,20 @@ static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s) + * and will use it to proper setup the SCSI device. + * + * VPD page numbers must be sorted, so insert 0xb0 at the +- * right place with an in-place insert. After the initialization +- * part of the for loop is executed, the device response is +- * at r[0] to r[page_idx - 1]. ++ * right place with an in-place insert. When the while loop ++ * begins the device response is at r[0] to r[page_idx - 1]. + */ +- for (page_idx = lduw_be_p(r->buf + 2) + 4; +- page_idx > 4 && r->buf[page_idx - 1] >= 0xb0; +- page_idx--) { ++ page_idx = lduw_be_p(r->buf + 2) + 4; ++ page_idx = MIN(page_idx, r->buflen); ++ while (page_idx > 4 && r->buf[page_idx - 1] >= 0xb0) { + if (page_idx < r->buflen) { + r->buf[page_idx] = r->buf[page_idx - 1]; + } ++ page_idx--; ++ } ++ if (page_idx < r->buflen) { ++ r->buf[page_idx] = 0xb0; + } +- r->buf[page_idx] = 0xb0; + stw_be_p(r->buf + 2, lduw_be_p(r->buf + 2) + 1); + } + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-scsi-generic-do-not-do-VPD-emulation-for-sense-other.patch b/SOURCES/kvm-scsi-generic-do-not-do-VPD-emulation-for-sense-other.patch new file mode 100644 index 0000000..8125b9e --- /dev/null +++ b/SOURCES/kvm-scsi-generic-do-not-do-VPD-emulation-for-sense-other.patch @@ -0,0 +1,61 @@ +From b6e4458e8c01c680b607495f1408b53c38138db0 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Wed, 7 Nov 2018 18:00:07 +0100 +Subject: [PATCH 33/34] scsi-generic: do not do VPD emulation for sense other + than ILLEGAL_REQUEST +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Paolo Bonzini +Message-id: <20181107180007.22954-10-pbonzini@redhat.com> +Patchwork-id: 82942 +O-Subject: [RHEL7.6.z qemu-kvm-rhev PATCH 9/9] scsi-generic: do not do VPD emulation for sense other than ILLEGAL_REQUEST +Bugzilla: 1566195 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Philippe Mathieu-Daudé + +Pass other sense, such as UNIT_ATTENTION or BUSY, directly to the +guest. + +Reported-by: Max Reitz +Signed-off-by: Paolo Bonzini +(cherry picked from commit 763c56872b08b98fde062a1feca003f200e7bd5c) +Signed-off-by: Miroslav Rezanina +--- + hw/scsi/scsi-generic.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c +index f218cef..4ac53e4 100644 +--- a/hw/scsi/scsi-generic.c ++++ b/hw/scsi/scsi-generic.c +@@ -247,7 +247,6 @@ static void scsi_read_complete(void * opaque, int ret) + { + SCSIGenericReq *r = (SCSIGenericReq *)opaque; + SCSIDevice *s = r->req.dev; +- SCSISense sense; + int len; + + assert(r->req.aiocb != NULL); +@@ -270,11 +269,14 @@ static void scsi_read_complete(void * opaque, int ret) + * resulted in sense error but would need emulation. + * In this case, emulate a valid VPD response. + */ +- if (s->needs_vpd_bl_emulation && ++ if (s->needs_vpd_bl_emulation && ret == 0 && ++ (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) && + r->req.cmd.buf[0] == INQUIRY && + (r->req.cmd.buf[1] & 0x01) && + r->req.cmd.buf[2] == 0xb0) { +- if (sg_io_sense_from_errno(-ret, &r->io_header, &sense)) { ++ SCSISense sense = ++ scsi_parse_sense_buf(r->req.sense, r->io_header.sb_len_wr); ++ if (sense.key == ILLEGAL_REQUEST) { + len = scsi_generic_emulate_block_limits(r, s); + /* + * No need to let scsi_read_complete go on and handle an +-- +1.8.3.1 + diff --git a/SOURCES/kvm-scsi-generic-keep-VPD-page-list-sorted.patch b/SOURCES/kvm-scsi-generic-keep-VPD-page-list-sorted.patch new file mode 100644 index 0000000..7448ced --- /dev/null +++ b/SOURCES/kvm-scsi-generic-keep-VPD-page-list-sorted.patch @@ -0,0 +1,73 @@ +From 4967a91c66070002024af212794ddff2acaa0ad1 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Wed, 7 Nov 2018 18:00:04 +0100 +Subject: [PATCH 30/34] scsi-generic: keep VPD page list sorted +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Paolo Bonzini +Message-id: <20181107180007.22954-7-pbonzini@redhat.com> +Patchwork-id: 82944 +O-Subject: [RHEL7.6.z qemu-kvm-rhev PATCH 6/9] scsi-generic: keep VPD page list sorted +Bugzilla: 1566195 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Philippe Mathieu-Daudé + +Block limits emulation is just placing 0xb0 as the final byte of the +VPD pages list. However, VPD page numbers must be sorted, so change +that to an in-place insert. Since I couldn't find any disk that triggered +the loop more than once, this was tested by adding manually 0xb1 +at the end of the list and checking that 0xb0 was added before. + +Reported-by: Max Reitz +Reviewed-by: Max Reitz +Signed-off-by: Paolo Bonzini +(cherry picked from commit 6c219fc8a112fc69b29f59ea2c7865717ff6e3e0) +Signed-off-by: Miroslav Rezanina +--- + hw/scsi/scsi-generic.c | 19 +++++++++++++++---- + 1 file changed, 15 insertions(+), 4 deletions(-) + +diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c +index 4266003..98c6a34 100644 +--- a/hw/scsi/scsi-generic.c ++++ b/hw/scsi/scsi-generic.c +@@ -145,7 +145,7 @@ static int execute_command(BlockBackend *blk, + + static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s) + { +- uint8_t page, page_len; ++ uint8_t page, page_idx; + + /* + * EVPD set to zero returns the standard INQUIRY data. +@@ -191,10 +191,21 @@ static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s) + * + * This way, the guest kernel will be aware of the support + * and will use it to proper setup the SCSI device. ++ * ++ * VPD page numbers must be sorted, so insert 0xb0 at the ++ * right place with an in-place insert. After the initialization ++ * part of the for loop is executed, the device response is ++ * at r[0] to r[page_idx - 1]. + */ +- page_len = r->buf[3]; +- r->buf[page_len + 4] = 0xb0; +- r->buf[3] = ++page_len; ++ for (page_idx = lduw_be_p(r->buf + 2) + 4; ++ page_idx > 4 && r->buf[page_idx - 1] >= 0xb0; ++ page_idx--) { ++ if (page_idx < r->buflen) { ++ r->buf[page_idx] = r->buf[page_idx - 1]; ++ } ++ } ++ r->buf[page_idx] = 0xb0; ++ stw_be_p(r->buf + 2, lduw_be_p(r->buf + 2) + 1); + } + } + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-scsi-generic-prevent-guest-from-exceeding-SG_IO-limi.patch b/SOURCES/kvm-scsi-generic-prevent-guest-from-exceeding-SG_IO-limi.patch new file mode 100644 index 0000000..e9dccc9 --- /dev/null +++ b/SOURCES/kvm-scsi-generic-prevent-guest-from-exceeding-SG_IO-limi.patch @@ -0,0 +1,91 @@ +From ee5342df3b512da1ac1e85a7a65a50942d220019 Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +Date: Wed, 24 Apr 2019 09:29:42 +0200 +Subject: [PATCH 02/12] scsi-generic: prevent guest from exceeding SG_IO limits + +RH-Author: Stefan Hajnoczi +Message-id: <20190424092942.29071-2-stefanha@redhat.com> +Patchwork-id: 85876 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/1] scsi-generic: prevent guest from exceeding SG_IO limits +Bugzilla: 1693879 +RH-Acked-by: Sergio Lopez Pascual +RH-Acked-by: Pankaj Gupta +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Stefano Garzarella + +From: Paolo Bonzini + +Bugzilla: 1693879 +Brew: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=21260328 +Upstream status: downstream only, see below + +Until Linux 4.5, the kernel placed a limit of UIO_MAXIOV pages on +SG_IO ioctls (and if the limit is exceeded, a confusing ENOMEM error is +returned[1]). The patches that removed the limitation are not easy to +backport to RHEL7, so instead we work around it in QEMU: just prevent +the guest from exceeding these limits by capping the maximum transfer +length to that value in the block limits VPD page. + +The customer has already tested the workaround of changing +max_sectors_kb in the guest. This fix has the same effect on the guest +but does not require manually setting max_sectors_kb inside the guest. + +RHEL8 does not have the problem; because this is for SCSI passthrough, +live migration is not an issue. + +[1] Oh well, at least it was easier to follow the kernel source knowing + it had to end as ENOMEM... + +Signed-off-by: Paolo Bonzini +Signed-off-by: Stefan Hajnoczi +Signed-off-by: Miroslav Rezanina +--- + hw/scsi/scsi-generic.c | 17 +++++++++++++---- + 1 file changed, 13 insertions(+), 4 deletions(-) + +diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c +index e21adf9..b815b91 100644 +--- a/hw/scsi/scsi-generic.c ++++ b/hw/scsi/scsi-generic.c +@@ -144,6 +144,17 @@ static int execute_command(BlockBackend *blk, + return 0; + } + ++/* ++ * RHEL: Linux placed a hard limit on SG_IO transfers equal to UIO_MAXIOV ++ * pages until 4.5, which we need to factor in the block limits we return. ++ */ ++static uint32_t rhel_sg_max_transfer(SCSIDevice *s) ++{ ++ uint32_t max_transfer = blk_get_max_transfer(s->conf.blk); ++ max_transfer = MIN_NON_ZERO(max_transfer, UIO_MAXIOV * qemu_real_host_page_size); ++ return max_transfer; ++} ++ + static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s) + { + uint8_t page, page_idx; +@@ -175,10 +186,8 @@ static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s) + if (s->type == TYPE_DISK && (r->req.cmd.buf[1] & 0x01)) { + page = r->req.cmd.buf[2]; + if (page == 0xb0) { +- uint32_t max_transfer = +- blk_get_max_transfer(s->conf.blk) / s->blocksize; ++ uint32_t max_transfer = rhel_sg_max_transfer(s) / s->blocksize; + +- assert(max_transfer); + stl_be_p(&r->buf[8], max_transfer); + /* Also take care of the opt xfer len. */ + stl_be_p(&r->buf[12], +@@ -219,7 +228,7 @@ static int scsi_generic_emulate_block_limits(SCSIGenericReq *r, SCSIDevice *s) + uint8_t buf[64]; + + SCSIBlockLimits bl = { +- .max_io_sectors = blk_get_max_transfer(s->conf.blk) / s->blocksize ++ .max_io_sectors = rhel_sg_max_transfer(s) / s->blocksize + }; + + memset(r->buf, 0, r->buflen); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-setup b/SOURCES/kvm-setup new file mode 100644 index 0000000..e59a643 --- /dev/null +++ b/SOURCES/kvm-setup @@ -0,0 +1,55 @@ +#!/bin/bash + +kvm_setup_powerpc () { + isPowerNV=no + isPOWER8=no + + grep -q '^platform[[:space:]]*:[[:space:]]*PowerNV' /proc/cpuinfo && isPowerNV=yes + grep -q '^cpu[[:space:]]*:[[:space:]]*POWER8' /proc/cpuinfo && isPOWER8=yes + + if [ "$isPowerNV" = "yes" ] ; then + # PowerNV platform, which is KVM HV capable + + if [ -z "$SUBCORES" ]; then + SUBCORES=1 + fi + + # Step 1. Load the KVM HVmodule + if ! modprobe -b kvm_hv; then + return + fi + + # On POWER8 a host core can only run threads of a single + # guest, meaning that SMT must be disabled on the host in + # order to run KVM guests. (Also applieds to POWER7, but we + # don't support that). + # + # Additionally, POWER8 guests can't benefit from transparent + # hugepages used to back them, and THPs allocated by any app + # can potentially interfere with HPT allocation (if they + # become locked, they can fragment the CMA). So, also disable + # THPs system wide. + # + # POWER9 doesn't have this limitation (though it will for hash + # guests on radix host when that's implemented). So, only set + # up subcores and disable SMT for POWER*. + if [ "$isPOWER8" = "yes" ] ; then + # Step 2. Configure subcore mode + /usr/sbin/ppc64_cpu --subcores-per-core=$SUBCORES + + # Step 3. Disable SMT (multithreading) + /usr/sbin/ppc64_cpu --smt=off + + # Step 4. Disable transparent hugepages (THP) + echo never > /sys/kernel/mm/transparent_hugepage/enabled + fi + fi +} + +case $(uname -m) in + ppc64|ppc64le) + kvm_setup_powerpc + ;; +esac + +exit 0 diff --git a/SOURCES/kvm-setup-ppc64.sysconfig b/SOURCES/kvm-setup-ppc64.sysconfig new file mode 100644 index 0000000..7fb22d4 --- /dev/null +++ b/SOURCES/kvm-setup-ppc64.sysconfig @@ -0,0 +1,3 @@ +# POWER8 parameter +# Set the number of subcores per core +#SUBCORES=1 diff --git a/SOURCES/kvm-setup.service b/SOURCES/kvm-setup.service new file mode 100644 index 0000000..9c4bf97 --- /dev/null +++ b/SOURCES/kvm-setup.service @@ -0,0 +1,14 @@ +[Unit] +Description=Perform system configuration to prepare system to run KVM guests +# Offlining CPUs can cause irqbalance to throw warnings if it's running +Before=irqbalance.service +# libvirtd reads CPU topology at startup, so change it before +Before=libvirtd.service + +[Service] +Type=oneshot +EnvironmentFile=-/etc/sysconfig/kvm +ExecStart=/usr/lib/systemd/kvm-setup + +[Install] +WantedBy=multi-user.target diff --git a/SOURCES/kvm-sheepdog-Fix-sd_co_create_opts-memory-leaks.patch b/SOURCES/kvm-sheepdog-Fix-sd_co_create_opts-memory-leaks.patch new file mode 100644 index 0000000..d3c892a --- /dev/null +++ b/SOURCES/kvm-sheepdog-Fix-sd_co_create_opts-memory-leaks.patch @@ -0,0 +1,63 @@ +From d9da0aedd0dc6aeee5751855eda3560f1757910c Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:47:44 +0200 +Subject: [PATCH 15/89] sheepdog: Fix sd_co_create_opts() memory leaks + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-2-kwolf@redhat.com> +Patchwork-id: 81074 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 01/73] sheepdog: Fix sd_co_create_opts() memory leaks +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Both the option string for the 'redundancy' option and the +SheepdogRedundancy object that is created accordingly could be leaked in +error paths. This fixes the memory leaks. + +Reported by Coverity (CID 1390614 and 1390641). + +Signed-off-by: Kevin Wolf +Message-id: 20180503153509.22223-1-kwolf@redhat.com +Reviewed-by: Jeff Cody +Signed-off-by: Jeff Cody +(cherry picked from commit a2cb9239b7610ffb00f9ced5cd7640d40b0e1ccf) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/sheepdog.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/block/sheepdog.c b/block/sheepdog.c +index d1c9bf5..933880c 100644 +--- a/block/sheepdog.c ++++ b/block/sheepdog.c +@@ -1977,6 +1977,7 @@ static SheepdogRedundancy *parse_redundancy_str(const char *opt) + } else { + ret = qemu_strtol(n2, NULL, 10, &parity); + if (ret < 0) { ++ g_free(redundancy); + return NULL; + } + +@@ -2172,7 +2173,7 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, + BlockdevCreateOptions *create_options = NULL; + QDict *qdict, *location_qdict; + Visitor *v; +- const char *redundancy; ++ char *redundancy; + Error *local_err = NULL; + int ret; + +@@ -2240,6 +2241,7 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, + fail: + qapi_free_BlockdevCreateOptions(create_options); + qobject_unref(qdict); ++ g_free(redundancy); + return ret; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-slirp-Correct-size-check-in-m_inc.patch b/SOURCES/kvm-slirp-Correct-size-check-in-m_inc.patch new file mode 100644 index 0000000..7804fd1 --- /dev/null +++ b/SOURCES/kvm-slirp-Correct-size-check-in-m_inc.patch @@ -0,0 +1,66 @@ +From 44dfd8eea4b562e77587cdd1b2ae6b6decc870f9 Mon Sep 17 00:00:00 2001 +From: Xiao Wang +Date: Wed, 8 Aug 2018 07:52:57 +0200 +Subject: [PATCH 2/5] slirp: Correct size check in m_inc() + +RH-Author: Xiao Wang +Message-id: <1533714777-24827-3-git-send-email-jasowang@redhat.com> +Patchwork-id: 81674 +O-Subject: [RHEL-7.6/7.5z qemu-kvm-rhev 2/2] slirp: Correct size check in m_inc() +Bugzilla: 1586255 +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: wexu@redhat.com +RH-Acked-by: Thomas Huth + +From: Peter Maydell + +The data in an mbuf buffer is not necessarily at the start of the +allocated buffer. (For instance m_adj() allows data to be trimmed +from the start by just advancing the pointer and reducing the length.) +This means that the allocated buffer size (m->m_size) and the +amount of space from the m_data pointer to the end of the +buffer (M_ROOM(m)) are not necessarily the same. + +Commit 864036e251f54c9 tried to change the m_inc() function from +taking the new allocated-buffer-size to taking the new room-size, +but forgot to change the initial "do we already have enough space" +check. This meant that if we were trying to extend a buffer which +had a leading gap between the buffer start and the data, we might +incorrectly decide it didn't need to be extended, and then +overrun the end of the buffer, causing memory corruption and +an eventual crash. + +Change the "already big enough?" condition from checking the +argument against m->m_size to checking against M_ROOM(). +This only makes a difference for the callsite in m_cat(); +the other three callsites all start with a freshly allocated +mbuf from m_get(), which will have m->m_size == M_ROOM(m). + +Fixes: 864036e251f54c9 +Fixes: https://bugs.launchpad.net/qemu/+bug/1785670 +Signed-off-by: Peter Maydell +Reviewed-by: Samuel Thibault +Message-id: 20180807114501.12370-1-peter.maydell@linaro.org +Tested-by: Dr. David Alan Gilbert +(cherry picked from commit 09b94ac0f29db3b022a77a5aa50dc9e37032689d) +Signed-off-by: Miroslav Rezanina +--- + slirp/mbuf.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/slirp/mbuf.c b/slirp/mbuf.c +index 0c189e1..1b78683 100644 +--- a/slirp/mbuf.c ++++ b/slirp/mbuf.c +@@ -154,7 +154,7 @@ m_inc(struct mbuf *m, int size) + int datasize; + + /* some compilers throw up on gotos. This one we can fake. */ +- if (m->m_size > size) { ++ if (M_ROOM(m) > size) { + return; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-slirp-check-sscanf-result-when-emulating-ident.patch b/SOURCES/kvm-slirp-check-sscanf-result-when-emulating-ident.patch new file mode 100644 index 0000000..f7c737b --- /dev/null +++ b/SOURCES/kvm-slirp-check-sscanf-result-when-emulating-ident.patch @@ -0,0 +1,62 @@ +From cbf833ce3cbbd9162c22573278497e5b8bd1ccb4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= +Date: Tue, 2 Apr 2019 14:03:50 +0200 +Subject: [PATCH 131/163] slirp: check sscanf result when emulating ident +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Marc-André Lureau +Message-id: <20190402140350.5604-1-marcandre.lureau@redhat.com> +Patchwork-id: 85306 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH] slirp: check sscanf result when emulating ident +Bugzilla: 1689793 +RH-Acked-by: Markus Armbruster +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Dr. David Alan Gilbert + +From: William Bowling + +When emulating ident in tcp_emu, if the strchr checks passed but the +sscanf check failed, two uninitialized variables would be copied and +sent in the reply, so move this code inside the if(sscanf()) clause. + +Signed-off-by: William Bowling +Cc: qemu-stable@nongnu.org +Cc: secalert@redhat.com +Message-Id: <1551476756-25749-1-git-send-email-will@wbowling.info> +Signed-off-by: Samuel Thibault +Reviewed-by: Philippe Mathieu-Daudé + +(cherry picked from commit d3222975c7d6cda9e25809dea05241188457b113) +Signed-off-by: Marc-André Lureau +Signed-off-by: Miroslav Rezanina +--- + slirp/tcp_subr.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c +index da0d537..98ceb4f 100644 +--- a/slirp/tcp_subr.c ++++ b/slirp/tcp_subr.c +@@ -660,12 +660,12 @@ tcp_emu(struct socket *so, struct mbuf *m) + break; + } + } ++ so_rcv->sb_cc = snprintf(so_rcv->sb_data, ++ so_rcv->sb_datalen, ++ "%d,%d\r\n", n1, n2); ++ so_rcv->sb_rptr = so_rcv->sb_data; ++ so_rcv->sb_wptr = so_rcv->sb_data + so_rcv->sb_cc; + } +- so_rcv->sb_cc = snprintf(so_rcv->sb_data, +- so_rcv->sb_datalen, +- "%d,%d\r\n", n1, n2); +- so_rcv->sb_rptr = so_rcv->sb_data; +- so_rcv->sb_wptr = so_rcv->sb_data + so_rcv->sb_cc; + } + m_free(m); + return 0; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-slirp-correct-size-computation-while-concatenating-m.patch b/SOURCES/kvm-slirp-correct-size-computation-while-concatenating-m.patch new file mode 100644 index 0000000..32e5e9f --- /dev/null +++ b/SOURCES/kvm-slirp-correct-size-computation-while-concatenating-m.patch @@ -0,0 +1,110 @@ +From 34a609b26d1c20f15b168ebf889705886f9c7d1d Mon Sep 17 00:00:00 2001 +From: Xiao Wang +Date: Mon, 30 Jul 2018 08:06:27 +0200 +Subject: [PATCH 13/15] slirp: correct size computation while concatenating + mbuf + +RH-Author: Xiao Wang +Message-id: <1532937987-25050-1-git-send-email-jasowang@redhat.com> +Patchwork-id: 81544 +O-Subject: [RHEL-7.6/7.5z qemu-kvm-rhev] slirp: correct size computation while concatenating mbuf +Bugzilla: 1586255 +RH-Acked-by: wexu@redhat.com +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Prasad J Pandit + +While reassembling incoming fragmented datagrams, 'm_cat' routine +extends the 'mbuf' buffer, if it has insufficient room. It computes +a wrong buffer size, which leads to overwriting adjacent heap buffer +area. Correct this size computation in m_cat. + +Reported-by: ZDI Disclosures +Signed-off-by: Prasad J Pandit +Signed-off-by: Samuel Thibault +(cherry picked from commit 864036e251f54c99d31df124aad7f34f01f5344c) +Signed-off-by: Miroslav Rezanina +--- + slirp/mbuf.c | 11 +++++------ + slirp/mbuf.h | 8 +++----- + 2 files changed, 8 insertions(+), 11 deletions(-) + +diff --git a/slirp/mbuf.c b/slirp/mbuf.c +index 5ff2455..18cbf75 100644 +--- a/slirp/mbuf.c ++++ b/slirp/mbuf.c +@@ -138,7 +138,7 @@ m_cat(struct mbuf *m, struct mbuf *n) + * If there's no room, realloc + */ + if (M_FREEROOM(m) < n->m_len) +- m_inc(m,m->m_size+MINCSIZE); ++ m_inc(m, m->m_len + n->m_len); + + memcpy(m->m_data+m->m_len, n->m_data, n->m_len); + m->m_len += n->m_len; +@@ -147,7 +147,7 @@ m_cat(struct mbuf *m, struct mbuf *n) + } + + +-/* make m size bytes large */ ++/* make m 'size' bytes large from m_data */ + void + m_inc(struct mbuf *m, int size) + { +@@ -158,12 +158,12 @@ m_inc(struct mbuf *m, int size) + + if (m->m_flags & M_EXT) { + datasize = m->m_data - m->m_ext; +- m->m_ext = g_realloc(m->m_ext, size); ++ m->m_ext = g_realloc(m->m_ext, size + datasize); + m->m_data = m->m_ext + datasize; + } else { + char *dat; + datasize = m->m_data - m->m_dat; +- dat = g_malloc(size); ++ dat = g_malloc(size + datasize); + memcpy(dat, m->m_dat, m->m_size); + + m->m_ext = dat; +@@ -171,8 +171,7 @@ m_inc(struct mbuf *m, int size) + m->m_flags |= M_EXT; + } + +- m->m_size = size; +- ++ m->m_size = size + datasize; + } + + +diff --git a/slirp/mbuf.h b/slirp/mbuf.h +index 893601f..33b8448 100644 +--- a/slirp/mbuf.h ++++ b/slirp/mbuf.h +@@ -33,8 +33,6 @@ + #ifndef MBUF_H + #define MBUF_H + +-#define MINCSIZE 4096 /* Amount to increase mbuf if too small */ +- + /* + * Macros for type conversion + * mtod(m,t) - convert mbuf pointer to data pointer of correct type +@@ -72,11 +70,11 @@ struct mbuf { + struct mbuf *m_prevpkt; /* Flags aren't used in the output queue */ + int m_flags; /* Misc flags */ + +- int m_size; /* Size of data */ ++ int m_size; /* Size of mbuf, from m_dat or m_ext */ + struct socket *m_so; + +- caddr_t m_data; /* Location of data */ +- int m_len; /* Amount of data in this mbuf */ ++ caddr_t m_data; /* Current location of data */ ++ int m_len; /* Amount of data in this mbuf, from m_data */ + + Slirp *slirp; + bool resolution_requested; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-slirp-don-t-manipulate-so_rcv-in-tcp_emu.patch b/SOURCES/kvm-slirp-don-t-manipulate-so_rcv-in-tcp_emu.patch new file mode 100644 index 0000000..35ad2bb --- /dev/null +++ b/SOURCES/kvm-slirp-don-t-manipulate-so_rcv-in-tcp_emu.patch @@ -0,0 +1,127 @@ +From d92a9042320bd959eac36dc76de9dc293d8da2ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= +Date: Mon, 20 May 2019 17:57:04 +0200 +Subject: [PATCH 3/3] slirp: don't manipulate so_rcv in tcp_emu() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Marc-André Lureau +Message-id: <20190520175704.6250-4-marcandre.lureau@redhat.com> +Patchwork-id: 88108 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 3/3] slirp: don't manipulate so_rcv in tcp_emu() +Bugzilla: 1669071 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Stefano Garzarella + +For some reason, EMU_IDENT is not like other "emulated" protocols and +tries to reconstitute the original buffer, if it came in multiple +packets. Unfortunately, it does so wrongly, as it doesn't respect the +sbuf circular buffer appending rules, nor does it maintain some of the +invariants (rptr is incremented without bounds, etc): this leads to +further memory corruption revealed by ASAN or various malloc +errors. Furthermore, the so_rcv buffer is regularly flushed, so there +is no guarantee that buffer reconstruction will do what is expected. + +Instead, do what the function comment says: "XXX Assumes the whole +command came in one packet", and don't touch so_rcv. + +Related to: https://bugzilla.redhat.com/show_bug.cgi?id=1664205 + +Cc: Prasad J Pandit +Signed-off-by: Marc-André Lureau + +(cherry picked from libslirp commit +9da0da837780f825b5db31db6620492f8b7cd5d6) + +[ MA - backported with style conflicts, and without qemu commit +a7104eda7dab99d0cdbd3595c211864cba415905 which is unnecessary with +this patch ] + +Signed-off-by: Marc-André Lureau +Signed-off-by: Miroslav Rezanina +--- + slirp/tcp_subr.c | 63 +++++++++++++++++++++++++++----------------------------- + 1 file changed, 30 insertions(+), 33 deletions(-) + +diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c +index a801a14..0152f72 100644 +--- a/slirp/tcp_subr.c ++++ b/slirp/tcp_subr.c +@@ -636,42 +636,39 @@ tcp_emu(struct socket *so, struct mbuf *m) + struct socket *tmpso; + struct sockaddr_in addr; + socklen_t addrlen = sizeof(struct sockaddr_in); +- struct sbuf *so_rcv = &so->so_rcv; +- +- memcpy(so_rcv->sb_wptr, m->m_data, m->m_len); +- so_rcv->sb_wptr += m->m_len; +- so_rcv->sb_rptr += m->m_len; +- m_inc(m, m->m_len + 1); +- m->m_data[m->m_len] = 0; /* NULL terminate */ +- if (strchr(m->m_data, '\r') || strchr(m->m_data, '\n')) { +- if (sscanf(so_rcv->sb_data, "%u%*[ ,]%u", &n1, &n2) == 2) { +- HTONS(n1); +- HTONS(n2); +- /* n2 is the one on our host */ +- for (tmpso = slirp->tcb.so_next; +- tmpso != &slirp->tcb; +- tmpso = tmpso->so_next) { +- if (tmpso->so_laddr.s_addr == so->so_laddr.s_addr && +- tmpso->so_lport == n2 && +- tmpso->so_faddr.s_addr == so->so_faddr.s_addr && +- tmpso->so_fport == n1) { +- if (getsockname(tmpso->s, +- (struct sockaddr *)&addr, &addrlen) == 0) +- n2 = addr.sin_port; +- break; +- } ++ char *eol = g_strstr_len(m->m_data, m->m_len, "\r\n"); ++ ++ if (!eol) { ++ return 1; ++ } ++ ++ *eol = '\0'; ++ if (sscanf(m->m_data, "%u%*[ ,]%u", &n1, &n2) == 2) { ++ HTONS(n1); ++ HTONS(n2); ++ /* n2 is the one on our host */ ++ for (tmpso = slirp->tcb.so_next; tmpso != &slirp->tcb; ++ tmpso = tmpso->so_next) { ++ if (tmpso->so_laddr.s_addr == so->so_laddr.s_addr && ++ tmpso->so_lport == n2 && ++ tmpso->so_faddr.s_addr == so->so_faddr.s_addr && ++ tmpso->so_fport == n1) { ++ if (getsockname(tmpso->s, (struct sockaddr *)&addr, ++ &addrlen) == 0) ++ n2 = addr.sin_port; ++ break; + } +- NTOHS(n1); +- NTOHS(n2); +- so_rcv->sb_cc = snprintf(so_rcv->sb_data, +- so_rcv->sb_datalen, +- "%d,%d\r\n", n1, n2); +- so_rcv->sb_rptr = so_rcv->sb_data; +- so_rcv->sb_wptr = so_rcv->sb_data + so_rcv->sb_cc; + } ++ NTOHS(n1); ++ NTOHS(n2); ++ m_inc(m, snprintf(NULL, 0, "%d,%d\r\n", n1, n2) + 1); ++ m->m_len = snprintf(m->m_data, M_ROOM(m), "%d,%d\r\n", n1, n2); ++ assert(m->m_len < M_ROOM(m)); ++ } else { ++ *eol = '\r'; + } +- m_free(m); +- return 0; ++ ++ return 1; + } + + case EMU_FTP: /* ftp */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-slirp-ensure-there-is-enough-space-in-mbuf-to-null-t.patch b/SOURCES/kvm-slirp-ensure-there-is-enough-space-in-mbuf-to-null-t.patch new file mode 100644 index 0000000..aec1fa7 --- /dev/null +++ b/SOURCES/kvm-slirp-ensure-there-is-enough-space-in-mbuf-to-null-t.patch @@ -0,0 +1,66 @@ +From dec442886deeedfbcb0c9958403627bc11a8d9e9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= +Date: Mon, 20 May 2019 17:57:03 +0200 +Subject: [PATCH 2/3] slirp: ensure there is enough space in mbuf to + null-terminate +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Marc-André Lureau +Message-id: <20190520175704.6250-3-marcandre.lureau@redhat.com> +Patchwork-id: 88109 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 2/3] slirp: ensure there is enough space in mbuf to null-terminate +Bugzilla: 1669071 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Stefano Garzarella + +Prevents from buffer overflows. +Related to: https://bugzilla.redhat.com/show_bug.cgi?id=1664205 + +Cc: Prasad J Pandit +Signed-off-by: Marc-André Lureau + +(cherry picked from libslirp commit +306fef58b54d793ba4b259728c21322765bda917) + +[ MA - backported with style conflicts fixes ] +Signed-off-by: Marc-André Lureau + +Signed-off-by: Miroslav Rezanina +--- + slirp/tcp_subr.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c +index a61632d..a801a14 100644 +--- a/slirp/tcp_subr.c ++++ b/slirp/tcp_subr.c +@@ -641,6 +641,7 @@ tcp_emu(struct socket *so, struct mbuf *m) + memcpy(so_rcv->sb_wptr, m->m_data, m->m_len); + so_rcv->sb_wptr += m->m_len; + so_rcv->sb_rptr += m->m_len; ++ m_inc(m, m->m_len + 1); + m->m_data[m->m_len] = 0; /* NULL terminate */ + if (strchr(m->m_data, '\r') || strchr(m->m_data, '\n')) { + if (sscanf(so_rcv->sb_data, "%u%*[ ,]%u", &n1, &n2) == 2) { +@@ -674,6 +675,7 @@ tcp_emu(struct socket *so, struct mbuf *m) + } + + case EMU_FTP: /* ftp */ ++ m_inc(m, m->m_len + 1); + *(m->m_data+m->m_len) = 0; /* NUL terminate for strstr */ + if ((bptr = (char *)strstr(m->m_data, "ORT")) != NULL) { + /* +@@ -771,6 +773,7 @@ tcp_emu(struct socket *so, struct mbuf *m) + /* + * Need to emulate DCC CHAT, DCC SEND and DCC MOVE + */ ++ m_inc(m, m->m_len + 1); + *(m->m_data+m->m_len) = 0; /* NULL terminate the string for strstr */ + if ((bptr = (char *)strstr(m->m_data, "DCC")) == NULL) + return 1; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-slirp-fix-big-little-endian-conversion-in-ident-prot.patch b/SOURCES/kvm-slirp-fix-big-little-endian-conversion-in-ident-prot.patch new file mode 100644 index 0000000..71f89b9 --- /dev/null +++ b/SOURCES/kvm-slirp-fix-big-little-endian-conversion-in-ident-prot.patch @@ -0,0 +1,54 @@ +From ea9ac12820138a0a9714178bee36e625ed103026 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= +Date: Mon, 20 May 2019 17:57:02 +0200 +Subject: [PATCH 1/3] slirp: fix big/little endian conversion in ident protocol +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Marc-André Lureau +Message-id: <20190520175704.6250-2-marcandre.lureau@redhat.com> +Patchwork-id: 88107 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/3] slirp: fix big/little endian conversion in ident protocol +Bugzilla: 1669071 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Stefano Garzarella + +From: Samuel Thibault + +Signed-off-by: Samuel Thibault +Reviewed-by: Philippe Mathieu-Daudé + +[ MA - backported to ease backport of + https://bugzilla.redhat.com/show_bug.cgi?id=1669068 ] + +(cherry picked from 1fd71067dae501f1c78618e9583c6cc72db0cfa6) +Signed-off-by: Marc-André Lureau + +Signed-off-by: Miroslav Rezanina +--- + slirp/tcp_subr.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c +index 98ceb4f..a61632d 100644 +--- a/slirp/tcp_subr.c ++++ b/slirp/tcp_subr.c +@@ -656,10 +656,12 @@ tcp_emu(struct socket *so, struct mbuf *m) + tmpso->so_fport == n1) { + if (getsockname(tmpso->s, + (struct sockaddr *)&addr, &addrlen) == 0) +- n2 = ntohs(addr.sin_port); ++ n2 = addr.sin_port; + break; + } + } ++ NTOHS(n1); ++ NTOHS(n2); + so_rcv->sb_cc = snprintf(so_rcv->sb_data, + so_rcv->sb_datalen, + "%d,%d\r\n", n1, n2); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-slirp-reformat-m_inc-routine.patch b/SOURCES/kvm-slirp-reformat-m_inc-routine.patch new file mode 100644 index 0000000..06a82cd --- /dev/null +++ b/SOURCES/kvm-slirp-reformat-m_inc-routine.patch @@ -0,0 +1,78 @@ +From 37e3851f6eac7999e7c5a7cfd41d53f72e47b03d Mon Sep 17 00:00:00 2001 +From: Xiao Wang +Date: Wed, 8 Aug 2018 07:52:56 +0200 +Subject: [PATCH 1/5] slirp: reformat m_inc routine + +RH-Author: Xiao Wang +Message-id: <1533714777-24827-2-git-send-email-jasowang@redhat.com> +Patchwork-id: 81675 +O-Subject: [RHEL-7.6/7.5z qemu-kvm-rhev 1/2] slirp: reformat m_inc routine +Bugzilla: 1586255 +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: wexu@redhat.com +RH-Acked-by: Thomas Huth + +From: Prasad J Pandit + +Coding style changes to the m_inc routine and minor refactoring. + +Reported-by: ZDI Disclosures +Signed-off-by: Prasad J Pandit +Signed-off-by: Samuel Thibault +(cherry picked from commit c22098c74a09164797fae6511c5eaf68f32c4dd8) +Signed-off-by: Miroslav Rezanina +--- + slirp/mbuf.c | 34 ++++++++++++++++------------------ + 1 file changed, 16 insertions(+), 18 deletions(-) + +diff --git a/slirp/mbuf.c b/slirp/mbuf.c +index 18cbf75..0c189e1 100644 +--- a/slirp/mbuf.c ++++ b/slirp/mbuf.c +@@ -151,27 +151,25 @@ m_cat(struct mbuf *m, struct mbuf *n) + void + m_inc(struct mbuf *m, int size) + { +- int datasize; ++ int datasize; + +- /* some compiles throw up on gotos. This one we can fake. */ +- if(m->m_size>size) return; ++ /* some compilers throw up on gotos. This one we can fake. */ ++ if (m->m_size > size) { ++ return; ++ } + +- if (m->m_flags & M_EXT) { +- datasize = m->m_data - m->m_ext; +- m->m_ext = g_realloc(m->m_ext, size + datasize); +- m->m_data = m->m_ext + datasize; +- } else { +- char *dat; +- datasize = m->m_data - m->m_dat; +- dat = g_malloc(size + datasize); +- memcpy(dat, m->m_dat, m->m_size); +- +- m->m_ext = dat; +- m->m_data = m->m_ext + datasize; +- m->m_flags |= M_EXT; +- } ++ if (m->m_flags & M_EXT) { ++ datasize = m->m_data - m->m_ext; ++ m->m_ext = g_realloc(m->m_ext, size + datasize); ++ } else { ++ datasize = m->m_data - m->m_dat; ++ m->m_ext = g_malloc(size + datasize); ++ memcpy(m->m_ext, m->m_dat, m->m_size); ++ m->m_flags |= M_EXT; ++ } + +- m->m_size = size + datasize; ++ m->m_data = m->m_ext + datasize; ++ m->m_size = size + datasize; + } + + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-spapr-Add-H-Call-H_HOME_NODE_ASSOCIATIVITY.patch b/SOURCES/kvm-spapr-Add-H-Call-H_HOME_NODE_ASSOCIATIVITY.patch new file mode 100644 index 0000000..22af4ef --- /dev/null +++ b/SOURCES/kvm-spapr-Add-H-Call-H_HOME_NODE_ASSOCIATIVITY.patch @@ -0,0 +1,123 @@ +From 3e4f22bfb8e4a6a4da948983c96fe60c12c957a3 Mon Sep 17 00:00:00 2001 +From: Laurent Vivier +Date: Wed, 2 Jan 2019 11:29:48 +0100 +Subject: [PATCH 2/8] spapr: Add H-Call H_HOME_NODE_ASSOCIATIVITY + +RH-Author: Laurent Vivier +Message-id: <20190102112948.18536-3-lvivier@redhat.com> +Patchwork-id: 83821 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 2/2] spapr: Add H-Call H_HOME_NODE_ASSOCIATIVITY +Bugzilla: 1626347 +RH-Acked-by: Thomas Huth +RH-Acked-by: Serhii Popovych +RH-Acked-by: David Gibson + +BZ: https://bugzilla.redhat.com/show_bug.cgi?id=1626347 + +H_HOME_NODE_ASSOCIATIVITY H-Call returns the associativity domain +designation associated with the identifier input parameter + +This fixes a crash when we try to hotplug a CPU in memory-less and +CPU-less numa node. In this case, the kernel tries to online the +node, but without the information provided by this h-call, the node id, +it cannot and the CPU is started while the node is not onlined. + +It also removes the warning message from the kernel: + VPHN is not supported. Disabling polling.. + +Signed-off-by: Laurent Vivier +Reviewed-by: Greg Kurz +Signed-off-by: David Gibson +(cherry picked from commit c24ba3d0a34f68ad2c6bf1a15bc43770005f6cc0) +Signed-off-by: Miroslav Rezanina +--- + hw/ppc/spapr.c | 1 + + hw/ppc/spapr_hcall.c | 40 ++++++++++++++++++++++++++++++++++++++++ + include/hw/ppc/spapr.h | 1 + + 3 files changed, 42 insertions(+) + +diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c +index b49f377..76676aa 100644 +--- a/hw/ppc/spapr.c ++++ b/hw/ppc/spapr.c +@@ -933,6 +933,7 @@ static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt) + add_str(hypertas, "hcall-sprg0"); + add_str(hypertas, "hcall-copy"); + add_str(hypertas, "hcall-debug"); ++ add_str(hypertas, "hcall-vphn"); + add_str(qemu_hypertas, "hcall-memop1"); + + if (!kvm_enabled() || kvmppc_spapr_use_multitce()) { +diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c +index 16bccdd..9dd5cfd 100644 +--- a/hw/ppc/spapr_hcall.c ++++ b/hw/ppc/spapr_hcall.c +@@ -1664,6 +1664,42 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, + return H_SUCCESS; + } + ++static target_ulong h_home_node_associativity(PowerPCCPU *cpu, ++ sPAPRMachineState *spapr, ++ target_ulong opcode, ++ target_ulong *args) ++{ ++ target_ulong flags = args[0]; ++ target_ulong procno = args[1]; ++ PowerPCCPU *tcpu; ++ int idx; ++ ++ /* only support procno from H_REGISTER_VPA */ ++ if (flags != 0x1) { ++ return H_FUNCTION; ++ } ++ ++ tcpu = spapr_find_cpu(procno); ++ if (tcpu == NULL) { ++ return H_P2; ++ } ++ ++ /* sequence is the same as in the "ibm,associativity" property */ ++ ++ idx = 0; ++#define ASSOCIATIVITY(a, b) (((uint64_t)(a) << 32) | \ ++ ((uint64_t)(b) & 0xffffffff)) ++ args[idx++] = ASSOCIATIVITY(0, 0); ++ args[idx++] = ASSOCIATIVITY(0, tcpu->node_id); ++ args[idx++] = ASSOCIATIVITY(procno, -1); ++ for ( ; idx < 6; idx++) { ++ args[idx] = -1; ++ } ++#undef ASSOCIATIVITY ++ ++ return H_SUCCESS; ++} ++ + static target_ulong h_get_cpu_characteristics(PowerPCCPU *cpu, + sPAPRMachineState *spapr, + target_ulong opcode, +@@ -1823,6 +1859,10 @@ static void hypercall_register_types(void) + + /* ibm,client-architecture-support support */ + spapr_register_hypercall(KVMPPC_H_CAS, h_client_architecture_support); ++ ++ /* Virtual Processor Home Node */ ++ spapr_register_hypercall(H_HOME_NODE_ASSOCIATIVITY, ++ h_home_node_associativity); + } + + type_init(hypercall_register_types) +diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h +index 5118af6..5f1add0 100644 +--- a/include/hw/ppc/spapr.h ++++ b/include/hw/ppc/spapr.h +@@ -432,6 +432,7 @@ struct sPAPRMachineState { + #define H_GET_EM_PARMS 0x2B8 + #define H_SET_MPP 0x2D0 + #define H_GET_MPP 0x2D4 ++#define H_HOME_NODE_ASSOCIATIVITY 0x2EC + #define H_XIRR_X 0x2FC + #define H_RANDOM 0x300 + #define H_SET_MODE 0x31C +-- +1.8.3.1 + diff --git a/SOURCES/kvm-spapr-Add-ibm-max-associativity-domains-property.patch b/SOURCES/kvm-spapr-Add-ibm-max-associativity-domains-property.patch new file mode 100644 index 0000000..ea49fb3 --- /dev/null +++ b/SOURCES/kvm-spapr-Add-ibm-max-associativity-domains-property.patch @@ -0,0 +1,63 @@ +From 5a7d9ce549b777d52cd680327e4dc17921ab882c Mon Sep 17 00:00:00 2001 +From: Serhii Popovych +Date: Wed, 2 May 2018 18:52:27 +0200 +Subject: [PATCH 04/13] spapr: Add ibm, max-associativity-domains property + +RH-Author: Serhii Popovych +Message-id: <1525287148-92715-2-git-send-email-spopovyc@redhat.com> +Patchwork-id: 80013 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/2] spapr: Add ibm, max-associativity-domains property +Bugzilla: 1570525 +RH-Acked-by: David Gibson +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier + +Now recent kernels (i.e. since linux-stable commit a346137e9142 +("powerpc/numa: Use ibm,max-associativity-domains to discover possible nodes") +support this property to mark initially memory-less NUMA nodes as "possible" +to allow further memory hot-add to them. + +Advertise this property for pSeries machines to let guest kernels detect +maximum supported node configuration and benefit from kernel side change +when hot-add memory to specific, possibly empty before, NUMA node. + +Signed-off-by: Serhii Popovych +Signed-off-by: David Gibson +(cherry picked from commit da9f80fbad21319194e73355dea8a1cff6a574e4) +Signed-off-by: Serhii Popovych +Signed-off-by: Miroslav Rezanina +--- + hw/ppc/spapr.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c +index 360a258..c9561e1 100644 +--- a/hw/ppc/spapr.c ++++ b/hw/ppc/spapr.c +@@ -910,6 +910,13 @@ static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt) + 0, cpu_to_be32(SPAPR_MEMORY_BLOCK_SIZE), + cpu_to_be32(max_cpus / smp_threads), + }; ++ uint32_t maxdomains[] = { ++ cpu_to_be32(4), ++ cpu_to_be32(0), ++ cpu_to_be32(0), ++ cpu_to_be32(0), ++ cpu_to_be32(nb_numa_nodes ? nb_numa_nodes - 1 : 0), ++ }; + + _FDT(rtas = fdt_add_subnode(fdt, 0, "rtas")); + +@@ -946,6 +953,9 @@ static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt) + _FDT(fdt_setprop(fdt, rtas, "ibm,associativity-reference-points", + refpoints, sizeof(refpoints))); + ++ _FDT(fdt_setprop(fdt, rtas, "ibm,max-associativity-domains", ++ maxdomains, sizeof(maxdomains))); ++ + _FDT(fdt_setprop_cell(fdt, rtas, "rtas-error-log-max", + RTAS_ERROR_LOG_MAX)); + _FDT(fdt_setprop_cell(fdt, rtas, "rtas-event-scan-rate", +-- +1.8.3.1 + diff --git a/SOURCES/kvm-spapr-Correct-inverted-test-in-spapr_pc_dimm_node.patch b/SOURCES/kvm-spapr-Correct-inverted-test-in-spapr_pc_dimm_node.patch new file mode 100644 index 0000000..6c81cbb --- /dev/null +++ b/SOURCES/kvm-spapr-Correct-inverted-test-in-spapr_pc_dimm_node.patch @@ -0,0 +1,52 @@ +From 300af272a7b1546ccd40face0c3f6d325f81aa49 Mon Sep 17 00:00:00 2001 +From: David Gibson +Date: Tue, 17 Jul 2018 01:36:38 +0200 +Subject: [PATCH 46/89] spapr: Correct inverted test in spapr_pc_dimm_node() + +RH-Author: David Gibson +Message-id: <20180717013638.11012-1-dgibson@redhat.com> +Patchwork-id: 81371 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH] spapr: Correct inverted test in spapr_pc_dimm_node() +Bugzilla: 1598287 +RH-Acked-by: Igor Mammedov +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier + +From: David Gibson + +This function was introduced between v2.11 and v2.12 to replace obsolete +ways of specifying the NUMA nodes for DIMMs. It's used to find the correct +node for an LMB, by locating which DIMM object it lies within. + +Unfortunately, one of the checks is inverted, so we check whether the +address is less than two different things, rather than actually checking +a range. This introduced a regression, meaning that after a reboot qemu +will advertise incorrect node information for memory to the guest. + +Signed-off-by: David Gibson +Reviewed-by: Greg Kurz +Reviewed-by: Igor Mammedov +(cherry picked from commit ccc2cef8b3f1dedd059924eb8ec1a87eff8ef607) + +Signed-off-by: David Gibson +Signed-off-by: Miroslav Rezanina +--- + hw/ppc/spapr.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c +index a580334..5f26aea 100644 +--- a/hw/ppc/spapr.c ++++ b/hw/ppc/spapr.c +@@ -659,7 +659,7 @@ static uint32_t spapr_pc_dimm_node(MemoryDeviceInfoList *list, ram_addr_t addr) + if (value && value->type == MEMORY_DEVICE_INFO_KIND_DIMM) { + PCDIMMDeviceInfo *pcdimm_info = value->u.dimm.data; + +- if (pcdimm_info->addr >= addr && ++ if (addr >= pcdimm_info->addr && + addr < (pcdimm_info->addr + pcdimm_info->size)) { + return pcdimm_info->node; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-spapr-Fix-ibm-max-associativity-domains-property-num.patch b/SOURCES/kvm-spapr-Fix-ibm-max-associativity-domains-property-num.patch new file mode 100644 index 0000000..f28c87e --- /dev/null +++ b/SOURCES/kvm-spapr-Fix-ibm-max-associativity-domains-property-num.patch @@ -0,0 +1,136 @@ +From e145e366df21d291ba3cbcf2b4982598637bcc01 Mon Sep 17 00:00:00 2001 +From: Laurent Vivier +Date: Wed, 2 Jan 2019 11:29:47 +0100 +Subject: [PATCH 1/8] spapr: Fix ibm, max-associativity-domains property number + of nodes + +RH-Author: Laurent Vivier +Message-id: <20190102112948.18536-2-lvivier@redhat.com> +Patchwork-id: 83820 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/2] spapr: Fix ibm, max-associativity-domains property number of nodes +Bugzilla: 1626347 +RH-Acked-by: Thomas Huth +RH-Acked-by: Serhii Popovych +RH-Acked-by: David Gibson + +From: Serhii Popovych + +BZ: https://bugzilla.redhat.com/show_bug.cgi?id=1626347 + +Laurent Vivier reported off by one with maximum number of NUMA nodes +provided by qemu-kvm being less by one than required according to +description of "ibm,max-associativity-domains" property in LoPAPR. + +It appears that I incorrectly treated LoPAPR description of this +property assuming it provides last valid domain (NUMA node here) +instead of maximum number of domains. + + ### Before hot-add + + (qemu) info numa + 3 nodes + node 0 cpus: 0 + node 0 size: 0 MB + node 0 plugged: 0 MB + node 1 cpus: + node 1 size: 1024 MB + node 1 plugged: 0 MB + node 2 cpus: + node 2 size: 0 MB + node 2 plugged: 0 MB + + $ numactl -H + available: 2 nodes (0-1) + node 0 cpus: 0 + node 0 size: 0 MB + node 0 free: 0 MB + node 1 cpus: + node 1 size: 999 MB + node 1 free: 658 MB + node distances: + node 0 1 + 0: 10 40 + 1: 40 10 + + ### Hot-add + + (qemu) object_add memory-backend-ram,id=mem0,size=1G + (qemu) device_add pc-dimm,id=dimm1,memdev=mem0,node=2 + (qemu) [ 87.704898] pseries-hotplug-mem: Attempting to hot-add 4 ... + + [ 87.705128] lpar: Attempting to resize HPT to shift 21 + ... + + ### After hot-add + + (qemu) info numa + 3 nodes + node 0 cpus: 0 + node 0 size: 0 MB + node 0 plugged: 0 MB + node 1 cpus: + node 1 size: 1024 MB + node 1 plugged: 0 MB + node 2 cpus: + node 2 size: 1024 MB + node 2 plugged: 1024 MB + + $ numactl -H + available: 2 nodes (0-1) + ^^^^^^^^^^^^^^^^^^^^^^^^ + Still only two nodes (and memory hot-added to node 0 below) + node 0 cpus: 0 + node 0 size: 1024 MB + node 0 free: 1021 MB + node 1 cpus: + node 1 size: 999 MB + node 1 free: 658 MB + node distances: + node 0 1 + 0: 10 40 + 1: 40 10 + +After fix applied numactl(8) reports 3 nodes available and memory +plugged into node 2 as expected. + +>From David Gibson: +------------------ + Qemu makes a distinction between "non NUMA" (nb_numa_nodes == 0) and + "NUMA with one node" (nb_numa_nodes == 1). But from a PAPR guests's + point of view these are equivalent. I don't want to present two + different cases to the guest when we don't need to, so even though the + guest can handle it, I'd prefer we put a '1' here for both the + nb_numa_nodes == 0 and nb_numa_nodes == 1 case. + +This consolidates everything discussed previously on mailing list. + +Fixes: da9f80fbad21 ("spapr: Add ibm,max-associativity-domains property") +Reported-by: Laurent Vivier +Signed-off-by: Serhii Popovych + +Signed-off-by: David Gibson +Reviewed-by: Greg Kurz +Reviewed-by: Laurent Vivier +(cherry picked from commit 3908a24fcb83913079d315de0ca6d598e8616dbb) +Signed-off-by: Laurent Vivier +Signed-off-by: Miroslav Rezanina +--- + hw/ppc/spapr.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c +index 5f26aea..b49f377 100644 +--- a/hw/ppc/spapr.c ++++ b/hw/ppc/spapr.c +@@ -915,7 +915,7 @@ static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt) + cpu_to_be32(0), + cpu_to_be32(0), + cpu_to_be32(0), +- cpu_to_be32(nb_numa_nodes ? nb_numa_nodes - 1 : 0), ++ cpu_to_be32(nb_numa_nodes ? nb_numa_nodes : 1), + }; + + _FDT(rtas = fdt_add_subnode(fdt, 0, "rtas")); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-spapr_pci-Remove-unhelpful-pagesize-warning.patch b/SOURCES/kvm-spapr_pci-Remove-unhelpful-pagesize-warning.patch new file mode 100644 index 0000000..dadcc0f --- /dev/null +++ b/SOURCES/kvm-spapr_pci-Remove-unhelpful-pagesize-warning.patch @@ -0,0 +1,61 @@ +From 95f87089d1bdd881775a5d09c59363f5460f7ce2 Mon Sep 17 00:00:00 2001 +From: David Gibson +Date: Thu, 14 Jun 2018 01:31:36 +0200 +Subject: [PATCH 5/9] spapr_pci: Remove unhelpful pagesize warning + +RH-Author: David Gibson +Message-id: <20180614013136.3504-1-dgibson@redhat.com> +Patchwork-id: 80681 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH] spapr_pci: Remove unhelpful pagesize warning +Bugzilla: 1505664 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Thomas Huth +RH-Acked-by: Serhii Popovych + +From: David Gibson + +By default, the IOMMU model built into the spapr virtual PCI host bridge +supports 4kiB and 64kiB IOMMU page sizes. However this can be overridden +which may be desirable to allow larger IOMMU page sizes when running a +guest with hugepage backing and passthrough devices. For that reason a +warning was printed when the device wasn't configured to allow the pagesize +with which guest RAM is backed. + +Experience has proven, however, that this message is more confusing than +useful. Worse it sometimes makes little sense when the host-available page +sizes don't match those available on the guest, which can happen with +a POWER8 guest running on a POWER9 KVM host. + +Long term we do want better handling to allow large IOMMU page sizes to be +used, but for now this parameter and warning don't really accomplish it. +So, remove the message, pending a better solution. + +Signed-off-by: David Gibson + +Signed-off-by: David Gibson +Signed-off-by: Miroslav Rezanina +--- + hw/ppc/spapr_pci.c | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c +index 39a1498..f936ce6 100644 +--- a/hw/ppc/spapr_pci.c ++++ b/hw/ppc/spapr_pci.c +@@ -1717,13 +1717,6 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) + } + + /* DMA setup */ +- if (((sphb->page_size_mask & qemu_getrampagesize()) == 0) +- && kvm_enabled()) { +- warn_report("System page size 0x%lx is not enabled in page_size_mask " +- "(0x%"PRIx64"). Performance may be slow", +- qemu_getrampagesize(), sphb->page_size_mask); +- } +- + for (i = 0; i < windows_supported; ++i) { + tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn[i]); + if (!tcet) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-sysbus-Fix-latent-bug-with-onboard-devices.patch b/SOURCES/kvm-sysbus-Fix-latent-bug-with-onboard-devices.patch new file mode 100644 index 0000000..48e2f37 --- /dev/null +++ b/SOURCES/kvm-sysbus-Fix-latent-bug-with-onboard-devices.patch @@ -0,0 +1,77 @@ +From 18b6e8931b3aebefbbccf545165ccc6fcdd09a1e Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:10 +0200 +Subject: [PATCH 43/53] sysbus: Fix latent bug with onboard devices +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-22-armbru@redhat.com> +Patchwork-id: 87995 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 21/31] sysbus: Fix latent bug with onboard devices +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +The first call of sysbus_get_default() creates the main system bus and +stores it in QOM as "/machine/unattached/sysbus". This must not +happen before main() creates "/machine", or else container_get() would +"helpfully" create it as "container" object, and the real creation of +"/machine" would later abort with "attempt to add duplicate property +'machine' to object (type 'container')". Has been that way ever since +we wired up busses in QOM (commit f968fc6892d, v1.2.0). + +I believe the bug is latent. I got it to bite by trying to +qdev_create() a sysbus device from a machine's .instance_init() +method. + +The fix is obvious: store the main system bus in QOM right after +creating "/machine". + +Signed-off-by: Markus Armbruster +Reviewed-by: Marc-André Lureau +Reviewed-by: Philippe Mathieu-Daudé +Reviewed-by: Thomas Huth +Message-Id: <20190308131445.17502-5-armbru@redhat.com> +Reviewed-by: Michael S. Tsirkin +(cherry picked from commit e2fb3fbbf9ce6b8eed00b53a91d3a316362f1b0d) +Signed-off-by: Miroslav Rezanina +--- + hw/core/sysbus.c | 3 --- + vl.c | 4 ++++ + 2 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c +index 5d0887f..db95cb0 100644 +--- a/hw/core/sysbus.c ++++ b/hw/core/sysbus.c +@@ -359,9 +359,6 @@ static void main_system_bus_create(void) + qbus_create_inplace(main_system_bus, system_bus_info.instance_size, + TYPE_SYSTEM_BUS, NULL, "main-system-bus"); + OBJECT(main_system_bus)->free = g_free; +- object_property_add_child(container_get(qdev_get_machine(), +- "/unattached"), +- "sysbus", OBJECT(main_system_bus), NULL); + } + + BusState *sysbus_get_default(void) +diff --git a/vl.c b/vl.c +index d46dff6..d89ac3a 100644 +--- a/vl.c ++++ b/vl.c +@@ -4162,6 +4162,10 @@ int main(int argc, char **argv, char **envp) + } + object_property_add_child(object_get_root(), "machine", + OBJECT(current_machine), &error_abort); ++ object_property_add_child(container_get(OBJECT(current_machine), ++ "/unattached"), ++ "sysbus", OBJECT(sysbus_get_default()), ++ NULL); + + if (machine_class->minimum_page_bits) { + if (!set_preferred_target_page_bits(machine_class->minimum_page_bits)) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-target-i386-add-MDS-NO-feature.patch b/SOURCES/kvm-target-i386-add-MDS-NO-feature.patch new file mode 100644 index 0000000..7c2f886 --- /dev/null +++ b/SOURCES/kvm-target-i386-add-MDS-NO-feature.patch @@ -0,0 +1,54 @@ +From b565bf8ec21b5aa9cc4cbadeca016bd7989a1a6c Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Tue, 13 Aug 2019 02:34:40 +0200 +Subject: [PATCH 2/4] target/i386: add MDS-NO feature + +RH-Author: Eduardo Habkost +Message-id: <20190813023440.3565-1-ehabkost@redhat.com> +Patchwork-id: 89947 +O-Subject: [RHEL-7.7.z qemu-kvm-rhev PATCH] target/i386: add MDS-NO feature +Bugzilla: 1716726 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Igor Mammedov +RH-Acked-by: Bandan Das + +From: Paolo Bonzini + +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1716726 +Brew: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=23024015 + +Microarchitectural Data Sampling is a hardware vulnerability which allows +unprivileged speculative access to data which is available in various CPU +internal buffers. + +Some Intel processors use the ARCH_CAP_MDS_NO bit in the +IA32_ARCH_CAPABILITIES +MSR to report that they are not vulnerable, make it available to guests. + +Signed-off-by: Paolo Bonzini +Message-Id: <20190516185320.28340-1-pbonzini@redhat.com> +Signed-off-by: Eduardo Habkost +(cherry picked from commit 20140a82c67467f53814ca197403d5e1b561a5e5) +Signed-off-by: Eduardo Habkost +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 928e53c..5d6b45b 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -1147,7 +1147,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + .type = MSR_FEATURE_WORD, + .feat_names = { + "rdctl-no", "ibrs-all", "rsba", "skip-l1dfl-vmentry", +- "ssb-no", NULL, NULL, NULL, ++ "ssb-no", "mds-no", NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-target-i386-cpu-Add-downstream-only-STIBP-CPUID-flag.patch b/SOURCES/kvm-target-i386-cpu-Add-downstream-only-STIBP-CPUID-flag.patch new file mode 100644 index 0000000..32f93e7 --- /dev/null +++ b/SOURCES/kvm-target-i386-cpu-Add-downstream-only-STIBP-CPUID-flag.patch @@ -0,0 +1,50 @@ +From 6e9de3f3fa020b351960f4f528f175aa802eb536 Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Wed, 26 Sep 2018 18:50:59 +0200 +Subject: [PATCH] target-i386: cpu: Add downstream-only STIBP CPUID flag + +RH-Author: Eduardo Habkost +Message-id: <20180926185059.20691-1-ehabkost@redhat.com> +Patchwork-id: 82301 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH] target-i386: cpu: Add downstream-only STIBP CPUID flag +Bugzilla: 1638077 +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Kashyap Chamarthy +RH-Acked-by: Igor Mammedov + +From: Paolo Bonzini + +We accidentally dropped the downstream-only STIBP CPUID flag +during the 2.12.0 rebase. + +STIBP is a CPUID flag that was considered for the Spectre +(CVE-2017-5715) mitigations, but in the end it was not necessary: +spec-ctrl/IBRS was deemed enough. The kernel KVM STIBP CPUID +code was never merged upstream, but it's present on RHEL-7. + +This means we may have existing VMs created on RHEL-7.5 hosts +with the STIBP flag enabled, and we need to support +live-migration of those VMs to RHEL-7.6. + +Signed-off-by: Eduardo Habkost +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 6b5acdf..91f5a97 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -1007,7 +1007,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, +- NULL, NULL, "spec-ctrl", NULL, ++ NULL, NULL, "spec-ctrl", "stibp", + NULL, NULL, NULL, "ssbd", + }, + .cpuid_eax = 7, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-target-i386-sev-fix-memory-leaks.patch b/SOURCES/kvm-target-i386-sev-fix-memory-leaks.patch new file mode 100644 index 0000000..57374d9 --- /dev/null +++ b/SOURCES/kvm-target-i386-sev-fix-memory-leaks.patch @@ -0,0 +1,148 @@ +From db0396e4d2663f41aaea944eaaf29141b20f5e1f Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 31 Aug 2018 14:24:58 +0200 +Subject: [PATCH 07/29] target/i386: sev: fix memory leaks + +RH-Author: Markus Armbruster +Message-id: <20180831142459.18567-2-armbru@redhat.com> +Patchwork-id: 81984 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/2] target/i386: sev: fix memory leaks +Bugzilla: 1624390 +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Miroslav Rezanina +RH-Acked-by: Auger Eric + +From: Paolo Bonzini + +Reported by Coverity. + +Signed-off-by: Paolo Bonzini +(cherry picked from commit bf3175b49952628f96d72d1247d8bb3aa5c2466c) +Signed-off-by: Miroslav Rezanina +--- + target/i386/sev.c | 32 +++++++++++++++++--------------- + 1 file changed, 17 insertions(+), 15 deletions(-) + +diff --git a/target/i386/sev.c b/target/i386/sev.c +index c011671..2395171 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -430,7 +430,8 @@ static int + sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain, + size_t *cert_chain_len) + { +- guchar *pdh_data, *cert_chain_data; ++ guchar *pdh_data = NULL; ++ guchar *cert_chain_data = NULL; + struct sev_user_data_pdh_cert_export export = {}; + int err, r; + +@@ -471,8 +472,9 @@ e_free: + SevCapability * + sev_get_capabilities(void) + { +- SevCapability *cap; +- guchar *pdh_data, *cert_chain_data; ++ SevCapability *cap = NULL; ++ guchar *pdh_data = NULL; ++ guchar *cert_chain_data = NULL; + size_t pdh_len = 0, cert_chain_len = 0; + uint32_t ebx; + int fd; +@@ -486,7 +488,7 @@ sev_get_capabilities(void) + + if (sev_get_pdh_info(fd, &pdh_data, &pdh_len, + &cert_chain_data, &cert_chain_len)) { +- return NULL; ++ goto out; + } + + cap = g_new0(SevCapability, 1); +@@ -502,9 +504,9 @@ sev_get_capabilities(void) + */ + cap->reduced_phys_bits = 1; + ++out: + g_free(pdh_data); + g_free(cert_chain_data); +- + close(fd); + return cap; + } +@@ -530,7 +532,7 @@ sev_launch_start(SEVState *s) + { + gsize sz; + int ret = 1; +- int fw_error; ++ int fw_error, rc; + QSevGuestInfo *sev = s->sev_info; + struct kvm_sev_launch_start *start; + guchar *session = NULL, *dh_cert = NULL; +@@ -543,7 +545,7 @@ sev_launch_start(SEVState *s) + &error_abort); + if (sev->session_file) { + if (sev_read_file_base64(sev->session_file, &session, &sz) < 0) { +- return 1; ++ goto out; + } + start->session_uaddr = (unsigned long)session; + start->session_len = sz; +@@ -551,18 +553,18 @@ sev_launch_start(SEVState *s) + + if (sev->dh_cert_file) { + if (sev_read_file_base64(sev->dh_cert_file, &dh_cert, &sz) < 0) { +- return 1; ++ goto out; + } + start->dh_uaddr = (unsigned long)dh_cert; + start->dh_len = sz; + } + + trace_kvm_sev_launch_start(start->policy, session, dh_cert); +- ret = sev_ioctl(s->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error); +- if (ret < 0) { ++ rc = sev_ioctl(s->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error); ++ if (rc < 0) { + error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'", + __func__, ret, fw_error, fw_error_to_str(fw_error)); +- return 1; ++ goto out; + } + + object_property_set_int(OBJECT(sev), start->handle, "handle", +@@ -570,12 +572,13 @@ sev_launch_start(SEVState *s) + sev_set_guest_state(SEV_STATE_LAUNCH_UPDATE); + s->handle = start->handle; + s->policy = start->policy; ++ ret = 0; + ++out: + g_free(start); + g_free(session); + g_free(dh_cert); +- +- return 0; ++ return ret; + } + + static int +@@ -712,7 +715,7 @@ sev_guest_init(const char *id) + uint32_t host_cbitpos; + struct sev_user_data_status status = {}; + +- s = g_new0(SEVState, 1); ++ sev_state = s = g_new0(SEVState, 1); + s->sev_info = lookup_sev_guest_info(id); + if (!s->sev_info) { + error_report("%s: '%s' is not a valid '%s' object", +@@ -720,7 +723,6 @@ sev_guest_init(const char *id) + goto err; + } + +- sev_state = s; + s->state = SEV_STATE_UNINIT; + + host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-target-ppc-Don-t-require-private-l1d-cache-on-POWER8.patch b/SOURCES/kvm-target-ppc-Don-t-require-private-l1d-cache-on-POWER8.patch new file mode 100644 index 0000000..c89979f --- /dev/null +++ b/SOURCES/kvm-target-ppc-Don-t-require-private-l1d-cache-on-POWER8.patch @@ -0,0 +1,72 @@ +From 9d53600d84c407efde9e186b814badca245cc6ab Mon Sep 17 00:00:00 2001 +From: Suraj Jitindar Singh +Date: Thu, 21 Jun 2018 06:56:48 +0200 +Subject: [PATCH 53/54] target/ppc: Don't require private l1d cache on POWER8 + for cap_ppc_safe_cache + +RH-Author: Suraj Jitindar Singh +Message-id: <1529564209-30369-3-git-send-email-sursingh@redhat.com> +Patchwork-id: 80930 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 2/3] target/ppc: Don't require private l1d cache on POWER8 for cap_ppc_safe_cache +Bugzilla: 1560847 +RH-Acked-by: Laurent Vivier +RH-Acked-by: David Gibson +RH-Acked-by: Miroslav Rezanina + +From: Suraj Jitindar Singh + +For cap_ppc_safe_cache to be set to workaround, we require both a l1d +cache flush instruction and private l1d cache. + +On POWER8 don't require private l1d cache. This means a guest on a +POWER8 machine can make use of the cache flush workarounds. + +Signed-off-by: Suraj Jitindar Singh +Signed-off-by: David Gibson +(cherry picked from commit 072f416a53ead5211c987cb2068ee9dbd7ba06cc) + +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1560847 + +Signed-off-by: Suraj Jitindar Singh +Signed-off-by: Miroslav Rezanina +--- + target/ppc/kvm.c | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c +index d787032..192c40d 100644 +--- a/target/ppc/kvm.c ++++ b/target/ppc/kvm.c +@@ -2461,11 +2461,28 @@ bool kvmppc_has_cap_mmu_hash_v3(void) + return cap_mmu_hash_v3; + } + ++static bool kvmppc_power8_host(void) ++{ ++ bool ret = false; ++#ifdef TARGET_PPC64 ++ { ++ uint32_t base_pvr = CPU_POWERPC_POWER_SERVER_MASK & mfpvr(); ++ ret = (base_pvr == CPU_POWERPC_POWER8E_BASE) || ++ (base_pvr == CPU_POWERPC_POWER8NVL_BASE) || ++ (base_pvr == CPU_POWERPC_POWER8_BASE); ++ } ++#endif /* TARGET_PPC64 */ ++ return ret; ++} ++ + static int parse_cap_ppc_safe_cache(struct kvm_ppc_cpu_char c) + { ++ bool l1d_thread_priv_req = !kvmppc_power8_host(); ++ + if (~c.behaviour & c.behaviour_mask & H_CPU_BEHAV_L1D_FLUSH_PR) { + return 2; +- } else if ((c.character & c.character_mask & H_CPU_CHAR_L1D_THREAD_PRIV) && ++ } else if ((!l1d_thread_priv_req || ++ c.character & c.character_mask & H_CPU_CHAR_L1D_THREAD_PRIV) && + (c.character & c.character_mask + & (H_CPU_CHAR_L1D_FLUSH_ORI30 | H_CPU_CHAR_L1D_FLUSH_TRIG2))) { + return 1; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-target-ppc-Factor-out-the-parsing-in-kvmppc_get_cpu_.patch b/SOURCES/kvm-target-ppc-Factor-out-the-parsing-in-kvmppc_get_cpu_.patch new file mode 100644 index 0000000..56be789 --- /dev/null +++ b/SOURCES/kvm-target-ppc-Factor-out-the-parsing-in-kvmppc_get_cpu_.patch @@ -0,0 +1,113 @@ +From f9867a5df572d3b52fcd08b86878de9426354503 Mon Sep 17 00:00:00 2001 +From: Suraj Jitindar Singh +Date: Thu, 21 Jun 2018 06:56:47 +0200 +Subject: [PATCH 52/54] target/ppc: Factor out the parsing in + kvmppc_get_cpu_characteristics() + +RH-Author: Suraj Jitindar Singh +Message-id: <1529564209-30369-2-git-send-email-sursingh@redhat.com> +Patchwork-id: 80927 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/3] target/ppc: Factor out the parsing in kvmppc_get_cpu_characteristics() +Bugzilla: 1560847 +RH-Acked-by: Laurent Vivier +RH-Acked-by: David Gibson +RH-Acked-by: Miroslav Rezanina + +From: Suraj Jitindar Singh + +Factor out the parsing of struct kvm_ppc_cpu_char in +kvmppc_get_cpu_characteristics() into a separate function for each cap +for simplicity. + +Signed-off-by: Suraj Jitindar Singh +Signed-off-by: David Gibson +(cherry picked from commit 8fea70440eb0d095442de7e80d586a285cf96be5) + +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1560847 + +Signed-off-by: Suraj Jitindar Singh +Signed-off-by: Miroslav Rezanina +--- + target/ppc/kvm.c | 59 +++++++++++++++++++++++++++++++++++++------------------- + 1 file changed, 39 insertions(+), 20 deletions(-) + +diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c +index 79a436a..d787032 100644 +--- a/target/ppc/kvm.c ++++ b/target/ppc/kvm.c +@@ -2461,6 +2461,41 @@ bool kvmppc_has_cap_mmu_hash_v3(void) + return cap_mmu_hash_v3; + } + ++static int parse_cap_ppc_safe_cache(struct kvm_ppc_cpu_char c) ++{ ++ if (~c.behaviour & c.behaviour_mask & H_CPU_BEHAV_L1D_FLUSH_PR) { ++ return 2; ++ } else if ((c.character & c.character_mask & H_CPU_CHAR_L1D_THREAD_PRIV) && ++ (c.character & c.character_mask ++ & (H_CPU_CHAR_L1D_FLUSH_ORI30 | H_CPU_CHAR_L1D_FLUSH_TRIG2))) { ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static int parse_cap_ppc_safe_bounds_check(struct kvm_ppc_cpu_char c) ++{ ++ if (~c.behaviour & c.behaviour_mask & H_CPU_BEHAV_BNDS_CHK_SPEC_BAR) { ++ return 2; ++ } else if (c.character & c.character_mask & H_CPU_CHAR_SPEC_BAR_ORI31) { ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static int parse_cap_ppc_safe_indirect_branch(struct kvm_ppc_cpu_char c) ++{ ++ if (c.character & c.character_mask & H_CPU_CHAR_CACHE_COUNT_DIS) { ++ return SPAPR_CAP_FIXED_CCD; ++ } else if (c.character & c.character_mask & H_CPU_CHAR_BCCTRL_SERIALISED) { ++ return SPAPR_CAP_FIXED_IBS; ++ } ++ ++ return 0; ++} ++ + static void kvmppc_get_cpu_characteristics(KVMState *s) + { + struct kvm_ppc_cpu_char c; +@@ -2479,26 +2514,10 @@ static void kvmppc_get_cpu_characteristics(KVMState *s) + if (ret < 0) { + return; + } +- /* Parse and set cap_ppc_safe_cache */ +- if (~c.behaviour & c.behaviour_mask & H_CPU_BEHAV_L1D_FLUSH_PR) { +- cap_ppc_safe_cache = 2; +- } else if ((c.character & c.character_mask & H_CPU_CHAR_L1D_THREAD_PRIV) && +- (c.character & c.character_mask +- & (H_CPU_CHAR_L1D_FLUSH_ORI30 | H_CPU_CHAR_L1D_FLUSH_TRIG2))) { +- cap_ppc_safe_cache = 1; +- } +- /* Parse and set cap_ppc_safe_bounds_check */ +- if (~c.behaviour & c.behaviour_mask & H_CPU_BEHAV_BNDS_CHK_SPEC_BAR) { +- cap_ppc_safe_bounds_check = 2; +- } else if (c.character & c.character_mask & H_CPU_CHAR_SPEC_BAR_ORI31) { +- cap_ppc_safe_bounds_check = 1; +- } +- /* Parse and set cap_ppc_safe_indirect_branch */ +- if (c.character & c.character_mask & H_CPU_CHAR_CACHE_COUNT_DIS) { +- cap_ppc_safe_indirect_branch = SPAPR_CAP_FIXED_CCD; +- } else if (c.character & c.character_mask & H_CPU_CHAR_BCCTRL_SERIALISED) { +- cap_ppc_safe_indirect_branch = SPAPR_CAP_FIXED_IBS; +- } ++ ++ cap_ppc_safe_cache = parse_cap_ppc_safe_cache(c); ++ cap_ppc_safe_bounds_check = parse_cap_ppc_safe_bounds_check(c); ++ cap_ppc_safe_indirect_branch = parse_cap_ppc_safe_indirect_branch(c); + } + + int kvmppc_get_cap_safe_cache(void) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-tcg-workaround-branch-instruction-overflow-in-tcg_ou.patch b/SOURCES/kvm-tcg-workaround-branch-instruction-overflow-in-tcg_ou.patch new file mode 100644 index 0000000..00e7bf3 --- /dev/null +++ b/SOURCES/kvm-tcg-workaround-branch-instruction-overflow-in-tcg_ou.patch @@ -0,0 +1,102 @@ +From 1ef4307f6e1299d337bc925ea7bbea41c2f99706 Mon Sep 17 00:00:00 2001 +From: Laurent Vivier +Date: Fri, 4 May 2018 09:38:55 +0200 +Subject: [PATCH 06/13] tcg: workaround branch instruction overflow in + tcg_out_qemu_ld/st + +RH-Author: Laurent Vivier +Message-id: <20180504093855.30922-1-lvivier@redhat.com> +Patchwork-id: 80025 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH] tcg: workaround branch instruction overflow in tcg_out_qemu_ld/st +Bugzilla: 1574577 +RH-Acked-by: Serhii Popovych +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Miroslav Rezanina + +ppc64 uses a BC instruction to call the tcg_out_qemu_ld/st +slow path. BC instruction uses a relative address encoded +on 14 bits. + +The slow path functions are added at the end of the generated +instructions buffer, in the reverse order of the callers. +So more we have slow path functions more the distance between +the caller (BC) and the function increases. + +This patch changes the behavior to generate the functions in +the same order of the callers. + +Cc: qemu-stable@nongnu.org +Fixes: 15fa08f845 ("tcg: Dynamically allocate TCGOps") +Signed-off-by: Laurent Vivier +Message-Id: <20180429235840.16659-1-lvivier@redhat.com> +Signed-off-by: Richard Henderson +(cherry picked from commit 6001f7729e12dd1d810291e4cbf83cee8e07441d) +Signed-off-by: Laurent Vivier +Signed-off-by: Miroslav Rezanina +--- + tcg/tcg-ldst.inc.c | 8 ++++---- + tcg/tcg.c | 2 +- + tcg/tcg.h | 2 +- + 3 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/tcg/tcg-ldst.inc.c b/tcg/tcg-ldst.inc.c +index 0e14cf4..47f41b9 100644 +--- a/tcg/tcg-ldst.inc.c ++++ b/tcg/tcg-ldst.inc.c +@@ -30,7 +30,7 @@ typedef struct TCGLabelQemuLdst { + TCGReg datahi_reg; /* reg index for high word to be loaded or stored */ + tcg_insn_unit *raddr; /* gen code addr of the next IR of qemu_ld/st IR */ + tcg_insn_unit *label_ptr[2]; /* label pointers to be updated */ +- struct TCGLabelQemuLdst *next; ++ QSIMPLEQ_ENTRY(TCGLabelQemuLdst) next; + } TCGLabelQemuLdst; + + +@@ -46,7 +46,7 @@ static bool tcg_out_ldst_finalize(TCGContext *s) + TCGLabelQemuLdst *lb; + + /* qemu_ld/st slow paths */ +- for (lb = s->ldst_labels; lb != NULL; lb = lb->next) { ++ QSIMPLEQ_FOREACH(lb, &s->ldst_labels, next) { + if (lb->is_ld) { + tcg_out_qemu_ld_slow_path(s, lb); + } else { +@@ -72,7 +72,7 @@ static inline TCGLabelQemuLdst *new_ldst_label(TCGContext *s) + { + TCGLabelQemuLdst *l = tcg_malloc(sizeof(*l)); + +- l->next = s->ldst_labels; +- s->ldst_labels = l; ++ QSIMPLEQ_INSERT_TAIL(&s->ldst_labels, l, next); ++ + return l; + } +diff --git a/tcg/tcg.c b/tcg/tcg.c +index bb24526..b84850b 100644 +--- a/tcg/tcg.c ++++ b/tcg/tcg.c +@@ -3324,7 +3324,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) + s->code_ptr = tb->tc.ptr; + + #ifdef TCG_TARGET_NEED_LDST_LABELS +- s->ldst_labels = NULL; ++ QSIMPLEQ_INIT(&s->ldst_labels); + #endif + #ifdef TCG_TARGET_NEED_POOL_LABELS + s->pool_labels = NULL; +diff --git a/tcg/tcg.h b/tcg/tcg.h +index 30896ca..a3076c5 100644 +--- a/tcg/tcg.h ++++ b/tcg/tcg.h +@@ -699,7 +699,7 @@ struct TCGContext { + + /* These structures are private to tcg-target.inc.c. */ + #ifdef TCG_TARGET_NEED_LDST_LABELS +- struct TCGLabelQemuLdst *ldst_labels; ++ QSIMPLEQ_HEAD(ldst_labels, TCGLabelQemuLdst) ldst_labels; + #endif + #ifdef TCG_TARGET_NEED_POOL_LABELS + struct TCGLabelPoolData *pool_labels; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-test-bdrv-drain-AIO_WAIT_WHILE-in-job-.commit-.abort.patch b/SOURCES/kvm-test-bdrv-drain-AIO_WAIT_WHILE-in-job-.commit-.abort.patch new file mode 100644 index 0000000..9d87885 --- /dev/null +++ b/SOURCES/kvm-test-bdrv-drain-AIO_WAIT_WHILE-in-job-.commit-.abort.patch @@ -0,0 +1,239 @@ +From 1b85fbf7ed7119dac8bc0fedfb019d0ce991d07d Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:40 +0200 +Subject: [PATCH 49/49] test-bdrv-drain: AIO_WAIT_WHILE() in job .commit/.abort + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-43-kwolf@redhat.com> +Patchwork-id: 82193 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 42/42] test-bdrv-drain: AIO_WAIT_WHILE() in job .commit/.abort +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This adds tests for calling AIO_WAIT_WHILE() in the .commit and .abort +callbacks. Both reasons why .abort could be called for a single job are +tested: Either .run or .prepare could return an error. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + tests/test-bdrv-drain.c | 116 +++++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 104 insertions(+), 12 deletions(-) + +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index f4b57f7..d6202b2 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -784,6 +784,8 @@ static void test_iothread_drain_subtree(void) + + typedef struct TestBlockJob { + BlockJob common; ++ int run_ret; ++ int prepare_ret; + bool should_complete; + } TestBlockJob; + +@@ -793,7 +795,23 @@ static int test_job_prepare(Job *job) + + /* Provoke an AIO_WAIT_WHILE() call to verify there is no deadlock */ + blk_flush(s->common.blk); +- return 0; ++ return s->prepare_ret; ++} ++ ++static void test_job_commit(Job *job) ++{ ++ TestBlockJob *s = container_of(job, TestBlockJob, common.job); ++ ++ /* Provoke an AIO_WAIT_WHILE() call to verify there is no deadlock */ ++ blk_flush(s->common.blk); ++} ++ ++static void test_job_abort(Job *job) ++{ ++ TestBlockJob *s = container_of(job, TestBlockJob, common.job); ++ ++ /* Provoke an AIO_WAIT_WHILE() call to verify there is no deadlock */ ++ blk_flush(s->common.blk); + } + + static int coroutine_fn test_job_run(Job *job, Error **errp) +@@ -809,7 +827,7 @@ static int coroutine_fn test_job_run(Job *job, Error **errp) + job_pause_point(&s->common.job); + } + +- return 0; ++ return s->run_ret; + } + + static void test_job_complete(Job *job, Error **errp) +@@ -827,14 +845,24 @@ BlockJobDriver test_job_driver = { + .run = test_job_run, + .complete = test_job_complete, + .prepare = test_job_prepare, ++ .commit = test_job_commit, ++ .abort = test_job_abort, + }, + }; + +-static void test_blockjob_common(enum drain_type drain_type, bool use_iothread) ++enum test_job_result { ++ TEST_JOB_SUCCESS, ++ TEST_JOB_FAIL_RUN, ++ TEST_JOB_FAIL_PREPARE, ++}; ++ ++static void test_blockjob_common(enum drain_type drain_type, bool use_iothread, ++ enum test_job_result result) + { + BlockBackend *blk_src, *blk_target; + BlockDriverState *src, *target; + BlockJob *job; ++ TestBlockJob *tjob; + IOThread *iothread = NULL; + AioContext *ctx; + int ret; +@@ -858,9 +886,23 @@ static void test_blockjob_common(enum drain_type drain_type, bool use_iothread) + blk_insert_bs(blk_target, target, &error_abort); + + aio_context_acquire(ctx); +- job = block_job_create("job0", &test_job_driver, NULL, src, 0, BLK_PERM_ALL, +- 0, 0, NULL, NULL, &error_abort); ++ tjob = block_job_create("job0", &test_job_driver, NULL, src, ++ 0, BLK_PERM_ALL, ++ 0, 0, NULL, NULL, &error_abort); ++ job = &tjob->common; + block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort); ++ ++ switch (result) { ++ case TEST_JOB_SUCCESS: ++ break; ++ case TEST_JOB_FAIL_RUN: ++ tjob->run_ret = -EIO; ++ break; ++ case TEST_JOB_FAIL_PREPARE: ++ tjob->prepare_ret = -EIO; ++ break; ++ } ++ + job_start(&job->job); + aio_context_release(ctx); + +@@ -918,7 +960,7 @@ static void test_blockjob_common(enum drain_type drain_type, bool use_iothread) + + aio_context_acquire(ctx); + ret = job_complete_sync(&job->job, &error_abort); +- g_assert_cmpint(ret, ==, 0); ++ g_assert_cmpint(ret, ==, (result == TEST_JOB_SUCCESS ? 0 : -EIO)); + + if (use_iothread) { + blk_set_aio_context(blk_src, qemu_get_aio_context()); +@@ -937,32 +979,68 @@ static void test_blockjob_common(enum drain_type drain_type, bool use_iothread) + + static void test_blockjob_drain_all(void) + { +- test_blockjob_common(BDRV_DRAIN_ALL, false); ++ test_blockjob_common(BDRV_DRAIN_ALL, false, TEST_JOB_SUCCESS); + } + + static void test_blockjob_drain(void) + { +- test_blockjob_common(BDRV_DRAIN, false); ++ test_blockjob_common(BDRV_DRAIN, false, TEST_JOB_SUCCESS); + } + + static void test_blockjob_drain_subtree(void) + { +- test_blockjob_common(BDRV_SUBTREE_DRAIN, false); ++ test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_SUCCESS); ++} ++ ++static void test_blockjob_error_drain_all(void) ++{ ++ test_blockjob_common(BDRV_DRAIN_ALL, false, TEST_JOB_FAIL_RUN); ++ test_blockjob_common(BDRV_DRAIN_ALL, false, TEST_JOB_FAIL_PREPARE); ++} ++ ++static void test_blockjob_error_drain(void) ++{ ++ test_blockjob_common(BDRV_DRAIN, false, TEST_JOB_FAIL_RUN); ++ test_blockjob_common(BDRV_DRAIN, false, TEST_JOB_FAIL_PREPARE); ++} ++ ++static void test_blockjob_error_drain_subtree(void) ++{ ++ test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_FAIL_RUN); ++ test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_FAIL_PREPARE); + } + + static void test_blockjob_iothread_drain_all(void) + { +- test_blockjob_common(BDRV_DRAIN_ALL, true); ++ test_blockjob_common(BDRV_DRAIN_ALL, true, TEST_JOB_SUCCESS); + } + + static void test_blockjob_iothread_drain(void) + { +- test_blockjob_common(BDRV_DRAIN, true); ++ test_blockjob_common(BDRV_DRAIN, true, TEST_JOB_SUCCESS); + } + + static void test_blockjob_iothread_drain_subtree(void) + { +- test_blockjob_common(BDRV_SUBTREE_DRAIN, true); ++ test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_SUCCESS); ++} ++ ++static void test_blockjob_iothread_error_drain_all(void) ++{ ++ test_blockjob_common(BDRV_DRAIN_ALL, true, TEST_JOB_FAIL_RUN); ++ test_blockjob_common(BDRV_DRAIN_ALL, true, TEST_JOB_FAIL_PREPARE); ++} ++ ++static void test_blockjob_iothread_error_drain(void) ++{ ++ test_blockjob_common(BDRV_DRAIN, true, TEST_JOB_FAIL_RUN); ++ test_blockjob_common(BDRV_DRAIN, true, TEST_JOB_FAIL_PREPARE); ++} ++ ++static void test_blockjob_iothread_error_drain_subtree(void) ++{ ++ test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_FAIL_RUN); ++ test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_FAIL_PREPARE); + } + + +@@ -1433,6 +1511,13 @@ int main(int argc, char **argv) + g_test_add_func("/bdrv-drain/blockjob/drain_subtree", + test_blockjob_drain_subtree); + ++ g_test_add_func("/bdrv-drain/blockjob/error/drain_all", ++ test_blockjob_error_drain_all); ++ g_test_add_func("/bdrv-drain/blockjob/error/drain", ++ test_blockjob_error_drain); ++ g_test_add_func("/bdrv-drain/blockjob/error/drain_subtree", ++ test_blockjob_error_drain_subtree); ++ + g_test_add_func("/bdrv-drain/blockjob/iothread/drain_all", + test_blockjob_iothread_drain_all); + g_test_add_func("/bdrv-drain/blockjob/iothread/drain", +@@ -1440,6 +1525,13 @@ int main(int argc, char **argv) + g_test_add_func("/bdrv-drain/blockjob/iothread/drain_subtree", + test_blockjob_iothread_drain_subtree); + ++ g_test_add_func("/bdrv-drain/blockjob/iothread/error/drain_all", ++ test_blockjob_iothread_error_drain_all); ++ g_test_add_func("/bdrv-drain/blockjob/iothread/error/drain", ++ test_blockjob_iothread_error_drain); ++ g_test_add_func("/bdrv-drain/blockjob/iothread/error/drain_subtree", ++ test_blockjob_iothread_error_drain_subtree); ++ + g_test_add_func("/bdrv-drain/deletion/drain", test_delete_by_drain); + g_test_add_func("/bdrv-drain/detach/drain_all", test_detach_by_drain_all); + g_test_add_func("/bdrv-drain/detach/drain", test_detach_by_drain); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-test-bdrv-drain-Add-test-for-node-deletion.patch b/SOURCES/kvm-test-bdrv-drain-Add-test-for-node-deletion.patch new file mode 100644 index 0000000..2bcb07a --- /dev/null +++ b/SOURCES/kvm-test-bdrv-drain-Add-test-for-node-deletion.patch @@ -0,0 +1,226 @@ +From e830be5d2de1d07f54b741f7a6e8bdea357bf044 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:07 +0200 +Subject: [PATCH 16/49] test-bdrv-drain: Add test for node deletion + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-10-kwolf@redhat.com> +Patchwork-id: 82162 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 09/42] test-bdrv-drain: Add test for node deletion +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +From: Max Reitz + +This patch adds two bdrv-drain tests for what happens if some BDS goes +away during the drainage. + +The basic idea is that you have a parent BDS with some child nodes. +Then, you drain one of the children. Because of that, the party who +actually owns the parent decides to (A) delete it, or (B) detach all its +children from it -- both while the child is still being drained. + +A real-world case where this can happen is the mirror block job, which +may exit if you drain one of its children. + +Signed-off-by: Max Reitz +Signed-off-by: Kevin Wolf +(cherry picked from commit 4c8158e359d194394c64acd21caf5e3f3f3141c2) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/test-bdrv-drain.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 169 insertions(+) + +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index 49786ea..8918a94 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -792,6 +792,172 @@ static void test_blockjob_drain_subtree(void) + test_blockjob_common(BDRV_SUBTREE_DRAIN); + } + ++ ++typedef struct BDRVTestTopState { ++ BdrvChild *wait_child; ++} BDRVTestTopState; ++ ++static void bdrv_test_top_close(BlockDriverState *bs) ++{ ++ BdrvChild *c, *next_c; ++ QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { ++ bdrv_unref_child(bs, c); ++ } ++} ++ ++static int coroutine_fn bdrv_test_top_co_preadv(BlockDriverState *bs, ++ uint64_t offset, uint64_t bytes, ++ QEMUIOVector *qiov, int flags) ++{ ++ BDRVTestTopState *tts = bs->opaque; ++ return bdrv_co_preadv(tts->wait_child, offset, bytes, qiov, flags); ++} ++ ++static BlockDriver bdrv_test_top_driver = { ++ .format_name = "test_top_driver", ++ .instance_size = sizeof(BDRVTestTopState), ++ ++ .bdrv_close = bdrv_test_top_close, ++ .bdrv_co_preadv = bdrv_test_top_co_preadv, ++ ++ .bdrv_child_perm = bdrv_format_default_perms, ++}; ++ ++typedef struct TestCoDeleteByDrainData { ++ BlockBackend *blk; ++ bool detach_instead_of_delete; ++ bool done; ++} TestCoDeleteByDrainData; ++ ++static void coroutine_fn test_co_delete_by_drain(void *opaque) ++{ ++ TestCoDeleteByDrainData *dbdd = opaque; ++ BlockBackend *blk = dbdd->blk; ++ BlockDriverState *bs = blk_bs(blk); ++ BDRVTestTopState *tts = bs->opaque; ++ void *buffer = g_malloc(65536); ++ QEMUIOVector qiov; ++ struct iovec iov = { ++ .iov_base = buffer, ++ .iov_len = 65536, ++ }; ++ ++ qemu_iovec_init_external(&qiov, &iov, 1); ++ ++ /* Pretend some internal write operation from parent to child. ++ * Important: We have to read from the child, not from the parent! ++ * Draining works by first propagating it all up the tree to the ++ * root and then waiting for drainage from root to the leaves ++ * (protocol nodes). If we have a request waiting on the root, ++ * everything will be drained before we go back down the tree, but ++ * we do not want that. We want to be in the middle of draining ++ * when this following requests returns. */ ++ bdrv_co_preadv(tts->wait_child, 0, 65536, &qiov, 0); ++ ++ g_assert_cmpint(bs->refcnt, ==, 1); ++ ++ if (!dbdd->detach_instead_of_delete) { ++ blk_unref(blk); ++ } else { ++ BdrvChild *c, *next_c; ++ QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { ++ bdrv_unref_child(bs, c); ++ } ++ } ++ ++ dbdd->done = true; ++} ++ ++/** ++ * Test what happens when some BDS has some children, you drain one of ++ * them and this results in the BDS being deleted. ++ * ++ * If @detach_instead_of_delete is set, the BDS is not going to be ++ * deleted but will only detach all of its children. ++ */ ++static void do_test_delete_by_drain(bool detach_instead_of_delete) ++{ ++ BlockBackend *blk; ++ BlockDriverState *bs, *child_bs, *null_bs; ++ BDRVTestTopState *tts; ++ TestCoDeleteByDrainData dbdd; ++ Coroutine *co; ++ ++ bs = bdrv_new_open_driver(&bdrv_test_top_driver, "top", BDRV_O_RDWR, ++ &error_abort); ++ bs->total_sectors = 65536 >> BDRV_SECTOR_BITS; ++ tts = bs->opaque; ++ ++ null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, ++ &error_abort); ++ bdrv_attach_child(bs, null_bs, "null-child", &child_file, &error_abort); ++ ++ /* This child will be the one to pass to requests through to, and ++ * it will stall until a drain occurs */ ++ child_bs = bdrv_new_open_driver(&bdrv_test, "child", BDRV_O_RDWR, ++ &error_abort); ++ child_bs->total_sectors = 65536 >> BDRV_SECTOR_BITS; ++ /* Takes our reference to child_bs */ ++ tts->wait_child = bdrv_attach_child(bs, child_bs, "wait-child", &child_file, ++ &error_abort); ++ ++ /* This child is just there to be deleted ++ * (for detach_instead_of_delete == true) */ ++ null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, ++ &error_abort); ++ bdrv_attach_child(bs, null_bs, "null-child", &child_file, &error_abort); ++ ++ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); ++ blk_insert_bs(blk, bs, &error_abort); ++ ++ /* Referenced by blk now */ ++ bdrv_unref(bs); ++ ++ g_assert_cmpint(bs->refcnt, ==, 1); ++ g_assert_cmpint(child_bs->refcnt, ==, 1); ++ g_assert_cmpint(null_bs->refcnt, ==, 1); ++ ++ ++ dbdd = (TestCoDeleteByDrainData){ ++ .blk = blk, ++ .detach_instead_of_delete = detach_instead_of_delete, ++ .done = false, ++ }; ++ co = qemu_coroutine_create(test_co_delete_by_drain, &dbdd); ++ qemu_coroutine_enter(co); ++ ++ /* Drain the child while the read operation is still pending. ++ * This should result in the operation finishing and ++ * test_co_delete_by_drain() resuming. Thus, @bs will be deleted ++ * and the coroutine will exit while this drain operation is still ++ * in progress. */ ++ bdrv_ref(child_bs); ++ bdrv_drain(child_bs); ++ bdrv_unref(child_bs); ++ ++ while (!dbdd.done) { ++ aio_poll(qemu_get_aio_context(), true); ++ } ++ ++ if (detach_instead_of_delete) { ++ /* Here, the reference has not passed over to the coroutine, ++ * so we have to delete the BB ourselves */ ++ blk_unref(blk); ++ } ++} ++ ++ ++static void test_delete_by_drain(void) ++{ ++ do_test_delete_by_drain(false); ++} ++ ++static void test_detach_by_drain(void) ++{ ++ do_test_delete_by_drain(true); ++} ++ ++ + int main(int argc, char **argv) + { + int ret; +@@ -839,6 +1005,9 @@ int main(int argc, char **argv) + g_test_add_func("/bdrv-drain/blockjob/drain_subtree", + test_blockjob_drain_subtree); + ++ g_test_add_func("/bdrv-drain/deletion", test_delete_by_drain); ++ g_test_add_func("/bdrv-drain/detach", test_detach_by_drain); ++ + ret = g_test_run(); + qemu_event_destroy(&done_event); + return ret; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-test-bdrv-drain-AioContext-switch-in-drained-section.patch b/SOURCES/kvm-test-bdrv-drain-AioContext-switch-in-drained-section.patch new file mode 100644 index 0000000..3674a59 --- /dev/null +++ b/SOURCES/kvm-test-bdrv-drain-AioContext-switch-in-drained-section.patch @@ -0,0 +1,76 @@ +From ed61540140ae22f897f15846a359dc11a0a0b027 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 1 Mar 2019 14:27:46 +0100 +Subject: [PATCH 4/9] test-bdrv-drain: AioContext switch in drained section + +RH-Author: Kevin Wolf +Message-id: <20190301142747.12251-5-kwolf@redhat.com> +Patchwork-id: 84766 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 4/5] test-bdrv-drain: AioContext switch in drained section +Bugzilla: 1671173 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Paolo Bonzini +RH-Acked-by: John Snow + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +(cherry picked from commit 247d2737715833525725d27e5cecf5840c62f900) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/test-bdrv-drain.c | 32 ++++++++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index 8641b54..05c6f12 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -1521,6 +1521,36 @@ static void test_append_to_drained(void) + blk_unref(blk); + } + ++static void test_set_aio_context(void) ++{ ++ BlockDriverState *bs; ++ IOThread *a = iothread_new(); ++ IOThread *b = iothread_new(); ++ AioContext *ctx_a = iothread_get_aio_context(a); ++ AioContext *ctx_b = iothread_get_aio_context(b); ++ ++ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, ++ &error_abort); ++ ++ bdrv_drained_begin(bs); ++ bdrv_set_aio_context(bs, ctx_a); ++ ++ aio_context_acquire(ctx_a); ++ bdrv_drained_end(bs); ++ ++ bdrv_drained_begin(bs); ++ bdrv_set_aio_context(bs, ctx_b); ++ aio_context_release(ctx_a); ++ aio_context_acquire(ctx_b); ++ bdrv_set_aio_context(bs, qemu_get_aio_context()); ++ aio_context_release(ctx_b); ++ bdrv_drained_end(bs); ++ ++ bdrv_unref(bs); ++ iothread_join(a); ++ iothread_join(b); ++} ++ + int main(int argc, char **argv) + { + int ret; +@@ -1602,6 +1632,8 @@ int main(int argc, char **argv) + + g_test_add_func("/bdrv-drain/attach/drain", test_append_to_drained); + ++ g_test_add_func("/bdrv-drain/set_aio_context", test_set_aio_context); ++ + ret = g_test_run(); + qemu_event_destroy(&done_event); + return ret; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-test-bdrv-drain-Drain-with-block-jobs-in-an-I-O-thre.patch b/SOURCES/kvm-test-bdrv-drain-Drain-with-block-jobs-in-an-I-O-thre.patch new file mode 100644 index 0000000..b3e32b2 --- /dev/null +++ b/SOURCES/kvm-test-bdrv-drain-Drain-with-block-jobs-in-an-I-O-thre.patch @@ -0,0 +1,206 @@ +From 226e66779d3cc43409b08618d2bbdf160c6d42d8 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:27 +0200 +Subject: [PATCH 36/49] test-bdrv-drain: Drain with block jobs in an I/O thread + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-30-kwolf@redhat.com> +Patchwork-id: 82182 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 29/42] test-bdrv-drain: Drain with block jobs in an I/O thread +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This extends the existing drain test with a block job to include +variants where the block job runs in a different AioContext. + +Signed-off-by: Kevin Wolf +Reviewed-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + tests/test-bdrv-drain.c | 92 +++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 86 insertions(+), 6 deletions(-) + +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index 9bcb3c7..3cf3ba3 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -174,6 +174,28 @@ static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs) + } + } + ++static void do_drain_begin_unlocked(enum drain_type drain_type, BlockDriverState *bs) ++{ ++ if (drain_type != BDRV_DRAIN_ALL) { ++ aio_context_acquire(bdrv_get_aio_context(bs)); ++ } ++ do_drain_begin(drain_type, bs); ++ if (drain_type != BDRV_DRAIN_ALL) { ++ aio_context_release(bdrv_get_aio_context(bs)); ++ } ++} ++ ++static void do_drain_end_unlocked(enum drain_type drain_type, BlockDriverState *bs) ++{ ++ if (drain_type != BDRV_DRAIN_ALL) { ++ aio_context_acquire(bdrv_get_aio_context(bs)); ++ } ++ do_drain_end(drain_type, bs); ++ if (drain_type != BDRV_DRAIN_ALL) { ++ aio_context_release(bdrv_get_aio_context(bs)); ++ } ++} ++ + static void test_drv_cb_common(enum drain_type drain_type, bool recursive) + { + BlockBackend *blk; +@@ -785,11 +807,13 @@ BlockJobDriver test_job_driver = { + }, + }; + +-static void test_blockjob_common(enum drain_type drain_type) ++static void test_blockjob_common(enum drain_type drain_type, bool use_iothread) + { + BlockBackend *blk_src, *blk_target; + BlockDriverState *src, *target; + BlockJob *job; ++ IOThread *iothread = NULL; ++ AioContext *ctx; + int ret; + + src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR, +@@ -797,21 +821,31 @@ static void test_blockjob_common(enum drain_type drain_type) + blk_src = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + blk_insert_bs(blk_src, src, &error_abort); + ++ if (use_iothread) { ++ iothread = iothread_new(); ++ ctx = iothread_get_aio_context(iothread); ++ blk_set_aio_context(blk_src, ctx); ++ } else { ++ ctx = qemu_get_aio_context(); ++ } ++ + target = bdrv_new_open_driver(&bdrv_test, "target", BDRV_O_RDWR, + &error_abort); + blk_target = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + blk_insert_bs(blk_target, target, &error_abort); + ++ aio_context_acquire(ctx); + job = block_job_create("job0", &test_job_driver, NULL, src, 0, BLK_PERM_ALL, + 0, 0, NULL, NULL, &error_abort); + block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort); + job_start(&job->job); ++ aio_context_release(ctx); + + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); + g_assert_true(job->job.busy); /* We're in job_sleep_ns() */ + +- do_drain_begin(drain_type, src); ++ do_drain_begin_unlocked(drain_type, src); + + if (drain_type == BDRV_DRAIN_ALL) { + /* bdrv_drain_all() drains both src and target */ +@@ -822,7 +856,14 @@ static void test_blockjob_common(enum drain_type drain_type) + g_assert_true(job->job.paused); + g_assert_false(job->job.busy); /* The job is paused */ + +- do_drain_end(drain_type, src); ++ do_drain_end_unlocked(drain_type, src); ++ ++ if (use_iothread) { ++ /* paused is reset in the I/O thread, wait for it */ ++ while (job->job.paused) { ++ aio_poll(qemu_get_aio_context(), false); ++ } ++ } + + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); +@@ -841,32 +882,64 @@ static void test_blockjob_common(enum drain_type drain_type) + + do_drain_end(drain_type, target); + ++ if (use_iothread) { ++ /* paused is reset in the I/O thread, wait for it */ ++ while (job->job.paused) { ++ aio_poll(qemu_get_aio_context(), false); ++ } ++ } ++ + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); + g_assert_true(job->job.busy); /* We're in job_sleep_ns() */ + ++ aio_context_acquire(ctx); + ret = job_complete_sync(&job->job, &error_abort); + g_assert_cmpint(ret, ==, 0); + ++ if (use_iothread) { ++ blk_set_aio_context(blk_src, qemu_get_aio_context()); ++ } ++ aio_context_release(ctx); ++ + blk_unref(blk_src); + blk_unref(blk_target); + bdrv_unref(src); + bdrv_unref(target); ++ ++ if (iothread) { ++ iothread_join(iothread); ++ } + } + + static void test_blockjob_drain_all(void) + { +- test_blockjob_common(BDRV_DRAIN_ALL); ++ test_blockjob_common(BDRV_DRAIN_ALL, false); + } + + static void test_blockjob_drain(void) + { +- test_blockjob_common(BDRV_DRAIN); ++ test_blockjob_common(BDRV_DRAIN, false); + } + + static void test_blockjob_drain_subtree(void) + { +- test_blockjob_common(BDRV_SUBTREE_DRAIN); ++ test_blockjob_common(BDRV_SUBTREE_DRAIN, false); ++} ++ ++static void test_blockjob_iothread_drain_all(void) ++{ ++ test_blockjob_common(BDRV_DRAIN_ALL, true); ++} ++ ++static void test_blockjob_iothread_drain(void) ++{ ++ test_blockjob_common(BDRV_DRAIN, true); ++} ++ ++static void test_blockjob_iothread_drain_subtree(void) ++{ ++ test_blockjob_common(BDRV_SUBTREE_DRAIN, true); + } + + +@@ -1337,6 +1410,13 @@ int main(int argc, char **argv) + g_test_add_func("/bdrv-drain/blockjob/drain_subtree", + test_blockjob_drain_subtree); + ++ g_test_add_func("/bdrv-drain/blockjob/iothread/drain_all", ++ test_blockjob_iothread_drain_all); ++ g_test_add_func("/bdrv-drain/blockjob/iothread/drain", ++ test_blockjob_iothread_drain); ++ g_test_add_func("/bdrv-drain/blockjob/iothread/drain_subtree", ++ test_blockjob_iothread_drain_subtree); ++ + g_test_add_func("/bdrv-drain/deletion/drain", test_delete_by_drain); + g_test_add_func("/bdrv-drain/detach/drain_all", test_detach_by_drain_all); + g_test_add_func("/bdrv-drain/detach/drain", test_detach_by_drain); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-test-bdrv-drain-Fix-outdated-comments.patch b/SOURCES/kvm-test-bdrv-drain-Fix-outdated-comments.patch new file mode 100644 index 0000000..ea1af43 --- /dev/null +++ b/SOURCES/kvm-test-bdrv-drain-Fix-outdated-comments.patch @@ -0,0 +1,67 @@ +From afdf8a5ba72ac761081edba383699bd45968349e Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 21 Sep 2018 12:46:28 +0200 +Subject: [PATCH 1/3] test-bdrv-drain: Fix outdated comments + +RH-Author: Kevin Wolf +Message-id: <20180921124630.29036-2-kwolf@redhat.com> +Patchwork-id: 82232 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/3] test-bdrv-drain: Fix outdated comments +Bugzilla: 1618584 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Eric Blake +RH-Acked-by: John Snow + +Commit 89bd030533e changed the test case from using job_sleep_ns() to +using qemu_co_sleep_ns() instead. Also, block_job_sleep_ns() became +job_sleep_ns() in commit 5d43e86e11f. + +In both cases, some comments in the test case were not updated. Do that +now. + +Reported-by: Max Reitz +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/test-bdrv-drain.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index d6202b2..7e7ba9b 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -820,9 +820,9 @@ static int coroutine_fn test_job_run(Job *job, Error **errp) + + job_transition_to_ready(&s->common.job); + while (!s->should_complete) { +- /* Avoid block_job_sleep_ns() because it marks the job as !busy. We +- * want to emulate some actual activity (probably some I/O) here so +- * that drain has to wait for this acitivity to stop. */ ++ /* Avoid job_sleep_ns() because it marks the job as !busy. We want to ++ * emulate some actual activity (probably some I/O) here so that drain ++ * has to wait for this activity to stop. */ + qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000); + job_pause_point(&s->common.job); + } +@@ -908,7 +908,7 @@ static void test_blockjob_common(enum drain_type drain_type, bool use_iothread, + + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); +- g_assert_true(job->job.busy); /* We're in job_sleep_ns() */ ++ g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ + + do_drain_begin_unlocked(drain_type, src); + +@@ -956,7 +956,7 @@ static void test_blockjob_common(enum drain_type drain_type, bool use_iothread, + + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); +- g_assert_true(job->job.busy); /* We're in job_sleep_ns() */ ++ g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ + + aio_context_acquire(ctx); + ret = job_complete_sync(&job->job, &error_abort); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-test-bdrv-drain-Graph-change-through-parent-callback.patch b/SOURCES/kvm-test-bdrv-drain-Graph-change-through-parent-callback.patch new file mode 100644 index 0000000..6515809 --- /dev/null +++ b/SOURCES/kvm-test-bdrv-drain-Graph-change-through-parent-callback.patch @@ -0,0 +1,173 @@ +From d365fdc837cbf347d8575592aeb96097b6a85923 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:11 +0200 +Subject: [PATCH 20/49] test-bdrv-drain: Graph change through parent callback + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-14-kwolf@redhat.com> +Patchwork-id: 82165 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 13/42] test-bdrv-drain: Graph change through parent callback +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Signed-off-by: Kevin Wolf +(cherry picked from commit 231281ab42dad2b407b941e36ad11cbc6586e937) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/test-bdrv-drain.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 130 insertions(+) + +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index 38706b0..f786326 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -977,6 +977,135 @@ static void test_detach_by_drain_subtree(void) + } + + ++struct detach_by_parent_data { ++ BlockDriverState *parent_b; ++ BdrvChild *child_b; ++ BlockDriverState *c; ++ BdrvChild *child_c; ++}; ++ ++static void detach_by_parent_aio_cb(void *opaque, int ret) ++{ ++ struct detach_by_parent_data *data = opaque; ++ ++ g_assert_cmpint(ret, ==, 0); ++ bdrv_unref_child(data->parent_b, data->child_b); ++ ++ bdrv_ref(data->c); ++ data->child_c = bdrv_attach_child(data->parent_b, data->c, "PB-C", ++ &child_file, &error_abort); ++} ++ ++/* ++ * Initial graph: ++ * ++ * PA PB ++ * \ / \ ++ * A B C ++ * ++ * PA has a pending write request whose callback changes the child nodes of PB: ++ * It removes B and adds C instead. The subtree of PB is drained, which will ++ * indirectly drain the write request, too. ++ */ ++static void test_detach_by_parent_cb(void) ++{ ++ BlockBackend *blk; ++ BlockDriverState *parent_a, *parent_b, *a, *b, *c; ++ BdrvChild *child_a, *child_b; ++ BlockAIOCB *acb; ++ struct detach_by_parent_data data; ++ ++ QEMUIOVector qiov; ++ struct iovec iov = { ++ .iov_base = NULL, ++ .iov_len = 0, ++ }; ++ qemu_iovec_init_external(&qiov, &iov, 1); ++ ++ /* Create all involved nodes */ ++ parent_a = bdrv_new_open_driver(&bdrv_test, "parent-a", BDRV_O_RDWR, ++ &error_abort); ++ parent_b = bdrv_new_open_driver(&bdrv_test, "parent-b", 0, ++ &error_abort); ++ ++ a = bdrv_new_open_driver(&bdrv_test, "a", BDRV_O_RDWR, &error_abort); ++ b = bdrv_new_open_driver(&bdrv_test, "b", BDRV_O_RDWR, &error_abort); ++ c = bdrv_new_open_driver(&bdrv_test, "c", BDRV_O_RDWR, &error_abort); ++ ++ /* blk is a BB for parent-a */ ++ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); ++ blk_insert_bs(blk, parent_a, &error_abort); ++ bdrv_unref(parent_a); ++ ++ /* Set child relationships */ ++ bdrv_ref(b); ++ bdrv_ref(a); ++ child_b = bdrv_attach_child(parent_b, b, "PB-B", &child_file, &error_abort); ++ child_a = bdrv_attach_child(parent_b, a, "PB-A", &child_backing, &error_abort); ++ ++ bdrv_ref(a); ++ bdrv_attach_child(parent_a, a, "PA-A", &child_file, &error_abort); ++ ++ g_assert_cmpint(parent_a->refcnt, ==, 1); ++ g_assert_cmpint(parent_b->refcnt, ==, 1); ++ g_assert_cmpint(a->refcnt, ==, 3); ++ g_assert_cmpint(b->refcnt, ==, 2); ++ g_assert_cmpint(c->refcnt, ==, 1); ++ ++ g_assert(QLIST_FIRST(&parent_b->children) == child_a); ++ g_assert(QLIST_NEXT(child_a, next) == child_b); ++ g_assert(QLIST_NEXT(child_b, next) == NULL); ++ ++ /* Start the evil write request */ ++ data = (struct detach_by_parent_data) { ++ .parent_b = parent_b, ++ .child_b = child_b, ++ .c = c, ++ }; ++ acb = blk_aio_preadv(blk, 0, &qiov, 0, detach_by_parent_aio_cb, &data); ++ g_assert(acb != NULL); ++ ++ /* Drain and check the expected result */ ++ bdrv_subtree_drained_begin(parent_b); ++ ++ g_assert(data.child_c != NULL); ++ ++ g_assert_cmpint(parent_a->refcnt, ==, 1); ++ g_assert_cmpint(parent_b->refcnt, ==, 1); ++ g_assert_cmpint(a->refcnt, ==, 3); ++ g_assert_cmpint(b->refcnt, ==, 1); ++ g_assert_cmpint(c->refcnt, ==, 2); ++ ++ g_assert(QLIST_FIRST(&parent_b->children) == data.child_c); ++ g_assert(QLIST_NEXT(data.child_c, next) == child_a); ++ g_assert(QLIST_NEXT(child_a, next) == NULL); ++ ++ g_assert_cmpint(parent_a->quiesce_counter, ==, 1); ++ g_assert_cmpint(parent_b->quiesce_counter, ==, 1); ++ g_assert_cmpint(a->quiesce_counter, ==, 1); ++ g_assert_cmpint(b->quiesce_counter, ==, 0); ++ g_assert_cmpint(c->quiesce_counter, ==, 1); ++ ++ bdrv_subtree_drained_end(parent_b); ++ ++ bdrv_unref(parent_b); ++ blk_unref(blk); ++ ++ /* XXX Once bdrv_close() unref's children instead of just detaching them, ++ * this won't be necessary any more. */ ++ bdrv_unref(a); ++ bdrv_unref(a); ++ bdrv_unref(c); ++ ++ g_assert_cmpint(a->refcnt, ==, 1); ++ g_assert_cmpint(b->refcnt, ==, 1); ++ g_assert_cmpint(c->refcnt, ==, 1); ++ bdrv_unref(a); ++ bdrv_unref(b); ++ bdrv_unref(c); ++} ++ ++ + int main(int argc, char **argv) + { + int ret; +@@ -1027,6 +1156,7 @@ int main(int argc, char **argv) + g_test_add_func("/bdrv-drain/deletion/drain", test_delete_by_drain); + g_test_add_func("/bdrv-drain/detach/drain", test_detach_by_drain); + g_test_add_func("/bdrv-drain/detach/drain_subtree", test_detach_by_drain_subtree); ++ g_test_add_func("/bdrv-drain/detach/parent_cb", test_detach_by_parent_cb); + + ret = g_test_run(); + qemu_event_destroy(&done_event); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-test-bdrv-drain-Test-AIO_WAIT_WHILE-in-completion-ca.patch b/SOURCES/kvm-test-bdrv-drain-Test-AIO_WAIT_WHILE-in-completion-ca.patch new file mode 100644 index 0000000..3136aa3 --- /dev/null +++ b/SOURCES/kvm-test-bdrv-drain-Test-AIO_WAIT_WHILE-in-completion-ca.patch @@ -0,0 +1,58 @@ +From c6dae9ed2996e40bfdcf6c680bd481c43182fb3d Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:30 +0200 +Subject: [PATCH 39/49] test-bdrv-drain: Test AIO_WAIT_WHILE() in completion + callback + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-33-kwolf@redhat.com> +Patchwork-id: 82181 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 32/42] test-bdrv-drain: Test AIO_WAIT_WHILE() in completion callback +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This is a regression test for a deadlock that occurred in block job +completion callbacks (via job_defer_to_main_loop) because the AioContext +lock was taken twice: once in job_finish_sync() and then again in +job_defer_to_main_loop_bh(). This would cause AIO_WAIT_WHILE() to hang. + +Signed-off-by: Kevin Wolf +Reviewed-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + tests/test-bdrv-drain.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index 3cf3ba3..05f3b55 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -774,6 +774,15 @@ typedef struct TestBlockJob { + bool should_complete; + } TestBlockJob; + ++static int test_job_prepare(Job *job) ++{ ++ TestBlockJob *s = container_of(job, TestBlockJob, common.job); ++ ++ /* Provoke an AIO_WAIT_WHILE() call to verify there is no deadlock */ ++ blk_flush(s->common.blk); ++ return 0; ++} ++ + static int coroutine_fn test_job_run(Job *job, Error **errp) + { + TestBlockJob *s = container_of(job, TestBlockJob, common.job); +@@ -804,6 +813,7 @@ BlockJobDriver test_job_driver = { + .drain = block_job_drain, + .run = test_job_run, + .complete = test_job_complete, ++ .prepare = test_job_prepare, + }, + }; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-test-bdrv-drain-Test-bdrv_append-to-drained-node.patch b/SOURCES/kvm-test-bdrv-drain-Test-bdrv_append-to-drained-node.patch new file mode 100644 index 0000000..ccd0efe --- /dev/null +++ b/SOURCES/kvm-test-bdrv-drain-Test-bdrv_append-to-drained-node.patch @@ -0,0 +1,86 @@ +From 55be3bf080a5b4768b88c1bc37b0867031add03d Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:20 +0200 +Subject: [PATCH 29/49] test-bdrv-drain: Test bdrv_append() to drained node + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-23-kwolf@redhat.com> +Patchwork-id: 82174 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 22/42] test-bdrv-drain: Test bdrv_append() to drained node +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Signed-off-by: Kevin Wolf +(cherry picked from commit b994c5bc515fe611885113e7cfa7e87817bfd4e2) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/test-bdrv-drain.c | 43 +++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 43 insertions(+) + +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index 1c8162f..9bcb3c7 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -1245,6 +1245,47 @@ static void test_detach_by_driver_cb(void) + test_detach_indirect(false); + } + ++static void test_append_to_drained(void) ++{ ++ BlockBackend *blk; ++ BlockDriverState *base, *overlay; ++ BDRVTestState *base_s, *overlay_s; ++ ++ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); ++ base = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort); ++ base_s = base->opaque; ++ blk_insert_bs(blk, base, &error_abort); ++ ++ overlay = bdrv_new_open_driver(&bdrv_test, "overlay", BDRV_O_RDWR, ++ &error_abort); ++ overlay_s = overlay->opaque; ++ ++ do_drain_begin(BDRV_DRAIN, base); ++ g_assert_cmpint(base->quiesce_counter, ==, 1); ++ g_assert_cmpint(base_s->drain_count, ==, 1); ++ g_assert_cmpint(base->in_flight, ==, 0); ++ ++ /* Takes ownership of overlay, so we don't have to unref it later */ ++ bdrv_append(overlay, base, &error_abort); ++ g_assert_cmpint(base->in_flight, ==, 0); ++ g_assert_cmpint(overlay->in_flight, ==, 0); ++ ++ g_assert_cmpint(base->quiesce_counter, ==, 1); ++ g_assert_cmpint(base_s->drain_count, ==, 1); ++ g_assert_cmpint(overlay->quiesce_counter, ==, 1); ++ g_assert_cmpint(overlay_s->drain_count, ==, 1); ++ ++ do_drain_end(BDRV_DRAIN, base); ++ ++ g_assert_cmpint(base->quiesce_counter, ==, 0); ++ g_assert_cmpint(base_s->drain_count, ==, 0); ++ g_assert_cmpint(overlay->quiesce_counter, ==, 0); ++ g_assert_cmpint(overlay_s->drain_count, ==, 0); ++ ++ bdrv_unref(base); ++ blk_unref(blk); ++} ++ + int main(int argc, char **argv) + { + int ret; +@@ -1303,6 +1344,8 @@ int main(int argc, char **argv) + g_test_add_func("/bdrv-drain/detach/parent_cb", test_detach_by_parent_cb); + g_test_add_func("/bdrv-drain/detach/driver_cb", test_detach_by_driver_cb); + ++ g_test_add_func("/bdrv-drain/attach/drain", test_append_to_drained); ++ + ret = g_test_run(); + qemu_event_destroy(&done_event); + return ret; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-test-bdrv-drain-Test-draining-job-source-child-and-p.patch b/SOURCES/kvm-test-bdrv-drain-Test-draining-job-source-child-and-p.patch new file mode 100644 index 0000000..1266558 --- /dev/null +++ b/SOURCES/kvm-test-bdrv-drain-Test-draining-job-source-child-and-p.patch @@ -0,0 +1,185 @@ +From 36cd4342651561d7dda8bccc0520a35ca505c466 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 21 Sep 2018 12:46:30 +0200 +Subject: [PATCH 3/3] test-bdrv-drain: Test draining job source child and + parent + +RH-Author: Kevin Wolf +Message-id: <20180921124630.29036-4-kwolf@redhat.com> +Patchwork-id: 82233 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 3/3] test-bdrv-drain: Test draining job source child and parent +Bugzilla: 1618584 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Paolo Bonzini +RH-Acked-by: John Snow + +For the block job drain test, don't only test draining the source and +the target node, but create a backing chain for the source +(source_backing <- source <- source_overlay) and test draining each of +the nodes in it. + +When using iothreads, the source node (and therefore the job) is in a +different AioContext than the drain, which happens from the main +thread. This way, the main thread waits in AIO_WAIT_WHILE() for the +iothread to make process and aio_wait_kick() is required to notify it. +The test validates that calling bdrv_wakeup() for a child or a parent +node will actually notify AIO_WAIT_WHILE() instead of letting it hang. + +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/test-bdrv-drain.c | 77 ++++++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 69 insertions(+), 8 deletions(-) + +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index 7e7ba9b..8641b54 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -786,6 +786,7 @@ typedef struct TestBlockJob { + BlockJob common; + int run_ret; + int prepare_ret; ++ bool running; + bool should_complete; + } TestBlockJob; + +@@ -818,12 +819,17 @@ static int coroutine_fn test_job_run(Job *job, Error **errp) + { + TestBlockJob *s = container_of(job, TestBlockJob, common.job); + ++ /* We are running the actual job code past the pause point in ++ * job_co_entry(). */ ++ s->running = true; ++ + job_transition_to_ready(&s->common.job); + while (!s->should_complete) { + /* Avoid job_sleep_ns() because it marks the job as !busy. We want to + * emulate some actual activity (probably some I/O) here so that drain + * has to wait for this activity to stop. */ +- qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000); ++ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000); ++ + job_pause_point(&s->common.job); + } + +@@ -856,11 +862,19 @@ enum test_job_result { + TEST_JOB_FAIL_PREPARE, + }; + +-static void test_blockjob_common(enum drain_type drain_type, bool use_iothread, +- enum test_job_result result) ++enum test_job_drain_node { ++ TEST_JOB_DRAIN_SRC, ++ TEST_JOB_DRAIN_SRC_CHILD, ++ TEST_JOB_DRAIN_SRC_PARENT, ++}; ++ ++static void test_blockjob_common_drain_node(enum drain_type drain_type, ++ bool use_iothread, ++ enum test_job_result result, ++ enum test_job_drain_node drain_node) + { + BlockBackend *blk_src, *blk_target; +- BlockDriverState *src, *target; ++ BlockDriverState *src, *src_backing, *src_overlay, *target, *drain_bs; + BlockJob *job; + TestBlockJob *tjob; + IOThread *iothread = NULL; +@@ -869,8 +883,32 @@ static void test_blockjob_common(enum drain_type drain_type, bool use_iothread, + + src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR, + &error_abort); ++ src_backing = bdrv_new_open_driver(&bdrv_test, "source-backing", ++ BDRV_O_RDWR, &error_abort); ++ src_overlay = bdrv_new_open_driver(&bdrv_test, "source-overlay", ++ BDRV_O_RDWR, &error_abort); ++ ++ bdrv_set_backing_hd(src_overlay, src, &error_abort); ++ bdrv_unref(src); ++ bdrv_set_backing_hd(src, src_backing, &error_abort); ++ bdrv_unref(src_backing); ++ + blk_src = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); +- blk_insert_bs(blk_src, src, &error_abort); ++ blk_insert_bs(blk_src, src_overlay, &error_abort); ++ ++ switch (drain_node) { ++ case TEST_JOB_DRAIN_SRC: ++ drain_bs = src; ++ break; ++ case TEST_JOB_DRAIN_SRC_CHILD: ++ drain_bs = src_backing; ++ break; ++ case TEST_JOB_DRAIN_SRC_PARENT: ++ drain_bs = src_overlay; ++ break; ++ default: ++ g_assert_not_reached(); ++ } + + if (use_iothread) { + iothread = iothread_new(); +@@ -906,11 +944,21 @@ static void test_blockjob_common(enum drain_type drain_type, bool use_iothread, + job_start(&job->job); + aio_context_release(ctx); + ++ if (use_iothread) { ++ /* job_co_entry() is run in the I/O thread, wait for the actual job ++ * code to start (we don't want to catch the job in the pause point in ++ * job_co_entry(). */ ++ while (!tjob->running) { ++ aio_poll(qemu_get_aio_context(), false); ++ } ++ } ++ + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); ++ g_assert_true(tjob->running); + g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ + +- do_drain_begin_unlocked(drain_type, src); ++ do_drain_begin_unlocked(drain_type, drain_bs); + + if (drain_type == BDRV_DRAIN_ALL) { + /* bdrv_drain_all() drains both src and target */ +@@ -921,7 +969,7 @@ static void test_blockjob_common(enum drain_type drain_type, bool use_iothread, + g_assert_true(job->job.paused); + g_assert_false(job->job.busy); /* The job is paused */ + +- do_drain_end_unlocked(drain_type, src); ++ do_drain_end_unlocked(drain_type, drain_bs); + + if (use_iothread) { + /* paused is reset in the I/O thread, wait for it */ +@@ -969,7 +1017,7 @@ static void test_blockjob_common(enum drain_type drain_type, bool use_iothread, + + blk_unref(blk_src); + blk_unref(blk_target); +- bdrv_unref(src); ++ bdrv_unref(src_overlay); + bdrv_unref(target); + + if (iothread) { +@@ -977,6 +1025,19 @@ static void test_blockjob_common(enum drain_type drain_type, bool use_iothread, + } + } + ++static void test_blockjob_common(enum drain_type drain_type, bool use_iothread, ++ enum test_job_result result) ++{ ++ test_blockjob_common_drain_node(drain_type, use_iothread, result, ++ TEST_JOB_DRAIN_SRC); ++ test_blockjob_common_drain_node(drain_type, use_iothread, result, ++ TEST_JOB_DRAIN_SRC_CHILD); ++ if (drain_type == BDRV_SUBTREE_DRAIN) { ++ test_blockjob_common_drain_node(drain_type, use_iothread, result, ++ TEST_JOB_DRAIN_SRC_PARENT); ++ } ++} ++ + static void test_blockjob_drain_all(void) + { + test_blockjob_common(BDRV_DRAIN_ALL, false, TEST_JOB_SUCCESS); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-test-bdrv-drain-Test-graph-changes-in-drain_all-sect.patch b/SOURCES/kvm-test-bdrv-drain-Test-graph-changes-in-drain_all-sect.patch new file mode 100644 index 0000000..72f2039 --- /dev/null +++ b/SOURCES/kvm-test-bdrv-drain-Test-graph-changes-in-drain_all-sect.patch @@ -0,0 +1,151 @@ +From d6a8d5cf2b3c225eeb8e6deed9103baf638982dd Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:18 +0200 +Subject: [PATCH 27/49] test-bdrv-drain: Test graph changes in drain_all + section + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-21-kwolf@redhat.com> +Patchwork-id: 82172 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 20/42] test-bdrv-drain: Test graph changes in drain_all section +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This tests both adding and remove a node between bdrv_drain_all_begin() +and bdrv_drain_all_end(), and enabled the existing detach test for +drain_all. + +Signed-off-by: Kevin Wolf +(cherry picked from commit 19f7a7e574a099dca13120441fbe723cea9c1dc2) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/test-bdrv-drain.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 73 insertions(+), 2 deletions(-) + +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index c4aa913..1c8162f 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -452,7 +452,7 @@ static void test_multiparent(void) + blk_unref(blk_b); + } + +-static void test_graph_change(void) ++static void test_graph_change_drain_subtree(void) + { + BlockBackend *blk_a, *blk_b; + BlockDriverState *bs_a, *bs_b, *backing; +@@ -531,6 +531,63 @@ static void test_graph_change(void) + blk_unref(blk_b); + } + ++static void test_graph_change_drain_all(void) ++{ ++ BlockBackend *blk_a, *blk_b; ++ BlockDriverState *bs_a, *bs_b; ++ BDRVTestState *a_s, *b_s; ++ ++ /* Create node A with a BlockBackend */ ++ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); ++ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR, ++ &error_abort); ++ a_s = bs_a->opaque; ++ blk_insert_bs(blk_a, bs_a, &error_abort); ++ ++ g_assert_cmpint(bs_a->quiesce_counter, ==, 0); ++ g_assert_cmpint(a_s->drain_count, ==, 0); ++ ++ /* Call bdrv_drain_all_begin() */ ++ bdrv_drain_all_begin(); ++ ++ g_assert_cmpint(bs_a->quiesce_counter, ==, 1); ++ g_assert_cmpint(a_s->drain_count, ==, 1); ++ ++ /* Create node B with a BlockBackend */ ++ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); ++ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR, ++ &error_abort); ++ b_s = bs_b->opaque; ++ blk_insert_bs(blk_b, bs_b, &error_abort); ++ ++ g_assert_cmpint(bs_a->quiesce_counter, ==, 1); ++ g_assert_cmpint(bs_b->quiesce_counter, ==, 1); ++ g_assert_cmpint(a_s->drain_count, ==, 1); ++ g_assert_cmpint(b_s->drain_count, ==, 1); ++ ++ /* Unref and finally delete node A */ ++ blk_unref(blk_a); ++ ++ g_assert_cmpint(bs_a->quiesce_counter, ==, 1); ++ g_assert_cmpint(bs_b->quiesce_counter, ==, 1); ++ g_assert_cmpint(a_s->drain_count, ==, 1); ++ g_assert_cmpint(b_s->drain_count, ==, 1); ++ ++ bdrv_unref(bs_a); ++ ++ g_assert_cmpint(bs_b->quiesce_counter, ==, 1); ++ g_assert_cmpint(b_s->drain_count, ==, 1); ++ ++ /* End the drained section */ ++ bdrv_drain_all_end(); ++ ++ g_assert_cmpint(bs_b->quiesce_counter, ==, 0); ++ g_assert_cmpint(b_s->drain_count, ==, 0); ++ ++ bdrv_unref(bs_b); ++ blk_unref(blk_b); ++} ++ + struct test_iothread_data { + BlockDriverState *bs; + enum drain_type drain_type; +@@ -966,6 +1023,10 @@ static void do_test_delete_by_drain(bool detach_instead_of_delete, + bdrv_subtree_drained_begin(bs); + bdrv_subtree_drained_end(bs); + break; ++ case BDRV_DRAIN_ALL: ++ bdrv_drain_all_begin(); ++ bdrv_drain_all_end(); ++ break; + default: + g_assert_not_reached(); + } +@@ -986,6 +1047,11 @@ static void test_delete_by_drain(void) + do_test_delete_by_drain(false, BDRV_DRAIN); + } + ++static void test_detach_by_drain_all(void) ++{ ++ do_test_delete_by_drain(true, BDRV_DRAIN_ALL); ++} ++ + static void test_detach_by_drain(void) + { + do_test_delete_by_drain(true, BDRV_DRAIN); +@@ -1214,7 +1280,11 @@ int main(int argc, char **argv) + + g_test_add_func("/bdrv-drain/nested", test_nested); + g_test_add_func("/bdrv-drain/multiparent", test_multiparent); +- g_test_add_func("/bdrv-drain/graph-change", test_graph_change); ++ ++ g_test_add_func("/bdrv-drain/graph-change/drain_subtree", ++ test_graph_change_drain_subtree); ++ g_test_add_func("/bdrv-drain/graph-change/drain_all", ++ test_graph_change_drain_all); + + g_test_add_func("/bdrv-drain/iothread/drain_all", test_iothread_drain_all); + g_test_add_func("/bdrv-drain/iothread/drain", test_iothread_drain); +@@ -1227,6 +1297,7 @@ int main(int argc, char **argv) + test_blockjob_drain_subtree); + + g_test_add_func("/bdrv-drain/deletion/drain", test_delete_by_drain); ++ g_test_add_func("/bdrv-drain/detach/drain_all", test_detach_by_drain_all); + g_test_add_func("/bdrv-drain/detach/drain", test_detach_by_drain); + g_test_add_func("/bdrv-drain/detach/drain_subtree", test_detach_by_drain_subtree); + g_test_add_func("/bdrv-drain/detach/parent_cb", test_detach_by_parent_cb); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-test-bdrv-drain-Test-nested-poll-in-bdrv_drain_poll_.patch b/SOURCES/kvm-test-bdrv-drain-Test-nested-poll-in-bdrv_drain_poll_.patch new file mode 100644 index 0000000..0b45207 --- /dev/null +++ b/SOURCES/kvm-test-bdrv-drain-Test-nested-poll-in-bdrv_drain_poll_.patch @@ -0,0 +1,62 @@ +From 8c657cb165e931b4eb6f3675495240437b0c1cb1 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:38 +0200 +Subject: [PATCH 47/49] test-bdrv-drain: Test nested poll in + bdrv_drain_poll_top_level() + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-41-kwolf@redhat.com> +Patchwork-id: 82189 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 40/42] test-bdrv-drain: Test nested poll in bdrv_drain_poll_top_level() +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This is a regression test for a deadlock that could occur in callbacks +called from the aio_poll() in bdrv_drain_poll_top_level(). The +AioContext lock wasn't released and therefore would be taken a second +time in the callback. This would cause a possible AIO_WAIT_WHILE() in +the callback to hang. + +Signed-off-by: Kevin Wolf +Reviewed-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + tests/test-bdrv-drain.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index 05f3b55..f4b57f7 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -636,6 +636,17 @@ static void test_iothread_aio_cb(void *opaque, int ret) + qemu_event_set(&done_event); + } + ++static void test_iothread_main_thread_bh(void *opaque) ++{ ++ struct test_iothread_data *data = opaque; ++ ++ /* Test that the AioContext is not yet locked in a random BH that is ++ * executed during drain, otherwise this would deadlock. */ ++ aio_context_acquire(bdrv_get_aio_context(data->bs)); ++ bdrv_flush(data->bs); ++ aio_context_release(bdrv_get_aio_context(data->bs)); ++} ++ + /* + * Starts an AIO request on a BDS that runs in the AioContext of iothread 1. + * The request involves a BH on iothread 2 before it can complete. +@@ -705,6 +716,8 @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread) + aio_context_acquire(ctx_a); + } + ++ aio_bh_schedule_oneshot(ctx_a, test_iothread_main_thread_bh, &data); ++ + /* The request is running on the IOThread a. Draining its block device + * will make sure that it has completed as far as the BDS is concerned, + * but the drain in this thread can continue immediately after +-- +1.8.3.1 + diff --git a/SOURCES/kvm-test-bdrv-drain-Test-node-deletion-in-subtree-recurs.patch b/SOURCES/kvm-test-bdrv-drain-Test-node-deletion-in-subtree-recurs.patch new file mode 100644 index 0000000..023ea3e --- /dev/null +++ b/SOURCES/kvm-test-bdrv-drain-Test-node-deletion-in-subtree-recurs.patch @@ -0,0 +1,106 @@ +From 7a587bf1f9cac772532a41e31202fd7ef8646928 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:09 +0200 +Subject: [PATCH 18/49] test-bdrv-drain: Test node deletion in subtree + recursion + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-12-kwolf@redhat.com> +Patchwork-id: 82159 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 11/42] test-bdrv-drain: Test node deletion in subtree recursion +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +If bdrv_do_drained_begin() polls during its subtree recursion, the graph +can change and mess up the bs->children iteration. Test that this +doesn't happen. + +Signed-off-by: Kevin Wolf +(cherry picked from commit ebd31837618cdc7bda83090773dcdd87475d55b7) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/test-bdrv-drain.c | 38 +++++++++++++++++++++++++++++--------- + 1 file changed, 29 insertions(+), 9 deletions(-) + +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index 8918a94..38706b0 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -875,7 +875,8 @@ static void coroutine_fn test_co_delete_by_drain(void *opaque) + * If @detach_instead_of_delete is set, the BDS is not going to be + * deleted but will only detach all of its children. + */ +-static void do_test_delete_by_drain(bool detach_instead_of_delete) ++static void do_test_delete_by_drain(bool detach_instead_of_delete, ++ enum drain_type drain_type) + { + BlockBackend *blk; + BlockDriverState *bs, *child_bs, *null_bs; +@@ -931,9 +932,23 @@ static void do_test_delete_by_drain(bool detach_instead_of_delete) + * test_co_delete_by_drain() resuming. Thus, @bs will be deleted + * and the coroutine will exit while this drain operation is still + * in progress. */ +- bdrv_ref(child_bs); +- bdrv_drain(child_bs); +- bdrv_unref(child_bs); ++ switch (drain_type) { ++ case BDRV_DRAIN: ++ bdrv_ref(child_bs); ++ bdrv_drain(child_bs); ++ bdrv_unref(child_bs); ++ break; ++ case BDRV_SUBTREE_DRAIN: ++ /* Would have to ref/unref bs here for !detach_instead_of_delete, but ++ * then the whole test becomes pointless because the graph changes ++ * don't occur during the drain any more. */ ++ assert(detach_instead_of_delete); ++ bdrv_subtree_drained_begin(bs); ++ bdrv_subtree_drained_end(bs); ++ break; ++ default: ++ g_assert_not_reached(); ++ } + + while (!dbdd.done) { + aio_poll(qemu_get_aio_context(), true); +@@ -946,15 +961,19 @@ static void do_test_delete_by_drain(bool detach_instead_of_delete) + } + } + +- + static void test_delete_by_drain(void) + { +- do_test_delete_by_drain(false); ++ do_test_delete_by_drain(false, BDRV_DRAIN); + } + + static void test_detach_by_drain(void) + { +- do_test_delete_by_drain(true); ++ do_test_delete_by_drain(true, BDRV_DRAIN); ++} ++ ++static void test_detach_by_drain_subtree(void) ++{ ++ do_test_delete_by_drain(true, BDRV_SUBTREE_DRAIN); + } + + +@@ -1005,8 +1024,9 @@ int main(int argc, char **argv) + g_test_add_func("/bdrv-drain/blockjob/drain_subtree", + test_blockjob_drain_subtree); + +- g_test_add_func("/bdrv-drain/deletion", test_delete_by_drain); +- g_test_add_func("/bdrv-drain/detach", test_detach_by_drain); ++ g_test_add_func("/bdrv-drain/deletion/drain", test_delete_by_drain); ++ g_test_add_func("/bdrv-drain/detach/drain", test_detach_by_drain); ++ g_test_add_func("/bdrv-drain/detach/drain_subtree", test_detach_by_drain_subtree); + + ret = g_test_run(); + qemu_event_destroy(&done_event); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-test-bdrv-drain-Test-that-bdrv_drain_invoke-doesn-t-.patch b/SOURCES/kvm-test-bdrv-drain-Test-that-bdrv_drain_invoke-doesn-t-.patch new file mode 100644 index 0000000..43255ea --- /dev/null +++ b/SOURCES/kvm-test-bdrv-drain-Test-that-bdrv_drain_invoke-doesn-t-.patch @@ -0,0 +1,252 @@ +From eae61f64dccec3d6e32a7ba8d934da629fdd8595 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:13 +0200 +Subject: [PATCH 22/49] test-bdrv-drain: Test that bdrv_drain_invoke() doesn't + poll + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-16-kwolf@redhat.com> +Patchwork-id: 82167 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 15/42] test-bdrv-drain: Test that bdrv_drain_invoke() doesn't poll +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +This adds a test case that goes wrong if bdrv_drain_invoke() calls +aio_poll(). + +Signed-off-by: Kevin Wolf +(cherry picked from commit 57320ca961c2e8488e1884b4ebbcb929b6901dc6) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/test-bdrv-drain.c | 102 +++++++++++++++++++++++++++++++++++++++++------- + 1 file changed, 88 insertions(+), 14 deletions(-) + +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index f786326..c4aa913 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -34,12 +34,16 @@ static QemuEvent done_event; + typedef struct BDRVTestState { + int drain_count; + AioContext *bh_indirection_ctx; ++ bool sleep_in_drain_begin; + } BDRVTestState; + + static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs) + { + BDRVTestState *s = bs->opaque; + s->drain_count++; ++ if (s->sleep_in_drain_begin) { ++ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000); ++ } + } + + static void coroutine_fn bdrv_test_co_drain_end(BlockDriverState *bs) +@@ -80,6 +84,22 @@ static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs, + return 0; + } + ++static void bdrv_test_child_perm(BlockDriverState *bs, BdrvChild *c, ++ const BdrvChildRole *role, ++ BlockReopenQueue *reopen_queue, ++ uint64_t perm, uint64_t shared, ++ uint64_t *nperm, uint64_t *nshared) ++{ ++ /* bdrv_format_default_perms() accepts only these two, so disguise ++ * detach_by_driver_cb_role as one of them. */ ++ if (role != &child_file && role != &child_backing) { ++ role = &child_file; ++ } ++ ++ bdrv_format_default_perms(bs, c, role, reopen_queue, perm, shared, ++ nperm, nshared); ++} ++ + static BlockDriver bdrv_test = { + .format_name = "test", + .instance_size = sizeof(BDRVTestState), +@@ -90,7 +110,7 @@ static BlockDriver bdrv_test = { + .bdrv_co_drain_begin = bdrv_test_co_drain_begin, + .bdrv_co_drain_end = bdrv_test_co_drain_end, + +- .bdrv_child_perm = bdrv_format_default_perms, ++ .bdrv_child_perm = bdrv_test_child_perm, + }; + + static void aio_ret_cb(void *opaque, int ret) +@@ -982,13 +1002,14 @@ struct detach_by_parent_data { + BdrvChild *child_b; + BlockDriverState *c; + BdrvChild *child_c; ++ bool by_parent_cb; + }; ++static struct detach_by_parent_data detach_by_parent_data; + +-static void detach_by_parent_aio_cb(void *opaque, int ret) ++static void detach_indirect_bh(void *opaque) + { + struct detach_by_parent_data *data = opaque; + +- g_assert_cmpint(ret, ==, 0); + bdrv_unref_child(data->parent_b, data->child_b); + + bdrv_ref(data->c); +@@ -996,6 +1017,25 @@ static void detach_by_parent_aio_cb(void *opaque, int ret) + &child_file, &error_abort); + } + ++static void detach_by_parent_aio_cb(void *opaque, int ret) ++{ ++ struct detach_by_parent_data *data = &detach_by_parent_data; ++ ++ g_assert_cmpint(ret, ==, 0); ++ if (data->by_parent_cb) { ++ detach_indirect_bh(data); ++ } ++} ++ ++static void detach_by_driver_cb_drained_begin(BdrvChild *child) ++{ ++ aio_bh_schedule_oneshot(qemu_get_current_aio_context(), ++ detach_indirect_bh, &detach_by_parent_data); ++ child_file.drained_begin(child); ++} ++ ++static BdrvChildRole detach_by_driver_cb_role; ++ + /* + * Initial graph: + * +@@ -1003,17 +1043,25 @@ static void detach_by_parent_aio_cb(void *opaque, int ret) + * \ / \ + * A B C + * +- * PA has a pending write request whose callback changes the child nodes of PB: +- * It removes B and adds C instead. The subtree of PB is drained, which will +- * indirectly drain the write request, too. ++ * by_parent_cb == true: Test that parent callbacks don't poll ++ * ++ * PA has a pending write request whose callback changes the child nodes of ++ * PB: It removes B and adds C instead. The subtree of PB is drained, which ++ * will indirectly drain the write request, too. ++ * ++ * by_parent_cb == false: Test that bdrv_drain_invoke() doesn't poll ++ * ++ * PA's BdrvChildRole has a .drained_begin callback that schedules a BH ++ * that does the same graph change. If bdrv_drain_invoke() calls it, the ++ * state is messed up, but if it is only polled in the single ++ * BDRV_POLL_WHILE() at the end of the drain, this should work fine. + */ +-static void test_detach_by_parent_cb(void) ++static void test_detach_indirect(bool by_parent_cb) + { + BlockBackend *blk; + BlockDriverState *parent_a, *parent_b, *a, *b, *c; + BdrvChild *child_a, *child_b; + BlockAIOCB *acb; +- struct detach_by_parent_data data; + + QEMUIOVector qiov; + struct iovec iov = { +@@ -1022,6 +1070,12 @@ static void test_detach_by_parent_cb(void) + }; + qemu_iovec_init_external(&qiov, &iov, 1); + ++ if (!by_parent_cb) { ++ detach_by_driver_cb_role = child_file; ++ detach_by_driver_cb_role.drained_begin = ++ detach_by_driver_cb_drained_begin; ++ } ++ + /* Create all involved nodes */ + parent_a = bdrv_new_open_driver(&bdrv_test, "parent-a", BDRV_O_RDWR, + &error_abort); +@@ -1037,6 +1091,13 @@ static void test_detach_by_parent_cb(void) + blk_insert_bs(blk, parent_a, &error_abort); + bdrv_unref(parent_a); + ++ /* If we want to get bdrv_drain_invoke() to call aio_poll(), the driver ++ * callback must not return immediately. */ ++ if (!by_parent_cb) { ++ BDRVTestState *s = parent_a->opaque; ++ s->sleep_in_drain_begin = true; ++ } ++ + /* Set child relationships */ + bdrv_ref(b); + bdrv_ref(a); +@@ -1044,7 +1105,9 @@ static void test_detach_by_parent_cb(void) + child_a = bdrv_attach_child(parent_b, a, "PB-A", &child_backing, &error_abort); + + bdrv_ref(a); +- bdrv_attach_child(parent_a, a, "PA-A", &child_file, &error_abort); ++ bdrv_attach_child(parent_a, a, "PA-A", ++ by_parent_cb ? &child_file : &detach_by_driver_cb_role, ++ &error_abort); + + g_assert_cmpint(parent_a->refcnt, ==, 1); + g_assert_cmpint(parent_b->refcnt, ==, 1); +@@ -1057,18 +1120,19 @@ static void test_detach_by_parent_cb(void) + g_assert(QLIST_NEXT(child_b, next) == NULL); + + /* Start the evil write request */ +- data = (struct detach_by_parent_data) { ++ detach_by_parent_data = (struct detach_by_parent_data) { + .parent_b = parent_b, + .child_b = child_b, + .c = c, ++ .by_parent_cb = by_parent_cb, + }; +- acb = blk_aio_preadv(blk, 0, &qiov, 0, detach_by_parent_aio_cb, &data); ++ acb = blk_aio_preadv(blk, 0, &qiov, 0, detach_by_parent_aio_cb, NULL); + g_assert(acb != NULL); + + /* Drain and check the expected result */ + bdrv_subtree_drained_begin(parent_b); + +- g_assert(data.child_c != NULL); ++ g_assert(detach_by_parent_data.child_c != NULL); + + g_assert_cmpint(parent_a->refcnt, ==, 1); + g_assert_cmpint(parent_b->refcnt, ==, 1); +@@ -1076,8 +1140,8 @@ static void test_detach_by_parent_cb(void) + g_assert_cmpint(b->refcnt, ==, 1); + g_assert_cmpint(c->refcnt, ==, 2); + +- g_assert(QLIST_FIRST(&parent_b->children) == data.child_c); +- g_assert(QLIST_NEXT(data.child_c, next) == child_a); ++ g_assert(QLIST_FIRST(&parent_b->children) == detach_by_parent_data.child_c); ++ g_assert(QLIST_NEXT(detach_by_parent_data.child_c, next) == child_a); + g_assert(QLIST_NEXT(child_a, next) == NULL); + + g_assert_cmpint(parent_a->quiesce_counter, ==, 1); +@@ -1105,6 +1169,15 @@ static void test_detach_by_parent_cb(void) + bdrv_unref(c); + } + ++static void test_detach_by_parent_cb(void) ++{ ++ test_detach_indirect(true); ++} ++ ++static void test_detach_by_driver_cb(void) ++{ ++ test_detach_indirect(false); ++} + + int main(int argc, char **argv) + { +@@ -1157,6 +1230,7 @@ int main(int argc, char **argv) + g_test_add_func("/bdrv-drain/detach/drain", test_detach_by_drain); + g_test_add_func("/bdrv-drain/detach/drain_subtree", test_detach_by_drain_subtree); + g_test_add_func("/bdrv-drain/detach/parent_cb", test_detach_by_parent_cb); ++ g_test_add_func("/bdrv-drain/detach/driver_cb", test_detach_by_driver_cb); + + ret = g_test_run(); + qemu_event_destroy(&done_event); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-test-bdrv-drain-bdrv_drain-works-with-cross-AioConte.patch b/SOURCES/kvm-test-bdrv-drain-bdrv_drain-works-with-cross-AioConte.patch new file mode 100644 index 0000000..fb6ca25 --- /dev/null +++ b/SOURCES/kvm-test-bdrv-drain-bdrv_drain-works-with-cross-AioConte.patch @@ -0,0 +1,297 @@ +From 6ad2df3cb4ec0fbabc5d3e1beab138a071415bba Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:54:59 +0200 +Subject: [PATCH 08/49] test-bdrv-drain: bdrv_drain() works with + cross-AioContext events + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-2-kwolf@redhat.com> +Patchwork-id: 82152 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 01/42] test-bdrv-drain: bdrv_drain() works with cross-AioContext events +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +As long as nobody keeps the other I/O thread from working, there is no +reason why bdrv_drain() wouldn't work with cross-AioContext events. The +key is that the root request we're waiting for is in the AioContext +we're polling (which it always is for bdrv_drain()) so that aio_poll() +is woken up in the end. + +Add a test case that shows that it works. Remove the comment in +bdrv_drain() that claims otherwise. + +Signed-off-by: Kevin Wolf +(cherry picked from commit bb6756895459f181e2f25e877d3d7a10c297b5c8) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/io.c | 4 -- + tests/test-bdrv-drain.c | 187 +++++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 186 insertions(+), 5 deletions(-) + +diff --git a/block/io.c b/block/io.c +index bb617de..7e0a169 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -369,10 +369,6 @@ void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent) + * + * Note that unlike bdrv_drain_all(), the caller must hold the BlockDriverState + * AioContext. +- * +- * Only this BlockDriverState's AioContext is run, so in-flight requests must +- * not depend on events in other AioContexts. In that case, use +- * bdrv_drain_all() instead. + */ + void coroutine_fn bdrv_co_drain(BlockDriverState *bs) + { +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index 403446e..dee0a10 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -27,9 +27,13 @@ + #include "block/blockjob_int.h" + #include "sysemu/block-backend.h" + #include "qapi/error.h" ++#include "iothread.h" ++ ++static QemuEvent done_event; + + typedef struct BDRVTestState { + int drain_count; ++ AioContext *bh_indirection_ctx; + } BDRVTestState; + + static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs) +@@ -50,16 +54,29 @@ static void bdrv_test_close(BlockDriverState *bs) + g_assert_cmpint(s->drain_count, >, 0); + } + ++static void co_reenter_bh(void *opaque) ++{ ++ aio_co_wake(opaque); ++} ++ + static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) + { ++ BDRVTestState *s = bs->opaque; ++ + /* We want this request to stay until the polling loop in drain waits for + * it to complete. We need to sleep a while as bdrv_drain_invoke() comes + * first and polls its result, too, but it shouldn't accidentally complete + * this request yet. */ + qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000); + ++ if (s->bh_indirection_ctx) { ++ aio_bh_schedule_oneshot(s->bh_indirection_ctx, co_reenter_bh, ++ qemu_coroutine_self()); ++ qemu_coroutine_yield(); ++ } ++ + return 0; + } + +@@ -490,6 +507,164 @@ static void test_graph_change(void) + blk_unref(blk_b); + } + ++struct test_iothread_data { ++ BlockDriverState *bs; ++ enum drain_type drain_type; ++ int *aio_ret; ++}; ++ ++static void test_iothread_drain_entry(void *opaque) ++{ ++ struct test_iothread_data *data = opaque; ++ ++ aio_context_acquire(bdrv_get_aio_context(data->bs)); ++ do_drain_begin(data->drain_type, data->bs); ++ g_assert_cmpint(*data->aio_ret, ==, 0); ++ do_drain_end(data->drain_type, data->bs); ++ aio_context_release(bdrv_get_aio_context(data->bs)); ++ ++ qemu_event_set(&done_event); ++} ++ ++static void test_iothread_aio_cb(void *opaque, int ret) ++{ ++ int *aio_ret = opaque; ++ *aio_ret = ret; ++ qemu_event_set(&done_event); ++} ++ ++/* ++ * Starts an AIO request on a BDS that runs in the AioContext of iothread 1. ++ * The request involves a BH on iothread 2 before it can complete. ++ * ++ * @drain_thread = 0 means that do_drain_begin/end are called from the main ++ * thread, @drain_thread = 1 means that they are called from iothread 1. Drain ++ * for this BDS cannot be called from iothread 2 because only the main thread ++ * may do cross-AioContext polling. ++ */ ++static void test_iothread_common(enum drain_type drain_type, int drain_thread) ++{ ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ BDRVTestState *s; ++ BlockAIOCB *acb; ++ int aio_ret; ++ struct test_iothread_data data; ++ ++ IOThread *a = iothread_new(); ++ IOThread *b = iothread_new(); ++ AioContext *ctx_a = iothread_get_aio_context(a); ++ AioContext *ctx_b = iothread_get_aio_context(b); ++ ++ QEMUIOVector qiov; ++ struct iovec iov = { ++ .iov_base = NULL, ++ .iov_len = 0, ++ }; ++ qemu_iovec_init_external(&qiov, &iov, 1); ++ ++ /* bdrv_drain_all() may only be called from the main loop thread */ ++ if (drain_type == BDRV_DRAIN_ALL && drain_thread != 0) { ++ goto out; ++ } ++ ++ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); ++ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, ++ &error_abort); ++ s = bs->opaque; ++ blk_insert_bs(blk, bs, &error_abort); ++ ++ blk_set_aio_context(blk, ctx_a); ++ aio_context_acquire(ctx_a); ++ ++ s->bh_indirection_ctx = ctx_b; ++ ++ aio_ret = -EINPROGRESS; ++ if (drain_thread == 0) { ++ acb = blk_aio_preadv(blk, 0, &qiov, 0, test_iothread_aio_cb, &aio_ret); ++ } else { ++ acb = blk_aio_preadv(blk, 0, &qiov, 0, aio_ret_cb, &aio_ret); ++ } ++ g_assert(acb != NULL); ++ g_assert_cmpint(aio_ret, ==, -EINPROGRESS); ++ ++ aio_context_release(ctx_a); ++ ++ data = (struct test_iothread_data) { ++ .bs = bs, ++ .drain_type = drain_type, ++ .aio_ret = &aio_ret, ++ }; ++ ++ switch (drain_thread) { ++ case 0: ++ if (drain_type != BDRV_DRAIN_ALL) { ++ aio_context_acquire(ctx_a); ++ } ++ ++ /* The request is running on the IOThread a. Draining its block device ++ * will make sure that it has completed as far as the BDS is concerned, ++ * but the drain in this thread can continue immediately after ++ * bdrv_dec_in_flight() and aio_ret might be assigned only slightly ++ * later. */ ++ qemu_event_reset(&done_event); ++ do_drain_begin(drain_type, bs); ++ g_assert_cmpint(bs->in_flight, ==, 0); ++ ++ if (drain_type != BDRV_DRAIN_ALL) { ++ aio_context_release(ctx_a); ++ } ++ qemu_event_wait(&done_event); ++ if (drain_type != BDRV_DRAIN_ALL) { ++ aio_context_acquire(ctx_a); ++ } ++ ++ g_assert_cmpint(aio_ret, ==, 0); ++ do_drain_end(drain_type, bs); ++ ++ if (drain_type != BDRV_DRAIN_ALL) { ++ aio_context_release(ctx_a); ++ } ++ break; ++ case 1: ++ qemu_event_reset(&done_event); ++ aio_bh_schedule_oneshot(ctx_a, test_iothread_drain_entry, &data); ++ qemu_event_wait(&done_event); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ ++ aio_context_acquire(ctx_a); ++ blk_set_aio_context(blk, qemu_get_aio_context()); ++ aio_context_release(ctx_a); ++ ++ bdrv_unref(bs); ++ blk_unref(blk); ++ ++out: ++ iothread_join(a); ++ iothread_join(b); ++} ++ ++static void test_iothread_drain_all(void) ++{ ++ test_iothread_common(BDRV_DRAIN_ALL, 0); ++ test_iothread_common(BDRV_DRAIN_ALL, 1); ++} ++ ++static void test_iothread_drain(void) ++{ ++ test_iothread_common(BDRV_DRAIN, 0); ++ test_iothread_common(BDRV_DRAIN, 1); ++} ++ ++static void test_iothread_drain_subtree(void) ++{ ++ test_iothread_common(BDRV_SUBTREE_DRAIN, 0); ++ test_iothread_common(BDRV_SUBTREE_DRAIN, 1); ++} ++ + + typedef struct TestBlockJob { + BlockJob common; +@@ -613,10 +788,13 @@ static void test_blockjob_drain_subtree(void) + + int main(int argc, char **argv) + { ++ int ret; ++ + bdrv_init(); + qemu_init_main_loop(&error_abort); + + g_test_init(&argc, &argv, NULL); ++ qemu_event_init(&done_event, false); + + g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all); + g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain); +@@ -643,10 +821,17 @@ int main(int argc, char **argv) + g_test_add_func("/bdrv-drain/multiparent", test_multiparent); + g_test_add_func("/bdrv-drain/graph-change", test_graph_change); + ++ g_test_add_func("/bdrv-drain/iothread/drain_all", test_iothread_drain_all); ++ g_test_add_func("/bdrv-drain/iothread/drain", test_iothread_drain); ++ g_test_add_func("/bdrv-drain/iothread/drain_subtree", ++ test_iothread_drain_subtree); ++ + g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all); + g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain); + g_test_add_func("/bdrv-drain/blockjob/drain_subtree", + test_blockjob_drain_subtree); + +- return g_test_run(); ++ ret = g_test_run(); ++ qemu_event_destroy(&done_event); ++ return ret; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-test-blockjob-Acquire-AioContext-around-job_cancel_s.patch b/SOURCES/kvm-test-blockjob-Acquire-AioContext-around-job_cancel_s.patch new file mode 100644 index 0000000..2e22827 --- /dev/null +++ b/SOURCES/kvm-test-blockjob-Acquire-AioContext-around-job_cancel_s.patch @@ -0,0 +1,85 @@ +From db5e1dfac375e869234340036cd232313c6d4d7b Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:28 +0200 +Subject: [PATCH 37/49] test-blockjob: Acquire AioContext around + job_cancel_sync() + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-31-kwolf@redhat.com> +Patchwork-id: 82183 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 30/42] test-blockjob: Acquire AioContext around job_cancel_sync() +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +All callers in QEMU proper hold the AioContext lock when calling +job_finish_sync(). test-blockjob should do the same when it calls the +function indirectly through job_cancel_sync(). + +Signed-off-by: Kevin Wolf +Reviewed-by: Fam Zheng +Signed-off-by: Miroslav Rezanina +--- + include/qemu/job.h | 6 ++++++ + tests/test-blockjob.c | 6 ++++++ + 2 files changed, 12 insertions(+) + +diff --git a/include/qemu/job.h b/include/qemu/job.h +index 407d549..35ac7a9 100644 +--- a/include/qemu/job.h ++++ b/include/qemu/job.h +@@ -509,6 +509,8 @@ void job_user_cancel(Job *job, bool force, Error **errp); + * + * Returns the return value from the job if the job actually completed + * during the call, or -ECANCELED if it was canceled. ++ * ++ * Callers must hold the AioContext lock of job->aio_context. + */ + int job_cancel_sync(Job *job); + +@@ -526,6 +528,8 @@ void job_cancel_sync_all(void); + * function). + * + * Returns the return value from the job. ++ * ++ * Callers must hold the AioContext lock of job->aio_context. + */ + int job_complete_sync(Job *job, Error **errp); + +@@ -551,6 +555,8 @@ void job_dismiss(Job **job, Error **errp); + * + * Returns 0 if the job is successfully completed, -ECANCELED if the job was + * cancelled before completing, and -errno in other error cases. ++ * ++ * Callers must hold the AioContext lock of job->aio_context. + */ + int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp); + +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index de4c1c2..652d1e8 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -223,6 +223,10 @@ static void cancel_common(CancelJob *s) + BlockJob *job = &s->common; + BlockBackend *blk = s->blk; + JobStatus sts = job->job.status; ++ AioContext *ctx; ++ ++ ctx = job->job.aio_context; ++ aio_context_acquire(ctx); + + job_cancel_sync(&job->job); + if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) { +@@ -232,6 +236,8 @@ static void cancel_common(CancelJob *s) + assert(job->job.status == JOB_STATUS_NULL); + job_unref(&job->job); + destroy_blk(blk); ++ ++ aio_context_release(ctx); + } + + static void test_cancel_created(void) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-test-hbitmap-Add-non-advancing-iter_next-tests.patch b/SOURCES/kvm-test-hbitmap-Add-non-advancing-iter_next-tests.patch new file mode 100644 index 0000000..4f47bb3 --- /dev/null +++ b/SOURCES/kvm-test-hbitmap-Add-non-advancing-iter_next-tests.patch @@ -0,0 +1,126 @@ +From c19a999e13b0584ba947801c8795e34e5928412c Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 6 Feb 2019 22:12:24 +0100 +Subject: [PATCH 14/33] test-hbitmap: Add non-advancing iter_next tests + +RH-Author: John Snow +Message-id: <20190206221243.7407-5-jsnow@redhat.com> +Patchwork-id: 84269 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 04/23] test-hbitmap: Add non-advancing iter_next tests +Bugzilla: 1658343 +RH-Acked-by: Thomas Huth +RH-Acked-by: Laurent Vivier +RH-Acked-by: Stefan Hajnoczi + +From: Max Reitz + +Add a function that wraps hbitmap_iter_next() and always calls it in +non-advancing mode first, and in advancing mode next. The result should +always be the same. + +By using this function everywhere we called hbitmap_iter_next() before, +we should get good test coverage for non-advancing hbitmap_iter_next(). + +Signed-off-by: Max Reitz +Reviewed-by: Fam Zheng +Reviewed-by: John Snow +Message-id: 20180613181823.13618-9-mreitz@redhat.com +Signed-off-by: Max Reitz +(cherry picked from commit 269576848ec3d57d2d958cf5ac69b08c44adf816) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/test-hbitmap.c | 36 ++++++++++++++++++++++++------------ + 1 file changed, 24 insertions(+), 12 deletions(-) + +diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c +index f2158f7..5e67ac1 100644 +--- a/tests/test-hbitmap.c ++++ b/tests/test-hbitmap.c +@@ -30,6 +30,18 @@ typedef struct TestHBitmapData { + } TestHBitmapData; + + ++static int64_t check_hbitmap_iter_next(HBitmapIter *hbi) ++{ ++ int next0, next1; ++ ++ next0 = hbitmap_iter_next(hbi, false); ++ next1 = hbitmap_iter_next(hbi, true); ++ ++ g_assert_cmpint(next0, ==, next1); ++ ++ return next0; ++} ++ + /* Check that the HBitmap and the shadow bitmap contain the same data, + * ignoring the same "first" bits. + */ +@@ -46,7 +58,7 @@ static void hbitmap_test_check(TestHBitmapData *data, + + i = first; + for (;;) { +- next = hbitmap_iter_next(&hbi, true); ++ next = check_hbitmap_iter_next(&hbi); + if (next < 0) { + next = data->size; + } +@@ -435,25 +447,25 @@ static void test_hbitmap_iter_granularity(TestHBitmapData *data, + /* Note that hbitmap_test_check has to be invoked manually in this test. */ + hbitmap_test_init(data, 131072 << 7, 7); + hbitmap_iter_init(&hbi, data->hb, 0); +- g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); ++ g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); + + hbitmap_test_set(data, ((L2 + L1 + 1) << 7) + 8, 8); + hbitmap_iter_init(&hbi, data->hb, 0); +- g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, (L2 + L1 + 1) << 7); +- g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); ++ g_assert_cmpint(check_hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); ++ g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); + + hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); + + hbitmap_test_set(data, (131072 << 7) - 8, 8); + hbitmap_iter_init(&hbi, data->hb, 0); +- g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, (L2 + L1 + 1) << 7); +- g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, 131071 << 7); +- g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); ++ g_assert_cmpint(check_hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); ++ g_assert_cmpint(check_hbitmap_iter_next(&hbi), ==, 131071 << 7); ++ g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); + + hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); +- g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, 131071 << 7); +- g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); ++ g_assert_cmpint(check_hbitmap_iter_next(&hbi), ==, 131071 << 7); ++ g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); + } + + static void hbitmap_test_set_boundary_bits(TestHBitmapData *data, ssize_t diff) +@@ -893,7 +905,7 @@ static void test_hbitmap_serialize_zeroes(TestHBitmapData *data, + for (i = 0; i < num_positions; i++) { + hbitmap_deserialize_zeroes(data->hb, positions[i], min_l1, true); + hbitmap_iter_init(&iter, data->hb, 0); +- next = hbitmap_iter_next(&iter, true); ++ next = check_hbitmap_iter_next(&iter); + if (i == num_positions - 1) { + g_assert_cmpint(next, ==, -1); + } else { +@@ -919,10 +931,10 @@ static void test_hbitmap_iter_and_reset(TestHBitmapData *data, + + hbitmap_iter_init(&hbi, data->hb, BITS_PER_LONG - 1); + +- hbitmap_iter_next(&hbi, true); ++ check_hbitmap_iter_next(&hbi); + + hbitmap_reset_all(data->hb); +- hbitmap_iter_next(&hbi, true); ++ check_hbitmap_iter_next(&hbi); + } + + static void test_hbitmap_next_zero_check(TestHBitmapData *data, int64_t start) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-tests-Add-unit-tests-for-image-locking.patch b/SOURCES/kvm-tests-Add-unit-tests-for-image-locking.patch new file mode 100644 index 0000000..3ece257 --- /dev/null +++ b/SOURCES/kvm-tests-Add-unit-tests-for-image-locking.patch @@ -0,0 +1,213 @@ +From dd4c2ba6b27a2af57c355fce5a868eb83c48625d Mon Sep 17 00:00:00 2001 +From: Max Reitz +Date: Mon, 4 Feb 2019 20:42:05 +0100 +Subject: [PATCH 05/33] tests: Add unit tests for image locking + +RH-Author: Max Reitz +Message-id: <20190204204207.18079-6-mreitz@redhat.com> +Patchwork-id: 84224 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 5/7] tests: Add unit tests for image locking +Bugzilla: 1551486 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Kevin Wolf + +From: Fam Zheng + +Signed-off-by: Fam Zheng +Signed-off-by: Kevin Wolf +(cherry picked from commit aef96d7d4f0b6746e329bfa7a1ea38e1611237e3) +Signed-off-by: Max Reitz +Signed-off-by: Miroslav Rezanina +--- + tests/Makefile.include | 2 + + tests/test-image-locking.c | 157 +++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 159 insertions(+) + create mode 100644 tests/test-image-locking.c + +diff --git a/tests/Makefile.include b/tests/Makefile.include +index 9dd63e5..c0376fb 100644 +--- a/tests/Makefile.include ++++ b/tests/Makefile.include +@@ -95,6 +95,7 @@ check-unit-y += tests/test-bdrv-drain$(EXESUF) + check-unit-y += tests/test-blockjob$(EXESUF) + check-unit-y += tests/test-blockjob-txn$(EXESUF) + check-unit-y += tests/test-block-backend$(EXESUF) ++check-unit-y += tests/test-image-locking$(EXESUF) + check-unit-y += tests/test-x86-cpuid$(EXESUF) + # all code tested by test-x86-cpuid is inside topology.h + gcov-files-test-x86-cpuid-y = +@@ -610,6 +611,7 @@ tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(te + tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y) + tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y) + tests/test-block-backend$(EXESUF): tests/test-block-backend.o $(test-block-obj-y) $(test-util-obj-y) ++tests/test-image-locking$(EXESUF): tests/test-image-locking.o $(test-block-obj-y) $(test-util-obj-y) + tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y) + tests/test-iov$(EXESUF): tests/test-iov.o $(test-util-obj-y) + tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o $(test-util-obj-y) $(test-crypto-obj-y) +diff --git a/tests/test-image-locking.c b/tests/test-image-locking.c +new file mode 100644 +index 0000000..7614cbf +--- /dev/null ++++ b/tests/test-image-locking.c +@@ -0,0 +1,157 @@ ++/* ++ * Image locking tests ++ * ++ * Copyright (c) 2018 Red Hat Inc. ++ * ++ * Author: Fam Zheng ++ * ++ * 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.h" ++#include "sysemu/block-backend.h" ++#include "qapi/error.h" ++#include "qapi/qmp/qdict.h" ++ ++static BlockBackend *open_image(const char *path, ++ uint64_t perm, uint64_t shared_perm, ++ Error **errp) ++{ ++ Error *local_err = NULL; ++ BlockBackend *blk; ++ QDict *options = qdict_new(); ++ ++ qdict_put_str(options, "driver", "raw"); ++ blk = blk_new_open(path, NULL, options, BDRV_O_RDWR, &local_err); ++ if (blk) { ++ g_assert_null(local_err); ++ if (blk_set_perm(blk, perm, shared_perm, errp)) { ++ blk_unref(blk); ++ blk = NULL; ++ } ++ } else { ++ error_propagate(errp, local_err); ++ } ++ return blk; ++} ++ ++static void check_locked_bytes(int fd, uint64_t perm_locks, ++ uint64_t shared_perm_locks) ++{ ++ int i; ++ ++ if (!perm_locks && !shared_perm_locks) { ++ g_assert(!qemu_lock_fd_test(fd, 0, 0, true)); ++ return; ++ } ++ for (i = 0; (1ULL << i) <= BLK_PERM_ALL; i++) { ++ uint64_t bit = (1ULL << i); ++ bool perm_expected = !!(bit & perm_locks); ++ bool shared_perm_expected = !!(bit & shared_perm_locks); ++ g_assert_cmpint(perm_expected, ==, ++ !!qemu_lock_fd_test(fd, 100 + i, 1, true)); ++ g_assert_cmpint(shared_perm_expected, ==, ++ !!qemu_lock_fd_test(fd, 200 + i, 1, true)); ++ } ++} ++ ++static void test_image_locking_basic(void) ++{ ++ BlockBackend *blk1, *blk2, *blk3; ++ char img_path[] = "/tmp/qtest.XXXXXX"; ++ uint64_t perm, shared_perm; ++ ++ int fd = mkstemp(img_path); ++ assert(fd >= 0); ++ ++ perm = BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ; ++ shared_perm = BLK_PERM_ALL; ++ blk1 = open_image(img_path, perm, shared_perm, &error_abort); ++ g_assert(blk1); ++ ++ check_locked_bytes(fd, perm, ~shared_perm); ++ ++ /* compatible perm between blk1 and blk2 */ ++ blk2 = open_image(img_path, perm | BLK_PERM_RESIZE, shared_perm, NULL); ++ g_assert(blk2); ++ check_locked_bytes(fd, perm | BLK_PERM_RESIZE, ~shared_perm); ++ ++ /* incompatible perm with already open blk1 and blk2 */ ++ blk3 = open_image(img_path, perm, BLK_PERM_WRITE_UNCHANGED, NULL); ++ g_assert_null(blk3); ++ ++ blk_unref(blk2); ++ ++ /* Check that extra bytes in blk2 are correctly unlocked */ ++ check_locked_bytes(fd, perm, ~shared_perm); ++ ++ blk_unref(blk1); ++ ++ /* Image is unused, no lock there */ ++ check_locked_bytes(fd, 0, 0); ++ blk3 = open_image(img_path, perm, BLK_PERM_WRITE_UNCHANGED, &error_abort); ++ g_assert(blk3); ++ blk_unref(blk3); ++ close(fd); ++ unlink(img_path); ++} ++ ++static void test_set_perm_abort(void) ++{ ++ BlockBackend *blk1, *blk2; ++ char img_path[] = "/tmp/qtest.XXXXXX"; ++ uint64_t perm, shared_perm; ++ int r; ++ int fd = mkstemp(img_path); ++ assert(fd >= 0); ++ ++ perm = BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ; ++ shared_perm = BLK_PERM_ALL; ++ blk1 = open_image(img_path, perm, shared_perm, &error_abort); ++ g_assert(blk1); ++ ++ blk2 = open_image(img_path, perm, shared_perm, &error_abort); ++ g_assert(blk2); ++ ++ check_locked_bytes(fd, perm, ~shared_perm); ++ ++ /* A failed blk_set_perm mustn't change perm status (locked bytes) */ ++ r = blk_set_perm(blk2, perm | BLK_PERM_RESIZE, BLK_PERM_WRITE_UNCHANGED, ++ NULL); ++ g_assert_cmpint(r, !=, 0); ++ check_locked_bytes(fd, perm, ~shared_perm); ++ blk_unref(blk1); ++ blk_unref(blk2); ++} ++ ++int main(int argc, char **argv) ++{ ++ bdrv_init(); ++ qemu_init_main_loop(&error_abort); ++ ++ g_test_init(&argc, &argv, NULL); ++ ++ if (qemu_has_ofd_lock()) { ++ g_test_add_func("/image-locking/basic", test_image_locking_basic); ++ g_test_add_func("/image-locking/set-perm-abort", test_set_perm_abort); ++ } ++ ++ return g_test_run(); ++} +-- +1.8.3.1 + diff --git a/SOURCES/kvm-tests-Simplify-.gitignore.patch b/SOURCES/kvm-tests-Simplify-.gitignore.patch new file mode 100644 index 0000000..2591bd0 --- /dev/null +++ b/SOURCES/kvm-tests-Simplify-.gitignore.patch @@ -0,0 +1,153 @@ +From 9cc9f8d61a69620645360e2ff399042ea992067b Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:17 +0100 +Subject: [PATCH 050/163] tests: Simplify .gitignore +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190322032241.8111-5-jsnow@redhat.com> +Patchwork-id: 85088 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 04/28] tests: Simplify .gitignore +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Eric Blake + +Commit 0bcc8e5b was yet another instance of 'git status' reporting +dirty files after an in-tree build, thanks to the new binary +tests/check-block-qdict. + +Instead of piecemeal exemptions of each new binary as they are +added, let's use git's negative globbing feature to exempt ALL +files that have a 'test-' or 'check-' prefix, except for the ones +ending in '.c' or '.sh'. We still have a couple of generated +files that then need (re-)exclusion, but the overall list is a +LOT shorter, and less prone to needing future edits. + +Signed-off-by: Eric Blake +Message-Id: <20180619203918.65450-1-eblake@redhat.com> +Reviewed-by: Philippe Mathieu-Daudé +(cherry picked from commit ac5de4984df282d64feb4af33b92e0a75652e2b6) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/.gitignore | 92 +++----------------------------------------------------- + 1 file changed, 5 insertions(+), 87 deletions(-) + +diff --git a/tests/.gitignore b/tests/.gitignore +index fb62d22..08e2df1 100644 +--- a/tests/.gitignore ++++ b/tests/.gitignore +@@ -2,100 +2,18 @@ atomic_add-bench + benchmark-crypto-cipher + benchmark-crypto-hash + benchmark-crypto-hmac +-check-qdict +-check-qnum +-check-qjson +-check-qlist +-check-qlit +-check-qnull +-check-qobject +-check-qstring +-check-qom-interface +-check-qom-proplist ++check-* ++!check-*.c ++!check-*.sh + qht-bench + rcutorture +-test-aio +-test-aio-multithread +-test-arm-mptimer +-test-base64 +-test-bdrv-drain +-test-bitops +-test-bitcnt +-test-blockjob +-test-blockjob-txn +-test-bufferiszero +-test-char +-test-clone-visitor +-test-coroutine +-test-crypto-afsplit +-test-crypto-block +-test-crypto-cipher +-test-crypto-hash +-test-crypto-hmac +-test-crypto-ivgen +-test-crypto-pbkdf +-test-crypto-secret +-test-crypto-tlscredsx509 +-test-crypto-tlscredsx509-work/ +-test-crypto-tlscredsx509-certs/ +-test-crypto-tlssession +-test-crypto-tlssession-work/ +-test-crypto-tlssession-client/ +-test-crypto-tlssession-server/ +-test-crypto-xts +-test-cutils +-test-hbitmap +-test-hmp +-test-int128 +-test-iov +-test-io-channel-buffer +-test-io-channel-command +-test-io-channel-command.fifo +-test-io-channel-file +-test-io-channel-file.txt +-test-io-channel-socket +-test-io-channel-tls +-test-io-task +-test-keyval +-test-logging +-test-mul64 +-test-opts-visitor ++test-* ++!test-*.c + test-qapi-commands.[ch] + test-qapi-events.[ch] + test-qapi-types.[ch] +-test-qapi-util + test-qapi-visit.[ch] +-test-qdev-global-props +-test-qemu-opts +-test-qdist +-test-qga +-test-qht +-test-qht-par +-test-qmp-cmds +-test-qmp-event +-test-qobject-input-strict +-test-qobject-input-visitor + test-qapi-introspect.[ch] +-test-qobject-output-visitor +-test-rcu-list +-test-replication +-test-shift128 +-test-string-input-visitor +-test-string-output-visitor +-test-thread-pool +-test-throttle +-test-timed-average +-test-uuid +-test-util-sockets +-test-visitor-serialization +-test-vmstate +-test-write-threshold +-test-x86-cpuid +-test-x86-cpuid-compat +-test-xbzrle +-test-netfilter +-test-filter-mirror +-test-filter-redirector + *-test + qapi-schema/*.test.* + vm/*.img +-- +1.8.3.1 + diff --git a/SOURCES/kvm-tests-add-iotests-helpers-for-dealing-with-TLS-certi.patch b/SOURCES/kvm-tests-add-iotests-helpers-for-dealing-with-TLS-certi.patch new file mode 100644 index 0000000..fc16215 --- /dev/null +++ b/SOURCES/kvm-tests-add-iotests-helpers-for-dealing-with-TLS-certi.patch @@ -0,0 +1,183 @@ +From bb9e921fa704fb0ccb9e79bb07ff9d229559e9ed Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:34 +0100 +Subject: [PATCH 067/163] tests: add iotests helpers for dealing with TLS + certificates +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190322032241.8111-22-jsnow@redhat.com> +Patchwork-id: 85111 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 21/28] tests: add iotests helpers for dealing with TLS certificates +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Daniel P. Berrangé + +Add helpers to common.tls for creating TLS certificates for a CA, +server and client. + +Signed-off-by: Daniel P. Berrangé +Message-Id: <20181116155325.22428-6-berrange@redhat.com> +Reviewed-by: Eric Blake +[eblake: spelling and quoting touchups] +Signed-off-by: Eric Blake +(cherry picked from commit a46b68410669fa14c4a85d9284953fc0d42392d0) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/common.tls | 137 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 137 insertions(+) + create mode 100644 tests/qemu-iotests/common.tls + +diff --git a/tests/qemu-iotests/common.tls b/tests/qemu-iotests/common.tls +new file mode 100644 +index 0000000..cecab26 +--- /dev/null ++++ b/tests/qemu-iotests/common.tls +@@ -0,0 +1,137 @@ ++#!/bin/bash ++# ++# Helpers for TLS related config ++# ++# Copyright (C) 2018 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 . ++# ++ ++tls_dir="${TEST_DIR}/tls" ++ ++function tls_x509_cleanup() ++{ ++ rm -f "${tls_dir}"/*.pem ++ rm -f "${tls_dir}"/*/*.pem ++ rmdir "${tls_dir}"/* ++ rmdir "${tls_dir}" ++} ++ ++ ++function tls_x509_init() ++{ ++ mkdir -p "${tls_dir}" ++ ++ # use a fixed key so we don't waste system entropy on ++ # each test run ++ cat > "${tls_dir}/key.pem" < "${tls_dir}/ca.info" <&1 | head -1 ++ ++ rm -f "${tls_dir}/ca.info" ++} ++ ++ ++function tls_x509_create_server() ++{ ++ caname=$1 ++ name=$2 ++ ++ mkdir -p "${tls_dir}/$name" ++ cat > "${tls_dir}/cert.info" <&1 | head -1 ++ ln -s "${tls_dir}/$caname-cert.pem" "${tls_dir}/$name/ca-cert.pem" ++ ln -s "${tls_dir}/key.pem" "${tls_dir}/$name/server-key.pem" ++ ++ rm -f "${tls_dir}/cert.info" ++} ++ ++ ++function tls_x509_create_client() ++{ ++ caname=$1 ++ name=$2 ++ ++ mkdir -p "${tls_dir}/$name" ++ cat > "${tls_dir}/cert.info" <&1 | head -1 ++ ln -s "${tls_dir}/$caname-cert.pem" "${tls_dir}/$name/ca-cert.pem" ++ ln -s "${tls_dir}/key.pem" "${tls_dir}/$name/client-key.pem" ++ ++ rm -f "${tls_dir}/cert.info" ++} +-- +1.8.3.1 + diff --git a/SOURCES/kvm-tests-add-tests-for-hbitmap_next_dirty_area.patch b/SOURCES/kvm-tests-add-tests-for-hbitmap_next_dirty_area.patch new file mode 100644 index 0000000..bb5b8b9 --- /dev/null +++ b/SOURCES/kvm-tests-add-tests-for-hbitmap_next_dirty_area.patch @@ -0,0 +1,152 @@ +From b60d35c274a27a49097d99c626a70d18d313bff2 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 21:48:32 +0100 +Subject: [PATCH 039/163] tests: add tests for hbitmap_next_dirty_area + +RH-Author: John Snow +Message-id: <20190320214838.22027-5-jsnow@redhat.com> +Patchwork-id: 84996 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 04/10] tests: add tests for hbitmap_next_dirty_area +Bugzilla: 1691048 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +Signed-off-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit bb6a0ec10ee3f791835f1479a8a3226f64cb6d75) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/test-hbitmap.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 107 insertions(+) + +diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c +index c0da31a..4f312e9 100644 +--- a/tests/test-hbitmap.c ++++ b/tests/test-hbitmap.c +@@ -1016,6 +1016,106 @@ static void test_hbitmap_next_zero_4(TestHBitmapData *data, const void *unused) + test_hbitmap_next_zero_do(data, 4); + } + ++static void test_hbitmap_next_dirty_area_check(TestHBitmapData *data, ++ uint64_t offset, ++ uint64_t count) ++{ ++ uint64_t off1, off2; ++ uint64_t len1 = 0, len2; ++ bool ret1, ret2; ++ int64_t end; ++ ++ off1 = offset; ++ len1 = count; ++ ret1 = hbitmap_next_dirty_area(data->hb, &off1, &len1); ++ ++ end = offset > data->size || data->size - offset < count ? data->size : ++ offset + count; ++ ++ for (off2 = offset; off2 < end && !hbitmap_get(data->hb, off2); off2++) { ++ ; ++ } ++ ++ for (len2 = 1; off2 + len2 < end && hbitmap_get(data->hb, off2 + len2); ++ len2++) { ++ ; ++ } ++ ++ ret2 = off2 < end; ++ if (!ret2) { ++ /* leave unchanged */ ++ off2 = offset; ++ len2 = count; ++ } ++ ++ g_assert_cmpint(ret1, ==, ret2); ++ g_assert_cmpint(off1, ==, off2); ++ g_assert_cmpint(len1, ==, len2); ++} ++ ++static void test_hbitmap_next_dirty_area_do(TestHBitmapData *data, ++ int granularity) ++{ ++ hbitmap_test_init(data, L3, granularity); ++ test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); ++ test_hbitmap_next_dirty_area_check(data, 0, 1); ++ test_hbitmap_next_dirty_area_check(data, L3 - 1, 1); ++ ++ hbitmap_set(data->hb, L2, 1); ++ test_hbitmap_next_dirty_area_check(data, 0, 1); ++ test_hbitmap_next_dirty_area_check(data, 0, L2); ++ test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); ++ test_hbitmap_next_dirty_area_check(data, L2 - 1, UINT64_MAX); ++ test_hbitmap_next_dirty_area_check(data, L2 - 1, 1); ++ test_hbitmap_next_dirty_area_check(data, L2 - 1, 2); ++ test_hbitmap_next_dirty_area_check(data, L2 - 1, 3); ++ test_hbitmap_next_dirty_area_check(data, L2, UINT64_MAX); ++ test_hbitmap_next_dirty_area_check(data, L2, 1); ++ test_hbitmap_next_dirty_area_check(data, L2 + 1, 1); ++ ++ hbitmap_set(data->hb, L2 + 5, L1); ++ test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); ++ test_hbitmap_next_dirty_area_check(data, L2 - 2, 8); ++ test_hbitmap_next_dirty_area_check(data, L2 + 1, 5); ++ test_hbitmap_next_dirty_area_check(data, L2 + 1, 3); ++ test_hbitmap_next_dirty_area_check(data, L2 + 4, L1); ++ test_hbitmap_next_dirty_area_check(data, L2 + 5, L1); ++ test_hbitmap_next_dirty_area_check(data, L2 + 7, L1); ++ test_hbitmap_next_dirty_area_check(data, L2 + L1, L1); ++ test_hbitmap_next_dirty_area_check(data, L2, 0); ++ test_hbitmap_next_dirty_area_check(data, L2 + 1, 0); ++ ++ hbitmap_set(data->hb, L2 * 2, L3 - L2 * 2); ++ test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); ++ test_hbitmap_next_dirty_area_check(data, L2, UINT64_MAX); ++ test_hbitmap_next_dirty_area_check(data, L2 + 1, UINT64_MAX); ++ test_hbitmap_next_dirty_area_check(data, L2 + 5 + L1 - 1, UINT64_MAX); ++ test_hbitmap_next_dirty_area_check(data, L2 + 5 + L1, 5); ++ test_hbitmap_next_dirty_area_check(data, L2 * 2 - L1, L1 + 1); ++ test_hbitmap_next_dirty_area_check(data, L2 * 2, L2); ++ ++ hbitmap_set(data->hb, 0, L3); ++ test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); ++} ++ ++static void test_hbitmap_next_dirty_area_0(TestHBitmapData *data, ++ const void *unused) ++{ ++ test_hbitmap_next_dirty_area_do(data, 0); ++} ++ ++static void test_hbitmap_next_dirty_area_1(TestHBitmapData *data, ++ const void *unused) ++{ ++ test_hbitmap_next_dirty_area_do(data, 1); ++} ++ ++static void test_hbitmap_next_dirty_area_4(TestHBitmapData *data, ++ const void *unused) ++{ ++ test_hbitmap_next_dirty_area_do(data, 4); ++} ++ + int main(int argc, char **argv) + { + g_test_init(&argc, &argv, NULL); +@@ -1082,6 +1182,13 @@ int main(int argc, char **argv) + hbitmap_test_add("/hbitmap/next_zero/next_zero_4", + test_hbitmap_next_zero_4); + ++ hbitmap_test_add("/hbitmap/next_dirty_area/next_dirty_area_0", ++ test_hbitmap_next_dirty_area_0); ++ hbitmap_test_add("/hbitmap/next_dirty_area/next_dirty_area_1", ++ test_hbitmap_next_dirty_area_1); ++ hbitmap_test_add("/hbitmap/next_dirty_area/next_dirty_area_4", ++ test_hbitmap_next_dirty_area_4); ++ + g_test_run(); + + return 0; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-tests-add-tests-for-hbitmap_next_zero-with-specified.patch b/SOURCES/kvm-tests-add-tests-for-hbitmap_next_zero-with-specified.patch new file mode 100644 index 0000000..57221f4 --- /dev/null +++ b/SOURCES/kvm-tests-add-tests-for-hbitmap_next_zero-with-specified.patch @@ -0,0 +1,106 @@ +From 3f336fb2dbf569591917be5a01fa8f760fa8ee77 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 20 Mar 2019 21:48:30 +0100 +Subject: [PATCH 037/163] tests: add tests for hbitmap_next_zero with specified + end parameter + +RH-Author: John Snow +Message-id: <20190320214838.22027-3-jsnow@redhat.com> +Patchwork-id: 84994 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 02/10] tests: add tests for hbitmap_next_zero with specified end parameter +Bugzilla: 1691048 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +From: Vladimir Sementsov-Ogievskiy + +Signed-off-by: Vladimir Sementsov-Ogievskiy +(cherry picked from commit fa9c2da29404be9baeb7b8f88fed3cb232688cd9) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/test-hbitmap.c | 32 ++++++++++++++++++++++++++++---- + 1 file changed, 28 insertions(+), 4 deletions(-) + +diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c +index b04a45a..c0da31a 100644 +--- a/tests/test-hbitmap.c ++++ b/tests/test-hbitmap.c +@@ -937,31 +937,49 @@ static void test_hbitmap_iter_and_reset(TestHBitmapData *data, + check_hbitmap_iter_next(&hbi); + } + +-static void test_hbitmap_next_zero_check(TestHBitmapData *data, int64_t start) ++static void test_hbitmap_next_zero_check_range(TestHBitmapData *data, ++ uint64_t start, ++ uint64_t count) + { +- int64_t ret1 = hbitmap_next_zero(data->hb, start, UINT64_MAX); ++ int64_t ret1 = hbitmap_next_zero(data->hb, start, count); + int64_t ret2 = start; +- for ( ; ret2 < data->size && hbitmap_get(data->hb, ret2); ret2++) { ++ int64_t end = start >= data->size || data->size - start < count ? ++ data->size : start + count; ++ ++ for ( ; ret2 < end && hbitmap_get(data->hb, ret2); ret2++) { + ; + } +- if (ret2 == data->size) { ++ if (ret2 == end) { + ret2 = -1; + } + + g_assert_cmpint(ret1, ==, ret2); + } + ++static void test_hbitmap_next_zero_check(TestHBitmapData *data, int64_t start) ++{ ++ test_hbitmap_next_zero_check_range(data, start, UINT64_MAX); ++} ++ + static void test_hbitmap_next_zero_do(TestHBitmapData *data, int granularity) + { + hbitmap_test_init(data, L3, granularity); + test_hbitmap_next_zero_check(data, 0); + test_hbitmap_next_zero_check(data, L3 - 1); ++ test_hbitmap_next_zero_check_range(data, 0, 1); ++ test_hbitmap_next_zero_check_range(data, L3 - 1, 1); + + hbitmap_set(data->hb, L2, 1); + test_hbitmap_next_zero_check(data, 0); + test_hbitmap_next_zero_check(data, L2 - 1); + test_hbitmap_next_zero_check(data, L2); + test_hbitmap_next_zero_check(data, L2 + 1); ++ test_hbitmap_next_zero_check_range(data, 0, 1); ++ test_hbitmap_next_zero_check_range(data, 0, L2); ++ test_hbitmap_next_zero_check_range(data, L2 - 1, 1); ++ test_hbitmap_next_zero_check_range(data, L2 - 1, 2); ++ test_hbitmap_next_zero_check_range(data, L2, 1); ++ test_hbitmap_next_zero_check_range(data, L2 + 1, 1); + + hbitmap_set(data->hb, L2 + 5, L1); + test_hbitmap_next_zero_check(data, 0); +@@ -970,6 +988,10 @@ static void test_hbitmap_next_zero_do(TestHBitmapData *data, int granularity) + test_hbitmap_next_zero_check(data, L2 + 5); + test_hbitmap_next_zero_check(data, L2 + L1 - 1); + test_hbitmap_next_zero_check(data, L2 + L1); ++ test_hbitmap_next_zero_check_range(data, L2, 6); ++ test_hbitmap_next_zero_check_range(data, L2 + 1, 3); ++ test_hbitmap_next_zero_check_range(data, L2 + 4, L1); ++ test_hbitmap_next_zero_check_range(data, L2 + 5, L1); + + hbitmap_set(data->hb, L2 * 2, L3 - L2 * 2); + test_hbitmap_next_zero_check(data, L2 * 2 - L1); +@@ -977,6 +999,8 @@ static void test_hbitmap_next_zero_do(TestHBitmapData *data, int granularity) + test_hbitmap_next_zero_check(data, L2 * 2 - 1); + test_hbitmap_next_zero_check(data, L2 * 2); + test_hbitmap_next_zero_check(data, L3 - 1); ++ test_hbitmap_next_zero_check_range(data, L2 * 2 - L1, L1 + 1); ++ test_hbitmap_next_zero_check_range(data, L2 * 2, L2); + + hbitmap_set(data->hb, 0, L3); + test_hbitmap_next_zero_check(data, 0); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-tests-blockjob-replace-Blockjob-with-Job.patch b/SOURCES/kvm-tests-blockjob-replace-Blockjob-with-Job.patch new file mode 100644 index 0000000..fd94af3 --- /dev/null +++ b/SOURCES/kvm-tests-blockjob-replace-Blockjob-with-Job.patch @@ -0,0 +1,233 @@ +From 4003c681cac2102b3c493e2f1a7f9af7e88e0d38 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:55 +0200 +Subject: [PATCH 17/25] tests/blockjob: replace Blockjob with Job + +RH-Author: John Snow +Message-id: <20180910181803.11781-18-jsnow@redhat.com> +Patchwork-id: 82103 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 17/25] tests/blockjob: replace Blockjob with Job +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +These tests don't actually test blockjobs anymore, they test +generic Job lifetimes. Change the types accordingly. + +Signed-off-by: John Snow +Reviewed-by: Max Reitz +Message-id: 20180906130225.5118-9-jsnow@redhat.com +Reviewed-by: Jeff Cody +Signed-off-by: Max Reitz +(cherry picked from commit 15fcc3513802c74838075048c6f2d9e58780ba06) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/test-blockjob.c | 98 ++++++++++++++++++++++++++------------------------- + 1 file changed, 50 insertions(+), 48 deletions(-) + +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index ad4a65b..8e8b680 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -206,18 +206,20 @@ static const BlockJobDriver test_cancel_driver = { + }, + }; + +-static CancelJob *create_common(BlockJob **pjob) ++static CancelJob *create_common(Job **pjob) + { + BlockBackend *blk; +- BlockJob *job; ++ Job *job; ++ BlockJob *bjob; + CancelJob *s; + + blk = create_blk(NULL); +- job = mk_job(blk, "Steve", &test_cancel_driver, true, +- JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS); +- job_ref(&job->job); +- assert(job->job.status == JOB_STATUS_CREATED); +- s = container_of(job, CancelJob, common); ++ bjob = mk_job(blk, "Steve", &test_cancel_driver, true, ++ JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS); ++ job = &bjob->job; ++ job_ref(job); ++ assert(job->status == JOB_STATUS_CREATED); ++ s = container_of(bjob, CancelJob, common); + s->blk = blk; + + *pjob = job; +@@ -242,7 +244,7 @@ static void cancel_common(CancelJob *s) + + static void test_cancel_created(void) + { +- BlockJob *job; ++ Job *job; + CancelJob *s; + + s = create_common(&job); +@@ -251,119 +253,119 @@ static void test_cancel_created(void) + + static void test_cancel_running(void) + { +- BlockJob *job; ++ Job *job; + CancelJob *s; + + s = create_common(&job); + +- job_start(&job->job); +- assert(job->job.status == JOB_STATUS_RUNNING); ++ job_start(job); ++ assert(job->status == JOB_STATUS_RUNNING); + + cancel_common(s); + } + + static void test_cancel_paused(void) + { +- BlockJob *job; ++ Job *job; + CancelJob *s; + + s = create_common(&job); + +- job_start(&job->job); +- assert(job->job.status == JOB_STATUS_RUNNING); ++ job_start(job); ++ assert(job->status == JOB_STATUS_RUNNING); + +- job_user_pause(&job->job, &error_abort); +- job_enter(&job->job); +- assert(job->job.status == JOB_STATUS_PAUSED); ++ job_user_pause(job, &error_abort); ++ job_enter(job); ++ assert(job->status == JOB_STATUS_PAUSED); + + cancel_common(s); + } + + static void test_cancel_ready(void) + { +- BlockJob *job; ++ Job *job; + CancelJob *s; + + s = create_common(&job); + +- job_start(&job->job); +- assert(job->job.status == JOB_STATUS_RUNNING); ++ job_start(job); ++ assert(job->status == JOB_STATUS_RUNNING); + + s->should_converge = true; +- job_enter(&job->job); +- assert(job->job.status == JOB_STATUS_READY); ++ job_enter(job); ++ assert(job->status == JOB_STATUS_READY); + + cancel_common(s); + } + + static void test_cancel_standby(void) + { +- BlockJob *job; ++ Job *job; + CancelJob *s; + + s = create_common(&job); + +- job_start(&job->job); +- assert(job->job.status == JOB_STATUS_RUNNING); ++ job_start(job); ++ assert(job->status == JOB_STATUS_RUNNING); + + s->should_converge = true; +- job_enter(&job->job); +- assert(job->job.status == JOB_STATUS_READY); ++ job_enter(job); ++ assert(job->status == JOB_STATUS_READY); + +- job_user_pause(&job->job, &error_abort); +- job_enter(&job->job); +- assert(job->job.status == JOB_STATUS_STANDBY); ++ job_user_pause(job, &error_abort); ++ job_enter(job); ++ assert(job->status == JOB_STATUS_STANDBY); + + cancel_common(s); + } + + static void test_cancel_pending(void) + { +- BlockJob *job; ++ Job *job; + CancelJob *s; + + s = create_common(&job); + +- job_start(&job->job); +- assert(job->job.status == JOB_STATUS_RUNNING); ++ job_start(job); ++ assert(job->status == JOB_STATUS_RUNNING); + + s->should_converge = true; +- job_enter(&job->job); +- assert(job->job.status == JOB_STATUS_READY); ++ job_enter(job); ++ assert(job->status == JOB_STATUS_READY); + +- job_complete(&job->job, &error_abort); +- job_enter(&job->job); ++ job_complete(job, &error_abort); ++ job_enter(job); + while (!s->completed) { + aio_poll(qemu_get_aio_context(), true); + } +- assert(job->job.status == JOB_STATUS_PENDING); ++ assert(job->status == JOB_STATUS_PENDING); + + cancel_common(s); + } + + static void test_cancel_concluded(void) + { +- BlockJob *job; ++ Job *job; + CancelJob *s; + + s = create_common(&job); + +- job_start(&job->job); +- assert(job->job.status == JOB_STATUS_RUNNING); ++ job_start(job); ++ assert(job->status == JOB_STATUS_RUNNING); + + s->should_converge = true; +- job_enter(&job->job); +- assert(job->job.status == JOB_STATUS_READY); ++ job_enter(job); ++ assert(job->status == JOB_STATUS_READY); + +- job_complete(&job->job, &error_abort); +- job_enter(&job->job); ++ job_complete(job, &error_abort); ++ job_enter(job); + while (!s->completed) { + aio_poll(qemu_get_aio_context(), true); + } +- assert(job->job.status == JOB_STATUS_PENDING); ++ assert(job->status == JOB_STATUS_PENDING); + +- job_finalize(&job->job, &error_abort); +- assert(job->job.status == JOB_STATUS_CONCLUDED); ++ job_finalize(job, &error_abort); ++ assert(job->status == JOB_STATUS_CONCLUDED); + + cancel_common(s); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-tests-check-if-qemu-nbd-is-still-alive-before-waitin.patch b/SOURCES/kvm-tests-check-if-qemu-nbd-is-still-alive-before-waitin.patch new file mode 100644 index 0000000..81991da --- /dev/null +++ b/SOURCES/kvm-tests-check-if-qemu-nbd-is-still-alive-before-waitin.patch @@ -0,0 +1,70 @@ +From 44ff04f6ce608282484fa9894d7fc91e8722015d Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:33 +0100 +Subject: [PATCH 066/163] tests: check if qemu-nbd is still alive before + waiting +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190322032241.8111-21-jsnow@redhat.com> +Patchwork-id: 85102 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 20/28] tests: check if qemu-nbd is still alive before waiting +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Daniel P. Berrangé + +If the qemu-nbd UNIX socket has not shown up, the tests will sleep a bit +and then check again repeatedly for up to 30 seconds. This is pointless +if the qemu-nbd process has quit due to an error, so check whether the +pid is still alive before waiting and retrying. + +Signed-off-by: Daniel P. Berrangé +Message-Id: <20181116155325.22428-5-berrange@redhat.com> +Reviewed-by: Eric Blake +Signed-off-by: Eric Blake +(cherry picked from commit b39b58d5d0da3e7057d7d636641018b0fc25139b) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/common.nbd | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/tests/qemu-iotests/common.nbd b/tests/qemu-iotests/common.nbd +index 27357f3..9f841ab 100644 +--- a/tests/qemu-iotests/common.nbd ++++ b/tests/qemu-iotests/common.nbd +@@ -37,11 +37,19 @@ function nbd_server_stop() + + function nbd_server_wait_for_unix_socket() + { ++ pid=$1 ++ + for ((i = 0; i < 300; i++)) + do + if [ -r "$nbd_unix_socket" ]; then + return + fi ++ kill -s 0 $pid 2>/dev/null ++ if test $? != 0 ++ then ++ echo "qemu-nbd unexpectedly quit" ++ exit 1 ++ fi + sleep 0.1 + done + echo "Failed in check of unix socket created by qemu-nbd" +@@ -52,5 +60,5 @@ function nbd_server_start_unix_socket() + { + nbd_server_stop + $QEMU_NBD -v -t -k "$nbd_unix_socket" "$@" & +- nbd_server_wait_for_unix_socket ++ nbd_server_wait_for_unix_socket $! + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-tests-crypto-Use-the-IEC-binary-prefix-definitions.patch b/SOURCES/kvm-tests-crypto-Use-the-IEC-binary-prefix-definitions.patch new file mode 100644 index 0000000..c518c6d --- /dev/null +++ b/SOURCES/kvm-tests-crypto-Use-the-IEC-binary-prefix-definitions.patch @@ -0,0 +1,134 @@ +From 6da2990d427f11549d7d5ee6f230af95e6b69219 Mon Sep 17 00:00:00 2001 +From: "Daniel P. Berrange" +Date: Wed, 24 Apr 2019 10:30:22 +0200 +Subject: [PATCH 03/12] tests/crypto: Use the IEC binary prefix definitions +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Daniel P. Berrange +Message-id: <20190424103030.2925-2-berrange@redhat.com> +Patchwork-id: 85892 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/9] tests/crypto: Use the IEC binary prefix definitions +Bugzilla: 1666336 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: John Snow +RH-Acked-by: Eric Blake + +From: Philippe Mathieu-Daudé + +It eases code review, unit is explicit. + +Patch generated using: + + $ git grep -n '[<>][<>]= ?[1-5]0' + +and modified manually. + +Suggested-by: Eric Blake +Signed-off-by: Philippe Mathieu-Daudé +Message-Id: <20180625124238.25339-45-f4bug@amsat.org> +Signed-off-by: Paolo Bonzini +(cherry picked from commit 68dbb6d05db59fe39af0c192005490576d9f5b7c) +Signed-off-by: Miroslav Rezanina +--- + tests/benchmark-crypto-cipher.c | 6 +++--- + tests/benchmark-crypto-hash.c | 5 +++-- + tests/benchmark-crypto-hmac.c | 6 +++--- + 3 files changed, 9 insertions(+), 8 deletions(-) + +diff --git a/tests/benchmark-crypto-cipher.c b/tests/benchmark-crypto-cipher.c +index cf98443..f5a0d0b 100644 +--- a/tests/benchmark-crypto-cipher.c ++++ b/tests/benchmark-crypto-cipher.c +@@ -11,6 +11,7 @@ + * top-level directory. + */ + #include "qemu/osdep.h" ++#include "qemu/units.h" + #include "crypto/init.h" + #include "crypto/cipher.h" + +@@ -56,8 +57,7 @@ static void test_cipher_speed(const void *opaque) + total += chunk_size; + } while (g_test_timer_elapsed() < 5.0); + +- total /= 1024 * 1024; /* to MB */ +- ++ total /= MiB; + g_print("cbc(aes128): "); + g_print("Testing chunk_size %zu bytes ", chunk_size); + g_print("done: %.2f MB in %.2f secs: ", total, g_test_timer_last()); +@@ -78,7 +78,7 @@ int main(int argc, char **argv) + g_test_init(&argc, &argv, NULL); + g_assert(qcrypto_init(NULL) == 0); + +- for (i = 512; i <= (64 * 1204); i *= 2) { ++ for (i = 512; i <= 64 * KiB; i *= 2) { + memset(name, 0 , sizeof(name)); + snprintf(name, sizeof(name), "/crypto/cipher/speed-%zu", i); + g_test_add_data_func(name, (void *)i, test_cipher_speed); +diff --git a/tests/benchmark-crypto-hash.c b/tests/benchmark-crypto-hash.c +index 122bfb6..9b6f7a9 100644 +--- a/tests/benchmark-crypto-hash.c ++++ b/tests/benchmark-crypto-hash.c +@@ -11,6 +11,7 @@ + * top-level directory. + */ + #include "qemu/osdep.h" ++#include "qemu/units.h" + #include "crypto/init.h" + #include "crypto/hash.h" + +@@ -39,7 +40,7 @@ static void test_hash_speed(const void *opaque) + total += chunk_size; + } while (g_test_timer_elapsed() < 5.0); + +- total /= 1024 * 1024; /* to MB */ ++ total /= MiB; + g_print("sha256: "); + g_print("Testing chunk_size %zu bytes ", chunk_size); + g_print("done: %.2f MB in %.2f secs: ", total, g_test_timer_last()); +@@ -57,7 +58,7 @@ int main(int argc, char **argv) + g_test_init(&argc, &argv, NULL); + g_assert(qcrypto_init(NULL) == 0); + +- for (i = 512; i <= (64 * 1204); i *= 2) { ++ for (i = 512; i <= 64 * KiB; i *= 2) { + memset(name, 0 , sizeof(name)); + snprintf(name, sizeof(name), "/crypto/hash/speed-%zu", i); + g_test_add_data_func(name, (void *)i, test_hash_speed); +diff --git a/tests/benchmark-crypto-hmac.c b/tests/benchmark-crypto-hmac.c +index c30250d..f1dfa24 100644 +--- a/tests/benchmark-crypto-hmac.c ++++ b/tests/benchmark-crypto-hmac.c +@@ -11,6 +11,7 @@ + * top-level directory. + */ + #include "qemu/osdep.h" ++#include "qemu/units.h" + #include "crypto/init.h" + #include "crypto/hmac.h" + +@@ -53,8 +54,7 @@ static void test_hmac_speed(const void *opaque) + total += chunk_size; + } while (g_test_timer_elapsed() < 5.0); + +- total /= 1024 * 1024; /* to MB */ +- ++ total /= MiB; + g_print("hmac(sha256): "); + g_print("Testing chunk_size %zu bytes ", chunk_size); + g_print("done: %.2f MB in %.2f secs: ", total, g_test_timer_last()); +@@ -72,7 +72,7 @@ int main(int argc, char **argv) + g_test_init(&argc, &argv, NULL); + g_assert(qcrypto_init(NULL) == 0); + +- for (i = 512; i <= (64 * 1204); i *= 2) { ++ for (i = 512; i <= 64 * KiB; i *= 2) { + memset(name, 0 , sizeof(name)); + snprintf(name, sizeof(name), "/crypto/hmac/speed-%zu", i); + g_test_add_data_func(name, (void *)i, test_hmac_speed); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-tests-exercise-NBD-server-in-TLS-mode.patch b/SOURCES/kvm-tests-exercise-NBD-server-in-TLS-mode.patch new file mode 100644 index 0000000..80448e1 --- /dev/null +++ b/SOURCES/kvm-tests-exercise-NBD-server-in-TLS-mode.patch @@ -0,0 +1,262 @@ +From 74a1f98290aa6ec986bea9bcd87c4fb8b93afd4d Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:35 +0100 +Subject: [PATCH 068/163] tests: exercise NBD server in TLS mode +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190322032241.8111-23-jsnow@redhat.com> +Patchwork-id: 85104 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 22/28] tests: exercise NBD server in TLS mode +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Daniel P. Berrangé + +Add tests that validate it is possible to connect to an NBD server +running TLS mode. Also test mis-matched TLS vs non-TLS connections +correctly fail. + +Signed-off-by: Daniel P. Berrangé +Message-Id: <20181116155325.22428-7-berrange@redhat.com> +Reviewed-by: Eric Blake +Tested-by: Eric Blake +[eblake: rebase to iotests shell cleanups, use ss instead of socat for +port probing, sanitize port number in expected output] +Signed-off-by: Eric Blake +(cherry picked from commit afcd1c2f2d438930a17eb87293c0ac2c377158fa) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/233 | 102 ++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/233.out | 30 +++++++++++++ + tests/qemu-iotests/common.nbd | 45 +++++++++++++++++++ + tests/qemu-iotests/group | 1 + + 4 files changed, 178 insertions(+) + create mode 100755 tests/qemu-iotests/233 + create mode 100644 tests/qemu-iotests/233.out + +diff --git a/tests/qemu-iotests/233 b/tests/qemu-iotests/233 +new file mode 100755 +index 0000000..46013ce +--- /dev/null ++++ b/tests/qemu-iotests/233 +@@ -0,0 +1,102 @@ ++#!/bin/bash ++# ++# Test NBD TLS certificate / authorization integration ++# ++# Copyright (C) 2018 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 . ++# ++ ++# creator ++owner=berrange@redhat.com ++ ++seq=$(basename $0) ++echo "QA output created by $seq" ++ ++status=1 # failure is the default! ++ ++_cleanup() ++{ ++ nbd_server_stop ++ _cleanup_test_img ++ tls_x509_cleanup ++} ++trap "_cleanup; exit \$status" 0 1 2 3 15 ++ ++# get standard environment, filters and checks ++. ./common.rc ++. ./common.filter ++. ./common.pattern ++. ./common.tls ++. ./common.nbd ++ ++_supported_fmt raw qcow2 ++_supported_proto file ++# If porting to non-Linux, consider using socat instead of ss in common.nbd ++_supported_os Linux ++_require_command QEMU_NBD ++ ++nbd_server_set_tcp_port ++tls_x509_init ++ ++echo ++echo "== preparing TLS creds ==" ++ ++tls_x509_create_root_ca "ca1" ++tls_x509_create_root_ca "ca2" ++tls_x509_create_server "ca1" "server1" ++tls_x509_create_client "ca1" "client1" ++tls_x509_create_client "ca2" "client2" ++ ++echo ++echo "== preparing image ==" ++_make_test_img 64M ++ ++ ++echo ++echo "== check TLS client to plain server fails ==" ++nbd_server_start_tcp_socket "$TEST_IMG" ++ ++$QEMU_IMG info --image-opts \ ++ --object tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 \ ++ driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ ++ 2>&1 | sed "s/$nbd_tcp_port/PORT/g" ++ ++nbd_server_stop ++ ++echo ++echo "== check plain client to TLS server fails ==" ++ ++nbd_server_start_tcp_socket --object tls-creds-x509,dir=${tls_dir}/server1,endpoint=server,id=tls0,verify-peer=yes --tls-creds tls0 "$TEST_IMG" ++ ++$QEMU_IMG info nbd://localhost:$nbd_tcp_port 2>&1 | sed "s/$nbd_tcp_port/PORT/g" ++ ++echo ++echo "== check TLS works ==" ++$QEMU_IMG info --image-opts \ ++ --object tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 \ ++ driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ ++ 2>&1 | sed "s/$nbd_tcp_port/PORT/g" ++ ++echo ++echo "== check TLS with different CA fails ==" ++$QEMU_IMG info --image-opts \ ++ --object tls-creds-x509,dir=${tls_dir}/client2,endpoint=client,id=tls0 \ ++ driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ ++ 2>&1 | sed "s/$nbd_tcp_port/PORT/g" ++ ++# success, all done ++echo "*** done" ++rm -f $seq.full ++status=0 +diff --git a/tests/qemu-iotests/233.out b/tests/qemu-iotests/233.out +new file mode 100644 +index 0000000..616e923 +--- /dev/null ++++ b/tests/qemu-iotests/233.out +@@ -0,0 +1,30 @@ ++QA output created by 233 ++ ++== preparing TLS creds == ++Generating a self signed certificate... ++Generating a self signed certificate... ++Generating a signed certificate... ++Generating a signed certificate... ++Generating a signed certificate... ++ ++== preparing image == ++Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 ++ ++== check TLS client to plain server fails == ++qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Denied by server for option 5 (starttls) ++server reported: TLS not configured ++ ++== check plain client to TLS server fails == ++qemu-img: Could not open 'nbd://localhost:PORT': TLS negotiation required before option 8 (structured reply) ++server reported: Option 0x8 not permitted before TLS ++ ++== check TLS works == ++image: nbd://127.0.0.1:PORT ++file format: nbd ++virtual size: 64M (67108864 bytes) ++disk size: unavailable ++ ++== check TLS with different CA fails == ++option negotiation failed: Verify failed: No certificate was found. ++qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': The certificate hasn't got a known issuer ++*** done +diff --git a/tests/qemu-iotests/common.nbd b/tests/qemu-iotests/common.nbd +index 9f841ab..0f4497a 100644 +--- a/tests/qemu-iotests/common.nbd ++++ b/tests/qemu-iotests/common.nbd +@@ -20,6 +20,7 @@ + # + + nbd_unix_socket="${TEST_DIR}/qemu-nbd.sock" ++nbd_tcp_addr="127.0.0.1" + nbd_pid_file="${TEST_DIR}/qemu-nbd.pid" + + function nbd_server_stop() +@@ -62,3 +63,47 @@ function nbd_server_start_unix_socket() + $QEMU_NBD -v -t -k "$nbd_unix_socket" "$@" & + nbd_server_wait_for_unix_socket $! + } ++ ++function nbd_server_set_tcp_port() ++{ ++ (ss --help) >/dev/null 2>&1 || _notrun "ss utility not found, skipping test" ++ ++ for ((port = 10809; port <= 10909; port++)) ++ do ++ if ! ss -tln | grep -sqE ":$port\b"; then ++ nbd_tcp_port=$port ++ return ++ fi ++ done ++ ++ echo "Cannot find free TCP port for nbd in range 10809-10909" ++ exit 1 ++} ++ ++function nbd_server_wait_for_tcp_socket() ++{ ++ pid=$1 ++ ++ for ((i = 0; i < 300; i++)) ++ do ++ if ss -tln | grep -sqE ":$nbd_tcp_port\b"; then ++ return ++ fi ++ kill -s 0 $pid 2>/dev/null ++ if test $? != 0 ++ then ++ echo "qemu-nbd unexpectedly quit" ++ exit 1 ++ fi ++ sleep 0.1 ++ done ++ echo "Failed in check of TCP socket created by qemu-nbd" ++ exit 1 ++} ++ ++function nbd_server_start_tcp_socket() ++{ ++ nbd_server_stop ++ $QEMU_NBD -v -t -b $nbd_tcp_addr -p $nbd_tcp_port "$@" & ++ nbd_server_wait_for_tcp_socket $! ++} +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index bee2855..b3aeb6b 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -226,6 +226,7 @@ + 229 auto quick + 231 auto quick + 232 auto quick ++233 auto quick + 234 auto quick migration + 236 auto quick + 242 rw auto quick +-- +1.8.3.1 + diff --git a/SOURCES/kvm-tests-pull-qemu-nbd-iotest-helpers-into-common.nbd-f.patch b/SOURCES/kvm-tests-pull-qemu-nbd-iotest-helpers-into-common.nbd-f.patch new file mode 100644 index 0000000..400f0e9 --- /dev/null +++ b/SOURCES/kvm-tests-pull-qemu-nbd-iotest-helpers-into-common.nbd-f.patch @@ -0,0 +1,185 @@ +From ef0a9b41515ff3392afdaeeb37bd32568ba8ffe4 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 22 Mar 2019 03:22:32 +0100 +Subject: [PATCH 065/163] tests: pull qemu-nbd iotest helpers into common.nbd + file +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190322032241.8111-20-jsnow@redhat.com> +Patchwork-id: 85108 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 19/28] tests: pull qemu-nbd iotest helpers into common.nbd file +Bugzilla: 1691563 +RH-Acked-by: Max Reitz +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +From: Daniel P. Berrangé + +The helpers for starting/stopping qemu-nbd in 058 will be useful in +other test cases, so move them into a common.nbd file. + +Signed-off-by: Daniel P. Berrangé +Message-Id: <20181116155325.22428-4-berrange@redhat.com> +Reviewed-by: Eric Blake +[eblake: fix shell quoting] +Signed-off-by: Eric Blake +(cherry picked from commit e6d5d6fdd43484f60f678e66c21f0d1286749977) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/058 | 47 +++++++----------------------------- + tests/qemu-iotests/common.nbd | 56 +++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 64 insertions(+), 39 deletions(-) + create mode 100644 tests/qemu-iotests/common.nbd + +diff --git a/tests/qemu-iotests/058 b/tests/qemu-iotests/058 +index 0d741a7..d6d4f94 100755 +--- a/tests/qemu-iotests/058 ++++ b/tests/qemu-iotests/058 +@@ -28,55 +28,19 @@ echo "QA output created by $seq" + + status=1 # failure is the default! + +-nbd_unix_socket=$TEST_DIR/test_qemu_nbd_socket +-nbd_snapshot_img="nbd:unix:$nbd_unix_socket" +-rm -f "${TEST_DIR}/qemu-nbd.pid" +- +-_cleanup_nbd() +-{ +- local NBD_SNAPSHOT_PID +- if [ -f "${TEST_DIR}/qemu-nbd.pid" ]; then +- read NBD_SNAPSHOT_PID < "${TEST_DIR}/qemu-nbd.pid" +- rm -f "${TEST_DIR}/qemu-nbd.pid" +- if [ -n "$NBD_SNAPSHOT_PID" ]; then +- kill "$NBD_SNAPSHOT_PID" +- fi +- fi +- rm -f "$nbd_unix_socket" +-} +- +-_wait_for_nbd() +-{ +- for ((i = 0; i < 300; i++)) +- do +- if [ -r "$nbd_unix_socket" ]; then +- return +- fi +- sleep 0.1 +- done +- echo "Failed in check of unix socket created by qemu-nbd" +- exit 1 +-} +- +-converted_image=$TEST_IMG.converted +- + _export_nbd_snapshot() + { +- _cleanup_nbd +- $QEMU_NBD -v -t -k "$nbd_unix_socket" "$TEST_IMG" -l $1 & +- _wait_for_nbd ++ nbd_server_start_unix_socket "$TEST_IMG" -l $1 + } + + _export_nbd_snapshot1() + { +- _cleanup_nbd +- $QEMU_NBD -v -t -k "$nbd_unix_socket" "$TEST_IMG" -l snapshot.name=$1 & +- _wait_for_nbd ++ nbd_server_start_unix_socket "$TEST_IMG" -l snapshot.name=$1 + } + + _cleanup() + { +- _cleanup_nbd ++ nbd_server_stop + _cleanup_test_img + rm -f "$converted_image" + } +@@ -86,6 +50,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 + . ./common.rc + . ./common.filter + . ./common.pattern ++. ./common.nbd + + _supported_fmt qcow2 + _supported_proto file +@@ -94,6 +59,10 @@ _require_command QEMU_NBD + # Internal snapshots are (currently) impossible with refcount_bits=1 + _unsupported_imgopts 'refcount_bits=1[^0-9]' + ++nbd_snapshot_img="nbd:unix:$nbd_unix_socket" ++ ++converted_image=$TEST_IMG.converted ++ + # Use -f raw instead of -f $IMGFMT for the NBD connection + QEMU_IO_NBD="$QEMU_IO -f raw --cache=$CACHEMODE" + +diff --git a/tests/qemu-iotests/common.nbd b/tests/qemu-iotests/common.nbd +new file mode 100644 +index 0000000..27357f3 +--- /dev/null ++++ b/tests/qemu-iotests/common.nbd +@@ -0,0 +1,56 @@ ++#!/bin/bash ++# -*- shell-script-mode -*- ++# ++# Helpers for NBD server related config ++# ++# Copyright (C) 2018 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 . ++# ++ ++nbd_unix_socket="${TEST_DIR}/qemu-nbd.sock" ++nbd_pid_file="${TEST_DIR}/qemu-nbd.pid" ++ ++function nbd_server_stop() ++{ ++ local NBD_PID ++ if [ -f "$nbd_pid_file" ]; then ++ read NBD_PID < "$nbd_pid_file" ++ rm -f "$nbd_pid_file" ++ if [ -n "$NBD_PID" ]; then ++ kill "$NBD_PID" ++ fi ++ fi ++ rm -f "$nbd_unix_socket" ++} ++ ++function nbd_server_wait_for_unix_socket() ++{ ++ for ((i = 0; i < 300; i++)) ++ do ++ if [ -r "$nbd_unix_socket" ]; then ++ return ++ fi ++ sleep 0.1 ++ done ++ echo "Failed in check of unix socket created by qemu-nbd" ++ exit 1 ++} ++ ++function nbd_server_start_unix_socket() ++{ ++ nbd_server_stop ++ $QEMU_NBD -v -t -k "$nbd_unix_socket" "$@" & ++ nbd_server_wait_for_unix_socket ++} +-- +1.8.3.1 + diff --git a/SOURCES/kvm-tests-qemu-iotests-add-bitmap-resize-test-246.patch b/SOURCES/kvm-tests-qemu-iotests-add-bitmap-resize-test-246.patch new file mode 100644 index 0000000..fbbef11 --- /dev/null +++ b/SOURCES/kvm-tests-qemu-iotests-add-bitmap-resize-test-246.patch @@ -0,0 +1,471 @@ +From 47d5d00fbef6568ecbf0cc6367c8629d75c275af Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Wed, 3 Apr 2019 22:42:52 +0200 +Subject: [PATCH 157/163] tests/qemu-iotests: add bitmap resize test 246 + +RH-Author: John Snow +Message-id: <20190403224253.5251-5-jsnow@redhat.com> +Patchwork-id: 85436 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 4/5] tests/qemu-iotests: add bitmap resize test 246 +Bugzilla: 1666884 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Max Reitz +RH-Acked-by: Sergio Lopez Pascual + +Test that we can actually resize qcow2 images with persistent bitmaps +correctly. Throw some other goofy stuff at the test while we're at it, +like adding bitmaps of different granularities and at different times. + +Signed-off-by: John Snow +Signed-off-by: Vladimir Sementsov-Ogievskiy +Tested-by: Eric Blake +Message-id: 20190311185147.52309-5-vsementsov@virtuozzo.com + [vsmentsov: drop \n from the end of test output, + test output changed a bit: some bitmaps goes in other order + int the output] +Signed-off-by: John Snow +(cherry picked from commit e2ec4119dc57e9d65e419b2e9071d35300aa1d92) +Signed-off-by: John Snow + +Signed-off-by: Miroslav Rezanina +--- + tests/qemu-iotests/246 | 114 ++++++++++++++++++ + tests/qemu-iotests/246.out | 295 +++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/group | 1 + + 3 files changed, 410 insertions(+) + create mode 100755 tests/qemu-iotests/246 + create mode 100644 tests/qemu-iotests/246.out + +diff --git a/tests/qemu-iotests/246 b/tests/qemu-iotests/246 +new file mode 100755 +index 0000000..b0997a3 +--- /dev/null ++++ b/tests/qemu-iotests/246 +@@ -0,0 +1,114 @@ ++#!/usr/bin/env python ++# ++# Test persistent bitmap resizing. ++# ++# Copyright (c) 2019 John Snow for 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 . ++# ++# owner=jsnow@redhat.com ++ ++import iotests ++from iotests import log ++ ++iotests.verify_image_format(supported_fmts=['qcow2']) ++size = 64 * 1024 * 1024 * 1024 ++gran_small = 32 * 1024 ++gran_large = 128 * 1024 ++ ++def query_bitmaps(vm): ++ res = vm.qmp("query-block") ++ return { "bitmaps": { device['device']: device.get('dirty-bitmaps', []) for ++ device in res['return'] } } ++ ++with iotests.FilePath('img') as img_path, \ ++ iotests.VM() as vm: ++ ++ log('--- Preparing image & VM ---\n') ++ iotests.qemu_img_create('-f', iotests.imgfmt, img_path, str(size)) ++ vm.add_drive(img_path) ++ ++ ++ log('--- 1st Boot (Establish Baseline Image) ---\n') ++ vm.launch() ++ ++ log('\n--- Adding bitmaps Small, Medium, Large, and Transient ---\n') ++ vm.qmp_log("block-dirty-bitmap-add", node="drive0", ++ name="Small", granularity=gran_small, persistent=True) ++ vm.qmp_log("block-dirty-bitmap-add", node="drive0", ++ name="Medium", persistent=True) ++ vm.qmp_log("block-dirty-bitmap-add", node="drive0", ++ name="Large", granularity=gran_large, persistent=True) ++ vm.qmp_log("block-dirty-bitmap-add", node="drive0", ++ name="Transient", persistent=False) ++ ++ log('--- Forcing flush of bitmaps to disk ---\n') ++ log(query_bitmaps(vm), indent=2) ++ vm.shutdown() ++ ++ ++ log('--- 2nd Boot (Grow Image) ---\n') ++ vm.launch() ++ log(query_bitmaps(vm), indent=2) ++ ++ log('--- Adding new bitmap, growing image, and adding 2nd new bitmap ---') ++ vm.qmp_log("block-dirty-bitmap-add", node="drive0", ++ name="New", persistent=True) ++ vm.qmp_log("human-monitor-command", ++ command_line="block_resize drive0 70G") ++ vm.qmp_log("block-dirty-bitmap-add", node="drive0", ++ name="Newtwo", persistent=True) ++ log(query_bitmaps(vm), indent=2) ++ ++ log('--- Forcing flush of bitmaps to disk ---\n') ++ vm.shutdown() ++ ++ ++ log('--- 3rd Boot (Shrink Image) ---\n') ++ vm.launch() ++ log(query_bitmaps(vm), indent=2) ++ ++ log('--- Adding "NewB" bitmap, removing "New" bitmap ---') ++ vm.qmp_log("block-dirty-bitmap-add", node="drive0", ++ name="NewB", persistent=True) ++ vm.qmp_log("block-dirty-bitmap-remove", node="drive0", ++ name="New") ++ ++ log('--- Truncating image ---\n') ++ vm.qmp_log("human-monitor-command", ++ command_line="block_resize drive0 50G") ++ ++ log('--- Adding "NewC" bitmap, removing "NewTwo" bitmap ---') ++ vm.qmp_log("block-dirty-bitmap-add", node="drive0", ++ name="NewC", persistent=True) ++ vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="Newtwo") ++ ++ log('--- Forcing flush of bitmaps to disk ---\n') ++ vm.shutdown() ++ ++ ++ log('--- 4th Boot (Verification and Cleanup) ---\n') ++ vm.launch() ++ log(query_bitmaps(vm), indent=2) ++ ++ log('--- Removing all Bitmaps ---\n') ++ vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="Small") ++ vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="Medium") ++ vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="Large") ++ vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="NewB") ++ vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="NewC") ++ log(query_bitmaps(vm), indent=2) ++ ++ log('\n--- Done ---') ++ vm.shutdown() +diff --git a/tests/qemu-iotests/246.out b/tests/qemu-iotests/246.out +new file mode 100644 +index 0000000..b991945 +--- /dev/null ++++ b/tests/qemu-iotests/246.out +@@ -0,0 +1,295 @@ ++--- Preparing image & VM --- ++ ++--- 1st Boot (Establish Baseline Image) --- ++ ++ ++--- Adding bitmaps Small, Medium, Large, and Transient --- ++ ++{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 32768, "name": "Small", "node": "drive0", "persistent": true}} ++{"return": {}} ++{"execute": "block-dirty-bitmap-add", "arguments": {"name": "Medium", "node": "drive0", "persistent": true}} ++{"return": {}} ++{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 131072, "name": "Large", "node": "drive0", "persistent": true}} ++{"return": {}} ++{"execute": "block-dirty-bitmap-add", "arguments": {"name": "Transient", "node": "drive0", "persistent": false}} ++{"return": {}} ++--- Forcing flush of bitmaps to disk --- ++ ++{ ++ "bitmaps": { ++ "drive0": [ ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 65536, ++ "name": "Transient", ++ "persistent": false, ++ "recording": true, ++ "status": "active" ++ }, ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 131072, ++ "name": "Large", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ }, ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 65536, ++ "name": "Medium", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ }, ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 32768, ++ "name": "Small", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ } ++ ] ++ } ++} ++--- 2nd Boot (Grow Image) --- ++ ++{ ++ "bitmaps": { ++ "drive0": [ ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 32768, ++ "name": "Small", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ }, ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 65536, ++ "name": "Medium", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ }, ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 131072, ++ "name": "Large", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ } ++ ] ++ } ++} ++--- Adding new bitmap, growing image, and adding 2nd new bitmap --- ++{"execute": "block-dirty-bitmap-add", "arguments": {"name": "New", "node": "drive0", "persistent": true}} ++{"return": {}} ++{"execute": "human-monitor-command", "arguments": {"command_line": "block_resize drive0 70G"}} ++{"return": ""} ++{"execute": "block-dirty-bitmap-add", "arguments": {"name": "Newtwo", "node": "drive0", "persistent": true}} ++{"return": {}} ++{ ++ "bitmaps": { ++ "drive0": [ ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 65536, ++ "name": "Newtwo", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ }, ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 65536, ++ "name": "New", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ }, ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 32768, ++ "name": "Small", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ }, ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 65536, ++ "name": "Medium", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ }, ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 131072, ++ "name": "Large", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ } ++ ] ++ } ++} ++--- Forcing flush of bitmaps to disk --- ++ ++--- 3rd Boot (Shrink Image) --- ++ ++{ ++ "bitmaps": { ++ "drive0": [ ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 65536, ++ "name": "New", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ }, ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 65536, ++ "name": "Newtwo", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ }, ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 32768, ++ "name": "Small", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ }, ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 65536, ++ "name": "Medium", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ }, ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 131072, ++ "name": "Large", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ } ++ ] ++ } ++} ++--- Adding "NewB" bitmap, removing "New" bitmap --- ++{"execute": "block-dirty-bitmap-add", "arguments": {"name": "NewB", "node": "drive0", "persistent": true}} ++{"return": {}} ++{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "New", "node": "drive0"}} ++{"return": {}} ++--- Truncating image --- ++ ++{"execute": "human-monitor-command", "arguments": {"command_line": "block_resize drive0 50G"}} ++{"return": ""} ++--- Adding "NewC" bitmap, removing "NewTwo" bitmap --- ++{"execute": "block-dirty-bitmap-add", "arguments": {"name": "NewC", "node": "drive0", "persistent": true}} ++{"return": {}} ++{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "Newtwo", "node": "drive0"}} ++{"return": {}} ++--- Forcing flush of bitmaps to disk --- ++ ++--- 4th Boot (Verification and Cleanup) --- ++ ++{ ++ "bitmaps": { ++ "drive0": [ ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 65536, ++ "name": "NewB", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ }, ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 65536, ++ "name": "NewC", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ }, ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 32768, ++ "name": "Small", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ }, ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 65536, ++ "name": "Medium", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ }, ++ { ++ "busy": false, ++ "count": 0, ++ "granularity": 131072, ++ "name": "Large", ++ "persistent": true, ++ "recording": true, ++ "status": "active" ++ } ++ ] ++ } ++} ++--- Removing all Bitmaps --- ++ ++{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "Small", "node": "drive0"}} ++{"return": {}} ++{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "Medium", "node": "drive0"}} ++{"return": {}} ++{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "Large", "node": "drive0"}} ++{"return": {}} ++{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "NewB", "node": "drive0"}} ++{"return": {}} ++{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "NewC", "node": "drive0"}} ++{"return": {}} ++{ ++ "bitmaps": { ++ "drive0": [] ++ } ++} ++ ++--- Done --- +diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group +index b3aeb6b..7da1334 100644 +--- a/tests/qemu-iotests/group ++++ b/tests/qemu-iotests/group +@@ -230,3 +230,4 @@ + 234 auto quick migration + 236 auto quick + 242 rw auto quick ++246 rw auto quick +-- +1.8.3.1 + diff --git a/SOURCES/kvm-tests-test-bdrv-drain-bdrv_drain_all-works-in-corout.patch b/SOURCES/kvm-tests-test-bdrv-drain-bdrv_drain_all-works-in-corout.patch new file mode 100644 index 0000000..b31ed76 --- /dev/null +++ b/SOURCES/kvm-tests-test-bdrv-drain-bdrv_drain_all-works-in-corout.patch @@ -0,0 +1,83 @@ +From 0ace323e4037200dc16bda1d77bbdc4f2711a658 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:03 +0200 +Subject: [PATCH 12/49] tests/test-bdrv-drain: bdrv_drain_all() works in + coroutines now + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-6-kwolf@redhat.com> +Patchwork-id: 82156 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 05/42] tests/test-bdrv-drain: bdrv_drain_all() works in coroutines now +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +Since we use bdrv_do_drained_begin/end() for bdrv_drain_all_begin/end(), +coroutine context is automatically left with a BH, preventing the +deadlocks that made bdrv_drain_all*() unsafe in coroutine context. Now +that we even removed the old polling code as dead code, it's obvious +that it's compatible now. + +Enable the coroutine test cases for bdrv_drain_all(). + +Signed-off-by: Kevin Wolf +Reviewed-by: Stefan Hajnoczi +(cherry picked from commit 6d0252f2f9cb49925deb1c41101462c9481dfc90) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/test-bdrv-drain.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c +index f1276a1..f5d85c9 100644 +--- a/tests/test-bdrv-drain.c ++++ b/tests/test-bdrv-drain.c +@@ -233,6 +233,11 @@ static void test_drv_cb_drain_subtree(void) + test_drv_cb_common(BDRV_SUBTREE_DRAIN, true); + } + ++static void test_drv_cb_co_drain_all(void) ++{ ++ call_in_coroutine(test_drv_cb_drain_all); ++} ++ + static void test_drv_cb_co_drain(void) + { + call_in_coroutine(test_drv_cb_drain); +@@ -289,6 +294,11 @@ static void test_quiesce_drain_subtree(void) + test_quiesce_common(BDRV_SUBTREE_DRAIN, true); + } + ++static void test_quiesce_co_drain_all(void) ++{ ++ call_in_coroutine(test_quiesce_drain_all); ++} ++ + static void test_quiesce_co_drain(void) + { + call_in_coroutine(test_quiesce_drain); +@@ -795,7 +805,8 @@ int main(int argc, char **argv) + g_test_add_func("/bdrv-drain/driver-cb/drain_subtree", + test_drv_cb_drain_subtree); + +- // XXX bdrv_drain_all() doesn't work in coroutine context ++ g_test_add_func("/bdrv-drain/driver-cb/co/drain_all", ++ test_drv_cb_co_drain_all); + g_test_add_func("/bdrv-drain/driver-cb/co/drain", test_drv_cb_co_drain); + g_test_add_func("/bdrv-drain/driver-cb/co/drain_subtree", + test_drv_cb_co_drain_subtree); +@@ -806,7 +817,8 @@ int main(int argc, char **argv) + g_test_add_func("/bdrv-drain/quiesce/drain_subtree", + test_quiesce_drain_subtree); + +- // XXX bdrv_drain_all() doesn't work in coroutine context ++ g_test_add_func("/bdrv-drain/quiesce/co/drain_all", ++ test_quiesce_co_drain_all); + g_test_add_func("/bdrv-drain/quiesce/co/drain", test_quiesce_co_drain); + g_test_add_func("/bdrv-drain/quiesce/co/drain_subtree", + test_quiesce_co_drain_subtree); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-tests-test-blockjob-remove-exit-callback.patch b/SOURCES/kvm-tests-test-blockjob-remove-exit-callback.patch new file mode 100644 index 0000000..3273f47 --- /dev/null +++ b/SOURCES/kvm-tests-test-blockjob-remove-exit-callback.patch @@ -0,0 +1,88 @@ +From b7db3ff5fc3664af536364f8274ed4826566b1dd Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:56 +0200 +Subject: [PATCH 18/25] tests/test-blockjob: remove exit callback + +RH-Author: John Snow +Message-id: <20180910181803.11781-19-jsnow@redhat.com> +Patchwork-id: 82108 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 18/25] tests/test-blockjob: remove exit callback +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +We remove the exit callback and the completed boolean along with it. +We can simulate it just fine by waiting for the job to defer to the +main loop, and then giving it one final kick to get the main loop +portion to run. + +Signed-off-by: John Snow +Reviewed-by: Max Reitz +Message-id: 20180906130225.5118-10-jsnow@redhat.com +Reviewed-by: Jeff Cody +Signed-off-by: Max Reitz +(cherry picked from commit c0345e9d3bd2f3672d481be7514b9ad181878921) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/test-blockjob.c | 16 ++++++---------- + 1 file changed, 6 insertions(+), 10 deletions(-) + +diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c +index 8e8b680..de4c1c2 100644 +--- a/tests/test-blockjob.c ++++ b/tests/test-blockjob.c +@@ -160,15 +160,8 @@ typedef struct CancelJob { + BlockBackend *blk; + bool should_converge; + bool should_complete; +- bool completed; + } CancelJob; + +-static void cancel_job_exit(Job *job) +-{ +- CancelJob *s = container_of(job, CancelJob, common.job); +- s->completed = true; +-} +- + static void cancel_job_complete(Job *job, Error **errp) + { + CancelJob *s = container_of(job, CancelJob, common.job); +@@ -201,7 +194,6 @@ static const BlockJobDriver test_cancel_driver = { + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .run = cancel_job_run, +- .exit = cancel_job_exit, + .complete = cancel_job_complete, + }, + }; +@@ -335,9 +327,11 @@ static void test_cancel_pending(void) + + job_complete(job, &error_abort); + job_enter(job); +- while (!s->completed) { ++ while (!job->deferred_to_main_loop) { + aio_poll(qemu_get_aio_context(), true); + } ++ assert(job->status == JOB_STATUS_READY); ++ aio_poll(qemu_get_aio_context(), true); + assert(job->status == JOB_STATUS_PENDING); + + cancel_common(s); +@@ -359,9 +353,11 @@ static void test_cancel_concluded(void) + + job_complete(job, &error_abort); + job_enter(job); +- while (!s->completed) { ++ while (!job->deferred_to_main_loop) { + aio_poll(qemu_get_aio_context(), true); + } ++ assert(job->status == JOB_STATUS_READY); ++ aio_poll(qemu_get_aio_context(), true); + assert(job->status == JOB_STATUS_PENDING); + + job_finalize(job, &error_abort); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-tests-test-blockjob-txn-move-.exit-to-.clean.patch b/SOURCES/kvm-tests-test-blockjob-txn-move-.exit-to-.clean.patch new file mode 100644 index 0000000..34fe464 --- /dev/null +++ b/SOURCES/kvm-tests-test-blockjob-txn-move-.exit-to-.clean.patch @@ -0,0 +1,53 @@ +From 45cb87c803124a009d401ab959bb4881f84ac4e1 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Mon, 10 Sep 2018 18:17:57 +0200 +Subject: [PATCH 19/25] tests/test-blockjob-txn: move .exit to .clean + +RH-Author: John Snow +Message-id: <20180910181803.11781-20-jsnow@redhat.com> +Patchwork-id: 82097 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 19/25] tests/test-blockjob-txn: move .exit to .clean +Bugzilla: 1626061 +RH-Acked-by: Max Reitz +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Kevin Wolf + +The exit callback in this test actually only performs cleanup. + +Signed-off-by: John Snow +Reviewed-by: Max Reitz +Message-id: 20180906130225.5118-11-jsnow@redhat.com +Reviewed-by: Jeff Cody +Signed-off-by: Max Reitz +(cherry picked from commit d498b2b6d6f5a547593a3123c9ed8967965c0c44) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + tests/test-blockjob-txn.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c +index ef29f35..86606f9 100644 +--- a/tests/test-blockjob-txn.c ++++ b/tests/test-blockjob-txn.c +@@ -24,7 +24,7 @@ typedef struct { + int *result; + } TestBlockJob; + +-static void test_block_job_exit(Job *job) ++static void test_block_job_clean(Job *job) + { + BlockJob *bjob = container_of(job, BlockJob, job); + BlockDriverState *bs = blk_bs(bjob->blk); +@@ -73,7 +73,7 @@ static const BlockJobDriver test_block_job_driver = { + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .run = test_block_job_run, +- .exit = test_block_job_exit, ++ .clean = test_block_job_clean, + }, + }; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-tests-virtio-blk-test-Disable-auto-read-only.patch b/SOURCES/kvm-tests-virtio-blk-test-Disable-auto-read-only.patch new file mode 100644 index 0000000..8215269 --- /dev/null +++ b/SOURCES/kvm-tests-virtio-blk-test-Disable-auto-read-only.patch @@ -0,0 +1,43 @@ +From b8529f23bd95687344653d4ffd56080d35b4a94f Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 15 Mar 2019 18:09:57 +0100 +Subject: [PATCH 001/163] tests/virtio-blk-test: Disable auto-read-only + +RH-Author: Kevin Wolf +Message-id: <20190315181010.14964-2-kwolf@redhat.com> +Patchwork-id: 84878 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 01/14] tests/virtio-blk-test: Disable auto-read-only +Bugzilla: 1685989 +RH-Acked-by: John Snow +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Miroslav Rezanina + +tests/virtio-blk-test uses a temporary image file that it deletes while +QEMU is still running, so it can't be reopened when writers are +attached or detached. Disable auto-read-only to keep it always writable. + +Signed-off-by: Kevin Wolf +Reviewed-by: Eric Blake +(cherry picked from commit 70304118bb920f2aa961530b64efcce761e4cb5b) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + tests/virtio-blk-test.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c +index 9be9ffb..2ed4e74 100644 +--- a/tests/virtio-blk-test.c ++++ b/tests/virtio-blk-test.c +@@ -62,7 +62,7 @@ static QOSState *pci_test_start(void) + QOSState *qs; + const char *arch = qtest_get_arch(); + char *tmp_path; +- const char *cmd = "-drive if=none,id=drive0,file=%s,format=raw " ++ const char *cmd = "-drive if=none,id=drive0,file=%s,format=raw,auto-read-only=off " + "-drive if=none,id=drive1,file=null-co://,format=raw " + "-device virtio-blk-pci,id=drv0,drive=drive0," + "addr=%x.%x"; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-throttle-groups-fix-hang-when-group-member-leaves.patch b/SOURCES/kvm-throttle-groups-fix-hang-when-group-member-leaves.patch new file mode 100644 index 0000000..38d320b --- /dev/null +++ b/SOURCES/kvm-throttle-groups-fix-hang-when-group-member-leaves.patch @@ -0,0 +1,68 @@ +From 542fe943f769002a9826f057fcf32acdb1cdc41e Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +Date: Mon, 23 Jul 2018 16:36:29 +0200 +Subject: [PATCH 89/89] throttle-groups: fix hang when group member leaves + +RH-Author: Stefan Hajnoczi +Message-id: <20180723163629.7600-2-stefanha@redhat.com> +Patchwork-id: 81469 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/1] throttle-groups: fix hang when group member leaves +Bugzilla: 1535914 +RH-Acked-by: Fam Zheng +RH-Acked-by: Laurent Vivier +RH-Acked-by: Miroslav Rezanina + +Throttle groups consist of members sharing one throttling state +(including bps/iops limits). Round-robin scheduling is used to ensure +fairness. If a group member already has a timer pending then other +groups members do not schedule their own timers. The next group member +will have its turn when the existing timer expires. + +A hang may occur when a group member leaves while it had a timer +scheduled. Although the code carefully removes the group member from +the round-robin list, it does not schedule the next member. Therefore +remaining members continue to wait for the removed member's timer to +expire. + +This patch schedules the next request if a timer is pending. +Unfortunately the actual bug is a race condition that I've been unable +to capture in a test case. + +Sometimes drive2 hangs when drive1 is removed from the throttling group: + + $ qemu ... -drive if=none,id=drive1,cache=none,format=qcow2,file=data1.qcow2,iops=100,group=foo \ + -device virtio-blk-pci,id=virtio-blk-pci0,drive=drive1 \ + -drive if=none,id=drive2,cache=none,format=qcow2,file=data2.qcow2,iops=10,group=foo \ + -device virtio-blk-pci,id=virtio-blk-pci1,drive=drive2 + (guest-console1)# fio -filename /dev/vda 4k-seq-read.job + (guest-console2)# fio -filename /dev/vdb 4k-seq-read.job + (qmp) {"execute": "block_set_io_throttle", "arguments": {"device": "drive1","bps": 0,"bps_rd": 0,"bps_wr": 0,"iops": 0,"iops_rd": 0,"iops_wr": 0}} + +Reported-by: Nini Gu +Signed-off-by: Stefan Hajnoczi +Message-id: 20180704145410.794-1-stefanha@redhat.com +Cc: Alberto Garcia +Signed-off-by: Stefan Hajnoczi +Signed-off-by: Miroslav Rezanina +--- + block/throttle-groups.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/block/throttle-groups.c b/block/throttle-groups.c +index 36cc043..e297b04 100644 +--- a/block/throttle-groups.c ++++ b/block/throttle-groups.c +@@ -564,6 +564,10 @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm) + + qemu_mutex_lock(&tg->lock); + for (i = 0; i < 2; i++) { ++ if (timer_pending(tgm->throttle_timers.timers[i])) { ++ tg->any_timer_armed[i] = false; ++ schedule_next_request(tgm, i); ++ } + if (tg->tokens[i] == tgm) { + token = throttle_group_next_tgm(tgm); + /* Take care of the case where this is the last tgm in the group */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-ui-Allow-specifying-rendernode-display-option-for-eg.patch b/SOURCES/kvm-ui-Allow-specifying-rendernode-display-option-for-eg.patch new file mode 100644 index 0000000..3f2b6eb --- /dev/null +++ b/SOURCES/kvm-ui-Allow-specifying-rendernode-display-option-for-eg.patch @@ -0,0 +1,53 @@ +From 5c8182b9ee049c2a66ae41aaf664a8f97c86cf16 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 5 Mar 2019 08:26:15 +0100 +Subject: [PATCH 7/9] ui: Allow specifying 'rendernode' display option for + egl-headless +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Gerd Hoffmann +Message-id: <20190305082617.14614-3-kraxel@redhat.com> +Patchwork-id: 84798 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 2/4] ui: Allow specifying 'rendernode' display option for egl-headless +Bugzilla: 1648236 +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Marc-André Lureau +RH-Acked-by: John Snow +RH-Acked-by: Erik Skultety + +From: Erik Skultety + +As libvirt can't predict which rendernode QEMU would pick, it +won't adjust the permissions on the device, hence QEMU getting +"Permission denied" when opening the DRI device. Therefore, enable +'rendernode' option for egl-headless display type. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1648236 + +Signed-off-by: Erik Skultety +Message-id: 27f4617f19aa1072114f10f1aa9dd199735ef982.1542362949.git.eskultet@redhat.com +Signed-off-by: Gerd Hoffmann +(cherry picked from commit 91e61947eb2be21b00091d34f5692f89cef41376) +Signed-off-by: Miroslav Rezanina +--- + ui/egl-headless.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ui/egl-headless.c b/ui/egl-headless.c +index 7c87712..03fec49 100644 +--- a/ui/egl-headless.c ++++ b/ui/egl-headless.c +@@ -175,7 +175,7 @@ static void egl_headless_init(DisplayState *ds, DisplayOptions *opts) + egl_dpy *edpy; + int idx; + +- if (egl_rendernode_init(NULL) < 0) { ++ if (egl_rendernode_init(opts->u.egl_headless.rendernode) < 0) { + error_report("egl: render node init failed"); + exit(1); + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-usb-call-reset-handler-before-updating-state.patch b/SOURCES/kvm-usb-call-reset-handler-before-updating-state.patch new file mode 100644 index 0000000..82d983e --- /dev/null +++ b/SOURCES/kvm-usb-call-reset-handler-before-updating-state.patch @@ -0,0 +1,48 @@ +From ea3cee9424ef12e8f1d6dda411efef4488f98936 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Mon, 3 Jun 2019 13:45:48 +0200 +Subject: [PATCH 5/9] usb: call reset handler before updating state +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Gerd Hoffmann +Message-id: <20190603134550.30153-3-kraxel@redhat.com> +Patchwork-id: 88447 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 2/4] usb: call reset handler before updating state +Bugzilla: 1710861 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +That way the device reset handler can see what +the before-reset state of the device is. + +Signed-off-by: Gerd Hoffmann +Message-id: 20190522094702.17619-2-kraxel@redhat.com +(cherry picked from commit 7ed4657396add28382081a15557c78cd480c1cf1) +Signed-off-by: Miroslav Rezanina +--- + hw/usb/core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/usb/core.c b/hw/usb/core.c +index 241ae66..07b67fb 100644 +--- a/hw/usb/core.c ++++ b/hw/usb/core.c +@@ -87,10 +87,10 @@ void usb_device_reset(USBDevice *dev) + if (dev == NULL || !dev->attached) { + return; + } ++ usb_device_handle_reset(dev); + dev->remote_wakeup = 0; + dev->addr = 0; + dev->state = USB_STATE_DEFAULT; +- usb_device_handle_reset(dev); + } + + void usb_wakeup(USBEndpoint *ep, unsigned int stream) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-usb-hcd-xhci-test-add-a-test-for-ccid-hotplug.patch b/SOURCES/kvm-usb-hcd-xhci-test-add-a-test-for-ccid-hotplug.patch new file mode 100644 index 0000000..9748f93 --- /dev/null +++ b/SOURCES/kvm-usb-hcd-xhci-test-add-a-test-for-ccid-hotplug.patch @@ -0,0 +1,66 @@ +From 175943dc22bc1ff3622fd5132656375f22306f27 Mon Sep 17 00:00:00 2001 +From: Serhii Popovych +Date: Mon, 9 Jul 2018 11:31:17 +0200 +Subject: [PATCH 27/89] usb-hcd-xhci-test: add a test for ccid hotplug +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Serhii Popovych +Message-id: <1531135878-18813-3-git-send-email-spopovyc@redhat.com> +Patchwork-id: 81267 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 2/3] usb-hcd-xhci-test: add a test for ccid hotplug +Bugzilla: 1556678 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Marc-André Lureau +RH-Acked-by: David Gibson + +From: Marc-André Lureau + +Signed-off-by: Marc-André Lureau +Message-id: 20180531195119.22021-5-marcandre.lureau@redhat.com +Signed-off-by: Gerd Hoffmann +(cherry picked from commit 1a3ff20e67330a15d62b00c2916e3541872103c0) +Signed-off-by: Serhii Popovych +Signed-off-by: Miroslav Rezanina + +Conflicts: + tests/usb-hcd-xhci-test.c + +Due to disabled UAS that adds if/endif and comment line. +--- + tests/usb-hcd-xhci-test.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/tests/usb-hcd-xhci-test.c b/tests/usb-hcd-xhci-test.c +index 192b7f7..8c3fb36 100644 +--- a/tests/usb-hcd-xhci-test.c ++++ b/tests/usb-hcd-xhci-test.c +@@ -37,6 +37,15 @@ static void test_usb_uas_hotplug(void) + } + #endif + ++static void test_usb_ccid_hotplug(void) ++{ ++ qtest_qmp_device_add("usb-ccid", "ccid", NULL); ++ qtest_qmp_device_del("ccid"); ++ /* check the device can be added again */ ++ qtest_qmp_device_add("usb-ccid", "ccid", NULL); ++ qtest_qmp_device_del("ccid"); ++} ++ + int main(int argc, char **argv) + { + int ret; +@@ -48,6 +57,8 @@ int main(int argc, char **argv) + #if 0 /* Disabled for Red Hat Enterprise Linux 7 */ + qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug); + #endif ++ qtest_add_func("/xhci/pci/hotplug/usb-ccid", test_usb_ccid_hotplug); ++ + qtest_start("-device nec-usb-xhci,id=xhci" + " -drive id=drive0,if=none,file=null-co://,format=raw"); + ret = g_test_run(); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-usb-host-avoid-libusb_set_configuration-calls.patch b/SOURCES/kvm-usb-host-avoid-libusb_set_configuration-calls.patch new file mode 100644 index 0000000..0f8230f --- /dev/null +++ b/SOURCES/kvm-usb-host-avoid-libusb_set_configuration-calls.patch @@ -0,0 +1,69 @@ +From 1ae7aca2d3a283cf75f1cdf2df404ecb97423236 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Mon, 3 Jun 2019 13:45:50 +0200 +Subject: [PATCH 7/9] usb-host: avoid libusb_set_configuration calls +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Gerd Hoffmann +Message-id: <20190603134550.30153-5-kraxel@redhat.com> +Patchwork-id: 88450 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 4/4] usb-host: avoid libusb_set_configuration calls +Bugzilla: 1710861 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +Seems some devices become confused when we call +libusb_set_configuration(). So before calling the function check +whenever the device has multiple configurations in the first place, and +in case it hasn't (which is the case for the majority of devices) simply +skip the call as it will have no effect anyway. + +Signed-off-by: Gerd Hoffmann +Message-id: 20190522094702.17619-4-kraxel@redhat.com +(cherry picked from commit bfe44898848614cfcb3a269bc965afbe1f0f331c) +Signed-off-by: Miroslav Rezanina +--- + hw/usb/host-libusb.c | 18 ++++++++++-------- + 1 file changed, 10 insertions(+), 8 deletions(-) + +diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c +index 1d7e386..878c515 100644 +--- a/hw/usb/host-libusb.c ++++ b/hw/usb/host-libusb.c +@@ -1216,19 +1216,21 @@ static void usb_host_set_address(USBHostDevice *s, int addr) + + static void usb_host_set_config(USBHostDevice *s, int config, USBPacket *p) + { +- int rc; ++ int rc = 0; + + trace_usb_host_set_config(s->bus_num, s->addr, config); + + usb_host_release_interfaces(s); +- rc = libusb_set_configuration(s->dh, config); +- if (rc != 0) { +- usb_host_libusb_error("libusb_set_configuration", rc); +- p->status = USB_RET_STALL; +- if (rc == LIBUSB_ERROR_NO_DEVICE) { +- usb_host_nodev(s); ++ if (s->ddesc.bNumConfigurations != 1) { ++ rc = libusb_set_configuration(s->dh, config); ++ if (rc != 0) { ++ usb_host_libusb_error("libusb_set_configuration", rc); ++ p->status = USB_RET_STALL; ++ if (rc == LIBUSB_ERROR_NO_DEVICE) { ++ usb_host_nodev(s); ++ } ++ return; + } +- return; + } + p->status = usb_host_claim_interfaces(s, config); + if (p->status != USB_RET_SUCCESS) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-usb-host-skip-open-on-pending-postload-bh.patch b/SOURCES/kvm-usb-host-skip-open-on-pending-postload-bh.patch new file mode 100644 index 0000000..318dcd7 --- /dev/null +++ b/SOURCES/kvm-usb-host-skip-open-on-pending-postload-bh.patch @@ -0,0 +1,78 @@ +From 05af6159ba18882c0bdef512abd3c852b515430b Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Wed, 13 Jun 2018 14:07:30 +0200 +Subject: [PATCH 03/57] usb-host: skip open on pending postload bh + +RH-Author: Gerd Hoffmann +Message-id: <20180613140730.16401-2-kraxel@redhat.com> +Patchwork-id: 80667 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/1] usb-host: skip open on pending postload bh +Bugzilla: 1572851 +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Laurent Vivier + +usb-host emulates a device unplug after live migration, because the +device state is unknown and unplug/replug makes sure the guest +re-initializes the device into a working state. This can't be done in +post-load though, so post-load just schedules a bottom half which +executes after vmload is complete. + +It can happen that the device autoscan timer hits the race window +between scheduling and running the bottom half, which in turn can +triggers an assert(). + +Fix that issue by just ignoring the usb_host_open() call in case the +bottom half didn't execute yet. + +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1572851 +Signed-off-by: Gerd Hoffmann +Message-id: 20180503062932.17233-1-kraxel@redhat.com +(cherry picked from commit 3280ea8edede3814553aa19fa27a58daedd48ad9) +Signed-off-by: Miroslav Rezanina +--- + hw/usb/host-libusb.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c +index 1b0be07..0290fb8 100644 +--- a/hw/usb/host-libusb.c ++++ b/hw/usb/host-libusb.c +@@ -102,6 +102,7 @@ struct USBHostDevice { + /* callbacks & friends */ + QEMUBH *bh_nodev; + QEMUBH *bh_postld; ++ bool bh_postld_pending; + Notifier exit; + + /* request queues */ +@@ -866,6 +867,10 @@ static int usb_host_open(USBHostDevice *s, libusb_device *dev) + int rc; + Error *local_err = NULL; + ++ if (s->bh_postld_pending) { ++ return -1; ++ } ++ + trace_usb_host_open_started(bus_num, addr); + + if (s->dh != NULL) { +@@ -1524,6 +1529,7 @@ static void usb_host_post_load_bh(void *opaque) + if (udev->attached) { + usb_device_detach(udev); + } ++ dev->bh_postld_pending = false; + usb_host_auto_check(NULL); + } + +@@ -1535,6 +1541,7 @@ static int usb_host_post_load(void *opaque, int version_id) + dev->bh_postld = qemu_bh_new(usb_host_post_load_bh, dev); + } + qemu_bh_schedule(dev->bh_postld); ++ dev->bh_postld_pending = true; + return 0; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-usb-host-skip-reset-for-untouched-devices.patch b/SOURCES/kvm-usb-host-skip-reset-for-untouched-devices.patch new file mode 100644 index 0000000..a46e487 --- /dev/null +++ b/SOURCES/kvm-usb-host-skip-reset-for-untouched-devices.patch @@ -0,0 +1,47 @@ +From 2aed844c2485cfc07bee58f67be64749655f7f12 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Mon, 3 Jun 2019 13:45:49 +0200 +Subject: [PATCH 6/9] usb-host: skip reset for untouched devices +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Gerd Hoffmann +Message-id: <20190603134550.30153-4-kraxel@redhat.com> +Patchwork-id: 88448 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 3/4] usb-host: skip reset for untouched devices +Bugzilla: 1710861 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Max Reitz +RH-Acked-by: Miroslav Rezanina + +If the guest didn't talk to the device yet, skip the reset. +Without this usb-host devices get resetted a number of times +at boot time for no good reason. + +Signed-off-by: Gerd Hoffmann +Message-id: 20190522094702.17619-3-kraxel@redhat.com +(cherry picked from commit 65f14ab98da1da920f98ee8734dc1588b01d6b2b) +Signed-off-by: Miroslav Rezanina +--- + hw/usb/host-libusb.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c +index 0425f0e..1d7e386 100644 +--- a/hw/usb/host-libusb.c ++++ b/hw/usb/host-libusb.c +@@ -1450,6 +1450,9 @@ static void usb_host_handle_reset(USBDevice *udev) + if (!s->allow_guest_reset) { + return; + } ++ if (udev->addr == 0) { ++ return; ++ } + + trace_usb_host_reset(s->bus_num, s->addr); + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-usb-storage-Add-rerror-werror-properties.patch b/SOURCES/kvm-usb-storage-Add-rerror-werror-properties.patch new file mode 100644 index 0000000..30b26da --- /dev/null +++ b/SOURCES/kvm-usb-storage-Add-rerror-werror-properties.patch @@ -0,0 +1,104 @@ +From 222934faee20b22e77f711d6bc5d986c2e1fd297 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Mon, 2 Jul 2018 12:20:17 +0200 +Subject: [PATCH 51/57] usb-storage: Add rerror/werror properties + +RH-Author: Kevin Wolf +Message-id: <20180702122017.29664-2-kwolf@redhat.com> +Patchwork-id: 81183 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 1/1] usb-storage: Add rerror/werror properties +Bugzilla: 1595180 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Max Reitz +RH-Acked-by: John Snow + +The error handling policy was traditionally set with -drive, but with +-blockdev it is no longer possible to set frontend options. scsi-disk +(and other block devices) have long supported qdev properties to +configure the error handling policy, so let's add these options to +usb-storage as well and just forward them to the internal scsi-disk +instance. + +Signed-off-by: Kevin Wolf +Reviewed-by: Markus Armbruster +(cherry picked from commit b8efb36b9e99dbea7370139c0866b97a933f78d4) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + hw/scsi/scsi-bus.c | 11 ++++++++++- + hw/usb/dev-storage.c | 2 ++ + include/hw/scsi/scsi.h | 2 ++ + 3 files changed, 14 insertions(+), 1 deletion(-) + +diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c +index 9646743..5905f6b 100644 +--- a/hw/scsi/scsi-bus.c ++++ b/hw/scsi/scsi-bus.c +@@ -226,6 +226,8 @@ static void scsi_qdev_unrealize(DeviceState *qdev, Error **errp) + SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, + int unit, bool removable, int bootindex, + bool share_rw, ++ BlockdevOnError rerror, ++ BlockdevOnError werror, + const char *serial, Error **errp) + { + const char *driver; +@@ -262,6 +264,10 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, + object_unparent(OBJECT(dev)); + return NULL; + } ++ ++ qdev_prop_set_enum(dev, "rerror", rerror); ++ qdev_prop_set_enum(dev, "werror", werror); ++ + object_property_set_bool(OBJECT(dev), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); +@@ -285,7 +291,10 @@ void scsi_bus_legacy_handle_cmdline(SCSIBus *bus) + } + qemu_opts_loc_restore(dinfo->opts); + scsi_bus_legacy_add_drive(bus, blk_by_legacy_dinfo(dinfo), +- unit, false, -1, false, NULL, &error_fatal); ++ unit, false, -1, false, ++ BLOCKDEV_ON_ERROR_AUTO, ++ BLOCKDEV_ON_ERROR_AUTO, ++ NULL, &error_fatal); + } + loc_pop(&loc); + } +diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c +index b56c75a..68e2062 100644 +--- a/hw/usb/dev-storage.c ++++ b/hw/usb/dev-storage.c +@@ -634,6 +634,7 @@ static void usb_msd_storage_realize(USBDevice *dev, Error **errp) + &usb_msd_scsi_info_storage, NULL); + scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable, + s->conf.bootindex, s->conf.share_rw, ++ s->conf.rerror, s->conf.werror, + dev->serial, + errp); + blk_unref(blk); +@@ -687,6 +688,7 @@ static const VMStateDescription vmstate_usb_msd = { + + static Property msd_properties[] = { + DEFINE_BLOCK_PROPERTIES(MSDState, conf), ++ DEFINE_BLOCK_ERROR_PROPERTIES(MSDState, conf), + DEFINE_PROP_BIT("removable", MSDState, removable, 0, false), + DEFINE_PROP_END_OF_LIST(), + }; +diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h +index e35137e..1a7290d 100644 +--- a/include/hw/scsi/scsi.h ++++ b/include/hw/scsi/scsi.h +@@ -154,6 +154,8 @@ static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d) + SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, + int unit, bool removable, int bootindex, + bool share_rw, ++ BlockdevOnError rerror, ++ BlockdevOnError werror, + const char *serial, Error **errp); + void scsi_bus_legacy_handle_cmdline(SCSIBus *bus); + void scsi_legacy_handle_cmdline(void); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-util-async-use-qemu_aio_coroutine_enter-in-co_schedu.patch b/SOURCES/kvm-util-async-use-qemu_aio_coroutine_enter-in-co_schedu.patch new file mode 100644 index 0000000..0d689a6 --- /dev/null +++ b/SOURCES/kvm-util-async-use-qemu_aio_coroutine_enter-in-co_schedu.patch @@ -0,0 +1,77 @@ +From b700e58ee749512368c40a5f84b01c11d24903b9 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 14 Sep 2018 10:55:22 +0200 +Subject: [PATCH 31/49] util/async: use qemu_aio_coroutine_enter in + co_schedule_bh_cb + +RH-Author: Kevin Wolf +Message-id: <20180914105540.18077-25-kwolf@redhat.com> +Patchwork-id: 82176 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 24/42] util/async: use qemu_aio_coroutine_enter in co_schedule_bh_cb +Bugzilla: 1601212 +RH-Acked-by: John Snow +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +From: Sergio Lopez + +AIO Coroutines shouldn't by managed by an AioContext different than the +one assigned when they are created. aio_co_enter avoids entering a +coroutine from a different AioContext, calling aio_co_schedule instead. + +Scheduled coroutines are then entered by co_schedule_bh_cb using +qemu_coroutine_enter, which just calls qemu_aio_coroutine_enter with the +current AioContext obtained with qemu_get_current_aio_context. +Eventually, co->ctx will be set to the AioContext passed as an argument +to qemu_aio_coroutine_enter. + +This means that, if an IO Thread's AioConext is being processed by the +Main Thread (due to aio_poll being called with a BDS AioContext, as it +happens in AIO_WAIT_WHILE among other places), the AioContext from some +coroutines may be wrongly replaced with the one from the Main Thread. + +This is the root cause behind some crashes, mainly triggered by the +drain code at block/io.c. The most common are these abort and failed +assertion: + +util/async.c:aio_co_schedule +456 if (scheduled) { +457 fprintf(stderr, +458 "%s: Co-routine was already scheduled in '%s'\n", +459 __func__, scheduled); +460 abort(); +461 } + +util/qemu-coroutine-lock.c: +286 assert(mutex->holder == self); + +But it's also known to cause random errors at different locations, and +even SIGSEGV with broken coroutine backtraces. + +By using qemu_aio_coroutine_enter directly in co_schedule_bh_cb, we can +pass the correct AioContext as an argument, making sure co->ctx is not +wrongly altered. + +Signed-off-by: Sergio Lopez +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + util/async.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/util/async.c b/util/async.c +index 4dd9d95..5693191 100644 +--- a/util/async.c ++++ b/util/async.c +@@ -391,7 +391,7 @@ static void co_schedule_bh_cb(void *opaque) + + /* Protected by write barrier in qemu_aio_coroutine_enter */ + atomic_set(&co->scheduled, NULL); +- qemu_coroutine_enter(co); ++ qemu_aio_coroutine_enter(ctx, co); + aio_context_release(ctx); + } + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-util-implement-simple-iova-tree.patch b/SOURCES/kvm-util-implement-simple-iova-tree.patch new file mode 100644 index 0000000..2dac4a3 --- /dev/null +++ b/SOURCES/kvm-util-implement-simple-iova-tree.patch @@ -0,0 +1,322 @@ +From 87c2556917718a14f46ca4f9a4fc1095429866d7 Mon Sep 17 00:00:00 2001 +From: Peter Xu +Date: Mon, 3 Sep 2018 04:52:40 +0200 +Subject: [PATCH 25/29] util: implement simple iova tree + +RH-Author: Peter Xu +Message-id: <20180903045241.6456-9-peterx@redhat.com> +Patchwork-id: 82026 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 8/9] util: implement simple iova tree +Bugzilla: 1623859 +RH-Acked-by: Xiao Wang +RH-Acked-by: Auger Eric +RH-Acked-by: Michael S. Tsirkin + +Introduce a simplest iova tree implementation based on GTree. + +CC: QEMU Stable +Signed-off-by: Peter Xu +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit eecf5eedbdc0fc04f39abcf3afeedfbf21b25ca4) +Signed-off-by: Peter Xu +Signed-off-by: Miroslav Rezanina +--- + MAINTAINERS | 6 +++ + include/qemu/iova-tree.h | 134 +++++++++++++++++++++++++++++++++++++++++++++++ + util/Makefile.objs | 1 + + util/iova-tree.c | 114 ++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 255 insertions(+) + create mode 100644 include/qemu/iova-tree.h + create mode 100644 util/iova-tree.c + +diff --git a/MAINTAINERS b/MAINTAINERS +index 53d5216..1e32116 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -1787,6 +1787,12 @@ F: include/sysemu/replay.h + F: docs/replay.txt + F: stubs/replay.c + ++IOVA Tree ++M: Peter Xu ++S: Maintained ++F: include/qemu/iova-tree.h ++F: util/iova-tree.c ++ + Usermode Emulation + ------------------ + Overall +diff --git a/include/qemu/iova-tree.h b/include/qemu/iova-tree.h +new file mode 100644 +index 0000000..b061932 +--- /dev/null ++++ b/include/qemu/iova-tree.h +@@ -0,0 +1,134 @@ ++/* ++ * An very simplified iova tree implementation based on GTree. ++ * ++ * Copyright 2018 Red Hat, Inc. ++ * ++ * Authors: ++ * Peter Xu ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ */ ++#ifndef IOVA_TREE_H ++#define IOVA_TREE_H ++ ++/* ++ * Currently the iova tree will only allow to keep ranges ++ * information, and no extra user data is allowed for each element. A ++ * benefit is that we can merge adjacent ranges internally within the ++ * tree. It can save a lot of memory when the ranges are splitted but ++ * mostly continuous. ++ * ++ * Note that current implementation does not provide any thread ++ * protections. Callers of the iova tree should be responsible ++ * for the thread safety issue. ++ */ ++ ++#include "qemu/osdep.h" ++#include "exec/memory.h" ++#include "exec/hwaddr.h" ++ ++#define IOVA_OK (0) ++#define IOVA_ERR_INVALID (-1) /* Invalid parameters */ ++#define IOVA_ERR_OVERLAP (-2) /* IOVA range overlapped */ ++ ++typedef struct IOVATree IOVATree; ++typedef struct DMAMap { ++ hwaddr iova; ++ hwaddr translated_addr; ++ hwaddr size; /* Inclusive */ ++ IOMMUAccessFlags perm; ++} QEMU_PACKED DMAMap; ++typedef gboolean (*iova_tree_iterator)(DMAMap *map); ++ ++/** ++ * iova_tree_new: ++ * ++ * Create a new iova tree. ++ * ++ * Returns: the tree pointer when succeeded, or NULL if error. ++ */ ++IOVATree *iova_tree_new(void); ++ ++/** ++ * iova_tree_insert: ++ * ++ * @tree: the iova tree to insert ++ * @map: the mapping to insert ++ * ++ * Insert an iova range to the tree. If there is overlapped ++ * ranges, IOVA_ERR_OVERLAP will be returned. ++ * ++ * Return: 0 if succeeded, or <0 if error. ++ */ ++int iova_tree_insert(IOVATree *tree, DMAMap *map); ++ ++/** ++ * iova_tree_remove: ++ * ++ * @tree: the iova tree to remove range from ++ * @map: the map range to remove ++ * ++ * Remove mappings from the tree that are covered by the map range ++ * provided. The range does not need to be exactly what has inserted, ++ * all the mappings that are included in the provided range will be ++ * removed from the tree. Here map->translated_addr is meaningless. ++ * ++ * Return: 0 if succeeded, or <0 if error. ++ */ ++int iova_tree_remove(IOVATree *tree, DMAMap *map); ++ ++/** ++ * iova_tree_find: ++ * ++ * @tree: the iova tree to search from ++ * @map: the mapping to search ++ * ++ * Search for a mapping in the iova tree that overlaps with the ++ * mapping range specified. Only the first found mapping will be ++ * returned. ++ * ++ * Return: DMAMap pointer if found, or NULL if not found. Note that ++ * the returned DMAMap pointer is maintained internally. User should ++ * only read the content but never modify or free the content. Also, ++ * user is responsible to make sure the pointer is valid (say, no ++ * concurrent deletion in progress). ++ */ ++DMAMap *iova_tree_find(IOVATree *tree, DMAMap *map); ++ ++/** ++ * iova_tree_find_address: ++ * ++ * @tree: the iova tree to search from ++ * @iova: the iova address to find ++ * ++ * Similar to iova_tree_find(), but it tries to find mapping with ++ * range iova=iova & size=0. ++ * ++ * Return: same as iova_tree_find(). ++ */ ++DMAMap *iova_tree_find_address(IOVATree *tree, hwaddr iova); ++ ++/** ++ * iova_tree_foreach: ++ * ++ * @tree: the iova tree to iterate on ++ * @iterator: the interator for the mappings, return true to stop ++ * ++ * Iterate over the iova tree. ++ * ++ * Return: 1 if found any overlap, 0 if not, <0 if error. ++ */ ++void iova_tree_foreach(IOVATree *tree, iova_tree_iterator iterator); ++ ++/** ++ * iova_tree_destroy: ++ * ++ * @tree: the iova tree to destroy ++ * ++ * Destroy an existing iova tree. ++ * ++ * Return: None. ++ */ ++void iova_tree_destroy(IOVATree *tree); ++ ++#endif +diff --git a/util/Makefile.objs b/util/Makefile.objs +index 728c354..e1c3fed 100644 +--- a/util/Makefile.objs ++++ b/util/Makefile.objs +@@ -47,4 +47,5 @@ util-obj-y += qht.o + util-obj-y += range.o + util-obj-y += stats64.o + util-obj-y += systemd.o ++util-obj-y += iova-tree.o + util-obj-$(CONFIG_LINUX) += vfio-helpers.o +diff --git a/util/iova-tree.c b/util/iova-tree.c +new file mode 100644 +index 0000000..2d9cebf +--- /dev/null ++++ b/util/iova-tree.c +@@ -0,0 +1,114 @@ ++/* ++ * IOVA tree implementation based on GTree. ++ * ++ * Copyright 2018 Red Hat, Inc. ++ * ++ * Authors: ++ * Peter Xu ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ */ ++ ++#include ++#include "qemu/iova-tree.h" ++ ++struct IOVATree { ++ GTree *tree; ++}; ++ ++static int iova_tree_compare(gconstpointer a, gconstpointer b, gpointer data) ++{ ++ const DMAMap *m1 = a, *m2 = b; ++ ++ if (m1->iova > m2->iova + m2->size) { ++ return 1; ++ } ++ ++ if (m1->iova + m1->size < m2->iova) { ++ return -1; ++ } ++ ++ /* Overlapped */ ++ return 0; ++} ++ ++IOVATree *iova_tree_new(void) ++{ ++ IOVATree *iova_tree = g_new0(IOVATree, 1); ++ ++ /* We don't have values actually, no need to free */ ++ iova_tree->tree = g_tree_new_full(iova_tree_compare, NULL, g_free, NULL); ++ ++ return iova_tree; ++} ++ ++DMAMap *iova_tree_find(IOVATree *tree, DMAMap *map) ++{ ++ return g_tree_lookup(tree->tree, map); ++} ++ ++DMAMap *iova_tree_find_address(IOVATree *tree, hwaddr iova) ++{ ++ DMAMap map = { .iova = iova, .size = 0 }; ++ ++ return iova_tree_find(tree, &map); ++} ++ ++static inline void iova_tree_insert_internal(GTree *gtree, DMAMap *range) ++{ ++ /* Key and value are sharing the same range data */ ++ g_tree_insert(gtree, range, range); ++} ++ ++int iova_tree_insert(IOVATree *tree, DMAMap *map) ++{ ++ DMAMap *new; ++ ++ if (map->iova + map->size < map->iova || map->perm == IOMMU_NONE) { ++ return IOVA_ERR_INVALID; ++ } ++ ++ /* We don't allow to insert range that overlaps with existings */ ++ if (iova_tree_find(tree, map)) { ++ return IOVA_ERR_OVERLAP; ++ } ++ ++ new = g_new0(DMAMap, 1); ++ memcpy(new, map, sizeof(*new)); ++ iova_tree_insert_internal(tree->tree, new); ++ ++ return IOVA_OK; ++} ++ ++static gboolean iova_tree_traverse(gpointer key, gpointer value, ++ gpointer data) ++{ ++ iova_tree_iterator iterator = data; ++ DMAMap *map = key; ++ ++ g_assert(key == value); ++ ++ return iterator(map); ++} ++ ++void iova_tree_foreach(IOVATree *tree, iova_tree_iterator iterator) ++{ ++ g_tree_foreach(tree->tree, iova_tree_traverse, iterator); ++} ++ ++int iova_tree_remove(IOVATree *tree, DMAMap *map) ++{ ++ DMAMap *overlap; ++ ++ while ((overlap = iova_tree_find(tree, map))) { ++ g_tree_remove(tree->tree, overlap); ++ } ++ ++ return IOVA_OK; ++} ++ ++void iova_tree_destroy(IOVATree *tree) ++{ ++ g_tree_destroy(tree->tree); ++ g_free(tree); ++} +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vdi-Fix-vdi_co_do_create-return-value.patch b/SOURCES/kvm-vdi-Fix-vdi_co_do_create-return-value.patch new file mode 100644 index 0000000..4ea5742 --- /dev/null +++ b/SOURCES/kvm-vdi-Fix-vdi_co_do_create-return-value.patch @@ -0,0 +1,42 @@ +From e4a86a6c362d1c962c23241ebcbb090d71ea554f Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:37 +0200 +Subject: [PATCH 68/89] vdi: Fix vdi_co_do_create() return value + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-55-kwolf@redhat.com> +Patchwork-id: 81126 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 54/73] vdi: Fix vdi_co_do_create() return value +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +.bdrv_co_create() is supposed to return 0 on success, but vdi could +return a positive value instead. Fix this. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: Jeff Cody +(cherry picked from commit 53618dd83885cc551a3833e228cf714494602142) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/vdi.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/block/vdi.c b/block/vdi.c +index 41859a8..1d8ed67 100644 +--- a/block/vdi.c ++++ b/block/vdi.c +@@ -865,6 +865,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, + } + } + ++ ret = 0; + exit: + blk_unref(blk); + bdrv_unref(bs_file); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vfio-Inhibit-ballooning-based-on-group-attachment-to.patch b/SOURCES/kvm-vfio-Inhibit-ballooning-based-on-group-attachment-to.patch new file mode 100644 index 0000000..913f09c --- /dev/null +++ b/SOURCES/kvm-vfio-Inhibit-ballooning-based-on-group-attachment-to.patch @@ -0,0 +1,129 @@ +From d171459ebce617bcd42e7c4a5932b3b0f3fa36d2 Mon Sep 17 00:00:00 2001 +From: Alex Williamson +Date: Mon, 3 Dec 2018 21:53:07 +0100 +Subject: [PATCH 20/34] vfio: Inhibit ballooning based on group attachment to a + container + +RH-Author: Alex Williamson +Message-id: <154387398693.26945.4148583224290166798.stgit@gimli.home> +Patchwork-id: 83229 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 3/7] vfio: Inhibit ballooning based on group attachment to a container +Bugzilla: 1619778 +RH-Acked-by: Peter Xu +RH-Acked-by: Cornelia Huck +RH-Acked-by: Auger Eric +RH-Acked-by: David Hildenbrand + +Bugzilla: 1619778 + +We use a VFIOContainer to associate an AddressSpace to one or more +VFIOGroups. The VFIOContainer represents the DMA context for that +AdressSpace for those VFIOGroups and is synchronized to changes in +that AddressSpace via a MemoryListener. For IOMMU backed devices, +maintaining the DMA context for a VFIOGroup generally involves +pinning a host virtual address in order to create a stable host +physical address and then mapping a translation from the associated +guest physical address to that host physical address into the IOMMU. + +While the above maintains the VFIOContainer synchronized to the QEMU +memory API of the VM, memory ballooning occurs outside of that API. +Inflating the memory balloon (ie. cooperatively capturing pages from +the guest for use by the host) simply uses MADV_DONTNEED to "zap" +pages from QEMU's host virtual address space. The page pinning and +IOMMU mapping above remains in place, negating the host's ability to +reuse the page, but the host virtual to host physical mapping of the +page is invalidated outside of QEMU's memory API. + +When the balloon is later deflated, attempting to cooperatively +return pages to the guest, the page is simply freed by the guest +balloon driver, allowing it to be used in the guest and incurring a +page fault when that occurs. The page fault maps a new host physical +page backing the existing host virtual address, meanwhile the +VFIOContainer still maintains the translation to the original host +physical address. At this point the guest vCPU and any assigned +devices will map different host physical addresses to the same guest +physical address. Badness. + +The IOMMU typically does not have page level granularity with which +it can track this mapping without also incurring inefficiencies in +using page size mappings throughout. MMU notifiers in the host +kernel also provide indicators for invalidating the mapping on +balloon inflation, not for updating the mapping when the balloon is +deflated. For these reasons we assume a default behavior that the +mapping of each VFIOGroup into the VFIOContainer is incompatible +with memory ballooning and increment the balloon inhibitor to match +the attached VFIOGroups. + +Reviewed-by: Peter Xu +Signed-off-by: Alex Williamson +(cherry picked from commit c65ee433153b5925e183a00ebf568e160077c694) +Signed-off-by: Miroslav Rezanina +--- + hw/vfio/common.c | 30 ++++++++++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + +diff --git a/hw/vfio/common.c b/hw/vfio/common.c +index 07ffa0b..7e8f289 100644 +--- a/hw/vfio/common.c ++++ b/hw/vfio/common.c +@@ -32,6 +32,7 @@ + #include "hw/hw.h" + #include "qemu/error-report.h" + #include "qemu/range.h" ++#include "sysemu/balloon.h" + #include "sysemu/kvm.h" + #include "trace.h" + #include "qapi/error.h" +@@ -1039,6 +1040,33 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, + + space = vfio_get_address_space(as); + ++ /* ++ * VFIO is currently incompatible with memory ballooning insofar as the ++ * madvise to purge (zap) the page from QEMU's address space does not ++ * interact with the memory API and therefore leaves stale virtual to ++ * physical mappings in the IOMMU if the page was previously pinned. We ++ * therefore add a balloon inhibit for each group added to a container, ++ * whether the container is used individually or shared. This provides ++ * us with options to allow devices within a group to opt-in and allow ++ * ballooning, so long as it is done consistently for a group (for instance ++ * if the device is an mdev device where it is known that the host vendor ++ * driver will never pin pages outside of the working set of the guest ++ * driver, which would thus not be ballooning candidates). ++ * ++ * The first opportunity to induce pinning occurs here where we attempt to ++ * attach the group to existing containers within the AddressSpace. If any ++ * pages are already zapped from the virtual address space, such as from a ++ * previous ballooning opt-in, new pinning will cause valid mappings to be ++ * re-established. Likewise, when the overall MemoryListener for a new ++ * container is registered, a replay of mappings within the AddressSpace ++ * will occur, re-establishing any previously zapped pages as well. ++ * ++ * NB. Balloon inhibiting does not currently block operation of the ++ * balloon driver or revoke previously pinned pages, it only prevents ++ * calling madvise to modify the virtual mapping of ballooned pages. ++ */ ++ qemu_balloon_inhibit(true); ++ + QLIST_FOREACH(container, &space->containers, next) { + if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) { + group->container = container; +@@ -1227,6 +1255,7 @@ close_fd_exit: + close(fd); + + put_space_exit: ++ qemu_balloon_inhibit(false); + vfio_put_address_space(space); + + return ret; +@@ -1347,6 +1376,7 @@ void vfio_put_group(VFIOGroup *group) + return; + } + ++ qemu_balloon_inhibit(false); + vfio_kvm_device_del_group(group); + vfio_disconnect_container(group); + QLIST_REMOVE(group, next); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vfio-ccw-pci-Allow-devices-to-opt-in-for-ballooning.patch b/SOURCES/kvm-vfio-ccw-pci-Allow-devices-to-opt-in-for-ballooning.patch new file mode 100644 index 0000000..dfc5051 --- /dev/null +++ b/SOURCES/kvm-vfio-ccw-pci-Allow-devices-to-opt-in-for-ballooning.patch @@ -0,0 +1,204 @@ +From f353edbaafbd3b501495a240ae0d4d679c4ae929 Mon Sep 17 00:00:00 2001 +From: Alex Williamson +Date: Mon, 3 Dec 2018 21:53:21 +0100 +Subject: [PATCH 21/34] vfio/ccw/pci: Allow devices to opt-in for ballooning + +RH-Author: Alex Williamson +Message-id: <154387400169.26945.14372894868026827700.stgit@gimli.home> +Patchwork-id: 83230 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 4/7] vfio/ccw/pci: Allow devices to opt-in for ballooning +Bugzilla: 1619778 +RH-Acked-by: Peter Xu +RH-Acked-by: Cornelia Huck +RH-Acked-by: Auger Eric +RH-Acked-by: David Hildenbrand + +Bugzilla: 1619778 + +If a vfio assigned device makes use of a physical IOMMU, then memory +ballooning is necessarily inhibited due to the page pinning, lack of +page level granularity at the IOMMU, and sufficient notifiers to both +remove the page on balloon inflation and add it back on deflation. +However, not all devices are backed by a physical IOMMU. In the case +of mediated devices, if a vendor driver is well synchronized with the +guest driver, such that only pages actively used by the guest driver +are pinned by the host mdev vendor driver, then there should be no +overlap between pages available for the balloon driver and pages +actively in use by the device. Under these conditions, ballooning +should be safe. + +vfio-ccw devices are always mediated devices and always operate under +the constraints above. Therefore we can consider all vfio-ccw devices +as balloon compatible. + +The situation is far from straightforward with vfio-pci. These +devices can be physical devices with physical IOMMU backing or +mediated devices where it is unknown whether a physical IOMMU is in +use or whether the vendor driver is well synchronized to the working +set of the guest driver. The safest approach is therefore to assume +all vfio-pci devices are incompatible with ballooning, but allow user +opt-in should they have further insight into mediated devices. + +Signed-off-by: Alex Williamson +(cherry picked from commit 238e91728503d400e1c4e644e3a9b80f9e621682) +Signed-off-by: Miroslav Rezanina +--- + hw/vfio/ccw.c | 9 +++++++++ + hw/vfio/common.c | 23 ++++++++++++++++++++++- + hw/vfio/pci.c | 26 +++++++++++++++++++++++++- + hw/vfio/trace-events | 1 + + include/hw/vfio/vfio-common.h | 2 ++ + 5 files changed, 59 insertions(+), 2 deletions(-) + +diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c +index fe34b50..0c74dda 100644 +--- a/hw/vfio/ccw.c ++++ b/hw/vfio/ccw.c +@@ -362,6 +362,15 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) + } + } + ++ /* ++ * All vfio-ccw devices are believed to operate in a way compatible with ++ * memory ballooning, ie. pages pinned in the host are in the current ++ * working set of the guest driver and therefore never overlap with pages ++ * available to the guest balloon driver. This needs to be set before ++ * vfio_get_device() for vfio common to handle the balloon inhibitor. ++ */ ++ vcdev->vdev.balloon_allowed = true; ++ + if (vfio_get_device(group, cdev->mdevid, &vcdev->vdev, &err)) { + g_free(vcdev->vdev.name); + goto out_device_err; +diff --git a/hw/vfio/common.c b/hw/vfio/common.c +index 7e8f289..cda2d1f 100644 +--- a/hw/vfio/common.c ++++ b/hw/vfio/common.c +@@ -1376,7 +1376,9 @@ void vfio_put_group(VFIOGroup *group) + return; + } + +- qemu_balloon_inhibit(false); ++ if (!group->balloon_allowed) { ++ qemu_balloon_inhibit(false); ++ } + vfio_kvm_device_del_group(group); + vfio_disconnect_container(group); + QLIST_REMOVE(group, next); +@@ -1412,6 +1414,25 @@ int vfio_get_device(VFIOGroup *group, const char *name, + return ret; + } + ++ /* ++ * Clear the balloon inhibitor for this group if the driver knows the ++ * device operates compatibly with ballooning. Setting must be consistent ++ * per group, but since compatibility is really only possible with mdev ++ * currently, we expect singleton groups. ++ */ ++ if (vbasedev->balloon_allowed != group->balloon_allowed) { ++ if (!QLIST_EMPTY(&group->device_list)) { ++ error_setg(errp, ++ "Inconsistent device balloon setting within group"); ++ return -1; ++ } ++ ++ if (!group->balloon_allowed) { ++ group->balloon_allowed = true; ++ qemu_balloon_inhibit(false); ++ } ++ } ++ + vbasedev->fd = fd; + vbasedev->group = group; + QLIST_INSERT_HEAD(&group->device_list, vbasedev, next); +diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c +index c00b91c..3bc7636 100644 +--- a/hw/vfio/pci.c ++++ b/hw/vfio/pci.c +@@ -2802,12 +2802,13 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) + VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); + VFIODevice *vbasedev_iter; + VFIOGroup *group; +- char *tmp, group_path[PATH_MAX], *group_name; ++ char *tmp, *subsys, group_path[PATH_MAX], *group_name; + Error *err = NULL; + ssize_t len; + struct stat st; + int groupid; + int ret, i = 0; ++ bool is_mdev; + + QLIST_FOREACH(group, &vfio_group_list, next) { + QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { +@@ -2879,6 +2880,27 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) + } + } + ++ /* ++ * Mediated devices *might* operate compatibly with memory ballooning, but ++ * we cannot know for certain, it depends on whether the mdev vendor driver ++ * stays in sync with the active working set of the guest driver. Prevent ++ * the x-balloon-allowed option unless this is minimally an mdev device. ++ */ ++ tmp = g_strdup_printf("%s/subsystem", vdev->vbasedev.sysfsdev); ++ subsys = realpath(tmp, NULL); ++ g_free(tmp); ++ is_mdev = (strcmp(subsys, "/sys/bus/mdev") == 0); ++ free(subsys); ++ ++ trace_vfio_mdev(vdev->vbasedev.name, is_mdev); ++ ++ if (vdev->vbasedev.balloon_allowed && !is_mdev) { ++ error_setg(errp, "x-balloon-allowed only potentially compatible " ++ "with mdev devices"); ++ vfio_put_group(group); ++ goto error; ++ } ++ + ret = vfio_get_device(group, vdev->vbasedev.name, &vdev->vbasedev, errp); + if (ret) { + vfio_put_group(group); +@@ -3176,6 +3198,8 @@ static Property vfio_pci_dev_properties[] = { + DEFINE_PROP_BIT("x-igd-opregion", VFIOPCIDevice, features, + VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT, false), + DEFINE_PROP_BOOL("x-no-mmap", VFIOPCIDevice, vbasedev.no_mmap, false), ++ DEFINE_PROP_BOOL("x-balloon-allowed", VFIOPCIDevice, ++ vbasedev.balloon_allowed, false), + DEFINE_PROP_BOOL("x-no-kvm-intx", VFIOPCIDevice, no_kvm_intx, false), + DEFINE_PROP_BOOL("x-no-kvm-msi", VFIOPCIDevice, no_kvm_msi, false), + DEFINE_PROP_BOOL("x-no-kvm-msix", VFIOPCIDevice, no_kvm_msix, false), +diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events +index 20109cb..9487887 100644 +--- a/hw/vfio/trace-events ++++ b/hw/vfio/trace-events +@@ -39,6 +39,7 @@ vfio_pci_hot_reset_result(const char *name, const char *result) "%s hot reset: % + vfio_populate_device_config(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device %s config:\n size: 0x%lx, offset: 0x%lx, flags: 0x%lx" + vfio_populate_device_get_irq_info_failure(void) "VFIO_DEVICE_GET_IRQ_INFO failure: %m" + vfio_realize(const char *name, int group_id) " (%s) group %d" ++vfio_mdev(const char *name, bool is_mdev) " (%s) is_mdev %d" + vfio_add_ext_cap_dropped(const char *name, uint16_t cap, uint16_t offset) "%s 0x%x@0x%x" + vfio_pci_reset(const char *name) " (%s)" + vfio_pci_reset_flr(const char *name) "%s FLR/VFIO_DEVICE_RESET" +diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h +index d936014..c5c4cac 100644 +--- a/include/hw/vfio/vfio-common.h ++++ b/include/hw/vfio/vfio-common.h +@@ -122,6 +122,7 @@ typedef struct VFIODevice { + bool reset_works; + bool needs_reset; + bool no_mmap; ++ bool balloon_allowed; + VFIODeviceOps *ops; + unsigned int num_irqs; + unsigned int num_regions; +@@ -141,6 +142,7 @@ typedef struct VFIOGroup { + QLIST_HEAD(, VFIODevice) device_list; + QLIST_ENTRY(VFIOGroup) next; + QLIST_ENTRY(VFIOGroup) container_next; ++ bool balloon_allowed; + } VFIOGroup; + + typedef struct VFIODMABuf { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vfio-pci-Default-display-option-to-off.patch b/SOURCES/kvm-vfio-pci-Default-display-option-to-off.patch new file mode 100644 index 0000000..e0392cb --- /dev/null +++ b/SOURCES/kvm-vfio-pci-Default-display-option-to-off.patch @@ -0,0 +1,46 @@ +From a58a90bf5d832135b8e36fdeafed35a92abe79bd Mon Sep 17 00:00:00 2001 +From: Alex Williamson +Date: Tue, 12 Jun 2018 14:51:43 +0200 +Subject: [PATCH 01/57] vfio/pci: Default display option to "off" + +RH-Author: Alex Williamson +Message-id: <20180612145121.14019.7760.stgit@gimli.home> +Patchwork-id: 80641 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH] vfio/pci: Default display option to "off" +Bugzilla: 1583050 +RH-Acked-by: Gerd Hoffmann +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Laurent Vivier + +Commit a9994687cb9b ("vfio/display: core & wireup") added display +support to vfio-pci with the default being "auto", which breaks +existing VMs when the vGPU requires GL support but had no previous +requirement for a GL compatible configuration. "Off" is the safer +default as we impose no new requirements to VM configurations. + +Fixes: a9994687cb9b ("vfio/display: core & wireup") +Cc: qemu-stable@nongnu.org +Cc: Gerd Hoffmann +Signed-off-by: Alex Williamson +(cherry picked from commit 8151a9c56d31eeeea872b8103c8b86d03c411667) +Signed-off-by: Miroslav Rezanina +--- + hw/vfio/pci.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c +index 34b9d19..4683eb4 100644 +--- a/hw/vfio/pci.c ++++ b/hw/vfio/pci.c +@@ -3167,7 +3167,7 @@ static Property vfio_pci_dev_properties[] = { + DEFINE_PROP_PCI_HOST_DEVADDR("host", VFIOPCIDevice, host), + DEFINE_PROP_STRING("sysfsdev", VFIOPCIDevice, vbasedev.sysfsdev), + DEFINE_PROP_ON_OFF_AUTO("display", VFIOPCIDevice, +- display, ON_OFF_AUTO_AUTO), ++ display, ON_OFF_AUTO_OFF), + DEFINE_PROP_UINT32("x-intx-mmap-timeout-ms", VFIOPCIDevice, + intx.mmap_timeout, 1100), + DEFINE_PROP_BIT("x-vga", VFIOPCIDevice, features, +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vfio-pci-Fix-failure-to-close-file-descriptor-on-err.patch b/SOURCES/kvm-vfio-pci-Fix-failure-to-close-file-descriptor-on-err.patch new file mode 100644 index 0000000..5c9f55b --- /dev/null +++ b/SOURCES/kvm-vfio-pci-Fix-failure-to-close-file-descriptor-on-err.patch @@ -0,0 +1,44 @@ +From da8839a4373667b017e935d7c365af6cbfd97bb9 Mon Sep 17 00:00:00 2001 +From: Alex Williamson +Date: Mon, 3 Dec 2018 21:54:02 +0100 +Subject: [PATCH 23/34] vfio/pci: Fix failure to close file descriptor on error + +RH-Author: Alex Williamson +Message-id: <154387404228.26945.9774510369852793346.stgit@gimli.home> +Patchwork-id: 83232 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 6/7] vfio/pci: Fix failure to close file descriptor on error +Bugzilla: 1619778 +RH-Acked-by: Peter Xu +RH-Acked-by: Cornelia Huck +RH-Acked-by: Auger Eric +RH-Acked-by: David Hildenbrand + +Bugzilla: 1619778 + +A new error path fails to close the device file descriptor when +triggered by a ballooning incompatibility within the group. Fix it. + +Fixes: 238e91728503 ("vfio/ccw/pci: Allow devices to opt-in for ballooning") +Reviewed-by: Peter Xu +Signed-off-by: Alex Williamson +(cherry picked from commit 8709b3954d4161bad30ccc435408ec50e10f53cc) +Signed-off-by: Miroslav Rezanina +--- + hw/vfio/common.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/hw/vfio/common.c b/hw/vfio/common.c +index cda2d1f..3ab92bd 100644 +--- a/hw/vfio/common.c ++++ b/hw/vfio/common.c +@@ -1424,6 +1424,7 @@ int vfio_get_device(VFIOGroup *group, const char *name, + if (!QLIST_EMPTY(&group->device_list)) { + error_setg(errp, + "Inconsistent device balloon setting within group"); ++ close(fd); + return -1; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vfio-pci-Handle-subsystem-realpath-returning-NULL.patch b/SOURCES/kvm-vfio-pci-Handle-subsystem-realpath-returning-NULL.patch new file mode 100644 index 0000000..e7809eb --- /dev/null +++ b/SOURCES/kvm-vfio-pci-Handle-subsystem-realpath-returning-NULL.patch @@ -0,0 +1,46 @@ +From 9cf75f15324ca15f013d3dc6a95bb5066a58d375 Mon Sep 17 00:00:00 2001 +From: Alex Williamson +Date: Mon, 3 Dec 2018 21:53:40 +0100 +Subject: [PATCH 22/34] vfio/pci: Handle subsystem realpath() returning NULL + +RH-Author: Alex Williamson +Message-id: <154387402054.26945.6430332743475303163.stgit@gimli.home> +Patchwork-id: 83231 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 5/7] vfio/pci: Handle subsystem realpath() returning NULL +Bugzilla: 1619778 +RH-Acked-by: Peter Xu +RH-Acked-by: Cornelia Huck +RH-Acked-by: Auger Eric +RH-Acked-by: David Hildenbrand + +Bugzilla: 1619778 + +Fix error reported by Coverity where realpath can return NULL, +resulting in a segfault in strcmp(). This should never happen given +that we're working through regularly structured sysfs paths, but +trivial enough to easily avoid. + +Fixes: 238e91728503 ("vfio/ccw/pci: Allow devices to opt-in for ballooning") +Signed-off-by: Alex Williamson +(cherry picked from commit a1c0f886496cfb4c336f8eb4155ed424567d653e) +Signed-off-by: Miroslav Rezanina +--- + hw/vfio/pci.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c +index 3bc7636..ba3a393 100644 +--- a/hw/vfio/pci.c ++++ b/hw/vfio/pci.c +@@ -2889,7 +2889,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) + tmp = g_strdup_printf("%s/subsystem", vdev->vbasedev.sysfsdev); + subsys = realpath(tmp, NULL); + g_free(tmp); +- is_mdev = (strcmp(subsys, "/sys/bus/mdev") == 0); ++ is_mdev = subsys && (strcmp(subsys, "/sys/bus/mdev") == 0); + free(subsys); + + trace_vfio_mdev(vdev->vbasedev.name, is_mdev); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vfio-pci-do-not-set-the-PCIDevice-has_rom-attribute.patch b/SOURCES/kvm-vfio-pci-do-not-set-the-PCIDevice-has_rom-attribute.patch new file mode 100644 index 0000000..81812ca --- /dev/null +++ b/SOURCES/kvm-vfio-pci-do-not-set-the-PCIDevice-has_rom-attribute.patch @@ -0,0 +1,67 @@ +From 3483f36dbf965a4ca01c4a2d3bce1132340596d1 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Fri, 31 Aug 2018 16:25:52 +0200 +Subject: [PATCH 10/29] vfio/pci: do not set the PCIDevice 'has_rom' attribute +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: plai@redhat.com +Message-id: <1535732759-22481-3-git-send-email-plai@redhat.com> +Patchwork-id: 82004 +O-Subject: [RHEL7.6 PATCH BZ 1539280 2/9] vfio/pci: do not set the PCIDevice 'has_rom' attribute +Bugzilla: 1539280 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Pankaj Gupta +RH-Acked-by: Miroslav Rezanina + +From: Cédric Le Goater + +PCI devices needing a ROM allocate an optional MemoryRegion with +pci_add_option_rom(). pci_del_option_rom() does the cleanup when the +device is destroyed. The only action taken by this routine is to call +vmstate_unregister_ram() which clears the id string of the optional +ROM RAMBlock and now, also flags the RAMBlock as non-migratable. This +was recently added by commit b895de502717 ("migration: discard +non-migratable RAMBlocks"), . + +VFIO devices do their own loading of the PCI option ROM in +vfio_pci_size_rom(). The memory region is switched to an I/O region +and the PCI attribute 'has_rom' is set but the RAMBlock of the ROM +region is not allocated. When the associated PCI device is deleted, +pci_del_option_rom() calls vmstate_unregister_ram() which tries to +flag a NULL RAMBlock, leading to a SEGV. + +It seems that 'has_rom' was set to have memory_region_destroy() +called, but since commit 469b046ead06 ("memory: remove +memory_region_destroy") this is not necessary anymore as the +MemoryRegion is freed automagically. + +Remove the PCIDevice 'has_rom' attribute setting in vfio. + +Fixes: b895de502717 ("migration: discard non-migratable RAMBlocks") +Signed-off-by: Cédric Le Goater +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Alex Williamson +(cherry picked from commit 26c0ae56386edacc8b0da40264748f59afedb1bb) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + hw/vfio/pci.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c +index 4683eb4..c00b91c 100644 +--- a/hw/vfio/pci.c ++++ b/hw/vfio/pci.c +@@ -990,7 +990,6 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) + pci_register_bar(&vdev->pdev, PCI_ROM_SLOT, + PCI_BASE_ADDRESS_SPACE_MEMORY, &vdev->pdev.rom); + +- vdev->pdev.has_rom = true; + vdev->rom_read_failed = false; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vga-catch-depth-0.patch b/SOURCES/kvm-vga-catch-depth-0.patch new file mode 100644 index 0000000..93f9889 --- /dev/null +++ b/SOURCES/kvm-vga-catch-depth-0.patch @@ -0,0 +1,82 @@ +From 18adbd3883efdd681ed30402c0127971bf058031 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 29 May 2018 10:57:04 +0200 +Subject: [PATCH 6/8] vga: catch depth 0 + +RH-Author: Gerd Hoffmann +Message-id: <20180529105704.21419-2-kraxel@redhat.com> +Patchwork-id: 80500 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/1] vga: catch depth 0 +Bugzilla: 1575541 +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Laszlo Ersek +RH-Acked-by: Stefan Hajnoczi + +depth == 0 is used to indicate 256 color modes. Our region calculation +goes wrong in that case. So detect that and just take the safe code +path we already have for the wraparound case. + +While being at it also catch depth == 15 (where our region size +calculation goes wrong too). And make the comment more verbose, +explaining what is going on here. + +Without this windows guest install might trigger an assert due to trying +to check dirty bitmap outside the snapshot region. + +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1575541 +Signed-off-by: Gerd Hoffmann +Message-id: 20180514103117.21059-1-kraxel@redhat.com +(cherry picked from commit a89fe6c329799e47aaa1663650f076b28808e186) +Signed-off-by: Miroslav Rezanina +--- + hw/display/vga.c | 23 ++++++++++++++++++----- + 1 file changed, 18 insertions(+), 5 deletions(-) + +diff --git a/hw/display/vga.c b/hw/display/vga.c +index 7218133..a7794f6 100644 +--- a/hw/display/vga.c ++++ b/hw/display/vga.c +@@ -1480,13 +1480,28 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) + + s->get_resolution(s, &width, &height); + disp_width = width; ++ depth = s->get_bpp(s); + + region_start = (s->start_addr * 4); + region_end = region_start + (ram_addr_t)s->line_offset * height; +- region_end += width * s->get_bpp(s) / 8; /* scanline length */ ++ region_end += width * depth / 8; /* scanline length */ + region_end -= s->line_offset; +- if (region_end > s->vbe_size) { +- /* wraps around (can happen with cirrus vbe modes) */ ++ if (region_end > s->vbe_size || depth == 0 || depth == 15) { ++ /* ++ * We land here on: ++ * - wraps around (can happen with cirrus vbe modes) ++ * - depth == 0 (256 color palette video mode) ++ * - depth == 15 ++ * ++ * Take the safe and slow route: ++ * - create a dirty bitmap snapshot for all vga memory. ++ * - force shadowing (so all vga memory access goes ++ * through vga_read_*() helpers). ++ * ++ * Given this affects only vga features which are pretty much ++ * unused by modern guests there should be no performance ++ * impact. ++ */ + region_start = 0; + region_end = s->vbe_size; + force_shadow = true; +@@ -1520,8 +1535,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) + } + } + +- depth = s->get_bpp(s); +- + /* + * Check whether we can share the surface with the backend + * or whether we need a shadow surface. We share native +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vhdx-Fix-vhdx_co_create-return-value.patch b/SOURCES/kvm-vhdx-Fix-vhdx_co_create-return-value.patch new file mode 100644 index 0000000..1e89787 --- /dev/null +++ b/SOURCES/kvm-vhdx-Fix-vhdx_co_create-return-value.patch @@ -0,0 +1,43 @@ +From 8510a5a2d268749fd0a55072375ba8e4618717b0 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Tue, 26 Jun 2018 09:48:38 +0200 +Subject: [PATCH 69/89] vhdx: Fix vhdx_co_create() return value + +RH-Author: Kevin Wolf +Message-id: <20180626094856.6924-56-kwolf@redhat.com> +Patchwork-id: 81127 +O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 55/73] vhdx: Fix vhdx_co_create() return value +Bugzilla: 1513543 +RH-Acked-by: Jeffrey Cody +RH-Acked-by: Max Reitz +RH-Acked-by: Fam Zheng + +.bdrv_co_create() is supposed to return 0 on success, but vhdx could +return a positive value instead. Fix this. + +Signed-off-by: Kevin Wolf +Reviewed-by: Max Reitz +Reviewed-by: Jeff Cody +(cherry picked from commit 4a5f2779bad769184550869931937acd0707ec3b) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + block/vhdx.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/block/vhdx.c b/block/vhdx.c +index 6731298..31ac53b 100644 +--- a/block/vhdx.c ++++ b/block/vhdx.c +@@ -1950,7 +1950,7 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, + goto delete_and_exit; + } + +- ++ ret = 0; + delete_and_exit: + blk_unref(blk); + bdrv_unref(bs); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vhost-allow-backends-to-filter-memory-sections.patch b/SOURCES/kvm-vhost-allow-backends-to-filter-memory-sections.patch new file mode 100644 index 0000000..b0338d3 --- /dev/null +++ b/SOURCES/kvm-vhost-allow-backends-to-filter-memory-sections.patch @@ -0,0 +1,117 @@ +From 6f1ec0b51c03a266c26f3108bbdf45987f01b53f Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Thu, 21 Jun 2018 18:54:41 +0200 +Subject: [PATCH 32/57] vhost: allow backends to filter memory sections + +RH-Author: plai@redhat.com +Message-id: <1529607285-9942-7-git-send-email-plai@redhat.com> +Patchwork-id: 80941 +O-Subject: [RHEL7.6 PATCH BZ 1526645 06/10] vhost: allow backends to filter memory sections +Bugzilla: 1526645 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Maxime Coquelin +RH-Acked-by: Laurent Vivier + +From: Tiwei Bie + +This patch introduces a vhost op for vhost backends to allow +them to filter the memory sections that they can handle. + +Signed-off-by: Tiwei Bie +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 988a27754bbbc45698f7acb54352e5a1ae699514) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + hw/virtio/vhost-user.c | 11 +++++++++++ + hw/virtio/vhost.c | 9 +++++++-- + include/hw/virtio/vhost-backend.h | 4 ++++ + 3 files changed, 22 insertions(+), 2 deletions(-) + +diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c +index 85d8fd2..ebb946a 100644 +--- a/hw/virtio/vhost-user.c ++++ b/hw/virtio/vhost-user.c +@@ -1620,6 +1620,16 @@ vhost_user_crypto_close_session(struct vhost_dev *dev, uint64_t session_id) + return 0; + } + ++static bool vhost_user_mem_section_filter(struct vhost_dev *dev, ++ MemoryRegionSection *section) ++{ ++ bool result; ++ ++ result = memory_region_get_fd(section->mr) >= 0; ++ ++ return result; ++} ++ + const VhostOps user_ops = { + .backend_type = VHOST_BACKEND_TYPE_USER, + .vhost_backend_init = vhost_user_init, +@@ -1650,4 +1660,5 @@ const VhostOps user_ops = { + .vhost_set_config = vhost_user_set_config, + .vhost_crypto_create_session = vhost_user_crypto_create_session, + .vhost_crypto_close_session = vhost_user_crypto_close_session, ++ .vhost_backend_mem_section_filter = vhost_user_mem_section_filter, + }; +diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c +index 9d5850a..1ae68ff 100644 +--- a/hw/virtio/vhost.c ++++ b/hw/virtio/vhost.c +@@ -386,7 +386,7 @@ static int vhost_verify_ring_mappings(struct vhost_dev *dev, + return r; + } + +-static bool vhost_section(MemoryRegionSection *section) ++static bool vhost_section(struct vhost_dev *dev, MemoryRegionSection *section) + { + bool result; + bool log_dirty = memory_region_get_dirty_log_mask(section->mr) & +@@ -399,6 +399,11 @@ static bool vhost_section(MemoryRegionSection *section) + */ + result &= !log_dirty; + ++ if (result && dev->vhost_ops->vhost_backend_mem_section_filter) { ++ result &= ++ dev->vhost_ops->vhost_backend_mem_section_filter(dev, section); ++ } ++ + trace_vhost_section(section->mr->name, result); + return result; + } +@@ -632,7 +637,7 @@ static void vhost_region_addnop(MemoryListener *listener, + struct vhost_dev *dev = container_of(listener, struct vhost_dev, + memory_listener); + +- if (!vhost_section(section)) { ++ if (!vhost_section(dev, section)) { + return; + } + vhost_region_add_section(dev, section); +diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h +index 5dac61f..81283ec 100644 +--- a/include/hw/virtio/vhost-backend.h ++++ b/include/hw/virtio/vhost-backend.h +@@ -101,6 +101,9 @@ typedef int (*vhost_crypto_create_session_op)(struct vhost_dev *dev, + typedef int (*vhost_crypto_close_session_op)(struct vhost_dev *dev, + uint64_t session_id); + ++typedef bool (*vhost_backend_mem_section_filter_op)(struct vhost_dev *dev, ++ MemoryRegionSection *section); ++ + typedef struct VhostOps { + VhostBackendType backend_type; + vhost_backend_init vhost_backend_init; +@@ -138,6 +141,7 @@ typedef struct VhostOps { + vhost_set_config_op vhost_set_config; + vhost_crypto_create_session_op vhost_crypto_create_session; + vhost_crypto_close_session_op vhost_crypto_close_session; ++ vhost_backend_mem_section_filter_op vhost_backend_mem_section_filter; + } VhostOps; + + extern const VhostOps user_ops; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vhost-user-add-Net-prefix-to-internal-state-structur.patch b/SOURCES/kvm-vhost-user-add-Net-prefix-to-internal-state-structur.patch new file mode 100644 index 0000000..0d5d1e8 --- /dev/null +++ b/SOURCES/kvm-vhost-user-add-Net-prefix-to-internal-state-structur.patch @@ -0,0 +1,200 @@ +From 3c3163e2db902b0c585971f73308b6dc90ed45a0 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Thu, 21 Jun 2018 18:54:36 +0200 +Subject: [PATCH 27/57] vhost-user: add Net prefix to internal state structure + +RH-Author: plai@redhat.com +Message-id: <1529607285-9942-2-git-send-email-plai@redhat.com> +Patchwork-id: 80943 +O-Subject: [RHEL7.6 PATCH BZ 1526645 01/10] vhost-user: add Net prefix to internal state structure +Bugzilla: 1526645 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Maxime Coquelin +RH-Acked-by: Laurent Vivier + +From: Tiwei Bie + +We are going to introduce a shared vhost user state which +will be named as 'VhostUserState'. So add 'Net' prefix to +the existing internal state structure in the vhost-user +netdev to avoid conflict. + +Signed-off-by: Tiwei Bie +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 703878e2e0975123b1a89a006c0204c469333278) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + net/vhost-user.c | 38 +++++++++++++++++++------------------- + 1 file changed, 19 insertions(+), 19 deletions(-) + +diff --git a/net/vhost-user.c b/net/vhost-user.c +index e0f16c8..fa28aad 100644 +--- a/net/vhost-user.c ++++ b/net/vhost-user.c +@@ -20,38 +20,38 @@ + #include "qemu/option.h" + #include "trace.h" + +-typedef struct VhostUserState { ++typedef struct NetVhostUserState { + NetClientState nc; + CharBackend chr; /* only queue index 0 */ + VHostNetState *vhost_net; + guint watch; + uint64_t acked_features; + bool started; +-} VhostUserState; ++} NetVhostUserState; + + VHostNetState *vhost_user_get_vhost_net(NetClientState *nc) + { +- VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); ++ NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); + assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER); + return s->vhost_net; + } + + uint64_t vhost_user_get_acked_features(NetClientState *nc) + { +- VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); ++ NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); + assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER); + return s->acked_features; + } + + static void vhost_user_stop(int queues, NetClientState *ncs[]) + { +- VhostUserState *s; ++ NetVhostUserState *s; + int i; + + for (i = 0; i < queues; i++) { + assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER); + +- s = DO_UPCAST(VhostUserState, nc, ncs[i]); ++ s = DO_UPCAST(NetVhostUserState, nc, ncs[i]); + + if (s->vhost_net) { + /* save acked features */ +@@ -68,7 +68,7 @@ static int vhost_user_start(int queues, NetClientState *ncs[], CharBackend *be) + { + VhostNetOptions options; + struct vhost_net *net = NULL; +- VhostUserState *s; ++ NetVhostUserState *s; + int max_queues; + int i; + +@@ -77,7 +77,7 @@ static int vhost_user_start(int queues, NetClientState *ncs[], CharBackend *be) + for (i = 0; i < queues; i++) { + assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER); + +- s = DO_UPCAST(VhostUserState, nc, ncs[i]); ++ s = DO_UPCAST(NetVhostUserState, nc, ncs[i]); + + options.net_backend = ncs[i]; + options.opaque = be; +@@ -123,7 +123,7 @@ static ssize_t vhost_user_receive(NetClientState *nc, const uint8_t *buf, + without GUEST_ANNOUNCE capability. + */ + if (size == 60) { +- VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); ++ NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); + int r; + static int display_rarp_failure = 1; + char mac_addr[6]; +@@ -146,7 +146,7 @@ static ssize_t vhost_user_receive(NetClientState *nc, const uint8_t *buf, + + static void vhost_user_cleanup(NetClientState *nc) + { +- VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); ++ NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); + + if (s->vhost_net) { + vhost_net_cleanup(s->vhost_net); +@@ -180,7 +180,7 @@ static bool vhost_user_has_ufo(NetClientState *nc) + + static NetClientInfo net_vhost_user_info = { + .type = NET_CLIENT_DRIVER_VHOST_USER, +- .size = sizeof(VhostUserState), ++ .size = sizeof(NetVhostUserState), + .receive = vhost_user_receive, + .cleanup = vhost_user_cleanup, + .has_vnet_hdr = vhost_user_has_vnet_hdr, +@@ -190,7 +190,7 @@ static NetClientInfo net_vhost_user_info = { + static gboolean net_vhost_user_watch(GIOChannel *chan, GIOCondition cond, + void *opaque) + { +- VhostUserState *s = opaque; ++ NetVhostUserState *s = opaque; + + qemu_chr_fe_disconnect(&s->chr); + +@@ -203,7 +203,7 @@ static void chr_closed_bh(void *opaque) + { + const char *name = opaque; + NetClientState *ncs[MAX_QUEUE_NUM]; +- VhostUserState *s; ++ NetVhostUserState *s; + Error *err = NULL; + int queues; + +@@ -212,7 +212,7 @@ static void chr_closed_bh(void *opaque) + MAX_QUEUE_NUM); + assert(queues < MAX_QUEUE_NUM); + +- s = DO_UPCAST(VhostUserState, nc, ncs[0]); ++ s = DO_UPCAST(NetVhostUserState, nc, ncs[0]); + + qmp_set_link(name, false, &err); + vhost_user_stop(queues, ncs); +@@ -229,7 +229,7 @@ static void net_vhost_user_event(void *opaque, int event) + { + const char *name = opaque; + NetClientState *ncs[MAX_QUEUE_NUM]; +- VhostUserState *s; ++ NetVhostUserState *s; + Chardev *chr; + Error *err = NULL; + int queues; +@@ -239,7 +239,7 @@ static void net_vhost_user_event(void *opaque, int event) + MAX_QUEUE_NUM); + assert(queues < MAX_QUEUE_NUM); + +- s = DO_UPCAST(VhostUserState, nc, ncs[0]); ++ s = DO_UPCAST(NetVhostUserState, nc, ncs[0]); + chr = qemu_chr_fe_get_driver(&s->chr); + trace_vhost_user_event(chr->label, event); + switch (event) { +@@ -283,7 +283,7 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, + { + Error *err = NULL; + NetClientState *nc, *nc0 = NULL; +- VhostUserState *s; ++ NetVhostUserState *s; + int i; + + assert(name); +@@ -296,7 +296,7 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, + nc->queue_index = i; + if (!nc0) { + nc0 = nc; +- s = DO_UPCAST(VhostUserState, nc, nc); ++ s = DO_UPCAST(NetVhostUserState, nc, nc); + if (!qemu_chr_fe_init(&s->chr, chr, &err)) { + error_report_err(err); + return -1; +@@ -305,7 +305,7 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, + + } + +- s = DO_UPCAST(VhostUserState, nc, nc0); ++ s = DO_UPCAST(NetVhostUserState, nc, nc0); + do { + if (qemu_chr_fe_wait_connected(&s->chr, &err) < 0) { + error_report_err(err); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vhost-user-allow-slave-to-send-fds-via-slave-channel.patch b/SOURCES/kvm-vhost-user-allow-slave-to-send-fds-via-slave-channel.patch new file mode 100644 index 0000000..b827d9b --- /dev/null +++ b/SOURCES/kvm-vhost-user-allow-slave-to-send-fds-via-slave-channel.patch @@ -0,0 +1,144 @@ +From 4395bef7b50eba7711d47a5a9064ef048cdee8d2 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Thu, 21 Jun 2018 18:54:42 +0200 +Subject: [PATCH 33/57] vhost-user: allow slave to send fds via slave channel + +RH-Author: plai@redhat.com +Message-id: <1529607285-9942-8-git-send-email-plai@redhat.com> +Patchwork-id: 80935 +O-Subject: [RHEL7.6 PATCH BZ 1526645 07/10] vhost-user: allow slave to send fds via slave channel +Bugzilla: 1526645 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Maxime Coquelin +RH-Acked-by: Laurent Vivier + +From: Tiwei Bie + +Introduce VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD protocol +feature to allow slave to send at most 8 descriptors +in each message to master via ancillary data using the +slave channel. + +Suggested-by: Michael S. Tsirkin +Signed-off-by: Tiwei Bie +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 5f57fbeaaf7c4cd33152d7f2e449caab4d4209d9) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + docs/interop/vhost-user.txt | 5 +++++ + hw/virtio/vhost-user.c | 27 +++++++++++++++++---------- + 2 files changed, 22 insertions(+), 10 deletions(-) + +diff --git a/docs/interop/vhost-user.txt b/docs/interop/vhost-user.txt +index 534caab..682a683 100644 +--- a/docs/interop/vhost-user.txt ++++ b/docs/interop/vhost-user.txt +@@ -367,6 +367,10 @@ The fd is provided via VHOST_USER_SET_SLAVE_REQ_FD ancillary data. + A slave may then send VHOST_USER_SLAVE_* messages to the master + using this fd communication channel. + ++If VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD protocol feature is negotiated, ++slave can send file descriptors (at most 8 descriptors in each message) ++to master via ancillary data using this fd communication channel. ++ + Protocol features + ----------------- + +@@ -380,6 +384,7 @@ Protocol features + #define VHOST_USER_PROTOCOL_F_CRYPTO_SESSION 7 + #define VHOST_USER_PROTOCOL_F_PAGEFAULT 8 + #define VHOST_USER_PROTOCOL_F_CONFIG 9 ++#define VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD 10 + + Master message types + -------------------- +diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c +index ebb946a..e8027ad 100644 +--- a/hw/virtio/vhost-user.c ++++ b/hw/virtio/vhost-user.c +@@ -30,6 +30,7 @@ + + #define VHOST_MEMORY_MAX_NREGIONS 8 + #define VHOST_USER_F_PROTOCOL_FEATURES 30 ++#define VHOST_USER_SLAVE_MAX_FDS 8 + + /* + * Maximum size of virtio device config space +@@ -47,6 +48,7 @@ enum VhostUserProtocolFeature { + VHOST_USER_PROTOCOL_F_CRYPTO_SESSION = 7, + VHOST_USER_PROTOCOL_F_PAGEFAULT = 8, + VHOST_USER_PROTOCOL_F_CONFIG = 9, ++ VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD = 10, + VHOST_USER_PROTOCOL_F_MAX + }; + +@@ -854,10 +856,10 @@ static void slave_read(void *opaque) + int size, ret = 0; + struct iovec iov; + struct msghdr msgh; +- int fd = -1; ++ int fd[VHOST_USER_SLAVE_MAX_FDS]; + char control[CMSG_SPACE(sizeof(fd))]; + struct cmsghdr *cmsg; +- size_t fdsize; ++ int i, fdsize = 0; + + memset(&msgh, 0, sizeof(msgh)); + msgh.msg_iov = &iov; +@@ -865,6 +867,8 @@ static void slave_read(void *opaque) + msgh.msg_control = control; + msgh.msg_controllen = sizeof(control); + ++ memset(fd, -1, sizeof(fd)); ++ + /* Read header */ + iov.iov_base = &hdr; + iov.iov_len = VHOST_USER_HDR_SIZE; +@@ -885,7 +889,7 @@ static void slave_read(void *opaque) + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + fdsize = cmsg->cmsg_len - CMSG_LEN(0); +- memcpy(&fd, CMSG_DATA(cmsg), fdsize); ++ memcpy(fd, CMSG_DATA(cmsg), fdsize); + break; + } + } +@@ -913,14 +917,15 @@ static void slave_read(void *opaque) + break; + default: + error_report("Received unexpected msg type."); +- if (fd != -1) { +- close(fd); +- } + ret = -EINVAL; + } + +- /* Message handlers need to make sure that fd will be consumed. */ +- fd = -1; ++ /* Close the remaining file descriptors. */ ++ for (i = 0; i < fdsize; i++) { ++ if (fd[i] != -1) { ++ close(fd[i]); ++ } ++ } + + /* + * REPLY_ACK feature handling. Other reply types has to be managed +@@ -954,8 +959,10 @@ err: + qemu_set_fd_handler(u->slave_fd, NULL, NULL, NULL); + close(u->slave_fd); + u->slave_fd = -1; +- if (fd != -1) { +- close(fd); ++ for (i = 0; i < fdsize; i++) { ++ if (fd[i] != -1) { ++ close(fd[i]); ++ } + } + return; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vhost-user-bridge-support-host-notifier.patch b/SOURCES/kvm-vhost-user-bridge-support-host-notifier.patch new file mode 100644 index 0000000..bd366fe --- /dev/null +++ b/SOURCES/kvm-vhost-user-bridge-support-host-notifier.patch @@ -0,0 +1,214 @@ +From c331104889ecc51581ead4a154d37ad4d054ca73 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Thu, 21 Jun 2018 18:54:40 +0200 +Subject: [PATCH 31/57] vhost-user-bridge: support host notifier + +RH-Author: plai@redhat.com +Message-id: <1529607285-9942-6-git-send-email-plai@redhat.com> +Patchwork-id: 80934 +O-Subject: [RHEL7.6 PATCH BZ 1526645 05/10] vhost-user-bridge: support host notifier +Bugzilla: 1526645 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Maxime Coquelin +RH-Acked-by: Laurent Vivier + +From: Tiwei Bie + +This patch introduces the host notifier support in +vhost-user-bridge. A new option (-H) is added to use +the host notifier. This is mainly used to test the +host notifier implementation in vhost user. + +Signed-off-by: Tiwei Bie +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit e3af2928f8270498097af163bb023b633c0d8d1c) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + tests/vhost-user-bridge.c | 98 +++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 95 insertions(+), 3 deletions(-) + +diff --git a/tests/vhost-user-bridge.c b/tests/vhost-user-bridge.c +index e0605a5..0884294 100644 +--- a/tests/vhost-user-bridge.c ++++ b/tests/vhost-user-bridge.c +@@ -29,6 +29,7 @@ + + #define _FILE_OFFSET_BITS 64 + ++#include "qemu/atomic.h" + #include "qemu/osdep.h" + #include "qemu/iov.h" + #include "standard-headers/linux/virtio_net.h" +@@ -65,6 +66,11 @@ typedef struct VubrDev { + int sock; + int ready; + int quit; ++ struct { ++ int fd; ++ void *addr; ++ pthread_t thread; ++ } notifier; + } VubrDev; + + static void +@@ -445,14 +451,22 @@ static uint64_t + vubr_get_features(VuDev *dev) + { + return 1ULL << VIRTIO_NET_F_GUEST_ANNOUNCE | +- 1ULL << VIRTIO_NET_F_MRG_RXBUF; ++ 1ULL << VIRTIO_NET_F_MRG_RXBUF | ++ 1ULL << VIRTIO_F_VERSION_1; + } + + static void + vubr_queue_set_started(VuDev *dev, int qidx, bool started) + { ++ VubrDev *vubr = container_of(dev, VubrDev, vudev); + VuVirtq *vq = vu_get_queue(dev, qidx); + ++ if (started && vubr->notifier.fd >= 0) { ++ vu_set_queue_host_notifier(dev, vq, vubr->notifier.fd, ++ getpagesize(), ++ qidx * getpagesize()); ++ } ++ + if (qidx % 2 == 1) { + vu_set_queue_handler(dev, vq, started ? vubr_handle_tx : NULL); + } +@@ -522,6 +536,8 @@ vubr_new(const char *path, bool client) + vubr_die("socket"); + } + ++ dev->notifier.fd = -1; ++ + un.sun_family = AF_UNIX; + strcpy(un.sun_path, path); + len = sizeof(un.sun_family) + strlen(path); +@@ -559,6 +575,73 @@ vubr_new(const char *path, bool client) + return dev; + } + ++static void *notifier_thread(void *arg) ++{ ++ VuDev *dev = (VuDev *)arg; ++ VubrDev *vubr = container_of(dev, VubrDev, vudev); ++ int pagesize = getpagesize(); ++ int qidx; ++ ++ while (true) { ++ for (qidx = 0; qidx < VHOST_MAX_NR_VIRTQUEUE; qidx++) { ++ uint16_t *n = vubr->notifier.addr + pagesize * qidx; ++ ++ if (*n == qidx) { ++ *n = 0xffff; ++ /* We won't miss notifications if we reset ++ * the memory first. */ ++ smp_mb(); ++ ++ DPRINT("Got a notification for queue%d via host notifier.\n", ++ qidx); ++ ++ if (qidx % 2 == 1) { ++ vubr_handle_tx(dev, qidx); ++ } ++ } ++ usleep(1000); ++ } ++ } ++ ++ return NULL; ++} ++ ++static void ++vubr_host_notifier_setup(VubrDev *dev) ++{ ++ char template[] = "/tmp/vubr-XXXXXX"; ++ pthread_t thread; ++ size_t length; ++ void *addr; ++ int fd; ++ ++ length = getpagesize() * VHOST_MAX_NR_VIRTQUEUE; ++ ++ fd = mkstemp(template); ++ if (fd < 0) { ++ vubr_die("mkstemp()"); ++ } ++ ++ if (posix_fallocate(fd, 0, length) != 0) { ++ vubr_die("posix_fallocate()"); ++ } ++ ++ addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); ++ if (addr == MAP_FAILED) { ++ vubr_die("mmap()"); ++ } ++ ++ memset(addr, 0xff, length); ++ ++ if (pthread_create(&thread, NULL, notifier_thread, &dev->vudev) != 0) { ++ vubr_die("pthread_create()"); ++ } ++ ++ dev->notifier.fd = fd; ++ dev->notifier.addr = addr; ++ dev->notifier.thread = thread; ++} ++ + static void + vubr_set_host(struct sockaddr_in *saddr, const char *host) + { +@@ -673,8 +756,9 @@ main(int argc, char *argv[]) + VubrDev *dev; + int opt; + bool client = false; ++ bool host_notifier = false; + +- while ((opt = getopt(argc, argv, "l:r:u:c")) != -1) { ++ while ((opt = getopt(argc, argv, "l:r:u:cH")) != -1) { + + switch (opt) { + case 'l': +@@ -693,6 +777,9 @@ main(int argc, char *argv[]) + case 'c': + client = true; + break; ++ case 'H': ++ host_notifier = true; ++ break; + default: + goto out; + } +@@ -708,6 +795,10 @@ main(int argc, char *argv[]) + return 1; + } + ++ if (host_notifier) { ++ vubr_host_notifier_setup(dev); ++ } ++ + vubr_backend_udp_setup(dev, lhost, lport, rhost, rport); + vubr_run(dev); + +@@ -717,7 +808,7 @@ main(int argc, char *argv[]) + + out: + fprintf(stderr, "Usage: %s ", argv[0]); +- fprintf(stderr, "[-c] [-u ud_socket_path] [-l lhost:lport] [-r rhost:rport]\n"); ++ fprintf(stderr, "[-c] [-H] [-u ud_socket_path] [-l lhost:lport] [-r rhost:rport]\n"); + fprintf(stderr, "\t-u path to unix doman socket. default: %s\n", + DEFAULT_UD_SOCKET); + fprintf(stderr, "\t-l local host and port. default: %s:%s\n", +@@ -725,6 +816,7 @@ out: + fprintf(stderr, "\t-r remote host and port. default: %s:%s\n", + DEFAULT_RHOST, DEFAULT_RPORT); + fprintf(stderr, "\t-c client mode\n"); ++ fprintf(stderr, "\t-H use host notifier\n"); + + return 1; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vhost-user-introduce-shared-vhost-user-state.patch b/SOURCES/kvm-vhost-user-introduce-shared-vhost-user-state.patch new file mode 100644 index 0000000..5e72573 --- /dev/null +++ b/SOURCES/kvm-vhost-user-introduce-shared-vhost-user-state.patch @@ -0,0 +1,560 @@ +From c957ec8a9629189e188747c48ed830599457e7c0 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Thu, 21 Jun 2018 18:54:43 +0200 +Subject: [PATCH 34/57] vhost-user: introduce shared vhost-user state + +RH-Author: plai@redhat.com +Message-id: <1529607285-9942-9-git-send-email-plai@redhat.com> +Patchwork-id: 80942 +O-Subject: [RHEL7.6 PATCH BZ 1526645 08/10] vhost-user: introduce shared vhost-user state +Bugzilla: 1526645 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Maxime Coquelin +RH-Acked-by: Laurent Vivier + +From: Tiwei Bie + +When multi queue is enabled e.g. for a virtio-net device, +each queue pair will have a vhost_dev, and the only thing +shared between vhost devs currently is the chardev. This +patch introduces a vhost-user state structure which will +be shared by all vhost devs of the same virtio device. + +Signed-off-by: Tiwei Bie +Signed-off-by: Michael S. Tsirkin +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 4d0cf552d3a9585f380e8abdc313e4d416a56aa0) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + backends/cryptodev-vhost-user.c | 20 ++++++++++++++++- + hw/block/vhost-user-blk.c | 22 ++++++++++++++++++- + hw/scsi/vhost-user-scsi.c | 20 ++++++++++++++++- + hw/virtio/Makefile.objs | 2 +- + hw/virtio/vhost-stub.c | 10 +++++++++ + hw/virtio/vhost-user.c | 31 ++++++++++++++++++-------- + include/hw/virtio/vhost-user-blk.h | 2 ++ + include/hw/virtio/vhost-user-scsi.h | 2 ++ + include/hw/virtio/vhost-user.h | 20 +++++++++++++++++ + net/vhost-user.c | 44 ++++++++++++++++++++++++++++++------- + 10 files changed, 152 insertions(+), 21 deletions(-) + create mode 100644 include/hw/virtio/vhost-user.h + +diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c +index 862d4f2..d52dacc 100644 +--- a/backends/cryptodev-vhost-user.c ++++ b/backends/cryptodev-vhost-user.c +@@ -26,6 +26,7 @@ + #include "qapi/error.h" + #include "qapi/qmp/qerror.h" + #include "qemu/error-report.h" ++#include "hw/virtio/vhost-user.h" + #include "standard-headers/linux/virtio_crypto.h" + #include "sysemu/cryptodev-vhost.h" + #include "chardev/char-fe.h" +@@ -46,6 +47,7 @@ + typedef struct CryptoDevBackendVhostUser { + CryptoDevBackend parent_obj; + ++ VhostUserState *vhost_user; + CharBackend chr; + char *chr_name; + bool opened; +@@ -102,7 +104,7 @@ cryptodev_vhost_user_start(int queues, + continue; + } + +- options.opaque = &s->chr; ++ options.opaque = s->vhost_user; + options.backend_type = VHOST_BACKEND_TYPE_USER; + options.cc = b->conf.peers.ccs[i]; + s->vhost_crypto[i] = cryptodev_vhost_init(&options); +@@ -185,6 +187,7 @@ static void cryptodev_vhost_user_init( + size_t i; + Error *local_err = NULL; + Chardev *chr; ++ VhostUserState *user; + CryptoDevBackendClient *cc; + CryptoDevBackendVhostUser *s = + CRYPTODEV_BACKEND_VHOST_USER(backend); +@@ -215,6 +218,15 @@ static void cryptodev_vhost_user_init( + } + } + ++ user = vhost_user_init(); ++ if (!user) { ++ error_setg(errp, "Failed to init vhost_user"); ++ return; ++ } ++ ++ user->chr = &s->chr; ++ s->vhost_user = user; ++ + qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, + cryptodev_vhost_user_event, NULL, s, NULL, true); + +@@ -299,6 +311,12 @@ static void cryptodev_vhost_user_cleanup( + backend->conf.peers.ccs[i] = NULL; + } + } ++ ++ if (s->vhost_user) { ++ vhost_user_cleanup(s->vhost_user); ++ g_free(s->vhost_user); ++ s->vhost_user = NULL; ++ } + } + + static void cryptodev_vhost_user_set_chardev(Object *obj, +diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c +index 262baca..4021d71 100644 +--- a/hw/block/vhost-user-blk.c ++++ b/hw/block/vhost-user-blk.c +@@ -229,6 +229,7 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) + { + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserBlk *s = VHOST_USER_BLK(vdev); ++ VhostUserState *user; + int i, ret; + + if (!s->chardev.chr) { +@@ -246,6 +247,15 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) + return; + } + ++ user = vhost_user_init(); ++ if (!user) { ++ error_setg(errp, "vhost-user-blk: failed to init vhost_user"); ++ return; ++ } ++ ++ user->chr = &s->chardev; ++ s->vhost_user = user; ++ + virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, + sizeof(struct virtio_blk_config)); + +@@ -261,7 +271,7 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) + + vhost_dev_set_config_notifier(&s->dev, &blk_ops); + +- ret = vhost_dev_init(&s->dev, &s->chardev, VHOST_BACKEND_TYPE_USER, 0); ++ ret = vhost_dev_init(&s->dev, s->vhost_user, VHOST_BACKEND_TYPE_USER, 0); + if (ret < 0) { + error_setg(errp, "vhost-user-blk: vhost initialization failed: %s", + strerror(-ret)); +@@ -286,6 +296,10 @@ vhost_err: + virtio_err: + g_free(s->dev.vqs); + virtio_cleanup(vdev); ++ ++ vhost_user_cleanup(user); ++ g_free(user); ++ s->vhost_user = NULL; + } + + static void vhost_user_blk_device_unrealize(DeviceState *dev, Error **errp) +@@ -297,6 +311,12 @@ static void vhost_user_blk_device_unrealize(DeviceState *dev, Error **errp) + vhost_dev_cleanup(&s->dev); + g_free(s->dev.vqs); + virtio_cleanup(vdev); ++ ++ if (s->vhost_user) { ++ vhost_user_cleanup(s->vhost_user); ++ g_free(s->vhost_user); ++ s->vhost_user = NULL; ++ } + } + + static void vhost_user_blk_instance_init(Object *obj) +diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c +index 9389ed4..9355cfd 100644 +--- a/hw/scsi/vhost-user-scsi.c ++++ b/hw/scsi/vhost-user-scsi.c +@@ -69,6 +69,7 @@ static void vhost_user_scsi_realize(DeviceState *dev, Error **errp) + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); + VHostUserSCSI *s = VHOST_USER_SCSI(dev); + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); ++ VhostUserState *user; + Error *err = NULL; + int ret; + +@@ -85,19 +86,30 @@ static void vhost_user_scsi_realize(DeviceState *dev, Error **errp) + return; + } + ++ user = vhost_user_init(); ++ if (!user) { ++ error_setg(errp, "vhost-user-scsi: failed to init vhost_user"); ++ return; ++ } ++ user->chr = &vs->conf.chardev; ++ + vsc->dev.nvqs = 2 + vs->conf.num_queues; + vsc->dev.vqs = g_new(struct vhost_virtqueue, vsc->dev.nvqs); + vsc->dev.vq_index = 0; + vsc->dev.backend_features = 0; + +- ret = vhost_dev_init(&vsc->dev, (void *)&vs->conf.chardev, ++ ret = vhost_dev_init(&vsc->dev, user, + VHOST_BACKEND_TYPE_USER, 0); + if (ret < 0) { + error_setg(errp, "vhost-user-scsi: vhost initialization failed: %s", + strerror(-ret)); ++ vhost_user_cleanup(user); ++ g_free(user); + return; + } + ++ s->vhost_user = user; ++ + /* Channel and lun both are 0 for bootable vhost-user-scsi disk */ + vsc->channel = 0; + vsc->lun = 0; +@@ -117,6 +129,12 @@ static void vhost_user_scsi_unrealize(DeviceState *dev, Error **errp) + g_free(vsc->dev.vqs); + + virtio_scsi_common_unrealize(dev, errp); ++ ++ if (s->vhost_user) { ++ vhost_user_cleanup(s->vhost_user); ++ g_free(s->vhost_user); ++ s->vhost_user = NULL; ++ } + } + + static uint64_t vhost_user_scsi_get_features(VirtIODevice *vdev, +diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs +index a5a0936..00c6696 100644 +--- a/hw/virtio/Makefile.objs ++++ b/hw/virtio/Makefile.objs +@@ -12,5 +12,5 @@ obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock.o + #obj-$(CONFIG_VIRTIO_PCI) += virtio-crypto-pci.o + endif + +-common-obj-$(call lnot,$(CONFIG_LINUX)) += vhost-stub.o ++common-obj-$(call lnot,$(call land,$(CONFIG_VIRTIO),$(CONFIG_LINUX))) += vhost-stub.o + common-obj-$(CONFIG_ALL) += vhost-stub.o +diff --git a/hw/virtio/vhost-stub.c b/hw/virtio/vhost-stub.c +index 2d76cde..049089b 100644 +--- a/hw/virtio/vhost-stub.c ++++ b/hw/virtio/vhost-stub.c +@@ -1,7 +1,17 @@ + #include "qemu/osdep.h" + #include "hw/virtio/vhost.h" ++#include "hw/virtio/vhost-user.h" + + bool vhost_has_free_slot(void) + { + return true; + } ++ ++VhostUserState *vhost_user_init(void) ++{ ++ return NULL; ++} ++ ++void vhost_user_cleanup(VhostUserState *user) ++{ ++} +diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c +index e8027ad..a715c5c 100644 +--- a/hw/virtio/vhost-user.c ++++ b/hw/virtio/vhost-user.c +@@ -11,6 +11,7 @@ + #include "qemu/osdep.h" + #include "qapi/error.h" + #include "hw/virtio/vhost.h" ++#include "hw/virtio/vhost-user.h" + #include "hw/virtio/vhost-backend.h" + #include "hw/virtio/virtio-net.h" + #include "chardev/char-fe.h" +@@ -175,7 +176,8 @@ static VhostUserMsg m __attribute__ ((unused)); + + struct vhost_user { + struct vhost_dev *dev; +- CharBackend *chr; ++ /* Shared between vhost devs of the same virtio device */ ++ VhostUserState *user; + int slave_fd; + NotifierWithReturn postcopy_notifier; + struct PostCopyFD postcopy_fd; +@@ -201,7 +203,7 @@ static bool ioeventfd_enabled(void) + static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) + { + struct vhost_user *u = dev->opaque; +- CharBackend *chr = u->chr; ++ CharBackend *chr = u->user->chr; + uint8_t *p = (uint8_t *) msg; + int r, size = VHOST_USER_HDR_SIZE; + +@@ -287,7 +289,7 @@ static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg, + int *fds, int fd_num) + { + struct vhost_user *u = dev->opaque; +- CharBackend *chr = u->chr; ++ CharBackend *chr = u->user->chr; + int ret, size = VHOST_USER_HDR_SIZE + msg->hdr.size; + + /* +@@ -1090,7 +1092,7 @@ static int vhost_user_postcopy_waker(struct PostCopyFD *pcfd, RAMBlock *rb, + static int vhost_user_postcopy_advise(struct vhost_dev *dev, Error **errp) + { + struct vhost_user *u = dev->opaque; +- CharBackend *chr = u->chr; ++ CharBackend *chr = u->user->chr; + int ufd; + VhostUserMsg msg = { + .hdr.request = VHOST_USER_POSTCOPY_ADVISE, +@@ -1228,7 +1230,7 @@ static int vhost_user_postcopy_notifier(NotifierWithReturn *notifier, + return 0; + } + +-static int vhost_user_init(struct vhost_dev *dev, void *opaque) ++static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque) + { + uint64_t features, protocol_features; + struct vhost_user *u; +@@ -1237,7 +1239,7 @@ static int vhost_user_init(struct vhost_dev *dev, void *opaque) + assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); + + u = g_new0(struct vhost_user, 1); +- u->chr = opaque; ++ u->user = opaque; + u->slave_fd = -1; + u->dev = dev; + dev->opaque = u; +@@ -1313,7 +1315,7 @@ static int vhost_user_init(struct vhost_dev *dev, void *opaque) + return 0; + } + +-static int vhost_user_cleanup(struct vhost_dev *dev) ++static int vhost_user_backend_cleanup(struct vhost_dev *dev) + { + struct vhost_user *u; + +@@ -1637,10 +1639,21 @@ static bool vhost_user_mem_section_filter(struct vhost_dev *dev, + return result; + } + ++VhostUserState *vhost_user_init(void) ++{ ++ VhostUserState *user = g_new0(struct VhostUserState, 1); ++ ++ return user; ++} ++ ++void vhost_user_cleanup(VhostUserState *user) ++{ ++} ++ + const VhostOps user_ops = { + .backend_type = VHOST_BACKEND_TYPE_USER, +- .vhost_backend_init = vhost_user_init, +- .vhost_backend_cleanup = vhost_user_cleanup, ++ .vhost_backend_init = vhost_user_backend_init, ++ .vhost_backend_cleanup = vhost_user_backend_cleanup, + .vhost_backend_memslots_limit = vhost_user_memslots_limit, + .vhost_set_log_base = vhost_user_set_log_base, + .vhost_set_mem_table = vhost_user_set_mem_table, +diff --git a/include/hw/virtio/vhost-user-blk.h b/include/hw/virtio/vhost-user-blk.h +index 5804cc9..f1258ae 100644 +--- a/include/hw/virtio/vhost-user-blk.h ++++ b/include/hw/virtio/vhost-user-blk.h +@@ -21,6 +21,7 @@ + #include "hw/block/block.h" + #include "chardev/char-fe.h" + #include "hw/virtio/vhost.h" ++#include "hw/virtio/vhost-user.h" + + #define TYPE_VHOST_USER_BLK "vhost-user-blk" + #define VHOST_USER_BLK(obj) \ +@@ -36,6 +37,7 @@ typedef struct VHostUserBlk { + uint32_t config_wce; + uint32_t config_ro; + struct vhost_dev dev; ++ VhostUserState *vhost_user; + } VHostUserBlk; + + #endif +diff --git a/include/hw/virtio/vhost-user-scsi.h b/include/hw/virtio/vhost-user-scsi.h +index 01861f7..3ec34ae 100644 +--- a/include/hw/virtio/vhost-user-scsi.h ++++ b/include/hw/virtio/vhost-user-scsi.h +@@ -21,6 +21,7 @@ + #include "hw/qdev.h" + #include "hw/virtio/virtio-scsi.h" + #include "hw/virtio/vhost.h" ++#include "hw/virtio/vhost-user.h" + #include "hw/virtio/vhost-scsi-common.h" + + #define TYPE_VHOST_USER_SCSI "vhost-user-scsi" +@@ -30,6 +31,7 @@ + typedef struct VHostUserSCSI { + VHostSCSICommon parent_obj; + uint64_t host_features; ++ VhostUserState *vhost_user; + } VHostUserSCSI; + + #endif /* VHOST_USER_SCSI_H */ +diff --git a/include/hw/virtio/vhost-user.h b/include/hw/virtio/vhost-user.h +new file mode 100644 +index 0000000..eb8bc0d +--- /dev/null ++++ b/include/hw/virtio/vhost-user.h +@@ -0,0 +1,20 @@ ++/* ++ * Copyright (c) 2017-2018 Intel Corporation ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef HW_VIRTIO_VHOST_USER_H ++#define HW_VIRTIO_VHOST_USER_H ++ ++#include "chardev/char-fe.h" ++ ++typedef struct VhostUserState { ++ CharBackend *chr; ++} VhostUserState; ++ ++VhostUserState *vhost_user_init(void); ++void vhost_user_cleanup(VhostUserState *user); ++ ++#endif +diff --git a/net/vhost-user.c b/net/vhost-user.c +index fa28aad..608b837 100644 +--- a/net/vhost-user.c ++++ b/net/vhost-user.c +@@ -12,6 +12,7 @@ + #include "clients.h" + #include "net/vhost_net.h" + #include "net/vhost-user.h" ++#include "hw/virtio/vhost-user.h" + #include "chardev/char-fe.h" + #include "qapi/error.h" + #include "qapi/qapi-commands-net.h" +@@ -23,6 +24,7 @@ + typedef struct NetVhostUserState { + NetClientState nc; + CharBackend chr; /* only queue index 0 */ ++ VhostUserState *vhost_user; + VHostNetState *vhost_net; + guint watch; + uint64_t acked_features; +@@ -64,7 +66,8 @@ static void vhost_user_stop(int queues, NetClientState *ncs[]) + } + } + +-static int vhost_user_start(int queues, NetClientState *ncs[], CharBackend *be) ++static int vhost_user_start(int queues, NetClientState *ncs[], ++ VhostUserState *be) + { + VhostNetOptions options; + struct vhost_net *net = NULL; +@@ -144,7 +147,7 @@ static ssize_t vhost_user_receive(NetClientState *nc, const uint8_t *buf, + return size; + } + +-static void vhost_user_cleanup(NetClientState *nc) ++static void net_vhost_user_cleanup(NetClientState *nc) + { + NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); + +@@ -159,6 +162,11 @@ static void vhost_user_cleanup(NetClientState *nc) + s->watch = 0; + } + qemu_chr_fe_deinit(&s->chr, true); ++ if (s->vhost_user) { ++ vhost_user_cleanup(s->vhost_user); ++ g_free(s->vhost_user); ++ s->vhost_user = NULL; ++ } + } + + qemu_purge_queued_packets(nc); +@@ -182,7 +190,7 @@ static NetClientInfo net_vhost_user_info = { + .type = NET_CLIENT_DRIVER_VHOST_USER, + .size = sizeof(NetVhostUserState), + .receive = vhost_user_receive, +- .cleanup = vhost_user_cleanup, ++ .cleanup = net_vhost_user_cleanup, + .has_vnet_hdr = vhost_user_has_vnet_hdr, + .has_ufo = vhost_user_has_ufo, + }; +@@ -244,7 +252,7 @@ static void net_vhost_user_event(void *opaque, int event) + trace_vhost_user_event(chr->label, event); + switch (event) { + case CHR_EVENT_OPENED: +- if (vhost_user_start(queues, ncs, &s->chr) < 0) { ++ if (vhost_user_start(queues, ncs, s->vhost_user) < 0) { + qemu_chr_fe_disconnect(&s->chr); + return; + } +@@ -283,12 +291,19 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, + { + Error *err = NULL; + NetClientState *nc, *nc0 = NULL; +- NetVhostUserState *s; ++ VhostUserState *user = NULL; ++ NetVhostUserState *s = NULL; + int i; + + assert(name); + assert(queues > 0); + ++ user = vhost_user_init(); ++ if (!user) { ++ error_report("failed to init vhost_user"); ++ goto err; ++ } ++ + for (i = 0; i < queues; i++) { + nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name); + snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s", +@@ -299,17 +314,19 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, + s = DO_UPCAST(NetVhostUserState, nc, nc); + if (!qemu_chr_fe_init(&s->chr, chr, &err)) { + error_report_err(err); +- return -1; ++ goto err; + } ++ user->chr = &s->chr; + } +- ++ s = DO_UPCAST(NetVhostUserState, nc, nc); ++ s->vhost_user = user; + } + + s = DO_UPCAST(NetVhostUserState, nc, nc0); + do { + if (qemu_chr_fe_wait_connected(&s->chr, &err) < 0) { + error_report_err(err); +- return -1; ++ goto err; + } + qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, + net_vhost_user_event, NULL, nc0->name, NULL, +@@ -319,6 +336,17 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, + assert(s->vhost_net); + + return 0; ++ ++err: ++ if (user) { ++ vhost_user_cleanup(user); ++ g_free(user); ++ if (s) { ++ s->vhost_user = NULL; ++ } ++ } ++ ++ return -1; + } + + static Chardev *net_vhost_claim_chardev( +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vhost-user-support-receiving-file-descriptors-in-sla.patch b/SOURCES/kvm-vhost-user-support-receiving-file-descriptors-in-sla.patch new file mode 100644 index 0000000..0335f1c --- /dev/null +++ b/SOURCES/kvm-vhost-user-support-receiving-file-descriptors-in-sla.patch @@ -0,0 +1,106 @@ +From f625d197050d05d33172bd5e93d044155208befb Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Thu, 21 Jun 2018 18:54:38 +0200 +Subject: [PATCH 29/57] vhost-user: support receiving file descriptors in + slave_read + +RH-Author: plai@redhat.com +Message-id: <1529607285-9942-4-git-send-email-plai@redhat.com> +Patchwork-id: 80933 +O-Subject: [RHEL7.6 PATCH BZ 1526645 03/10] vhost-user: support receiving file descriptors in slave_read +Bugzilla: 1526645 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Maxime Coquelin +RH-Acked-by: Laurent Vivier + +From: Tiwei Bie + +Signed-off-by: Tiwei Bie +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 1f3a4519b1c107b5db2434b30638353978366b4d) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + hw/virtio/vhost-user.c | 41 ++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 40 insertions(+), 1 deletion(-) + +diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c +index 38da869..85d8fd2 100644 +--- a/hw/virtio/vhost-user.c ++++ b/hw/virtio/vhost-user.c +@@ -852,14 +852,44 @@ static void slave_read(void *opaque) + VhostUserHeader hdr = { 0, }; + VhostUserPayload payload = { 0, }; + int size, ret = 0; ++ struct iovec iov; ++ struct msghdr msgh; ++ int fd = -1; ++ char control[CMSG_SPACE(sizeof(fd))]; ++ struct cmsghdr *cmsg; ++ size_t fdsize; ++ ++ memset(&msgh, 0, sizeof(msgh)); ++ msgh.msg_iov = &iov; ++ msgh.msg_iovlen = 1; ++ msgh.msg_control = control; ++ msgh.msg_controllen = sizeof(control); + + /* Read header */ +- size = read(u->slave_fd, &hdr, VHOST_USER_HDR_SIZE); ++ iov.iov_base = &hdr; ++ iov.iov_len = VHOST_USER_HDR_SIZE; ++ ++ size = recvmsg(u->slave_fd, &msgh, 0); + if (size != VHOST_USER_HDR_SIZE) { + error_report("Failed to read from slave."); + goto err; + } + ++ if (msgh.msg_flags & MSG_CTRUNC) { ++ error_report("Truncated message."); ++ goto err; ++ } ++ ++ for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; ++ cmsg = CMSG_NXTHDR(&msgh, cmsg)) { ++ if (cmsg->cmsg_level == SOL_SOCKET && ++ cmsg->cmsg_type == SCM_RIGHTS) { ++ fdsize = cmsg->cmsg_len - CMSG_LEN(0); ++ memcpy(&fd, CMSG_DATA(cmsg), fdsize); ++ break; ++ } ++ } ++ + if (hdr.size > VHOST_USER_PAYLOAD_SIZE) { + error_report("Failed to read msg header." + " Size %d exceeds the maximum %zu.", hdr.size, +@@ -883,9 +913,15 @@ static void slave_read(void *opaque) + break; + default: + error_report("Received unexpected msg type."); ++ if (fd != -1) { ++ close(fd); ++ } + ret = -EINVAL; + } + ++ /* Message handlers need to make sure that fd will be consumed. */ ++ fd = -1; ++ + /* + * REPLY_ACK feature handling. Other reply types has to be managed + * directly in their request handlers. +@@ -918,6 +954,9 @@ err: + qemu_set_fd_handler(u->slave_fd, NULL, NULL, NULL); + close(u->slave_fd); + u->slave_fd = -1; ++ if (fd != -1) { ++ close(fd); ++ } + return; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vhost-user-support-registering-external-host-notifie.patch b/SOURCES/kvm-vhost-user-support-registering-external-host-notifie.patch new file mode 100644 index 0000000..8b013d9 --- /dev/null +++ b/SOURCES/kvm-vhost-user-support-registering-external-host-notifie.patch @@ -0,0 +1,314 @@ +From 3a5f44900a64ea2bdf809f1a8df8a4a255dbe014 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Thu, 21 Jun 2018 18:54:44 +0200 +Subject: [PATCH 35/57] vhost-user: support registering external host notifiers + +RH-Author: plai@redhat.com +Message-id: <1529607285-9942-10-git-send-email-plai@redhat.com> +Patchwork-id: 80940 +O-Subject: [RHEL7.6 PATCH BZ 1526645 09/10] vhost-user: support registering external host notifiers +Bugzilla: 1526645 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Maxime Coquelin +RH-Acked-by: Laurent Vivier + +From: Tiwei Bie + +This patch introduces VHOST_USER_PROTOCOL_F_HOST_NOTIFIER. +With this feature negotiated, vhost-user backend can register +memory region based host notifiers. And it will allow the guest +driver in the VM to notify the hardware accelerator at the +vhost-user backend directly. + +Signed-off-by: Tiwei Bie +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 44866521bd6ea8e5152a87664dea1eee90c9438b) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + docs/interop/vhost-user.txt | 33 ++++++++++++ + hw/virtio/vhost-user.c | 113 +++++++++++++++++++++++++++++++++++++++++ + include/hw/virtio/vhost-user.h | 8 +++ + 3 files changed, 154 insertions(+) + +diff --git a/docs/interop/vhost-user.txt b/docs/interop/vhost-user.txt +index 682a683..d51fd58 100644 +--- a/docs/interop/vhost-user.txt ++++ b/docs/interop/vhost-user.txt +@@ -132,6 +132,16 @@ Depending on the request type, payload can be: + Payload: Size bytes array holding the contents of the virtio + device's configuration space + ++ * Vring area description ++ ----------------------- ++ | u64 | size | offset | ++ ----------------------- ++ ++ u64: a 64-bit integer contains vring index and flags ++ Size: a 64-bit size of this area ++ Offset: a 64-bit offset of this area from the start of the ++ supplied file descriptor ++ + In QEMU the vhost-user message is implemented with the following struct: + + typedef struct VhostUserMsg { +@@ -146,6 +156,7 @@ typedef struct VhostUserMsg { + VhostUserLog log; + struct vhost_iotlb_msg iotlb; + VhostUserConfig config; ++ VhostUserVringArea area; + }; + } QEMU_PACKED VhostUserMsg; + +@@ -385,6 +396,7 @@ Protocol features + #define VHOST_USER_PROTOCOL_F_PAGEFAULT 8 + #define VHOST_USER_PROTOCOL_F_CONFIG 9 + #define VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD 10 ++#define VHOST_USER_PROTOCOL_F_HOST_NOTIFIER 11 + + Master message types + -------------------- +@@ -782,6 +794,27 @@ Slave message types + the VHOST_USER_NEED_REPLY flag, master must respond with zero when + operation is successfully completed, or non-zero otherwise. + ++ * VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG ++ ++ Id: 3 ++ Equivalent ioctl: N/A ++ Slave payload: vring area description ++ Master payload: N/A ++ ++ Sets host notifier for a specified queue. The queue index is contained ++ in the u64 field of the vring area description. The host notifier is ++ described by the file descriptor (typically it's a VFIO device fd) which ++ is passed as ancillary data and the size (which is mmap size and should ++ be the same as host page size) and offset (which is mmap offset) carried ++ in the vring area description. QEMU can mmap the file descriptor based ++ on the size and offset to get a memory range. Registering a host notifier ++ means mapping this memory range to the VM as the specified queue's notify ++ MMIO region. Slave sends this request to tell QEMU to de-register the ++ existing notifier if any and register the new notifier if the request is ++ sent with a file descriptor. ++ This request should be sent only when VHOST_USER_PROTOCOL_F_HOST_NOTIFIER ++ protocol feature has been successfully negotiated. ++ + VHOST_USER_PROTOCOL_F_REPLY_ACK: + ------------------------------- + The original vhost-user specification only demands replies for certain +diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c +index a715c5c..c30fc3d 100644 +--- a/hw/virtio/vhost-user.c ++++ b/hw/virtio/vhost-user.c +@@ -13,6 +13,7 @@ + #include "hw/virtio/vhost.h" + #include "hw/virtio/vhost-user.h" + #include "hw/virtio/vhost-backend.h" ++#include "hw/virtio/virtio.h" + #include "hw/virtio/virtio-net.h" + #include "chardev/char-fe.h" + #include "sysemu/kvm.h" +@@ -50,6 +51,7 @@ enum VhostUserProtocolFeature { + VHOST_USER_PROTOCOL_F_PAGEFAULT = 8, + VHOST_USER_PROTOCOL_F_CONFIG = 9, + VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD = 10, ++ VHOST_USER_PROTOCOL_F_HOST_NOTIFIER = 11, + VHOST_USER_PROTOCOL_F_MAX + }; + +@@ -94,6 +96,7 @@ typedef enum VhostUserSlaveRequest { + VHOST_USER_SLAVE_NONE = 0, + VHOST_USER_SLAVE_IOTLB_MSG = 1, + VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2, ++ VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG = 3, + VHOST_USER_SLAVE_MAX + } VhostUserSlaveRequest; + +@@ -138,6 +141,12 @@ static VhostUserConfig c __attribute__ ((unused)); + + sizeof(c.size) \ + + sizeof(c.flags)) + ++typedef struct VhostUserVringArea { ++ uint64_t u64; ++ uint64_t size; ++ uint64_t offset; ++} VhostUserVringArea; ++ + typedef struct { + VhostUserRequest request; + +@@ -159,6 +168,7 @@ typedef union { + struct vhost_iotlb_msg iotlb; + VhostUserConfig config; + VhostUserCryptoSession session; ++ VhostUserVringArea area; + } VhostUserPayload; + + typedef struct VhostUserMsg { +@@ -640,9 +650,37 @@ static int vhost_user_set_vring_num(struct vhost_dev *dev, + return vhost_set_vring(dev, VHOST_USER_SET_VRING_NUM, ring); + } + ++static void vhost_user_host_notifier_restore(struct vhost_dev *dev, ++ int queue_idx) ++{ ++ struct vhost_user *u = dev->opaque; ++ VhostUserHostNotifier *n = &u->user->notifier[queue_idx]; ++ VirtIODevice *vdev = dev->vdev; ++ ++ if (n->addr && !n->set) { ++ virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, true); ++ n->set = true; ++ } ++} ++ ++static void vhost_user_host_notifier_remove(struct vhost_dev *dev, ++ int queue_idx) ++{ ++ struct vhost_user *u = dev->opaque; ++ VhostUserHostNotifier *n = &u->user->notifier[queue_idx]; ++ VirtIODevice *vdev = dev->vdev; ++ ++ if (n->addr && n->set) { ++ virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, false); ++ n->set = false; ++ } ++} ++ + static int vhost_user_set_vring_base(struct vhost_dev *dev, + struct vhost_vring_state *ring) + { ++ vhost_user_host_notifier_restore(dev, ring->index); ++ + return vhost_set_vring(dev, VHOST_USER_SET_VRING_BASE, ring); + } + +@@ -676,6 +714,8 @@ static int vhost_user_get_vring_base(struct vhost_dev *dev, + .hdr.size = sizeof(msg.payload.state), + }; + ++ vhost_user_host_notifier_remove(dev, ring->index); ++ + if (vhost_user_write(dev, &msg, NULL, 0) < 0) { + return -1; + } +@@ -849,6 +889,66 @@ static int vhost_user_slave_handle_config_change(struct vhost_dev *dev) + return ret; + } + ++static int vhost_user_slave_handle_vring_host_notifier(struct vhost_dev *dev, ++ VhostUserVringArea *area, ++ int fd) ++{ ++ int queue_idx = area->u64 & VHOST_USER_VRING_IDX_MASK; ++ size_t page_size = qemu_real_host_page_size; ++ struct vhost_user *u = dev->opaque; ++ VhostUserState *user = u->user; ++ VirtIODevice *vdev = dev->vdev; ++ VhostUserHostNotifier *n; ++ void *addr; ++ char *name; ++ ++ if (!virtio_has_feature(dev->protocol_features, ++ VHOST_USER_PROTOCOL_F_HOST_NOTIFIER) || ++ vdev == NULL || queue_idx >= virtio_get_num_queues(vdev)) { ++ return -1; ++ } ++ ++ n = &user->notifier[queue_idx]; ++ ++ if (n->addr) { ++ virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, false); ++ object_unparent(OBJECT(&n->mr)); ++ munmap(n->addr, page_size); ++ n->addr = NULL; ++ } ++ ++ if (area->u64 & VHOST_USER_VRING_NOFD_MASK) { ++ return 0; ++ } ++ ++ /* Sanity check. */ ++ if (area->size != page_size) { ++ return -1; ++ } ++ ++ addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, ++ fd, area->offset); ++ if (addr == MAP_FAILED) { ++ return -1; ++ } ++ ++ name = g_strdup_printf("vhost-user/host-notifier@%p mmaps[%d]", ++ user, queue_idx); ++ memory_region_init_ram_device_ptr(&n->mr, OBJECT(vdev), name, ++ page_size, addr); ++ g_free(name); ++ ++ if (virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, true)) { ++ munmap(addr, page_size); ++ return -1; ++ } ++ ++ n->addr = addr; ++ n->set = true; ++ ++ return 0; ++} ++ + static void slave_read(void *opaque) + { + struct vhost_dev *dev = opaque; +@@ -917,6 +1017,10 @@ static void slave_read(void *opaque) + case VHOST_USER_SLAVE_CONFIG_CHANGE_MSG : + ret = vhost_user_slave_handle_config_change(dev); + break; ++ case VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG: ++ ret = vhost_user_slave_handle_vring_host_notifier(dev, &payload.area, ++ fd[0]); ++ break; + default: + error_report("Received unexpected msg type."); + ret = -EINVAL; +@@ -1648,6 +1752,15 @@ VhostUserState *vhost_user_init(void) + + void vhost_user_cleanup(VhostUserState *user) + { ++ int i; ++ ++ for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { ++ if (user->notifier[i].addr) { ++ object_unparent(OBJECT(&user->notifier[i].mr)); ++ munmap(user->notifier[i].addr, qemu_real_host_page_size); ++ user->notifier[i].addr = NULL; ++ } ++ } + } + + const VhostOps user_ops = { +diff --git a/include/hw/virtio/vhost-user.h b/include/hw/virtio/vhost-user.h +index eb8bc0d..fd66039 100644 +--- a/include/hw/virtio/vhost-user.h ++++ b/include/hw/virtio/vhost-user.h +@@ -9,9 +9,17 @@ + #define HW_VIRTIO_VHOST_USER_H + + #include "chardev/char-fe.h" ++#include "hw/virtio/virtio.h" ++ ++typedef struct VhostUserHostNotifier { ++ MemoryRegion mr; ++ void *addr; ++ bool set; ++} VhostUserHostNotifier; + + typedef struct VhostUserState { + CharBackend *chr; ++ VhostUserHostNotifier notifier[VIRTIO_QUEUE_MAX]; + } VhostUserState; + + VhostUserState *vhost_user_init(void); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vhost_net-don-t-set-backend-for-the-uninitialized-vi.patch b/SOURCES/kvm-vhost_net-don-t-set-backend-for-the-uninitialized-vi.patch new file mode 100644 index 0000000..a941479 --- /dev/null +++ b/SOURCES/kvm-vhost_net-don-t-set-backend-for-the-uninitialized-vi.patch @@ -0,0 +1,91 @@ +From 55e77ff717339c63b0408e0b9a1bcd313d6c0c48 Mon Sep 17 00:00:00 2001 +From: Xiao Wang +Date: Fri, 24 May 2019 12:56:42 +0200 +Subject: [PATCH] vhost_net: don't set backend for the uninitialized virtqueue + +RH-Author: Xiao Wang +Message-id: <1558702602-3677-1-git-send-email-jasowang@redhat.com> +Patchwork-id: 88204 +O-Subject: [RHEL-7.7 qemu-kvm PATCH] vhost_net: don't set backend for the uninitialized virtqueue +Bugzilla: 1608226 +RH-Acked-by: Thomas Huth +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Jens Freimann + +We used to set backend unconditionally, this won't work for some +guests (e.g windows driver) who may not initialize all virtqueues. For +kernel backend, this will fail since it may try to validate the rings +during setting backend. + +Fixing this by simply skipping the backend set when we find desc is +not ready. + +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Jason Wang +(cherry picked from commit 23bfaf77fa801ba30bb136de7cec47728eb02f4b) +Signed-off-by: Miroslav Rezanina +--- + hw/net/vhost_net.c | 10 ++++++++++ + hw/virtio/virtio.c | 5 +++++ + include/hw/virtio/virtio.h | 1 + + 3 files changed, 16 insertions(+) + +diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c +index e037db6..ec22727 100644 +--- a/hw/net/vhost_net.c ++++ b/hw/net/vhost_net.c +@@ -246,6 +246,11 @@ static int vhost_net_start_one(struct vhost_net *net, + qemu_set_fd_handler(net->backend, NULL, NULL, NULL); + file.fd = net->backend; + for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { ++ if (!virtio_queue_enabled(dev, net->dev.vq_index + ++ file.index)) { ++ /* Queue might not be ready for start */ ++ continue; ++ } + r = vhost_net_set_backend(&net->dev, &file); + if (r < 0) { + r = -errno; +@@ -258,6 +263,11 @@ fail: + file.fd = -1; + if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) { + while (file.index-- > 0) { ++ if (!virtio_queue_enabled(dev, net->dev.vq_index + ++ file.index)) { ++ /* Queue might not be ready for start */ ++ continue; ++ } + int r = vhost_net_set_backend(&net->dev, &file); + assert(r >= 0); + } +diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c +index 1c936ad..3492b20 100644 +--- a/hw/virtio/virtio.c ++++ b/hw/virtio/virtio.c +@@ -2317,6 +2317,11 @@ hwaddr virtio_queue_get_desc_addr(VirtIODevice *vdev, int n) + return vdev->vq[n].vring.desc; + } + ++bool virtio_queue_enabled(VirtIODevice *vdev, int n) ++{ ++ return virtio_queue_get_desc_addr(vdev, n) != 0; ++} ++ + hwaddr virtio_queue_get_avail_addr(VirtIODevice *vdev, int n) + { + return vdev->vq[n].vring.avail; +diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h +index 96313e8..3029758 100644 +--- a/include/hw/virtio/virtio.h ++++ b/include/hw/virtio/virtio.h +@@ -268,6 +268,7 @@ typedef struct VirtIORNGConf VirtIORNGConf; + VIRTIO_F_IOMMU_PLATFORM, false) + + hwaddr virtio_queue_get_desc_addr(VirtIODevice *vdev, int n); ++bool virtio_queue_enabled(VirtIODevice *vdev, int n); + hwaddr virtio_queue_get_avail_addr(VirtIODevice *vdev, int n); + hwaddr virtio_queue_get_used_addr(VirtIODevice *vdev, int n); + hwaddr virtio_queue_get_desc_size(VirtIODevice *vdev, int n); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-virtio-Return-true-from-virtio_queue_empty-if-broken.patch b/SOURCES/kvm-virtio-Return-true-from-virtio_queue_empty-if-broken.patch new file mode 100644 index 0000000..b76f1aa --- /dev/null +++ b/SOURCES/kvm-virtio-Return-true-from-virtio_queue_empty-if-broken.patch @@ -0,0 +1,108 @@ +From 6b1bea7682e1f321225e29cc6ee934a32f7b09d5 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 25 Jan 2019 22:50:07 +0100 +Subject: [PATCH 07/23] virtio: Return true from virtio_queue_empty if broken +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: John Snow +Message-id: <20190125225007.8197-8-jsnow@redhat.com> +Patchwork-id: 84122 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 7/7] virtio: Return true from virtio_queue_empty if broken +Bugzilla: 1597482 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Peter Xu +RH-Acked-by: Stefano Garzarella + +From: Fam Zheng + +Both virtio-blk and virtio-scsi use virtio_queue_empty() as the +loop condition in VQ handlers (virtio_blk_handle_vq, +virtio_scsi_handle_cmd_vq). When a device is marked broken in +virtqueue_pop, for example if a vIOMMU address translation failed, we +want to break out of the loop. + +This fixes a hanging problem when booting a CentOS 3.10.0-862.el7.x86_64 +kernel with ATS enabled: + + $ qemu-system-x86_64 \ + ... \ + -device intel-iommu,intremap=on,caching-mode=on,eim=on,device-iotlb=on \ + -device virtio-scsi-pci,iommu_platform=on,ats=on,id=scsi0,bus=pci.4,addr=0x0 + +The dead loop happens immediately when the kernel boots and initializes +the device, where virtio_scsi_data_plane_handle_cmd will not return: + + > ... + > #13 0x00005586602b7793 in virtio_scsi_handle_cmd_vq + > #14 0x00005586602b8d66 in virtio_scsi_data_plane_handle_cmd + > #15 0x00005586602ddab7 in virtio_queue_notify_aio_vq + > #16 0x00005586602dfc9f in virtio_queue_host_notifier_aio_poll + > #17 0x00005586607885da in run_poll_handlers_once + > #18 0x000055866078880e in try_poll_mode + > #19 0x00005586607888eb in aio_poll + > #20 0x0000558660784561 in aio_wait_bh_oneshot + > #21 0x00005586602b9582 in virtio_scsi_dataplane_stop + > #22 0x00005586605a7110 in virtio_bus_stop_ioeventfd + > #23 0x00005586605a9426 in virtio_pci_stop_ioeventfd + > #24 0x00005586605ab808 in virtio_pci_common_write + > #25 0x0000558660242396 in memory_region_write_accessor + > #26 0x00005586602425ab in access_with_adjusted_size + > #27 0x0000558660245281 in memory_region_dispatch_write + > #28 0x00005586601e008e in flatview_write_continue + > #29 0x00005586601e01d8 in flatview_write + > #30 0x00005586601e04de in address_space_write + > #31 0x00005586601e052f in address_space_rw + > #32 0x00005586602607f2 in kvm_cpu_exec + > #33 0x0000558660227148 in qemu_kvm_cpu_thread_fn + > #34 0x000055866078bde7 in qemu_thread_start + > #35 0x00007f5784906594 in start_thread + > #36 0x00007f5784639e6f in clone + +With this patch, virtio_queue_empty will now return 1 as soon as the +vdev is marked as broken, after a "virtio: zero sized buffers are not +allowed" error. + +To be consistent, update virtio_queue_empty_rcu as well. + +Signed-off-by: Fam Zheng +Message-Id: <20180910145616.8598-2-famz@redhat.com> +Signed-off-by: Paolo Bonzini +(cherry picked from commit 2d1df8591022737b8ef19d681ff74eda389f5198) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + hw/virtio/virtio.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c +index 77eadaa..1c936ad 100644 +--- a/hw/virtio/virtio.c ++++ b/hw/virtio/virtio.c +@@ -345,6 +345,10 @@ int virtio_queue_ready(VirtQueue *vq) + * Called within rcu_read_lock(). */ + static int virtio_queue_empty_rcu(VirtQueue *vq) + { ++ if (unlikely(vq->vdev->broken)) { ++ return 1; ++ } ++ + if (unlikely(!vq->vring.avail)) { + return 1; + } +@@ -360,6 +364,10 @@ int virtio_queue_empty(VirtQueue *vq) + { + bool empty; + ++ if (unlikely(vq->vdev->broken)) { ++ return 1; ++ } ++ + if (unlikely(!vq->vring.avail)) { + return 1; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-virtio-blk-Increase-in_flight-for-request-restart-BH.patch b/SOURCES/kvm-virtio-blk-Increase-in_flight-for-request-restart-BH.patch new file mode 100644 index 0000000..c0bb952 --- /dev/null +++ b/SOURCES/kvm-virtio-blk-Increase-in_flight-for-request-restart-BH.patch @@ -0,0 +1,62 @@ +From 399ed639cc85835eb7a4a7241df6c1741c0578ba Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 1 Mar 2019 14:27:44 +0100 +Subject: [PATCH 2/9] virtio-blk: Increase in_flight for request restart BH + +RH-Author: Kevin Wolf +Message-id: <20190301142747.12251-3-kwolf@redhat.com> +Patchwork-id: 84762 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 2/5] virtio-blk: Increase in_flight for request restart BH +Bugzilla: 1671173 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Paolo Bonzini +RH-Acked-by: John Snow + +virtio_blk_dma_restart_bh() submits new requests, so in order to make +sure that these requests are not started inside a drained section of the +attached BlockBackend, we need to make sure that draining the +BlockBackend waits for the BH to be executed. + +This BH is still questionable because its scheduled in the main thread +instead of the configured iothread. Leave a FIXME comment for this. + +But with this fix, enabling the data plane at least waits for these +requests (in bdrv_set_aio_context()) instead of changing the AioContext +under their feet and making them run in the wrong thread, causing +crashes and failures (e.g. due to missing locking). + +Signed-off-by: Kevin Wolf +(cherry picked from commit 680f200217748e0920b79ec1d524717c2f50935b) +Signed-off-by: Kevin Wolf +Signed-off-by: Miroslav Rezanina +--- + hw/block/virtio-blk.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c +index b1532e4..691ade4 100644 +--- a/hw/block/virtio-blk.c ++++ b/hw/block/virtio-blk.c +@@ -678,6 +678,7 @@ static void virtio_blk_dma_restart_bh(void *opaque) + if (mrb.num_reqs) { + virtio_blk_submit_multireq(s->blk, &mrb); + } ++ blk_dec_in_flight(s->conf.conf.blk); + aio_context_release(blk_get_aio_context(s->conf.conf.blk)); + } + +@@ -691,8 +692,11 @@ static void virtio_blk_dma_restart_cb(void *opaque, int running, + } + + if (!s->bh) { ++ /* FIXME The data plane is not started yet, so these requests are ++ * processed in the main thread. */ + s->bh = aio_bh_new(blk_get_aio_context(s->conf.conf.blk), + virtio_blk_dma_restart_bh, s); ++ blk_inc_in_flight(s->conf.conf.blk); + qemu_bh_schedule(s->bh); + } + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-virtio-gpu-disable-scanout-when-backing-resource-is-.patch b/SOURCES/kvm-virtio-gpu-disable-scanout-when-backing-resource-is-.patch new file mode 100644 index 0000000..660d7ac --- /dev/null +++ b/SOURCES/kvm-virtio-gpu-disable-scanout-when-backing-resource-is-.patch @@ -0,0 +1,51 @@ +From 3354ddad8488034064ae85e8414a77514a90b259 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Wed, 4 Jul 2018 09:54:09 +0200 +Subject: [PATCH 15/89] virtio-gpu: disable scanout when backing resource is + destroyed +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Gerd Hoffmann +Message-id: <20180704095409.14514-4-kraxel@redhat.com> +Patchwork-id: 81225 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 3/3] virtio-gpu: disable scanout when backing resource is destroyed +Bugzilla: 1589634 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Marc-André Lureau +RH-Acked-by: Laszlo Ersek + +Signed-off-by: Gerd Hoffmann +Reviewed-by: Marc-André Lureau +Message-id: 20180702162443.16796-4-kraxel@redhat.com +(cherry picked from commit 1fccd7c5a9a722a9cbf1bc91693f4618034f01ac) +Signed-off-by: Miroslav Rezanina +--- + hw/display/virtio-gpu.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c +index 336dc59..08cd567 100644 +--- a/hw/display/virtio-gpu.c ++++ b/hw/display/virtio-gpu.c +@@ -430,6 +430,16 @@ static void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id) + static void virtio_gpu_resource_destroy(VirtIOGPU *g, + struct virtio_gpu_simple_resource *res) + { ++ int i; ++ ++ if (res->scanout_bitmask) { ++ for (i = 0; i < g->conf.max_outputs; i++) { ++ if (res->scanout_bitmask & (1 << i)) { ++ virtio_gpu_disable_scanout(g, i); ++ } ++ } ++ } ++ + pixman_image_unref(res->image); + virtio_gpu_cleanup_mapping(res); + QTAILQ_REMOVE(&g->reslist, res, next); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-virtio-gpu-tweak-scanout-disable.patch b/SOURCES/kvm-virtio-gpu-tweak-scanout-disable.patch new file mode 100644 index 0000000..0f593a0 --- /dev/null +++ b/SOURCES/kvm-virtio-gpu-tweak-scanout-disable.patch @@ -0,0 +1,98 @@ +From 403646c800359dcc52f3bbbf348dda917667d9d1 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Wed, 4 Jul 2018 09:54:07 +0200 +Subject: [PATCH 13/89] virtio-gpu: tweak scanout disable. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Gerd Hoffmann +Message-id: <20180704095409.14514-2-kraxel@redhat.com> +Patchwork-id: 81226 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/3] virtio-gpu: tweak scanout disable. +Bugzilla: 1589634 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Marc-André Lureau +RH-Acked-by: Laszlo Ersek + +- Factor out the code to virtio_gpu_disable_scanout(). +- Allow disable scanout 0, show a message then. +- Clear scanout->resource_id. + +Signed-off-by: Gerd Hoffmann +Reviewed-by: Marc-André Lureau +Message-id: 20180702162443.16796-2-kraxel@redhat.com +(cherry picked from commit da566a18a7799e5a655cd9f87e222b672cc93e7b) +Signed-off-by: Miroslav Rezanina +--- + hw/display/virtio-gpu.c | 47 +++++++++++++++++++++++++++++------------------ + 1 file changed, 29 insertions(+), 18 deletions(-) + +diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c +index 2dd3c34..054ec73 100644 +--- a/hw/display/virtio-gpu.c ++++ b/hw/display/virtio-gpu.c +@@ -399,6 +399,34 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, + g->hostmem += res->hostmem; + } + ++static void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id) ++{ ++ struct virtio_gpu_scanout *scanout = &g->scanout[scanout_id]; ++ struct virtio_gpu_simple_resource *res; ++ DisplaySurface *ds = NULL; ++ ++ if (scanout->resource_id == 0) { ++ return; ++ } ++ ++ res = virtio_gpu_find_resource(g, scanout->resource_id); ++ if (res) { ++ res->scanout_bitmask &= ~(1 << scanout_id); ++ } ++ ++ if (scanout_id == 0) { ++ /* primary head */ ++ ds = qemu_create_message_surface(scanout->width ?: 640, ++ scanout->height ?: 480, ++ "Guest disabled display."); ++ } ++ dpy_gfx_replace_surface(scanout->con, ds); ++ scanout->resource_id = 0; ++ scanout->ds = NULL; ++ scanout->width = 0; ++ scanout->height = 0; ++} ++ + static void virtio_gpu_resource_destroy(VirtIOGPU *g, + struct virtio_gpu_simple_resource *res) + { +@@ -583,24 +611,7 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, + + g->enable = 1; + if (ss.resource_id == 0) { +- scanout = &g->scanout[ss.scanout_id]; +- if (scanout->resource_id) { +- res = virtio_gpu_find_resource(g, scanout->resource_id); +- if (res) { +- res->scanout_bitmask &= ~(1 << ss.scanout_id); +- } +- } +- if (ss.scanout_id == 0) { +- qemu_log_mask(LOG_GUEST_ERROR, +- "%s: illegal scanout id specified %d", +- __func__, ss.scanout_id); +- cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID; +- return; +- } +- dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL); +- scanout->ds = NULL; +- scanout->width = 0; +- scanout->height = 0; ++ virtio_gpu_disable_scanout(g, ss.scanout_id); + return; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-virtio-gpu-update-old-resource-too.patch b/SOURCES/kvm-virtio-gpu-update-old-resource-too.patch new file mode 100644 index 0000000..73ef420 --- /dev/null +++ b/SOURCES/kvm-virtio-gpu-update-old-resource-too.patch @@ -0,0 +1,58 @@ +From 10ece778cf0de01b5fb9c853fc27ea3bb7d40a5e Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Wed, 4 Jul 2018 09:54:08 +0200 +Subject: [PATCH 14/89] virtio-gpu: update old resource too. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Gerd Hoffmann +Message-id: <20180704095409.14514-3-kraxel@redhat.com> +Patchwork-id: 81227 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 2/3] virtio-gpu: update old resource too. +Bugzilla: 1589634 +RH-Acked-by: Laurent Vivier +RH-Acked-by: Marc-André Lureau +RH-Acked-by: Laszlo Ersek + +When switching scanout from one resource to another we must update the +scanout_bitmask field for both new (set bit) and old (clear bit) +resource. + +Signed-off-by: Gerd Hoffmann +Reviewed-by: Marc-André Lureau +Message-id: 20180702162443.16796-3-kraxel@redhat.com +(cherry picked from commit c806cfa036a7ec991170eb9899f3a676bfcc3253) +Signed-off-by: Miroslav Rezanina +--- + hw/display/virtio-gpu.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c +index 054ec73..336dc59 100644 +--- a/hw/display/virtio-gpu.c ++++ b/hw/display/virtio-gpu.c +@@ -590,7 +590,7 @@ static void virtio_unref_resource(pixman_image_t *image, void *data) + static void virtio_gpu_set_scanout(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) + { +- struct virtio_gpu_simple_resource *res; ++ struct virtio_gpu_simple_resource *res, *ores; + struct virtio_gpu_scanout *scanout; + pixman_format_code_t format; + uint32_t offset; +@@ -664,6 +664,11 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, + dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, scanout->ds); + } + ++ ores = virtio_gpu_find_resource(g, scanout->resource_id); ++ if (ores) { ++ ores->scanout_bitmask &= ~(1 << ss.scanout_id); ++ } ++ + res->scanout_bitmask |= (1 << ss.scanout_id); + scanout->resource_id = ss.resource_id; + scanout->x = ss.r.x; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-virtio-rng-process-pending-requests-on-DRIVER_OK.patch b/SOURCES/kvm-virtio-rng-process-pending-requests-on-DRIVER_OK.patch new file mode 100644 index 0000000..9570f1c --- /dev/null +++ b/SOURCES/kvm-virtio-rng-process-pending-requests-on-DRIVER_OK.patch @@ -0,0 +1,70 @@ +From 01ad4c3dbed4a506e4c7988026336181aa5fd991 Mon Sep 17 00:00:00 2001 +From: Pankaj Gupta +Date: Fri, 13 Jul 2018 12:52:28 +0200 +Subject: [PATCH 42/89] virtio-rng: process pending requests on DRIVER_OK + +RH-Author: Pankaj Gupta +Message-id: <20180713125228.14458-1-pagupta@redhat.com> +Patchwork-id: 81347 +O-Subject: [RHEL7.6 qemu-kvm-rhev PATCH] virtio-rng: process pending requests on DRIVER_OK +Bugzilla: 1576743 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Cornelia Huck +RH-Acked-by: Miroslav Rezanina + +virtio-rng device causes old guest kernels(2.6.32) to hang on latest qemu. +The driver attempts to read from the virtio-rng device too early in it's +initialization. Qemu detects guest is not ready and returns, resulting in +hang. + +To fix handle pending requests when guest is running and driver status is +set to 'VIRTIO_CONFIG_S_DRIVER_OK'. + +CC: qemu-stable@nongnu.org +Reported-by: Sergio lopez +Signed-off-by: Stefan Hajnoczi +Signed-off-by: Pankaj Gupta +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 5d9c9ea22ab4f3b3ee497523e34b6f4d3281f62d) +Signed-off-by: Pankaj Gupta +Signed-off-by: Miroslav Rezanina +--- + hw/virtio/virtio-rng.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c +index 289bbca..855f1b4 100644 +--- a/hw/virtio/virtio-rng.c ++++ b/hw/virtio/virtio-rng.c +@@ -156,6 +156,19 @@ static void check_rate_limit(void *opaque) + vrng->activate_timer = true; + } + ++static void virtio_rng_set_status(VirtIODevice *vdev, uint8_t status) ++{ ++ VirtIORNG *vrng = VIRTIO_RNG(vdev); ++ ++ if (!vdev->vm_running) { ++ return; ++ } ++ vdev->status = status; ++ ++ /* Something changed, try to process buffers */ ++ virtio_rng_process(vrng); ++} ++ + static void virtio_rng_device_realize(DeviceState *dev, Error **errp) + { + VirtIODevice *vdev = VIRTIO_DEVICE(dev); +@@ -261,6 +274,7 @@ static void virtio_rng_class_init(ObjectClass *klass, void *data) + vdc->realize = virtio_rng_device_realize; + vdc->unrealize = virtio_rng_device_unrealize; + vdc->get_features = get_features; ++ vdc->set_status = virtio_rng_set_status; + } + + static const TypeInfo virtio_rng_info = { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-virtio-scsi-Forbid-devices-with-different-iothreads-.patch b/SOURCES/kvm-virtio-scsi-Forbid-devices-with-different-iothreads-.patch new file mode 100644 index 0000000..15d674a --- /dev/null +++ b/SOURCES/kvm-virtio-scsi-Forbid-devices-with-different-iothreads-.patch @@ -0,0 +1,117 @@ +From f482ed4845d33ce74a990ee935475dfbabf1df70 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 10 May 2019 13:28:25 +0200 +Subject: [PATCH 22/53] virtio-scsi: Forbid devices with different iothreads + sharing a blockdev + +RH-Author: Markus Armbruster +Message-id: <20190510132825.29833-4-armbru@redhat.com> +Patchwork-id: 87266 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 3/3] virtio-scsi: Forbid devices with different iothreads sharing a blockdev +Bugzilla: 1673397 1673402 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Max Reitz +RH-Acked-by: Kevin Wolf + +From: Alberto Garcia + +This patch forbids attaching a disk to a SCSI device if its using a +different AioContext. Test case included. + +Signed-off-by: Alberto Garcia +Signed-off-by: Kevin Wolf +(cherry picked from commit eb97813ff5fd5bdffc8ed9f5be5a3a50eae70a2c) +Signed-off-by: Markus Armbruster +Signed-off-by: Miroslav Rezanina +--- + hw/scsi/virtio-scsi.c | 7 +++++++ + tests/qemu-iotests/240 | 22 ++++++++++++++++++++++ + tests/qemu-iotests/240.out | 20 ++++++++++++++++++++ + 3 files changed, 49 insertions(+) + +diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c +index 85073f6..391500b 100644 +--- a/hw/scsi/virtio-scsi.c ++++ b/hw/scsi/virtio-scsi.c +@@ -800,9 +800,16 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, + return; + } + if (s->ctx && !s->dataplane_fenced) { ++ AioContext *ctx; + if (blk_op_is_blocked(sd->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { + return; + } ++ ctx = blk_get_aio_context(sd->conf.blk); ++ if (ctx != s->ctx && ctx != qemu_get_aio_context()) { ++ error_setg(errp, "Cannot attach a blockdev that is using " ++ "a different iothread"); ++ return; ++ } + virtio_scsi_acquire(s); + blk_set_aio_context(sd->conf.blk, s->ctx); + virtio_scsi_release(s); +diff --git a/tests/qemu-iotests/240 b/tests/qemu-iotests/240 +index 5d499c9..65cc3b3 100755 +--- a/tests/qemu-iotests/240 ++++ b/tests/qemu-iotests/240 +@@ -101,6 +101,28 @@ run_qemu < +Date: Fri, 17 May 2019 14:51:31 +0200 +Subject: [PATCH 20/53] virtio-scsi: Move BlockBackend back to the main + AioContext on unplug + +RH-Author: Markus Armbruster +Message-id: <20190510132825.29833-2-armbru@redhat.com> +Patchwork-id: 87267 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/3] virtio-scsi: Move BlockBackend back to the main AioContext on unplug +Bugzilla: 1673397 1673402 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Max Reitz +RH-Acked-by: Kevin Wolf + +From: Alberto Garcia + +This fixes a crash when attaching a disk to a SCSI device using +iothreads, then detaching it and reattaching it again. Test case +included. + +Signed-off-by: Alberto Garcia +Signed-off-by: Kevin Wolf +(cherry picked from commit a6f230c8d13a7ff3a0c7f1097412f44bfd9eff0b) +[Trivial conflict in tests/qemu-iotests/group resolved] +Signed-off-by: Markus Armbruster +--- + hw/scsi/virtio-scsi.c | 6 ++++ + tests/qemu-iotests/240 | 89 ++++++++++++++++++++++++++++++++++++++++++++++ + tests/qemu-iotests/240.out | 18 ++++++++++ + tests/qemu-iotests/group | 1 + + 4 files changed, 114 insertions(+) + create mode 100755 tests/qemu-iotests/240 + create mode 100644 tests/qemu-iotests/240.out + +diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c +index 52a3c1d..85073f6 100644 +--- a/hw/scsi/virtio-scsi.c ++++ b/hw/scsi/virtio-scsi.c +@@ -841,6 +841,12 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev, + virtio_scsi_release(s); + } + ++ if (s->ctx) { ++ virtio_scsi_acquire(s); ++ blk_set_aio_context(sd->conf.blk, qemu_get_aio_context()); ++ virtio_scsi_release(s); ++ } ++ + qdev_simple_device_unplug_cb(hotplug_dev, dev, errp); + } + +diff --git a/tests/qemu-iotests/240 b/tests/qemu-iotests/240 +new file mode 100755 +index 0000000..ead7ee0 +--- /dev/null ++++ b/tests/qemu-iotests/240 +@@ -0,0 +1,89 @@ ++#!/bin/bash ++# ++# Test hot plugging and unplugging with iothreads ++# ++# Copyright (C) 2019 Igalia, S.L. ++# Author: 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) 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 . ++# ++ ++# creator ++owner=berto@igalia.com ++ ++seq=`basename $0` ++echo "QA output created by $seq" ++ ++status=1 # failure is the default! ++ ++# get standard environment, filters and checks ++. ./common.rc ++. ./common.filter ++ ++_supported_fmt generic ++_supported_proto generic ++_supported_os Linux ++ ++do_run_qemu() ++{ ++ echo Testing: "$@" ++ $QEMU -nographic -qmp stdio -serial none "$@" ++ echo ++} ++ ++# Remove QMP events from (pretty-printed) output. Doesn't handle ++# nested dicts correctly, but we don't get any of those in this test. ++_filter_qmp_events() ++{ ++ tr '\n' '\t' | sed -e \ ++ 's/{\s*"timestamp":\s*{[^}]*},\s*"event":[^,}]*\(,\s*"data":\s*{[^}]*}\)\?\s*}\s*//g' \ ++ | tr '\t' '\n' ++} ++ ++run_qemu() ++{ ++ do_run_qemu "$@" 2>&1 | _filter_qmp | _filter_qmp_events ++} ++ ++case "$QEMU_DEFAULT_MACHINE" in ++ s390-ccw-virtio) ++ virtio_scsi=virtio-scsi-ccw ++ ;; ++ *) ++ virtio_scsi=virtio-scsi-pci ++ ;; ++esac ++ ++echo ++echo === Unplug a SCSI disk and then plug it again === ++echo ++ ++run_qemu < +Date: Tue, 24 Jul 2018 15:13:08 +0200 +Subject: [PATCH 09/15] virtio-scsi: fix hotplug ->reset() vs event race + +RH-Author: Stefan Hajnoczi +Message-id: <20180724151308.20500-3-stefanha@redhat.com> +Patchwork-id: 81486 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 2/2] virtio-scsi: fix hotplug ->reset() vs event race +Bugzilla: 1607891 +RH-Acked-by: Igor Mammedov +RH-Acked-by: Pankaj Gupta +RH-Acked-by: Cornelia Huck + +There is a race condition during hotplug when iothread is used. It +occurs because virtio-scsi may be processing command queues in the +iothread while the monitor performs SCSI device hotplug. + +When a SCSI device is hotplugged the HotplugHandler->plug() callback is +invoked and virtio-scsi emits a rescan event to the guest. + +If the guest submits a SCSI command at this point then it may be +cancelled before hotplug completes. This happens because ->reset() is +called by hw/core/qdev.c:device_set_realized() after +HotplugHandler->plug() has been called and +hw/scsi/scsi-disk.c:scsi_disk_reset() purges all requests. + +This patch uses the new HotplugHandler->post_plug() callback to emit the +rescan event after ->reset(). This eliminates the race conditions where +requests could be cancelled. + +Reported-by: l00284672 +Cc: Paolo Bonzini +Cc: Fam Zheng +Signed-off-by: Stefan Hajnoczi +Message-Id: <20180716083732.3347-3-stefanha@redhat.com> +Signed-off-by: Paolo Bonzini +(cherry picked from commit 8449bcf94986156a1476d6647c75ec1ce3db64d0) +Signed-off-by: Stefan Hajnoczi +Signed-off-by: Miroslav Rezanina +--- + hw/scsi/virtio-scsi.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c +index 9f754c4..52a3c1d 100644 +--- a/hw/scsi/virtio-scsi.c ++++ b/hw/scsi/virtio-scsi.c +@@ -806,8 +806,16 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, + virtio_scsi_acquire(s); + blk_set_aio_context(sd->conf.blk, s->ctx); + virtio_scsi_release(s); +- + } ++} ++ ++/* Announce the new device after it has been plugged */ ++static void virtio_scsi_post_hotplug(HotplugHandler *hotplug_dev, ++ DeviceState *dev) ++{ ++ VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev); ++ VirtIOSCSI *s = VIRTIO_SCSI(vdev); ++ SCSIDevice *sd = SCSI_DEVICE(dev); + + if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { + virtio_scsi_acquire(s); +@@ -977,6 +985,7 @@ static void virtio_scsi_class_init(ObjectClass *klass, void *data) + vdc->start_ioeventfd = virtio_scsi_dataplane_start; + vdc->stop_ioeventfd = virtio_scsi_dataplane_stop; + hc->plug = virtio_scsi_hotplug; ++ hc->post_plug = virtio_scsi_post_hotplug; + hc->unplug = virtio_scsi_hotunplug; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-virtio-scsi-restart-DMA-after-iothread.patch b/SOURCES/kvm-virtio-scsi-restart-DMA-after-iothread.patch new file mode 100644 index 0000000..4e36ced --- /dev/null +++ b/SOURCES/kvm-virtio-scsi-restart-DMA-after-iothread.patch @@ -0,0 +1,71 @@ +From 64aa185d19f4e4afacd2501831049d6e615b5a84 Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +Date: Tue, 16 Jul 2019 13:22:15 +0200 +Subject: [PATCH 22/23] virtio-scsi: restart DMA after iothread + +RH-Author: Stefan Hajnoczi +Message-id: <20190716132215.18503-4-stefanha@redhat.com> +Patchwork-id: 89535 +O-Subject: [RHEL-7.8 RHEL-7.7.z qemu-kvm-rhev PATCH 3/3] virtio-scsi: restart DMA after iothread +Bugzilla: 1673546 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf + +When the 'cont' command resumes guest execution the vm change state +handlers are invoked. Unfortunately there is no explicit ordering +between classic qemu_add_vm_change_state_handler() callbacks. When two +layers of code both use vm change state handlers, we don't control which +handler runs first. + +virtio-scsi with iothreads hits a deadlock when a failed SCSI command is +restarted and completes before the iothread is re-initialized. + +This patch uses the new qdev_add_vm_change_state_handler() API to +guarantee that virtio-scsi's virtio change state handler executes before +the SCSI bus children. This way DMA is restarted after the iothread has +re-initialized. + +Signed-off-by: Stefan Hajnoczi +Signed-off-by: Kevin Wolf +(cherry picked from commit 1a8c091c4ea5db3126514e3f7df678c9ee328802) +Signed-off-by: Stefan Hajnoczi +Signed-off-by: Miroslav Rezanina +--- + hw/scsi/scsi-bus.c | 4 ++-- + hw/virtio/virtio.c | 4 ++-- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c +index 5905f6b..ee4c449 100644 +--- a/hw/scsi/scsi-bus.c ++++ b/hw/scsi/scsi-bus.c +@@ -206,8 +206,8 @@ static void scsi_qdev_realize(DeviceState *qdev, Error **errp) + error_propagate(errp, local_err); + return; + } +- dev->vmsentry = qemu_add_vm_change_state_handler(scsi_dma_restart_cb, +- dev); ++ dev->vmsentry = qdev_add_vm_change_state_handler(DEVICE(dev), ++ scsi_dma_restart_cb, dev); + } + + static void scsi_qdev_unrealize(DeviceState *qdev, Error **errp) +diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c +index 3492b20..08a4332 100644 +--- a/hw/virtio/virtio.c ++++ b/hw/virtio/virtio.c +@@ -2306,8 +2306,8 @@ void virtio_init(VirtIODevice *vdev, const char *name, + } else { + vdev->config = NULL; + } +- vdev->vmstate = qemu_add_vm_change_state_handler(virtio_vmstate_change, +- vdev); ++ vdev->vmstate = qdev_add_vm_change_state_handler(DEVICE(vdev), ++ virtio_vmstate_change, vdev); + vdev->device_endian = virtio_default_endian(); + vdev->use_guest_notifier_mask = true; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-virtio-support-setting-memory-region-based-host-noti.patch b/SOURCES/kvm-virtio-support-setting-memory-region-based-host-noti.patch new file mode 100644 index 0000000..0a8b79c --- /dev/null +++ b/SOURCES/kvm-virtio-support-setting-memory-region-based-host-noti.patch @@ -0,0 +1,129 @@ +From c8498903f19357ce72eeb976b3054c56896a6c44 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Thu, 21 Jun 2018 18:54:37 +0200 +Subject: [PATCH 28/57] virtio: support setting memory region based host + notifier + +RH-Author: plai@redhat.com +Message-id: <1529607285-9942-3-git-send-email-plai@redhat.com> +Patchwork-id: 80937 +O-Subject: [RHEL7.6 PATCH BZ 1526645 02/10] virtio: support setting memory region based host notifier +Bugzilla: 1526645 +RH-Acked-by: Michael S. Tsirkin +RH-Acked-by: Maxime Coquelin +RH-Acked-by: Laurent Vivier + +From: Tiwei Bie + +This patch introduces the support for setting memory region +based host notifiers for virtio device. This is helpful when +using a hardware accelerator for a virtio device, because +hardware heavily depends on the notification, this will allow +the guest driver in the VM to notify the hardware directly. + +Signed-off-by: Tiwei Bie +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 6f80e6170ede13605817e5c0ca73db0de7bdf261) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + hw/virtio/virtio-pci.c | 22 ++++++++++++++++++++++ + hw/virtio/virtio.c | 13 +++++++++++++ + include/hw/virtio/virtio-bus.h | 2 ++ + include/hw/virtio/virtio.h | 2 ++ + 4 files changed, 39 insertions(+) + +diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c +index 18f7c2c..b887fc4 100644 +--- a/hw/virtio/virtio-pci.c ++++ b/hw/virtio/virtio-pci.c +@@ -1037,6 +1037,27 @@ assign_error: + return r; + } + ++static int virtio_pci_set_host_notifier_mr(DeviceState *d, int n, ++ MemoryRegion *mr, bool assign) ++{ ++ VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); ++ int offset; ++ ++ if (n >= VIRTIO_QUEUE_MAX || !virtio_pci_modern(proxy) || ++ virtio_pci_queue_mem_mult(proxy) != memory_region_size(mr)) { ++ return -1; ++ } ++ ++ if (assign) { ++ offset = virtio_pci_queue_mem_mult(proxy) * n; ++ memory_region_add_subregion_overlap(&proxy->notify.mr, offset, mr, 1); ++ } else { ++ memory_region_del_subregion(&proxy->notify.mr, mr); ++ } ++ ++ return 0; ++} ++ + static void virtio_pci_vmstate_change(DeviceState *d, bool running) + { + VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); +@@ -2652,6 +2673,7 @@ static void virtio_pci_bus_class_init(ObjectClass *klass, void *data) + k->has_extra_state = virtio_pci_has_extra_state; + k->query_guest_notifiers = virtio_pci_query_guest_notifiers; + k->set_guest_notifiers = virtio_pci_set_guest_notifiers; ++ k->set_host_notifier_mr = virtio_pci_set_host_notifier_mr; + k->vmstate_change = virtio_pci_vmstate_change; + k->pre_plugged = virtio_pci_pre_plugged; + k->device_plugged = virtio_pci_device_plugged; +diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c +index 4bcb4f4..5549bb4 100644 +--- a/hw/virtio/virtio.c ++++ b/hw/virtio/virtio.c +@@ -2472,6 +2472,19 @@ EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq) + return &vq->host_notifier; + } + ++int virtio_queue_set_host_notifier_mr(VirtIODevice *vdev, int n, ++ MemoryRegion *mr, bool assign) ++{ ++ BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); ++ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); ++ ++ if (k->set_host_notifier_mr) { ++ return k->set_host_notifier_mr(qbus->parent, n, mr, assign); ++ } ++ ++ return -1; ++} ++ + void virtio_device_set_child_bus_name(VirtIODevice *vdev, char *bus_name) + { + g_free(vdev->bus_name); +diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h +index ced3d2d..7fec9dc 100644 +--- a/include/hw/virtio/virtio-bus.h ++++ b/include/hw/virtio/virtio-bus.h +@@ -52,6 +52,8 @@ typedef struct VirtioBusClass { + bool (*has_extra_state)(DeviceState *d); + bool (*query_guest_notifiers)(DeviceState *d); + int (*set_guest_notifiers)(DeviceState *d, int nvqs, bool assign); ++ int (*set_host_notifier_mr)(DeviceState *d, int n, ++ MemoryRegion *mr, bool assign); + void (*vmstate_change)(DeviceState *d, bool running); + /* + * Expose the features the transport layer supports before +diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h +index 41e13d2..96313e8 100644 +--- a/include/hw/virtio/virtio.h ++++ b/include/hw/virtio/virtio.h +@@ -240,6 +240,8 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align); + void virtio_queue_notify(VirtIODevice *vdev, int n); + uint16_t virtio_queue_vector(VirtIODevice *vdev, int n); + void virtio_queue_set_vector(VirtIODevice *vdev, int n, uint16_t vector); ++int virtio_queue_set_host_notifier_mr(VirtIODevice *vdev, int n, ++ MemoryRegion *mr, bool assign); + int virtio_set_status(VirtIODevice *vdev, uint8_t val); + void virtio_reset(void *opaque); + void virtio_update_irq(VirtIODevice *vdev); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-virtio-update-MemoryRegionCaches-when-guest-negotiat.patch b/SOURCES/kvm-virtio-update-MemoryRegionCaches-when-guest-negotiat.patch new file mode 100644 index 0000000..7d972ee --- /dev/null +++ b/SOURCES/kvm-virtio-update-MemoryRegionCaches-when-guest-negotiat.patch @@ -0,0 +1,72 @@ +From 6d719f4240ad6bbd2ae434cad1d56c5b6c16b7f3 Mon Sep 17 00:00:00 2001 +From: John Snow +Date: Fri, 25 Jan 2019 22:50:05 +0100 +Subject: [PATCH 05/23] virtio: update MemoryRegionCaches when guest negotiates + features + +RH-Author: John Snow +Message-id: <20190125225007.8197-6-jsnow@redhat.com> +Patchwork-id: 84121 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v2 5/7] virtio: update MemoryRegionCaches when guest negotiates features +Bugzilla: 1597482 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Peter Xu +RH-Acked-by: Stefano Garzarella + +From: Paolo Bonzini + +Because the cache is sized to include the rings and the event indices, +negotiating the VIRTIO_RING_F_EVENT_IDX feature will result in the size +of the cache changing. And because MemoryRegionCache accesses are +range-checked, if we skip this we end up with an assertion failure. +This happens with OpenBSD 6.3. + +Reported-by: Fam Zheng +Fixes: 97cd965c070152bc626c7507df9fb356bbe1cd81 +Cc: qemu-stable@nongnu.org +Signed-off-by: Paolo Bonzini +Tested-by: Fam Zheng +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit db812c4073c77c8a64db8d6663b3416a587c7b4a) +Signed-off-by: John Snow +Signed-off-by: Miroslav Rezanina +--- + hw/virtio/virtio.c | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c +index 5549bb4..77eadaa 100644 +--- a/hw/virtio/virtio.c ++++ b/hw/virtio/virtio.c +@@ -2021,14 +2021,25 @@ static int virtio_set_features_nocheck(VirtIODevice *vdev, uint64_t val) + + int virtio_set_features(VirtIODevice *vdev, uint64_t val) + { +- /* ++ int ret; ++ /* + * The driver must not attempt to set features after feature negotiation + * has finished. + */ + if (vdev->status & VIRTIO_CONFIG_S_FEATURES_OK) { + return -EINVAL; + } +- return virtio_set_features_nocheck(vdev, val); ++ ret = virtio_set_features_nocheck(vdev, val); ++ if (!ret && virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { ++ /* VIRTIO_RING_F_EVENT_IDX changes the size of the caches. */ ++ int i; ++ for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { ++ if (vdev->vq[i].vring.num != 0) { ++ virtio_init_region_cache(vdev, i); ++ } ++ } ++ } ++ return ret; + } + + int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vl-Create-block-backends-before-setting-machine-prop.patch b/SOURCES/kvm-vl-Create-block-backends-before-setting-machine-prop.patch new file mode 100644 index 0000000..754a265 --- /dev/null +++ b/SOURCES/kvm-vl-Create-block-backends-before-setting-machine-prop.patch @@ -0,0 +1,71 @@ +From ae3fa9b069ad0e99fda95a34b18c68effe576f85 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:13 +0200 +Subject: [PATCH 46/53] vl: Create block backends before setting machine + properties +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-25-armbru@redhat.com> +Patchwork-id: 87985 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 24/31] vl: Create block backends before setting machine properties +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +qemu-system-FOO's main() acts on command line arguments in its own +idiosyncratic order. There's not much method to its madness. +Whenever we find a case where one kind of command line argument needs +to refer to something created for another kind later, we rejigger the +order. + +Block devices get created long after machine properties get processed. +Therefore, block device machine properties can be created, but not +set. No such properties exist. But the next commit will create some. +Time to rejigger again: create block devices earlier. + +Signed-off-by: Markus Armbruster +Reviewed-by: Philippe Mathieu-Daudé +Message-Id: <20190308131445.17502-8-armbru@redhat.com> +Reviewed-by: Michael S. Tsirkin +(cherry picked from commit cda4aa9a5a08777cf13e164c0543bd4888b8adce) +[Trivial conflict in vl.c due to lack of commit 2d5a3a8b02a] + +Signed-off-by: Miroslav Rezanina +--- + vl.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/vl.c b/vl.c +index e0e60fb..15e87a4 100644 +--- a/vl.c ++++ b/vl.c +@@ -4465,6 +4465,13 @@ int main(int argc, char **argv, char **envp) + exit(0); + } + ++ /* ++ * Note: we need to create block backends before ++ * machine_set_property(), so machine properties can refer to ++ * them. ++ */ ++ configure_blockdev(&bdo_queue, machine_class, snapshot); ++ + machine_opts = qemu_get_machine_opts(); + if (qemu_opt_foreach(machine_opts, machine_set_property, current_machine, + NULL)) { +@@ -4590,8 +4597,6 @@ int main(int argc, char **argv, char **envp) + ram_mig_init(); + dirty_bitmap_mig_init(); + +- configure_blockdev(&bdo_queue, machine_class, snapshot); +- + if (qemu_opts_foreach(qemu_find_opts("mon"), + mon_init_func, NULL, NULL)) { + exit(1); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vl-Factor-configure_blockdev-out-of-main.patch b/SOURCES/kvm-vl-Factor-configure_blockdev-out-of-main.patch new file mode 100644 index 0000000..5afaa54 --- /dev/null +++ b/SOURCES/kvm-vl-Factor-configure_blockdev-out-of-main.patch @@ -0,0 +1,122 @@ +From 8278c1cf3ba3ad55f42ab690a83de66a76ec9316 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:12 +0200 +Subject: [PATCH 45/53] vl: Factor configure_blockdev() out of main() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-24-armbru@redhat.com> +Patchwork-id: 88008 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 23/31] vl: Factor configure_blockdev() out of main() +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +Signed-off-by: Markus Armbruster +Reviewed-by: Philippe Mathieu-Daudé +Message-Id: <20190308131445.17502-7-armbru@redhat.com> +Reviewed-by: Michael S. Tsirkin +(cherry picked from commit d11bf9bf0fd66057aa5e8acb1bbc797419d5a4e6) +[Conflict in vl.c due to lack of commit c4f26c9f37c and 7c89fcbac86] + +Signed-off-by: Miroslav Rezanina +--- + vl.c | 72 +++++++++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 41 insertions(+), 31 deletions(-) + +diff --git a/vl.c b/vl.c +index a5c17a0..e0e60fb 100644 +--- a/vl.c ++++ b/vl.c +@@ -1279,6 +1279,46 @@ typedef struct BlockdevOptionsQueueEntry { + + typedef QSIMPLEQ_HEAD(, BlockdevOptionsQueueEntry) BlockdevOptionsQueue; + ++static void configure_blockdev(BlockdevOptionsQueue *bdo_queue, ++ MachineClass *machine_class, int snapshot) ++{ ++ /* ++ * If the currently selected machine wishes to override the ++ * units-per-bus property of its default HBA interface type, do so ++ * now. ++ */ ++ if (machine_class->units_per_default_bus) { ++ override_max_devs(machine_class->block_default_type, ++ machine_class->units_per_default_bus); ++ } ++ ++ /* open the virtual block devices */ ++ while (!QSIMPLEQ_EMPTY(bdo_queue)) { ++ BlockdevOptionsQueueEntry *bdo = QSIMPLEQ_FIRST(bdo_queue); ++ ++ QSIMPLEQ_REMOVE_HEAD(bdo_queue, entry); ++ loc_push_restore(&bdo->loc); ++ qmp_blockdev_add(bdo->bdo, &error_fatal); ++ loc_pop(&bdo->loc); ++ qapi_free_BlockdevOptions(bdo->bdo); ++ g_free(bdo); ++ } ++ if (snapshot || replay_mode != REPLAY_MODE_NONE) { ++ qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot, ++ NULL, NULL); ++ } ++ if (qemu_opts_foreach(qemu_find_opts("drive"), drive_init_func, ++ &machine_class->block_default_type, NULL)) { ++ exit(1); ++ } ++ ++ default_drive(default_cdrom, snapshot, machine_class->block_default_type, 2, ++ CDROM_OPTS); ++ default_drive(default_floppy, snapshot, IF_FLOPPY, 0, FD_OPTS); ++ default_drive(default_sdcard, snapshot, IF_SD, 0, SD_OPTS); ++ ++} ++ + static QemuOptsList qemu_smp_opts = { + .name = "smp-opts", + .implied_opt_name = "cpus", +@@ -4550,37 +4590,7 @@ int main(int argc, char **argv, char **envp) + ram_mig_init(); + dirty_bitmap_mig_init(); + +- /* If the currently selected machine wishes to override the units-per-bus +- * property of its default HBA interface type, do so now. */ +- if (machine_class->units_per_default_bus) { +- override_max_devs(machine_class->block_default_type, +- machine_class->units_per_default_bus); +- } +- +- /* open the virtual block devices */ +- while (!QSIMPLEQ_EMPTY(&bdo_queue)) { +- BlockdevOptionsQueueEntry *bdo = QSIMPLEQ_FIRST(&bdo_queue); +- +- QSIMPLEQ_REMOVE_HEAD(&bdo_queue, entry); +- loc_push_restore(&bdo->loc); +- qmp_blockdev_add(bdo->bdo, &error_fatal); +- loc_pop(&bdo->loc); +- qapi_free_BlockdevOptions(bdo->bdo); +- g_free(bdo); +- } +- if (snapshot || replay_mode != REPLAY_MODE_NONE) { +- qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot, +- NULL, NULL); +- } +- if (qemu_opts_foreach(qemu_find_opts("drive"), drive_init_func, +- &machine_class->block_default_type, NULL)) { +- exit(1); +- } +- +- default_drive(default_cdrom, snapshot, machine_class->block_default_type, 2, +- CDROM_OPTS); +- default_drive(default_floppy, snapshot, IF_FLOPPY, 0, FD_OPTS); +- default_drive(default_sdcard, snapshot, IF_SD, 0, SD_OPTS); ++ configure_blockdev(&bdo_queue, machine_class, snapshot); + + if (qemu_opts_foreach(qemu_find_opts("mon"), + mon_init_func, NULL, NULL)) { +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vl-Fix-drive-blockdev-persistent-reservation-managem.patch b/SOURCES/kvm-vl-Fix-drive-blockdev-persistent-reservation-managem.patch new file mode 100644 index 0000000..16be6d0 --- /dev/null +++ b/SOURCES/kvm-vl-Fix-drive-blockdev-persistent-reservation-managem.patch @@ -0,0 +1,68 @@ +From 4e62b63ba343036417b227a36a28f9cbf94bdf0c Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Thu, 6 Jun 2019 12:12:44 +0200 +Subject: [PATCH 9/9] vl: Fix -drive / -blockdev persistent reservation + management + +RH-Author: Markus Armbruster +Message-id: <20190606121244.23738-2-armbru@redhat.com> +Patchwork-id: 88604 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/1] vl: Fix -drive / -blockdev persistent reservation management +Bugzilla: 1714160 +RH-Acked-by: John Snow +RH-Acked-by: Stefano Garzarella +RH-Acked-by: Paolo Bonzini + +qemu-system-FOO's main() acts on command line arguments in its own +idiosyncratic order. There's not much method to its madness. +Whenever we find a case where one kind of command line argument needs +to refer to something created for another kind later, we rejigger the +order. + +Recent commit cda4aa9a5a "vl: Create block backends before setting +machine properties" was such a rejigger. Block backends are now +created before "delayed" objects. This broke persistent reservation +management. Reproducer: + + $ qemu-system-x86_64 -object pr-manager-helper,id=pr-helper0,path=/tmp/pr-helper0.sock-drive -drive file=/dev/mapper/crypt,file.pr-manager=pr-helper0,format=raw,if=none,id=drive-scsi0-0-0-2 + qemu-system-x86_64: -drive file=/dev/mapper/crypt,file.pr-manager=pr-helper0,format=raw,if=none,id=drive-scsi0-0-0-2: No persistent reservation manager with id 'pr-helper0' + +The delayed pr-manager-helper object is created too late for use by +-drive or -blockdev. Normal objects are still created in time. + +pr-manager-helper has always been a delayed object (commit 7c9e527659 +"scsi, file-posix: add support for persistent reservation +management"). Turns out there's no real reason for that. Make it a +normal object. + +Fixes: cda4aa9a5a08777cf13e164c0543bd4888b8adce +Signed-off-by: Markus Armbruster +Message-Id: <20190604151251.9903-2-armbru@redhat.com> +Reviewed-by: Michal Privoznik +Cc: qemu-stable@nongnu.org +Signed-off-by: Paolo Bonzini +(cherry picked from commit 9ea18ed25a36527167e9676f25d983df5e7f76e6) +[Straightforward conflict resolved] + +Signed-off-by: Miroslav Rezanina +--- + vl.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/vl.c b/vl.c +index 61247eb..5b337e1 100644 +--- a/vl.c ++++ b/vl.c +@@ -2926,8 +2926,7 @@ static int machine_set_property(void *opaque, + */ + static bool object_create_initial(const char *type) + { +- if (g_str_equal(type, "rng-egd") || +- g_str_has_prefix(type, "pr-manager-")) { ++ if (g_str_equal(type, "rng-egd")) { + return false; + } + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vl-Fix-latent-bug-with-global-and-onboard-devices.patch b/SOURCES/kvm-vl-Fix-latent-bug-with-global-and-onboard-devices.patch new file mode 100644 index 0000000..5389dc8 --- /dev/null +++ b/SOURCES/kvm-vl-Fix-latent-bug-with-global-and-onboard-devices.patch @@ -0,0 +1,85 @@ +From 55d59dd99ea53889b67956467fb8b25fb57df019 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:09 +0200 +Subject: [PATCH 42/53] vl: Fix latent bug with -global and onboard devices +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-21-armbru@redhat.com> +Patchwork-id: 87991 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 20/31] vl: Fix latent bug with -global and onboard devices +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +main() registers the user's -global only after we create the machine +object, i.e. too late for devices created in the machine's +.instance_init(). + +Fortunately, we know the bug is only latent: the commit before +previous fixed a bug that would've crashed any attempt to create a +device in an .instance_init(). + +Signed-off-by: Markus Armbruster +Reviewed-by: Marc-André Lureau +Message-Id: <20190308131445.17502-4-armbru@redhat.com> +Reviewed-by: Michael S. Tsirkin +(cherry picked from commit fc4a473482d595da08ae20ce239f0b62fa55d0f2) +[The commit message's "we know the bug is only latent" part doesn't +apply downstream] + +Signed-off-by: Miroslav Rezanina +--- + vl.c | 19 ++----------------- + 1 file changed, 2 insertions(+), 17 deletions(-) + +diff --git a/vl.c b/vl.c +index 72c489f..d46dff6 100644 +--- a/vl.c ++++ b/vl.c +@@ -3052,17 +3052,6 @@ static void user_register_global_props(void) + global_init_func, NULL, NULL); + } + +-/* +- * Note: we should see that these properties are actually having a +- * priority: accel < machine < user. This means e.g. when user +- * specifies something in "-global", it'll always be used with highest +- * priority than either machine/accelerator compat properties. +- */ +-static void register_global_properties(MachineState *ms) +-{ +- user_register_global_props(); +-} +- + int main(int argc, char **argv, char **envp) + { + int i; +@@ -4119,6 +4108,8 @@ int main(int argc, char **argv, char **envp) + */ + loc_set_none(); + ++ user_register_global_props(); ++ + replay_configure(icount_opts); + + /* Maximum number of CPUs limited for Red Hat Enterprise Linux */ +@@ -4438,12 +4429,6 @@ int main(int argc, char **argv, char **envp) + configure_accelerator(current_machine); + + /* +- * Register all the global properties, including accel properties, +- * machine properties, and user-specified ones. +- */ +- register_global_properties(current_machine); +- +- /* + * Migration object can only be created after global properties + * are applied correctly. + */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vl-Improve-legibility-of-BlockdevOptions-queue.patch b/SOURCES/kvm-vl-Improve-legibility-of-BlockdevOptions-queue.patch new file mode 100644 index 0000000..8fe0616 --- /dev/null +++ b/SOURCES/kvm-vl-Improve-legibility-of-BlockdevOptions-queue.patch @@ -0,0 +1,96 @@ +From edb07556033d42de5dc065d03aef2ea1ae663d8d Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:11 +0200 +Subject: [PATCH 44/53] vl: Improve legibility of BlockdevOptions queue +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-23-armbru@redhat.com> +Patchwork-id: 87992 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 22/31] vl: Improve legibility of BlockdevOptions queue +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +Give the queue head type a name: BlockdevOptionsQueue. + +Rename the queue entry type from BlockdevOptions_queue to +BlockdevOptionsQueueEntry. + +Signed-off-by: Markus Armbruster +Message-Id: <20190308131445.17502-6-armbru@redhat.com> +Reviewed-by: Michael S. Tsirkin +Reviewed-by: Philippe Mathieu-Daudé +(cherry picked from commit 651af51c0882a4c60704716819878043c1b8a215) +Signed-off-by: Miroslav Rezanina +--- + vl.c | 22 ++++++++++++---------- + 1 file changed, 12 insertions(+), 10 deletions(-) + +diff --git a/vl.c b/vl.c +index d89ac3a..a5c17a0 100644 +--- a/vl.c ++++ b/vl.c +@@ -1271,6 +1271,14 @@ static void default_drive(int enable, int snapshot, BlockInterfaceType type, + + } + ++typedef struct BlockdevOptionsQueueEntry { ++ BlockdevOptions *bdo; ++ Location loc; ++ QSIMPLEQ_ENTRY(BlockdevOptionsQueueEntry) entry; ++} BlockdevOptionsQueueEntry; ++ ++typedef QSIMPLEQ_HEAD(, BlockdevOptionsQueueEntry) BlockdevOptionsQueue; ++ + static QemuOptsList qemu_smp_opts = { + .name = "smp-opts", + .implied_opt_name = "cpus", +@@ -3087,13 +3095,7 @@ int main(int argc, char **argv, char **envp) + Error *err = NULL; + bool list_data_dirs = false; + char *dir, **dirs; +- typedef struct BlockdevOptions_queue { +- BlockdevOptions *bdo; +- Location loc; +- QSIMPLEQ_ENTRY(BlockdevOptions_queue) entry; +- } BlockdevOptions_queue; +- QSIMPLEQ_HEAD(, BlockdevOptions_queue) bdo_queue +- = QSIMPLEQ_HEAD_INITIALIZER(bdo_queue); ++ BlockdevOptionsQueue bdo_queue = QSIMPLEQ_HEAD_INITIALIZER(bdo_queue); + + module_call_init(MODULE_INIT_TRACE); + +@@ -3224,7 +3226,7 @@ int main(int argc, char **argv, char **envp) + case QEMU_OPTION_blockdev: + { + Visitor *v; +- BlockdevOptions_queue *bdo; ++ BlockdevOptionsQueueEntry *bdo; + + v = qobject_input_visitor_new_str(optarg, "driver", &err); + if (!v) { +@@ -3232,7 +3234,7 @@ int main(int argc, char **argv, char **envp) + exit(1); + } + +- bdo = g_new(BlockdevOptions_queue, 1); ++ bdo = g_new(BlockdevOptionsQueueEntry, 1); + visit_type_BlockdevOptions(v, NULL, &bdo->bdo, + &error_fatal); + visit_free(v); +@@ -4557,7 +4559,7 @@ int main(int argc, char **argv, char **envp) + + /* open the virtual block devices */ + while (!QSIMPLEQ_EMPTY(&bdo_queue)) { +- BlockdevOptions_queue *bdo = QSIMPLEQ_FIRST(&bdo_queue); ++ BlockdevOptionsQueueEntry *bdo = QSIMPLEQ_FIRST(&bdo_queue); + + QSIMPLEQ_REMOVE_HEAD(&bdo_queue, entry); + loc_push_restore(&bdo->loc); +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vl-Prepare-fix-of-latent-bug-with-global-and-onboard.patch b/SOURCES/kvm-vl-Prepare-fix-of-latent-bug-with-global-and-onboard.patch new file mode 100644 index 0000000..f7be97b --- /dev/null +++ b/SOURCES/kvm-vl-Prepare-fix-of-latent-bug-with-global-and-onboard.patch @@ -0,0 +1,113 @@ +From fee69ca995ff34fc343fa3e0f57aab5f0e5f7351 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Fri, 17 May 2019 06:51:08 +0200 +Subject: [PATCH 41/53] vl: Prepare fix of latent bug with -global and onboard + devices +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Markus Armbruster +Message-id: <20190517065120.12028-20-armbru@redhat.com> +Patchwork-id: 87980 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 19/31] vl: Prepare fix of latent bug with -global and onboard devices +Bugzilla: 1624009 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Thomas Huth +RH-Acked-by: Miroslav Rezanina + +main() registers the user's -global only after we create the machine +object, i.e. too late for devices created in the machine's +.instance_init(). The next commit is upstream's fix. To make it +apply, we need to move machine and accelerator compat property +creation out of register_global_properties(), so its MachineState +parameter becomes unused. + +Create machine compat props right after select_machine(). Create +accelerator compat props in accel_init_machine(). In both cases, this +creates them as early as possible, and exactly where upstream creates +them since commit 1a3ec8c1564. Note that their relative order remains +unchanged; machine compat props still override accelerator compat +props. + +Why not backport commit 1a3ec8c1564 instead? That commit fixes a +latent bug in upstream's reorganization of compat properties (merge +commit 31ed41889e6). It has a bug fixed up in commit 79b9d4bde7d. It +doesn't apply without the reorganization. This commit reimplements +enough of the two commits (without fixing any bugs) to make the +remainder of the upstream series apply cleanly. + +Signed-off-by: Markus Armbruster +Signed-off-by: Miroslav Rezanina +--- + accel/accel.c | 2 ++ + hw/core/machine.c | 3 +-- + include/hw/boards.h | 2 +- + vl.c | 3 +-- + 4 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/accel/accel.c b/accel/accel.c +index 124f957..2e0bff5 100644 +--- a/accel/accel.c ++++ b/accel/accel.c +@@ -64,6 +64,8 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms) + ms->accelerator = NULL; + *(acc->allowed) = false; + object_unref(OBJECT(accel)); ++ } else { ++ accel_register_compat_props(ms->accelerator); + } + return ret; + } +diff --git a/hw/core/machine.c b/hw/core/machine.c +index b4804e9..e5b8881 100644 +--- a/hw/core/machine.c ++++ b/hw/core/machine.c +@@ -839,9 +839,8 @@ static void machine_class_finalize(ObjectClass *klass, void *data) + g_free(mc->name); + } + +-void machine_register_compat_props(MachineState *machine) ++void machine_register_compat_props(MachineClass *mc) + { +- MachineClass *mc = MACHINE_GET_CLASS(machine); + int i; + GlobalProperty *p; + +diff --git a/include/hw/boards.h b/include/hw/boards.h +index a609239..cf24cbf 100644 +--- a/include/hw/boards.h ++++ b/include/hw/boards.h +@@ -70,7 +70,7 @@ int machine_kvm_shadow_mem(MachineState *machine); + int machine_phandle_start(MachineState *machine); + bool machine_dump_guest_core(MachineState *machine); + bool machine_mem_merge(MachineState *machine); +-void machine_register_compat_props(MachineState *machine); ++void machine_register_compat_props(MachineClass *mc); + HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine); + void machine_set_cpu_numa_node(MachineState *machine, + const CpuInstanceProperties *props, +diff --git a/vl.c b/vl.c +index a22da93..72c489f 100644 +--- a/vl.c ++++ b/vl.c +@@ -3060,8 +3060,6 @@ static void user_register_global_props(void) + */ + static void register_global_properties(MachineState *ms) + { +- accel_register_compat_props(ms->accelerator); +- machine_register_compat_props(ms); + user_register_global_props(); + } + +@@ -4127,6 +4125,7 @@ int main(int argc, char **argv, char **envp) + limit_max_cpus_in_machines(); + + machine_class = select_machine(); ++ machine_register_compat_props(machine_class); + + set_memory_options(&ram_slots, &maxram_size, machine_class); + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vl-add-qemu_add_vm_change_state_handler_prio.patch b/SOURCES/kvm-vl-add-qemu_add_vm_change_state_handler_prio.patch new file mode 100644 index 0000000..df0ca05 --- /dev/null +++ b/SOURCES/kvm-vl-add-qemu_add_vm_change_state_handler_prio.patch @@ -0,0 +1,148 @@ +From f32ee7f16206334c90d2c92517617c08f436ca97 Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +Date: Tue, 16 Jul 2019 13:22:13 +0200 +Subject: [PATCH 20/23] vl: add qemu_add_vm_change_state_handler_prio() + +RH-Author: Stefan Hajnoczi +Message-id: <20190716132215.18503-2-stefanha@redhat.com> +Patchwork-id: 89536 +O-Subject: [RHEL-7.8 RHEL-7.7.z qemu-kvm-rhev PATCH 1/3] vl: add qemu_add_vm_change_state_handler_prio() +Bugzilla: 1673546 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: John Snow +RH-Acked-by: Kevin Wolf + +Add an API for registering vm change state handlers with a well-defined +ordering. This is necessary when handlers depend on each other. + +Small coding style fixes are included to make checkpatch.pl happy. + +Signed-off-by: Stefan Hajnoczi +Signed-off-by: Kevin Wolf +(cherry picked from commit 60dbc5a1c5176269669ffc26c081ab2cfb7f12f7) +Signed-off-by: Stefan Hajnoczi +Signed-off-by: Miroslav Rezanina + +Conflicts: + vl.c + The QTAILQ macros require an explicit name for the head type. +--- + include/sysemu/sysemu.h | 2 ++ + vl.c | 61 +++++++++++++++++++++++++++++++++++++++---------- + 2 files changed, 51 insertions(+), 12 deletions(-) + +diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h +index 2a6f4a5..723c7a9 100644 +--- a/include/sysemu/sysemu.h ++++ b/include/sysemu/sysemu.h +@@ -29,6 +29,8 @@ typedef void VMChangeStateHandler(void *opaque, int running, RunState state); + + VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb, + void *opaque); ++VMChangeStateEntry *qemu_add_vm_change_state_handler_prio( ++ VMChangeStateHandler *cb, void *opaque, int priority); + void qemu_del_vm_change_state_handler(VMChangeStateEntry *e); + void vm_state_notify(int running, RunState state); + +diff --git a/vl.c b/vl.c +index 5b337e1..6529d68 100644 +--- a/vl.c ++++ b/vl.c +@@ -1658,28 +1658,58 @@ static int machine_help_func(QemuOpts *opts, MachineState *machine) + struct vm_change_state_entry { + VMChangeStateHandler *cb; + void *opaque; +- QLIST_ENTRY (vm_change_state_entry) entries; ++ QTAILQ_ENTRY(vm_change_state_entry) entries; ++ int priority; + }; + +-static QLIST_HEAD(vm_change_state_head, vm_change_state_entry) vm_change_state_head; ++static QTAILQ_HEAD(VMChangeStateHead, ++ vm_change_state_entry) vm_change_state_head; + +-VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb, +- void *opaque) ++/** ++ * qemu_add_vm_change_state_handler_prio: ++ * @cb: the callback to invoke ++ * @opaque: user data passed to the callback ++ * @priority: low priorities execute first when the vm runs and the reverse is ++ * true when the vm stops ++ * ++ * Register a callback function that is invoked when the vm starts or stops ++ * running. ++ * ++ * Returns: an entry to be freed using qemu_del_vm_change_state_handler() ++ */ ++VMChangeStateEntry *qemu_add_vm_change_state_handler_prio( ++ VMChangeStateHandler *cb, void *opaque, int priority) + { + VMChangeStateEntry *e; ++ VMChangeStateEntry *other; + +- e = g_malloc0(sizeof (*e)); +- ++ e = g_malloc0(sizeof(*e)); + e->cb = cb; + e->opaque = opaque; +- QLIST_INSERT_HEAD(&vm_change_state_head, e, entries); ++ e->priority = priority; ++ ++ /* Keep list sorted in ascending priority order */ ++ QTAILQ_FOREACH(other, &vm_change_state_head, entries) { ++ if (priority < other->priority) { ++ QTAILQ_INSERT_BEFORE(other, e, entries); ++ return e; ++ } ++ } ++ ++ QTAILQ_INSERT_TAIL(&vm_change_state_head, e, entries); + return e; + } + ++VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb, ++ void *opaque) ++{ ++ return qemu_add_vm_change_state_handler_prio(cb, opaque, 0); ++} ++ + void qemu_del_vm_change_state_handler(VMChangeStateEntry *e) + { +- QLIST_REMOVE (e, entries); +- g_free (e); ++ QTAILQ_REMOVE(&vm_change_state_head, e, entries); ++ g_free(e); + } + + void vm_state_notify(int running, RunState state) +@@ -1688,8 +1718,15 @@ void vm_state_notify(int running, RunState state) + + trace_vm_state_notify(running, state); + +- QLIST_FOREACH_SAFE(e, &vm_change_state_head, entries, next) { +- e->cb(e->opaque, running, state); ++ if (running) { ++ QTAILQ_FOREACH_SAFE(e, &vm_change_state_head, entries, next) { ++ e->cb(e->opaque, running, state); ++ } ++ } else { ++ QTAILQ_FOREACH_REVERSE_SAFE(e, &vm_change_state_head, ++ VMChangeStateHead, entries, next) { ++ e->cb(e->opaque, running, state); ++ } + } + } + +@@ -3194,7 +3231,7 @@ int main(int argc, char **argv, char **envp) + } + rtc_clock = QEMU_CLOCK_HOST; + +- QLIST_INIT (&vm_change_state_head); ++ QTAILQ_INIT(&vm_change_state_head); + os_setup_early_signal_handling(); + + cpu_model = NULL; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-vnc-call-sasl_server_init-only-when-required.patch b/SOURCES/kvm-vnc-call-sasl_server_init-only-when-required.patch new file mode 100644 index 0000000..2f80000 --- /dev/null +++ b/SOURCES/kvm-vnc-call-sasl_server_init-only-when-required.patch @@ -0,0 +1,84 @@ +From cf93ce54ee801dca9d9da7dd10557b8772418520 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= +Date: Thu, 22 Nov 2018 18:34:50 +0100 +Subject: [PATCH 01/34] vnc: call sasl_server_init() only when required +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Marc-André Lureau +Message-id: <20181122183450.719-2-marcandre.lureau@redhat.com> +Patchwork-id: 83099 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/1] vnc: call sasl_server_init() only when required +Bugzilla: 1614302 +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Markus Armbruster + +VNC server is calling sasl_server_init() during startup of QEMU, even +if SASL auth has not been enabled. + +This may create undesirable warnings like "Could not find keytab file: +/etc/qemu/krb5.tab" when the user didn't configure SASL on host and +started VNC server. + +Instead, only initialize SASL when needed. Note that HMP/QMP "change +vnc" calls vnc_display_open() again, which will initialize SASL if +needed. + +Fix assignment in if condition, while touching this code. + +Related to: +https://bugzilla.redhat.com/show_bug.cgi?id=1609327 + +Signed-off-by: Marc-André Lureau +Reviewed-by: Daniel P. Berrangé +Message-id: 20180907063634.359-1-marcandre.lureau@redhat.com +Signed-off-by: Gerd Hoffmann + +(cherry picked from commit b5dc0d7d565048fcf2767060261d8385805aced1) + +[ Fix minor confict due to "qemu" vs "qemu-kvm" string difference ] +Signed-off-by: Marc-André Lureau + +Signed-off-by: Miroslav Rezanina +--- + ui/vnc.c | 15 ++++++++------- + 1 file changed, 8 insertions(+), 7 deletions(-) + +diff --git a/ui/vnc.c b/ui/vnc.c +index 0c3011b..86c6762 100644 +--- a/ui/vnc.c ++++ b/ui/vnc.c +@@ -3869,9 +3869,6 @@ void vnc_display_open(const char *id, Error **errp) + bool reverse = false; + const char *credid; + bool sasl = false; +-#ifdef CONFIG_VNC_SASL +- int saslErr; +-#endif + int acl = 0; + int lock_key_sync = 1; + int key_delay_ms; +@@ -4045,10 +4042,14 @@ void vnc_display_open(const char *id, Error **errp) + trace_vnc_auth_init(vd, 1, vd->ws_auth, vd->ws_subauth); + + #ifdef CONFIG_VNC_SASL +- if ((saslErr = sasl_server_init(NULL, "qemu-kvm")) != SASL_OK) { +- error_setg(errp, "Failed to initialize SASL auth: %s", +- sasl_errstring(saslErr, NULL, NULL)); +- goto fail; ++ if (sasl) { ++ int saslErr = sasl_server_init(NULL, "qemu-kvm"); ++ ++ if (saslErr != SASL_OK) { ++ error_setg(errp, "Failed to initialize SASL auth: %s", ++ sasl_errstring(saslErr, NULL, NULL)); ++ goto fail; ++ } + } + #endif + vd->lock_key_sync = lock_key_sync; +-- +1.8.3.1 + diff --git a/SOURCES/kvm-x86-Data-structure-changes-to-support-MSR-based-feat.patch b/SOURCES/kvm-x86-Data-structure-changes-to-support-MSR-based-feat.patch new file mode 100644 index 0000000..e903957 --- /dev/null +++ b/SOURCES/kvm-x86-Data-structure-changes-to-support-MSR-based-feat.patch @@ -0,0 +1,501 @@ +From d3ceeb5294b3dfec6fc86cc1111f12923b62e50c Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Tue, 4 Jun 2019 21:47:23 +0200 +Subject: [PATCH 04/23] x86: Data structure changes to support MSR based + features + +RH-Author: plai@redhat.com +Message-id: <1559684847-10889-5-git-send-email-plai@redhat.com> +Patchwork-id: 88535 +O-Subject: [RHEL7.7 qemu-kvm-rhev PATCH v4 4/8] x86: Data structure changes to support MSR based features +Bugzilla: 1709972 +RH-Acked-by: Eduardo Habkost +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Miroslav Rezanina + +From: Robert Hoo + +Add FeatureWordType indicator in struct FeatureWordInfo. +Change feature_word_info[] accordingly. +Change existing functions that refer to feature_word_info[] accordingly. + +Signed-off-by: Robert Hoo +Message-Id: <1539578845-37944-3-git-send-email-robert.hu@linux.intel.com> +[ehabkost: fixed hvf_enabled() case] +Signed-off-by: Eduardo Habkost +(cherry picked from commit 07585923485952bf4cb7da563c9f91fecc85d09c) +Signed-off-by: Paul Lai + +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 197 +++++++++++++++++++++++++++++++++++++++--------------- + 1 file changed, 142 insertions(+), 55 deletions(-) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 4c7364b..3a06d37 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -773,17 +773,36 @@ static void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, + /* missing: + CPUID_XSAVE_XSAVEC, CPUID_XSAVE_XSAVES */ + ++typedef enum FeatureWordType { ++ CPUID_FEATURE_WORD, ++ MSR_FEATURE_WORD, ++} FeatureWordType; ++ + typedef struct FeatureWordInfo { ++ FeatureWordType type; + /* feature flags names are taken from "Intel Processor Identification and + * the CPUID Instruction" and AMD's "CPUID Specification". + * In cases of disagreement between feature naming conventions, + * aliases may be added. + */ + const char *feat_names[32]; +- uint32_t cpuid_eax; /* Input EAX for CPUID */ +- bool cpuid_needs_ecx; /* CPUID instruction uses ECX as input */ +- uint32_t cpuid_ecx; /* Input ECX value for CPUID */ +- int cpuid_reg; /* output register (R_* constant) */ ++ union { ++ /* If type==CPUID_FEATURE_WORD */ ++ struct { ++ uint32_t eax; /* Input EAX for CPUID */ ++ bool needs_ecx; /* CPUID instruction uses ECX as input */ ++ uint32_t ecx; /* Input ECX value for CPUID */ ++ int reg; /* output register (R_* constant) */ ++ } cpuid; ++ /* If type==MSR_FEATURE_WORD */ ++ struct { ++ uint32_t index; ++ struct { /*CPUID that enumerate this MSR*/ ++ FeatureWord cpuid_class; ++ uint32_t cpuid_flag; ++ } cpuid_dep; ++ } msr; ++ }; + uint32_t tcg_features; /* Feature flags supported by TCG */ + uint32_t unmigratable_flags; /* Feature flags known to be unmigratable */ + uint32_t migratable_flags; /* Feature flags known to be migratable */ +@@ -793,6 +812,7 @@ typedef struct FeatureWordInfo { + + static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + [FEAT_1_EDX] = { ++ .type = CPUID_FEATURE_WORD, + .feat_names = { + "fpu", "vme", "de", "pse", + "tsc", "msr", "pae", "mce", +@@ -803,10 +823,11 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + "fxsr", "sse", "sse2", "ss", + "ht" /* Intel htt */, "tm", "ia64", "pbe", + }, +- .cpuid_eax = 1, .cpuid_reg = R_EDX, ++ .cpuid = {.eax = 1, .reg = R_EDX, }, + .tcg_features = TCG_FEATURES, + }, + [FEAT_1_ECX] = { ++ .type = CPUID_FEATURE_WORD, + .feat_names = { + "pni" /* Intel,AMD sse3 */, "pclmulqdq", "dtes64", "monitor", + "ds-cpl", "vmx", "smx", "est", +@@ -817,7 +838,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + "tsc-deadline", "aes", "xsave", "osxsave", + "avx", "f16c", "rdrand", "hypervisor", + }, +- .cpuid_eax = 1, .cpuid_reg = R_ECX, ++ .cpuid = { .eax = 1, .reg = R_ECX, }, + .tcg_features = TCG_EXT_FEATURES, + }, + /* Feature names that are already defined on feature_name[] but +@@ -826,6 +847,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + * to features[FEAT_8000_0001_EDX] if and only if CPU vendor is AMD. + */ + [FEAT_8000_0001_EDX] = { ++ .type = CPUID_FEATURE_WORD, + .feat_names = { + NULL /* fpu */, NULL /* vme */, NULL /* de */, NULL /* pse */, + NULL /* tsc */, NULL /* msr */, NULL /* pae */, NULL /* mce */, +@@ -836,10 +858,11 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + NULL /* fxsr */, "fxsr-opt", "pdpe1gb", "rdtscp", + NULL, "lm", "3dnowext", "3dnow", + }, +- .cpuid_eax = 0x80000001, .cpuid_reg = R_EDX, ++ .cpuid = { .eax = 0x80000001, .reg = R_EDX, }, + .tcg_features = TCG_EXT2_FEATURES, + }, + [FEAT_8000_0001_ECX] = { ++ .type = CPUID_FEATURE_WORD, + .feat_names = { + "lahf-lm", "cmp-legacy", "svm", "extapic", + "cr8legacy", "abm", "sse4a", "misalignsse", +@@ -850,7 +873,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + "perfctr-nb", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, +- .cpuid_eax = 0x80000001, .cpuid_reg = R_ECX, ++ .cpuid = { .eax = 0x80000001, .reg = R_ECX, }, + .tcg_features = TCG_EXT3_FEATURES, + /* + * TOPOEXT is always allowed but can't be enabled blindly by +@@ -860,6 +883,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + .no_autoenable_flags = CPUID_EXT3_TOPOEXT, + }, + [FEAT_C000_0001_EDX] = { ++ .type = CPUID_FEATURE_WORD, + .feat_names = { + NULL, NULL, "xstore", "xstore-en", + NULL, NULL, "xcrypt", "xcrypt-en", +@@ -870,10 +894,11 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, +- .cpuid_eax = 0xC0000001, .cpuid_reg = R_EDX, ++ .cpuid = { .eax = 0xC0000001, .reg = R_EDX, }, + .tcg_features = TCG_EXT4_FEATURES, + }, + [FEAT_KVM] = { ++ .type = CPUID_FEATURE_WORD, + .feat_names = { + "kvmclock", "kvm-nopiodelay", "kvm-mmu", "kvmclock", + "kvm-asyncpf", "kvm-steal-time", "kvm-pv-eoi", "kvm-pv-unhalt", +@@ -884,10 +909,11 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + "kvmclock-stable-bit", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, +- .cpuid_eax = KVM_CPUID_FEATURES, .cpuid_reg = R_EAX, ++ .cpuid = { .eax = KVM_CPUID_FEATURES, .reg = R_EAX, }, + .tcg_features = TCG_KVM_FEATURES, + }, + [FEAT_KVM_HINTS] = { ++ .type = CPUID_FEATURE_WORD, + .feat_names = { + "kvm-hint-dedicated", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, +@@ -898,7 +924,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, +- .cpuid_eax = KVM_CPUID_FEATURES, .cpuid_reg = R_EDX, ++ .cpuid = { .eax = KVM_CPUID_FEATURES, .reg = R_EDX, }, + .tcg_features = TCG_KVM_FEATURES, + /* + * KVM hints aren't auto-enabled by -cpu host, they need to be +@@ -907,6 +933,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + .no_autoenable_flags = ~0U, + }, + [FEAT_HYPERV_EAX] = { ++ .type = CPUID_FEATURE_WORD, + .feat_names = { + NULL /* hv_msr_vp_runtime_access */, NULL /* hv_msr_time_refcount_access */, + NULL /* hv_msr_synic_access */, NULL /* hv_msr_stimer_access */, +@@ -920,9 +947,10 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, +- .cpuid_eax = 0x40000003, .cpuid_reg = R_EAX, ++ .cpuid = { .eax = 0x40000003, .reg = R_EAX, }, + }, + [FEAT_HYPERV_EBX] = { ++ .type = CPUID_FEATURE_WORD, + .feat_names = { + NULL /* hv_create_partitions */, NULL /* hv_access_partition_id */, + NULL /* hv_access_memory_pool */, NULL /* hv_adjust_message_buffers */, +@@ -936,9 +964,10 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, +- .cpuid_eax = 0x40000003, .cpuid_reg = R_EBX, ++ .cpuid = { .eax = 0x40000003, .reg = R_EBX, }, + }, + [FEAT_HYPERV_EDX] = { ++ .type = CPUID_FEATURE_WORD, + .feat_names = { + NULL /* hv_mwait */, NULL /* hv_guest_debugging */, + NULL /* hv_perf_monitor */, NULL /* hv_cpu_dynamic_part */, +@@ -951,9 +980,10 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, +- .cpuid_eax = 0x40000003, .cpuid_reg = R_EDX, ++ .cpuid = { .eax = 0x40000003, .reg = R_EDX, }, + }, + [FEAT_SVM] = { ++ .type = CPUID_FEATURE_WORD, + .feat_names = { + "npt", "lbrv", "svm-lock", "nrip-save", + "tsc-scale", "vmcb-clean", "flushbyasid", "decodeassists", +@@ -964,10 +994,11 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, +- .cpuid_eax = 0x8000000A, .cpuid_reg = R_EDX, ++ .cpuid = { .eax = 0x8000000A, .reg = R_EDX, }, + .tcg_features = TCG_SVM_FEATURES, + }, + [FEAT_7_0_EBX] = { ++ .type = CPUID_FEATURE_WORD, + .feat_names = { + "fsgsbase", "tsc-adjust", NULL, "bmi1", + "hle", "avx2", NULL, "smep", +@@ -978,12 +1009,15 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + "clwb", "intel-pt", "avx512pf", "avx512er", + "avx512cd", "sha-ni", "avx512bw", "avx512vl", + }, +- .cpuid_eax = 7, +- .cpuid_needs_ecx = true, .cpuid_ecx = 0, +- .cpuid_reg = R_EBX, ++ .cpuid = { ++ .eax = 7, ++ .needs_ecx = true, .ecx = 0, ++ .reg = R_EBX, ++ }, + .tcg_features = TCG_7_0_EBX_FEATURES, + }, + [FEAT_7_0_ECX] = { ++ .type = CPUID_FEATURE_WORD, + .feat_names = { + NULL, "avx512vbmi", "umip", "pku", + "ospke", NULL, "avx512vbmi2", NULL, +@@ -994,12 +1028,15 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + NULL, "cldemote", NULL, NULL, + NULL, NULL, NULL, NULL, + }, +- .cpuid_eax = 7, +- .cpuid_needs_ecx = true, .cpuid_ecx = 0, +- .cpuid_reg = R_ECX, ++ .cpuid = { ++ .eax = 7, ++ .needs_ecx = true, .ecx = 0, ++ .reg = R_ECX, ++ }, + .tcg_features = TCG_7_0_ECX_FEATURES, + }, + [FEAT_7_0_EDX] = { ++ .type = CPUID_FEATURE_WORD, + .feat_names = { + NULL, NULL, "avx512-4vnniw", "avx512-4fmaps", + NULL, NULL, NULL, NULL, +@@ -1010,13 +1047,16 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + NULL, NULL, "spec-ctrl", "stibp", + NULL, "arch-capabilities", NULL, "ssbd", + }, +- .cpuid_eax = 7, +- .cpuid_needs_ecx = true, .cpuid_ecx = 0, +- .cpuid_reg = R_EDX, ++ .cpuid = { ++ .eax = 7, ++ .needs_ecx = true, .ecx = 0, ++ .reg = R_EDX, ++ }, + .tcg_features = TCG_7_0_EDX_FEATURES, + .unmigratable_flags = CPUID_7_0_EDX_ARCH_CAPABILITIES, + }, + [FEAT_8000_0007_EDX] = { ++ .type = CPUID_FEATURE_WORD, + .feat_names = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, +@@ -1027,12 +1067,12 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, +- .cpuid_eax = 0x80000007, +- .cpuid_reg = R_EDX, ++ .cpuid = { .eax = 0x80000007, .reg = R_EDX, }, + .tcg_features = TCG_APM_FEATURES, + .unmigratable_flags = CPUID_APM_INVTSC, + }, + [FEAT_8000_0008_EBX] = { ++ .type = CPUID_FEATURE_WORD, + .feat_names = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, +@@ -1043,12 +1083,12 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + NULL, "virt-ssbd", NULL, NULL, + NULL, NULL, NULL, NULL, + }, +- .cpuid_eax = 0x80000008, +- .cpuid_reg = R_EBX, ++ .cpuid = { .eax = 0x80000008, .reg = R_EBX, }, + .tcg_features = 0, + .unmigratable_flags = 0, + }, + [FEAT_XSAVE] = { ++ .type = CPUID_FEATURE_WORD, + .feat_names = { + "xsaveopt", "xsavec", "xgetbv1", "xsaves", + NULL, NULL, NULL, NULL, +@@ -1059,12 +1099,15 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, +- .cpuid_eax = 0xd, +- .cpuid_needs_ecx = true, .cpuid_ecx = 1, +- .cpuid_reg = R_EAX, ++ .cpuid = { ++ .eax = 0xd, ++ .needs_ecx = true, .ecx = 1, ++ .reg = R_EAX, ++ }, + .tcg_features = TCG_XSAVE_FEATURES, + }, + [FEAT_6_EAX] = { ++ .type = CPUID_FEATURE_WORD, + .feat_names = { + NULL, NULL, "arat", NULL, + NULL, NULL, NULL, NULL, +@@ -1075,13 +1118,16 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, +- .cpuid_eax = 6, .cpuid_reg = R_EAX, ++ .cpuid = { .eax = 6, .reg = R_EAX, }, + .tcg_features = TCG_6_EAX_FEATURES, + }, + [FEAT_XSAVE_COMP_LO] = { +- .cpuid_eax = 0xD, +- .cpuid_needs_ecx = true, .cpuid_ecx = 0, +- .cpuid_reg = R_EAX, ++ .type = CPUID_FEATURE_WORD, ++ .cpuid = { ++ .eax = 0xD, ++ .needs_ecx = true, .ecx = 0, ++ .reg = R_EAX, ++ }, + .tcg_features = ~0U, + .migratable_flags = XSTATE_FP_MASK | XSTATE_SSE_MASK | + XSTATE_YMM_MASK | XSTATE_BNDREGS_MASK | XSTATE_BNDCSR_MASK | +@@ -1089,9 +1135,12 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + XSTATE_PKRU_MASK, + }, + [FEAT_XSAVE_COMP_HI] = { +- .cpuid_eax = 0xD, +- .cpuid_needs_ecx = true, .cpuid_ecx = 0, +- .cpuid_reg = R_EDX, ++ .type = CPUID_FEATURE_WORD, ++ .cpuid = { ++ .eax = 0xD, ++ .needs_ecx = true, .ecx = 0, ++ .reg = R_EDX, ++ }, + .tcg_features = ~0U, + }, + }; +@@ -2846,21 +2895,41 @@ static const TypeInfo host_x86_cpu_type_info = { + + #endif + ++static char *feature_word_description(FeatureWordInfo *f, uint32_t bit) ++{ ++ assert(f->type == CPUID_FEATURE_WORD || f->type == MSR_FEATURE_WORD); ++ ++ switch (f->type) { ++ case CPUID_FEATURE_WORD: ++ { ++ const char *reg = get_register_name_32(f->cpuid.reg); ++ assert(reg); ++ return g_strdup_printf("CPUID.%02XH:%s", ++ f->cpuid.eax, reg); ++ } ++ case MSR_FEATURE_WORD: ++ return g_strdup_printf("MSR(%02XH)", ++ f->msr.index); ++ } ++ ++ return NULL; ++} ++ + static void report_unavailable_features(FeatureWord w, uint32_t mask) + { + FeatureWordInfo *f = &feature_word_info[w]; + int i; ++ char *feat_word_str; + + for (i = 0; i < 32; ++i) { + if ((1UL << i) & mask) { +- const char *reg = get_register_name_32(f->cpuid_reg); +- assert(reg); +- warn_report("%s doesn't support requested feature: " +- "CPUID.%02XH:%s%s%s [bit %d]", ++ feat_word_str = feature_word_description(f, i); ++ warn_report("%s doesn't support requested feature: %s%s%s [bit %d]", + accel_uses_host_cpuid() ? "host" : "TCG", +- f->cpuid_eax, reg, ++ feat_word_str, + f->feat_names[i] ? "." : "", + f->feat_names[i] ? f->feat_names[i] : "", i); ++ g_free(feat_word_str); + } + } + } +@@ -3104,11 +3173,18 @@ static void x86_cpu_get_feature_words(Object *obj, Visitor *v, + + for (w = 0; w < FEATURE_WORDS; w++) { + FeatureWordInfo *wi = &feature_word_info[w]; ++ /* ++ * We didn't have MSR features when "feature-words" was ++ * introduced. Therefore skipped other type entries. ++ */ ++ if (wi->type != CPUID_FEATURE_WORD) { ++ continue; ++ } + X86CPUFeatureWordInfo *qwi = &word_infos[w]; +- qwi->cpuid_input_eax = wi->cpuid_eax; +- qwi->has_cpuid_input_ecx = wi->cpuid_needs_ecx; +- qwi->cpuid_input_ecx = wi->cpuid_ecx; +- qwi->cpuid_register = x86_reg_info_32[wi->cpuid_reg].qapi_enum; ++ qwi->cpuid_input_eax = wi->cpuid.eax; ++ qwi->has_cpuid_input_ecx = wi->cpuid.needs_ecx; ++ qwi->cpuid_input_ecx = wi->cpuid.ecx; ++ qwi->cpuid_register = x86_reg_info_32[wi->cpuid.reg].qapi_enum; + qwi->features = array[w]; + + /* List will be in reverse order, but order shouldn't matter */ +@@ -3464,16 +3540,26 @@ static uint32_t x86_cpu_get_supported_feature_word(FeatureWord w, + bool migratable_only) + { + FeatureWordInfo *wi = &feature_word_info[w]; +- uint32_t r; ++ uint32_t r = 0; + + if (kvm_enabled()) { +- r = kvm_arch_get_supported_cpuid(kvm_state, wi->cpuid_eax, +- wi->cpuid_ecx, +- wi->cpuid_reg); ++ switch (wi->type) { ++ case CPUID_FEATURE_WORD: ++ r = kvm_arch_get_supported_cpuid(kvm_state, wi->cpuid.eax, ++ wi->cpuid.ecx, ++ wi->cpuid.reg); ++ break; ++ case MSR_FEATURE_WORD: ++ r = kvm_arch_get_supported_msr_feature(kvm_state, wi->msr.index); ++ break; ++ } + } else if (hvf_enabled()) { +- r = hvf_get_supported_cpuid(wi->cpuid_eax, +- wi->cpuid_ecx, +- wi->cpuid_reg); ++ if (wi->type != CPUID_FEATURE_WORD) { ++ return 0; ++ } ++ r = hvf_get_supported_cpuid(wi->cpuid.eax, ++ wi->cpuid.ecx, ++ wi->cpuid.reg); + } else if (tcg_enabled()) { + r = wi->tcg_features; + } else { +@@ -4534,9 +4620,10 @@ static void x86_cpu_adjust_feat_level(X86CPU *cpu, FeatureWord w) + { + CPUX86State *env = &cpu->env; + FeatureWordInfo *fi = &feature_word_info[w]; +- uint32_t eax = fi->cpuid_eax; ++ uint32_t eax = fi->cpuid.eax; + uint32_t region = eax & 0xF0000000; + ++ assert(feature_word_info[w].type == CPUID_FEATURE_WORD); + if (!env->features[w]) { + return; + } +-- +1.8.3.1 + diff --git a/SOURCES/kvm-x86-cpu-Enable-CLDEMOTE-Demote-Cache-Line-cpu-featur.patch b/SOURCES/kvm-x86-cpu-Enable-CLDEMOTE-Demote-Cache-Line-cpu-featur.patch new file mode 100644 index 0000000..c5c8e52 --- /dev/null +++ b/SOURCES/kvm-x86-cpu-Enable-CLDEMOTE-Demote-Cache-Line-cpu-featur.patch @@ -0,0 +1,71 @@ +From 45be760eda02e18ac33b603224a47b3954bfb762 Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Mon, 4 Feb 2019 17:31:05 +0100 +Subject: [PATCH 01/12] x86/cpu: Enable CLDEMOTE(Demote Cache Line) cpu feature + +RH-Author: plai@redhat.com +Message-id: <1549301465-19852-1-git-send-email-plai@redhat.com> +Patchwork-id: 84206 +O-Subject: [RHEL7.7 qemu-kvm-rhev PATCH BZ 1537776 RESEND] x86/cpu: Enable CLDEMOTE(Demote Cache Line) cpu feature +Bugzilla: 1537776 +RH-Acked-by: Bandan Das +RH-Acked-by: Eduardo Habkost +RH-Acked-by: John Snow + +The CLDEMOTE instruction hints to hardware that the cache line that +contains the linear address should be moved("demoted") from +the cache(s) closest to the processor core to a level more distant +from the processor core. This may accelerate subsequent accesses +to the line by other cores in the same coherence domain, +especially if the line was written by the core that demotes the line. + +Intel Snow Ridge has added new cpu feature, CLDEMOTE. +The new cpu feature needs to be exposed to guest VM. + +The bit definition: +CPUID.(EAX=7,ECX=0):ECX[bit 25] CLDEMOTE + +The release document ref below link: +https://software.intel.com/sites/default/files/managed/c5/15/\ +architecture-instruction-set-extensions-programming-reference.pdf + +Signed-off-by: Jingqi Liu +Message-Id: <1525406253-54846-1-git-send-email-jingqi.liu@intel.com> +Reviewed-by: Eduardo Habkost +Signed-off-by: Eduardo Habkost +(cherry picked from commit 0da0fb062841d0dcd8ba47e4a989d2e952cdf0ff) +Signed-off-by: Paul Lai +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 2 +- + target/i386/cpu.h | 1 + + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index c5156c8..4558b1a 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -991,7 +991,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + "avx512bitalg", NULL, "avx512-vpopcntdq", NULL, + "la57", NULL, NULL, NULL, + NULL, NULL, "rdpid", NULL, +- NULL, NULL, NULL, NULL, ++ NULL, "cldemote", NULL, NULL, + NULL, NULL, NULL, NULL, + }, + .cpuid_eax = 7, +diff --git a/target/i386/cpu.h b/target/i386/cpu.h +index 392065d..ea8c355 100644 +--- a/target/i386/cpu.h ++++ b/target/i386/cpu.h +@@ -682,6 +682,7 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS]; + #define CPUID_7_0_ECX_AVX512_VPOPCNTDQ (1U << 14) /* POPCNT for vectors of DW/QW */ + #define CPUID_7_0_ECX_LA57 (1U << 16) + #define CPUID_7_0_ECX_RDPID (1U << 22) ++#define CPUID_7_0_ECX_CLDEMOTE (1U << 25) /* CLDEMOTE Instruction */ + + #define CPUID_7_0_EDX_AVX512_4VNNIW (1U << 2) /* AVX512 Neural Network Instructions */ + #define CPUID_7_0_EDX_AVX512_4FMAPS (1U << 3) /* AVX512 Multiply Accumulation Single Precision */ +-- +1.8.3.1 + diff --git a/SOURCES/kvm-x86-define-a-new-MSR-based-feature-word-FEATURE_WORD.patch b/SOURCES/kvm-x86-define-a-new-MSR-based-feature-word-FEATURE_WORD.patch new file mode 100644 index 0000000..3d952c6 --- /dev/null +++ b/SOURCES/kvm-x86-define-a-new-MSR-based-feature-word-FEATURE_WORD.patch @@ -0,0 +1,128 @@ +From fbad7e91a2cb3a3610f1013f63d39473ab165b5b Mon Sep 17 00:00:00 2001 +From: "plai@redhat.com" +Date: Tue, 4 Jun 2019 21:47:24 +0200 +Subject: [PATCH 05/23] x86: define a new MSR based feature word -- + FEATURE_WORDS_ARCH_CAPABILITIES + +RH-Author: plai@redhat.com +Message-id: <1559684847-10889-6-git-send-email-plai@redhat.com> +Patchwork-id: 88534 +O-Subject: [RHEL7.7 qemu-kvm-rhev PATCH v4 5/8] x86: define a new MSR based feature word -- FEATURE_WORDS_ARCH_CAPABILITIES +Bugzilla: 1709972 +RH-Acked-by: Eduardo Habkost +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Miroslav Rezanina + +From: Robert Hoo + +Note RSBA is specially treated -- no matter host support it or not, qemu +pretends it is supported. + +Signed-off-by: Robert Hoo +Message-Id: <1539578845-37944-4-git-send-email-robert.hu@linux.intel.com> +[ehabkost: removed automatic enabling of RSBA] +Reviewed-by: Eduardo Habkost +Signed-off-by: Eduardo Habkost +(cherry picked from commit d86f963694df27f11b3681ffd225c9362de1b634) +Signed-off-by: Paul Lai + +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 24 +++++++++++++++++++++++- + target/i386/cpu.h | 8 ++++++++ + target/i386/kvm.c | 11 +++++++++++ + 3 files changed, 42 insertions(+), 1 deletion(-) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 3a06d37..478c5a4 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -1143,6 +1143,27 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + }, + .tcg_features = ~0U, + }, ++ /*Below are MSR exposed features*/ ++ [FEAT_ARCH_CAPABILITIES] = { ++ .type = MSR_FEATURE_WORD, ++ .feat_names = { ++ "rdctl-no", "ibrs-all", "rsba", "skip-l1dfl-vmentry", ++ "ssb-no", NULL, NULL, NULL, ++ NULL, NULL, NULL, NULL, ++ NULL, NULL, NULL, NULL, ++ NULL, NULL, NULL, NULL, ++ NULL, NULL, NULL, NULL, ++ NULL, NULL, NULL, NULL, ++ NULL, NULL, NULL, NULL, ++ }, ++ .msr = { ++ .index = MSR_IA32_ARCH_CAPABILITIES, ++ .cpuid_dep = { ++ FEAT_7_0_EDX, ++ CPUID_7_0_EDX_ARCH_CAPABILITIES ++ } ++ }, ++ }, + }; + + typedef struct X86RegisterInfo32 { +@@ -3550,7 +3571,8 @@ static uint32_t x86_cpu_get_supported_feature_word(FeatureWord w, + wi->cpuid.reg); + break; + case MSR_FEATURE_WORD: +- r = kvm_arch_get_supported_msr_feature(kvm_state, wi->msr.index); ++ r = kvm_arch_get_supported_msr_feature(kvm_state, ++ wi->msr.index); + break; + } + } else if (hvf_enabled()) { +diff --git a/target/i386/cpu.h b/target/i386/cpu.h +index eb39724..8ab313e 100644 +--- a/target/i386/cpu.h ++++ b/target/i386/cpu.h +@@ -501,6 +501,7 @@ typedef enum FeatureWord { + FEAT_6_EAX, /* CPUID[6].EAX */ + FEAT_XSAVE_COMP_LO, /* CPUID[EAX=0xd,ECX=0].EAX */ + FEAT_XSAVE_COMP_HI, /* CPUID[EAX=0xd,ECX=0].EDX */ ++ FEAT_ARCH_CAPABILITIES, + FEATURE_WORDS, + } FeatureWord; + +@@ -728,6 +729,13 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS]; + #define CPUID_TOPOLOGY_LEVEL_SMT (1U << 8) + #define CPUID_TOPOLOGY_LEVEL_CORE (2U << 8) + ++/* MSR Feature Bits */ ++#define MSR_ARCH_CAP_RDCL_NO (1U << 0) ++#define MSR_ARCH_CAP_IBRS_ALL (1U << 1) ++#define MSR_ARCH_CAP_RSBA (1U << 2) ++#define MSR_ARCH_CAP_SKIP_L1DFL_VMENTRY (1U << 3) ++#define MSR_ARCH_CAP_SSB_NO (1U << 4) ++ + #ifndef HYPERV_SPINLOCK_NEVER_RETRY + #define HYPERV_SPINLOCK_NEVER_RETRY 0xFFFFFFFF + #endif +diff --git a/target/i386/kvm.c b/target/i386/kvm.c +index 0ecec4a..88a4114 100644 +--- a/target/i386/kvm.c ++++ b/target/i386/kvm.c +@@ -1833,6 +1833,17 @@ static int kvm_put_msrs(X86CPU *cpu, int level) + } + #endif + ++ /* If host supports feature MSR, write down. */ ++ if (kvm_feature_msrs) { ++ int i; ++ for (i = 0; i < kvm_feature_msrs->nmsrs; i++) ++ if (kvm_feature_msrs->indices[i] == MSR_IA32_ARCH_CAPABILITIES) { ++ kvm_msr_entry_add(cpu, MSR_IA32_ARCH_CAPABILITIES, ++ env->features[FEAT_ARCH_CAPABILITIES]); ++ break; ++ } ++ } ++ + /* + * The following MSRs have side effects on the guest or are too heavy + * for normal writeback. Limit them to reset or full state updates. +-- +1.8.3.1 + diff --git a/SOURCES/kvm-x86-host-phys-bits-limit-option.patch b/SOURCES/kvm-x86-host-phys-bits-limit-option.patch new file mode 100644 index 0000000..7bf79d6 --- /dev/null +++ b/SOURCES/kvm-x86-host-phys-bits-limit-option.patch @@ -0,0 +1,96 @@ +From 714c0396845fe74f8ccb9a72a7f0ab6753248e6e Mon Sep 17 00:00:00 2001 +From: Eduardo Habkost +Date: Thu, 11 Apr 2019 21:48:45 +0200 +Subject: [PATCH 159/163] x86: host-phys-bits-limit option + +RH-Author: Eduardo Habkost +Message-id: <20190411214846.8816-2-ehabkost@redhat.com> +Patchwork-id: 85608 +O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 1/2] x86: host-phys-bits-limit option +Bugzilla: 1691519 +RH-Acked-by: Paolo Bonzini +RH-Acked-by: Pankaj Gupta +RH-Acked-by: Dr. David Alan Gilbert + +Some downstream distributions of QEMU set host-phys-bits=on by +default. This worked very well for most use cases, because +phys-bits really didn't have huge consequences. The only +difference was on the CPUID data seen by guests, and on the +handling of reserved bits. + +This changed in KVM commit 855feb673640 ("KVM: MMU: Add 5 level +EPT & Shadow page table support"). Now choosing a large +phys-bits value for a VM has bigger impact: it will make KVM use +5-level EPT even when it's not really necessary. This means +using the host phys-bits value may not be the best choice. + +Management software could address this problem by manually +configuring phys-bits depending on the size of the VM and the +amount of MMIO address space required for hotplug. But this is +not trivial to implement. + +However, there's another workaround that would work for most +cases: keep using the host phys-bits value, but only if it's +smaller than 48. This patch makes this possible by introducing a +new "-cpu" option: "host-phys-bits-limit". Management software +or users can make sure they will always use 4-level EPT using: +"host-phys-bits=on,host-phys-bits-limit=48". + +This behavior is still not enabled by default because QEMU +doesn't enable host-phys-bits=on by default. But users, +management software, or downstream distributions may choose to +change their defaults using the new option. + +Signed-off-by: Eduardo Habkost +Message-Id: <20181211192527.13254-1-ehabkost@redhat.com> +[ehabkost: removed test code while some issues are addressed] +Signed-off-by: Eduardo Habkost +(cherry picked from commit 258fe08bd341d2e230676228307294e41f33002c) +Signed-off-by: Eduardo Habkost + +Signed-off-by: Miroslav Rezanina +--- + target/i386/cpu.c | 5 +++++ + target/i386/cpu.h | 3 +++ + 2 files changed, 8 insertions(+) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 2f6d5f4..c5156c8 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -4826,6 +4826,10 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) + if (cpu->host_phys_bits) { + /* The user asked for us to use the host physical bits */ + cpu->phys_bits = host_phys_bits; ++ if (cpu->host_phys_bits_limit && ++ cpu->phys_bits > cpu->host_phys_bits_limit) { ++ cpu->phys_bits = cpu->host_phys_bits_limit; ++ } + } + + /* Print a warning if the user set it to a value that's not the +@@ -5377,6 +5381,7 @@ static Property x86_cpu_properties[] = { + DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true), + DEFINE_PROP_UINT32("phys-bits", X86CPU, phys_bits, 0), + DEFINE_PROP_BOOL("host-phys-bits", X86CPU, host_phys_bits, false), ++ DEFINE_PROP_UINT8("host-phys-bits-limit", X86CPU, host_phys_bits_limit, 0), + DEFINE_PROP_BOOL("fill-mtrr-mask", X86CPU, fill_mtrr_mask, true), + DEFINE_PROP_UINT32("level", X86CPU, env.cpuid_level, UINT32_MAX), + DEFINE_PROP_UINT32("xlevel", X86CPU, env.cpuid_xlevel, UINT32_MAX), +diff --git a/target/i386/cpu.h b/target/i386/cpu.h +index 75cf5ed..392065d 100644 +--- a/target/i386/cpu.h ++++ b/target/i386/cpu.h +@@ -1419,6 +1419,9 @@ struct X86CPU { + /* if true override the phys_bits value with a value read from the host */ + bool host_phys_bits; + ++ /* if set, limit maximum value for phys_bits when host_phys_bits is true */ ++ uint8_t host_phys_bits_limit; ++ + /* Stop SMI delivery for migration compatibility with old machines */ + bool kvm_no_smi_migration; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-x86.conf b/SOURCES/kvm-x86.conf new file mode 100644 index 0000000..3f7842a --- /dev/null +++ b/SOURCES/kvm-x86.conf @@ -0,0 +1,12 @@ +# Setting modprobe kvm_intel/kvm_amd nested = 1 +# only enables Nested Virtualization until the next reboot or +# module reload. Uncomment the option applicable +# to your system below to enable the feature permanently. +# +# User changes in this file are preserved across upgrades. +# +# For Intel +#options kvm_intel nested=1 +# +# For AMD +#options kvm_amd nested=1 diff --git a/SOURCES/kvm-xhci-fix-guest-triggerable-assert.patch b/SOURCES/kvm-xhci-fix-guest-triggerable-assert.patch new file mode 100644 index 0000000..bd0058c --- /dev/null +++ b/SOURCES/kvm-xhci-fix-guest-triggerable-assert.patch @@ -0,0 +1,45 @@ +From 27d574c17a28134a9e8c5cf12b2ae3635aa339c0 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Wed, 4 Jul 2018 09:00:54 +0200 +Subject: [PATCH 12/89] xhci: fix guest-triggerable assert + +RH-Author: Gerd Hoffmann +Message-id: <20180704090054.5101-2-kraxel@redhat.com> +Patchwork-id: 81224 +O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 1/1] xhci: fix guest-triggerable assert +Bugzilla: 1594135 +RH-Acked-by: Dr. David Alan Gilbert +RH-Acked-by: Laurent Vivier +RH-Acked-by: Markus Armbruster + +Set xhci into error state instead of throwing a core dump. + +Signed-off-by: Gerd Hoffmann +Message-id: 20180702162752.29233-1-kraxel@redhat.com +(cherry picked from commit 8f36ec708834dfad58af6feb0b69bb0be6077f0f) +Signed-off-by: Miroslav Rezanina +--- + hw/usb/hcd-xhci.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index 181e803..45fcce3 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -1954,7 +1954,12 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) + for (i = 0; i < length; i++) { + TRBType type; + type = xhci_ring_fetch(xhci, ring, &xfer->trbs[i], NULL); +- assert(type); ++ if (!type) { ++ xhci_die(xhci); ++ xhci_ep_free_xfer(xfer); ++ epctx->kick_active--; ++ return; ++ } + } + xfer->streamid = streamid; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm.conf b/SOURCES/kvm.conf new file mode 100644 index 0000000..24e60e9 --- /dev/null +++ b/SOURCES/kvm.conf @@ -0,0 +1,3 @@ +# +# User changes in this file are preserved across upgrades. +# diff --git a/SOURCES/qemu-ga.sysconfig b/SOURCES/qemu-ga.sysconfig new file mode 100644 index 0000000..f54018e --- /dev/null +++ b/SOURCES/qemu-ga.sysconfig @@ -0,0 +1,19 @@ +# This is a systemd environment file, not a shell script. +# It provides settings for "/lib/systemd/system/qemu-guest-agent.service". + +# Comma-separated blacklist of RPCs to disable, or empty list to enable all. +# +# You can get the list of RPC commands using "qemu-ga --blacklist='?'". +# There should be no spaces between commas and commands in the blacklist. +BLACKLIST_RPC=guest-file-open,guest-file-close,guest-file-read,guest-file-write,guest-file-seek,guest-file-flush + +# Fsfreeze hook script specification. +# +# FSFREEZE_HOOK_PATHNAME=/dev/null : disables the feature. +# +# FSFREEZE_HOOK_PATHNAME=/path/to/executable : enables the feature with the +# specified binary or shell script. +# +# FSFREEZE_HOOK_PATHNAME= : enables the feature with the +# default value (invoke "qemu-ga --help" to interrogate). +FSFREEZE_HOOK_PATHNAME=/etc/qemu-ga/fsfreeze-hook diff --git a/SOURCES/qemu-guest-agent.service b/SOURCES/qemu-guest-agent.service new file mode 100644 index 0000000..b33e951 --- /dev/null +++ b/SOURCES/qemu-guest-agent.service @@ -0,0 +1,20 @@ +[Unit] +Description=QEMU Guest Agent +BindsTo=dev-virtio\x2dports-org.qemu.guest_agent.0.device +After=dev-virtio\x2dports-org.qemu.guest_agent.0.device +IgnoreOnIsolate=True + +[Service] +UMask=0077 +EnvironmentFile=/etc/sysconfig/qemu-ga +ExecStart=/usr/bin/qemu-ga \ + --method=virtio-serial \ + --path=/dev/virtio-ports/org.qemu.guest_agent.0 \ + --blacklist=${BLACKLIST_RPC} \ + -F${FSFREEZE_HOOK_PATHNAME} +StandardError=syslog +Restart=always +RestartSec=0 + +[Install] +WantedBy=dev-virtio\x2dports-org.qemu.guest_agent.0.device diff --git a/SOURCES/qemu-pr-helper.service b/SOURCES/qemu-pr-helper.service new file mode 100644 index 0000000..a1d27b0 --- /dev/null +++ b/SOURCES/qemu-pr-helper.service @@ -0,0 +1,15 @@ +[Unit] +Description=Persistent Reservation Daemon for QEMU + +[Service] +WorkingDirectory=/tmp +Type=simple +ExecStart=/usr/bin/qemu-pr-helper +PrivateTmp=yes +ProtectSystem=strict +ReadWritePaths=/var/run +RestrictAddressFamilies=AF_UNIX +Restart=always +RestartSec=0 + +[Install] diff --git a/SOURCES/qemu-pr-helper.socket b/SOURCES/qemu-pr-helper.socket new file mode 100644 index 0000000..9d7c3e5 --- /dev/null +++ b/SOURCES/qemu-pr-helper.socket @@ -0,0 +1,9 @@ +[Unit] +Description=Persistent Reservation Daemon for QEMU + +[Socket] +ListenStream=/run/qemu-pr-helper.sock +SocketMode=0600 + +[Install] +WantedBy=multi-user.target diff --git a/SOURCES/vhost.conf b/SOURCES/vhost.conf new file mode 100644 index 0000000..68d6d7f --- /dev/null +++ b/SOURCES/vhost.conf @@ -0,0 +1,3 @@ +# Increase default vhost memory map limit to match +# KVM's memory slot limit +options vhost max_mem_regions=509 diff --git a/SPECS/qemu-kvm.spec b/SPECS/qemu-kvm.spec new file mode 100644 index 0000000..75d3169 --- /dev/null +++ b/SPECS/qemu-kvm.spec @@ -0,0 +1,10883 @@ +%define rhev 0 + +%global SLOF_gittagdate 20170303 +%global SLOF_gittagcommit 66d250e + +%global have_usbredir 1 +%global have_spice 1 +%global have_opengl 1 +%global have_fdt 0 +%global have_gluster 1 +%global have_kvm_setup 0 +%global have_seccomp 1 +%global have_memlock_limits 0 +%global have_vxhs 0 +%global have_vtd 0 +%global have_live_block_ops 1 +%global have_vhost_user 1 + +%ifnarch %{ix86} x86_64 + %global have_usbredir 0 +%endif + +%ifnarch s390 s390x + %global have_librdma 1 + %global have_tcmalloc 1 +%else + %global have_librdma 0 + %global have_tcmalloc 0 +%endif + +%ifarch %{ix86} + %global kvm_target i386 +%endif +%ifarch x86_64 + %global kvm_target x86_64 + %global have_vxhs 1 +%else + %global have_spice 0 + %global have_opengl 0 + %global have_gluster 0 +%endif +%ifarch %{power64} + %global kvm_target ppc64 + %global have_fdt 1 + %global have_kvm_setup 1 + %global have_memlock_limits 1 +%endif +%ifarch s390x s390 + %global kvm_target s390x +%endif +%ifarch ppc + %global kvm_target ppc + %global have_fdt 1 +%endif +%ifarch aarch64 + %global kvm_target aarch64 + %global have_fdt 1 +%endif + +#Versions of various parts: + +%define pkgname qemu-kvm +%define rhel_ma_suffix -ma +%define rhel_suffix -rhel +%define rhev_suffix -rhev + +# Setup for RHEL/RHEV package handling +# We need to define tree suffixes: +# - pkgsuffix: used for package name +# - extra_provides_suffix: used for dependency checking of other packages +# - conflicts_suffix: used to prevent installation of both RHEL and RHEV + +%if %{rhev} + %global pkgsuffix %{rhev_suffix} + %global extra_provides_suffix %{nil} + %global conflicts_suffix %{rhel_suffix} + %global obsoletes_version 15:0-0 + %global obsoletes_version2 15:0-0 + %global have_vtd 1 +%else + %global pkgsuffix %{rhel_ma_suffix} + %global extra_provides_suffix %{nil} + %global extra_provides_suffix2 %{rhel_suffix} + %global conflicts_suffix %{rhev_suffix} + %global conflicts_suffix2 %{rhel_suffix} + %global have_live_block_ops 0 + %global have_vhost_user 0 + %global obsoletes_version 15:0-0 +%endif + +# Macro to properly setup RHEL/RHEV conflict handling +%define rhel_rhev_conflicts() \ +Conflicts: %1%{conflicts_suffix} \ +Provides: %1%{extra_provides_suffix} = %{epoch}:%{version}-%{release} \ +%if 0%{?extra_provides_suffix2:1} \ +Provides: %1%{extra_provides_suffix2} = %{epoch}:%{version}-%{release} \ +%endif \ +%if 0%{?conflicts_suffix2:1} \ +Conflicts: %1%{conflicts_suffix2} \ +%endif \ +%if 0%{?obsoletes_version:1} \ +Obsoletes: %1 < %{obsoletes_version} \ +%endif \ +%if 0%{?obsoletes_version2:1} \ +Obsoletes: %1%{rhel_ma_suffix} < %{obsoletes_version2} \ +%endif + +Summary: QEMU is a machine emulator and virtualizer +Name: %{pkgname}%{?pkgsuffix} +Version: 2.12.0 +Release: 37%{?dist} +# Epoch because we pushed a qemu-1.0 package. AIUI this can't ever be dropped +Epoch: 10 +License: GPLv2 and GPLv2+ and CC-BY +Group: Development/Tools +URL: http://www.qemu.org/ +%if %{rhev} +ExclusiveArch: x86_64 %{power64} aarch64 s390x +%else +ExclusiveArch: %{power64} aarch64 s390x +%endif +%ifarch %{ix86} x86_64 +Requires: seabios-bin >= 1.10.2-1 +Requires: sgabios-bin +%endif +%ifnarch aarch64 s390x +Requires: seavgabios-bin >= 1.10.2-1 +Requires: ipxe-roms-qemu >= 20170123-1 +%endif +%ifarch %{power64} +Requires: SLOF >= %{SLOF_gittagdate}-1.git%{SLOF_gittagcommit} +%endif +Requires: %{pkgname}-common%{?pkgsuffix} = %{epoch}:%{version}-%{release} +%if %{have_seccomp} +Requires: libseccomp >= 2.3.0 +%endif +# For compressed guest memory dumps +Requires: lzo snappy +%if %{have_gluster} +Requires: glusterfs-api >= 3.6.0 +%endif +%if %{have_kvm_setup} +Requires(post): systemd-units + %ifarch %{power64} +Requires: powerpc-utils + %endif +%endif +Requires: libusbx >= 1.0.19 +%if %{have_usbredir} +Requires: usbredir >= 0.7.1 +%endif + + +# OOM killer breaks builds with parallel make on s390(x) +%ifarch s390 s390x + %define _smp_mflags %{nil} +%endif + +Source0: http://wiki.qemu.org/download/qemu-2.12.0.tar.xz + +# Creates /dev/kvm +Source3: 80-kvm.rules +# KSM control scripts +Source4: ksm.service +Source5: ksm.sysconfig +Source6: ksmctl.c +Source7: ksmtuned.service +Source8: ksmtuned +Source9: ksmtuned.conf +Source10: qemu-guest-agent.service +Source11: 99-qemu-guest-agent.rules +Source12: bridge.conf +Source13: qemu-ga.sysconfig +Source14: rhel6-virtio.rom +Source15: rhel6-pcnet.rom +Source16: rhel6-rtl8139.rom +Source17: rhel6-ne2k_pci.rom +Source18: bios-256k.bin +Source19: README.rhel6-gpxe-source +Source20: rhel6-e1000.rom +Source21: kvm-setup +Source22: kvm-setup.service +Source23: 85-kvm.preset +Source24: build_configure.sh +Source25: kvm-unit-tests.git-4ea7633.tar.bz2 +Source26: vhost.conf +Source27: kvm.conf +Source28: 95-kvm-memlock.conf +Source29: pxe-e1000e.rom +Source30: kvm-s390x.conf +Source31: kvm-x86.conf +Source32: qemu-pr-helper.service +Source33: qemu-pr-helper.socket +Source34: kvm-setup-ppc64.sysconfig + + + +Patch0001: 0001-Revert-qemu-pr-helper-use-new-libmultipath-API.patch +Patch0003: 0003-Initial-redhat-build.patch +Patch0004: 0004-Enable-disable-devices-for-RHEL-7.patch +Patch0005: 0005-Add-RHEL-7-machine-types.patch +Patch0006: 0006-Revert-kvm_stat-Remove.patch +Patch0007: 0007-Use-kvm-by-default.patch +Patch0008: 0008-add-qxl_screendump-monitor-command.patch +Patch0009: 0009-seabios-paravirt-allow-more-than-1TB-in-x86-guest.patch +Patch0010: 0010-vfio-cap-number-of-devices-that-can-be-assigned.patch +Patch0011: 0011-QMP-Forward-port-__com.redhat_drive_del-from-RHEL-6.patch +Patch0012: 0012-QMP-Forward-port-__com.redhat_drive_add-from-RHEL-6.patch +Patch0013: 0013-HMP-Forward-port-__com.redhat_drive_add-from-RHEL-6.patch +Patch0014: 0014-Add-support-statement-to-help-output.patch +Patch0015: 0015-vl-Round-memory-sizes-below-2MiB-up-to-2MiB.patch +Patch0016: 0016-use-recommended-max-vcpu-count.patch +Patch0017: 0017-Add-support-for-simpletrace.patch +Patch0018: 0018-Use-qemu-kvm-in-documentation-instead-of-qemu-system.patch +Patch0019: 0019-qmp-add-__com.redhat_reason-to-the-BLOCK_IO_ERROR-ev.patch +Patch0020: 0020-Migration-compat-for-pckbd.patch +Patch0021: 0021-Migration-compat-for-fdc.patch +Patch0022: 0022-spapr-Reduce-advertised-max-LUNs-for-spapr_vscsi.patch +Patch0023: 0023-RHEL-only-hw-char-pl011-fix-SBSA-reset.patch +Patch0024: 0024-blockdev-ignore-cache-options-for-empty-CDROM-drives.patch +Patch0025: 0025-usb-xhci-Fix-PCI-capability-order.patch +Patch0026: 0026-blockdev-ignore-aio-native-for-empty-drives.patch +Patch0027: 0027-virtio-scsi-Reject-scsi-cd-if-data-plane-enabled-RHE.patch +Patch0028: 0028-osdep-Force-define-F_OFD_GETLK-RHEL-only.patch +Patch0029: 0029-spapr-disable-cpu-hot-remove.patch +Patch0030: 0030-migration-Reenable-incoming-live-block-migration.patch +Patch0031: 0031-block-vxhs-improve-error-message-for-missing-bad-vxh.patch +Patch0032: 0032-serial-always-transmit-send-receive-buffers-on-migra.patch +Patch0033: 0033-target-i386-sanitize-x86-MSR_PAT-loaded-from-another.patch +Patch0034: 0034-spapr-disable-memory-hotplug.patch +# For bz#1558723 - Create RHEL-7.6 QEMU machine type for AArch64 +Patch35: kvm-AArch64-Add-virt-rhel7.6-machine-type.patch +# For bz#1570525 - [POWER][QEMU-KVM] Can't hotplug memory to initially memory-less NUMA node +Patch36: kvm-spapr-Add-ibm-max-associativity-domains-property.patch +# For bz#1570525 - [POWER][QEMU-KVM] Can't hotplug memory to initially memory-less NUMA node +Patch37: kvm-Revert-spapr-Don-t-allow-memory-hotplug-to-memory-le.patch +# For bz#1574577 - qemu-kvm segfaults when run guestfsd under TCG +Patch38: kvm-tcg-workaround-branch-instruction-overflow-in-tcg_ou.patch +# For bz#1523857 - [Pegas1.2 FEAT] KVM: Interactive Bootloader - qemu part +Patch39: kvm-s390-ccw-force-diag-308-subcode-to-unsigned-long.patch +# For bz#1523857 - [Pegas1.2 FEAT] KVM: Interactive Bootloader - qemu part +Patch40: kvm-pc-bios-s390-ccw-size_t-should-be-unsigned.patch +# For bz#1523857 - [Pegas1.2 FEAT] KVM: Interactive Bootloader - qemu part +Patch41: kvm-pc-bios-s390-ccw-rename-MAX_TABLE_ENTRIES-to-MAX_BOO.patch +# For bz#1523857 - [Pegas1.2 FEAT] KVM: Interactive Bootloader - qemu part +Patch42: kvm-pc-bios-s390-ccw-fix-loadparm-initialization-and-int.patch +# For bz#1523857 - [Pegas1.2 FEAT] KVM: Interactive Bootloader - qemu part +Patch43: kvm-pc-bios-s390-ccw-fix-non-sequential-boot-entries-eck.patch +# For bz#1523857 - [Pegas1.2 FEAT] KVM: Interactive Bootloader - qemu part +Patch44: kvm-pc-bios-s390-ccw-fix-non-sequential-boot-entries-enu.patch +# For bz#1566153 - IOERROR pause code lost after resuming a VM while I/O error is still present +Patch45: kvm-cpus-Fix-event-order-on-resume-of-stopped-guest.patch +# For bz#1523065 - "qemu-img resize" should fail to decrease the size of logical partition/lvm/iSCSI image with raw format +Patch46: kvm-qemu-img-Check-post-truncation-size.patch +# For bz#1562138 - RHEL 7.6 new qemu-kvm-ma machine type (s390x) +Patch47: kvm-s390x-add-RHEL-7.6-machine-type-for-ccw.patch +# For bz#1557054 - RHEL 7.6 new pseries machine type (ppc64le) +Patch48: kvm-redhat-define-HW_COMPAT_RHEL7_5.patch +# For bz#1557054 - RHEL 7.6 new pseries machine type (ppc64le) +Patch49: kvm-redhat-define-pseries-rhel7.6.0-machine-types.patch +# For bz#1578068 - Backwards compatibility of pc-*-rhel7.5.0 and older machine-types +Patch50: kvm-pc-pc-rhel75.5.0-compat-code.patch +# For bz#1575541 - qemu core dump while installing win10 guest +Patch51: kvm-vga-catch-depth-0.patch +# For bz#1583959 - Incorrect vcpu count limit for 7.4 machine types for windows guests +Patch52: kvm-Fix-x-hv-max-vps-compat-value-for-7.4-machine-type.patch +# For bz#1584984 - Vm starts failed with 'passthrough' smartcard +Patch53: kvm-ccid-card-passthru-fix-regression-in-realize.patch +# For bz#1542080 - Qemu core dump at cirrus_invalidate_region +Patch54: kvm-remove-duplicate-HW_COMPAT_RHEL7_5.patch +# For bz#1542080 - Qemu core dump at cirrus_invalidate_region +Patch55: kvm-Use-4-MB-vram-for-cirrus.patch +# For bz#1505664 - "qemu-kvm: System page size 0x1000000 is not enabled in page_size_mask (0x11000). Performance may be slow" show up while using hugepage as guest's memory +Patch56: kvm-spapr_pci-Remove-unhelpful-pagesize-warning.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch57: kvm-qobject-Use-qobject_to-instead-of-type-cast.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch58: kvm-qobject-Ensure-base-is-at-offset-0.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch59: kvm-qobject-use-a-QObjectBase_-struct.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch60: kvm-qobject-Replace-qobject_incref-QINCREF-qobject_decre.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch61: kvm-qobject-Modify-qobject_ref-to-return-obj.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch62: kvm-rbd-Drop-deprecated-drive-parameter-filename.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch63: kvm-iscsi-Drop-deprecated-drive-parameter-filename.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch64: kvm-block-Add-block-specific-QDict-header.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch65: kvm-qobject-Move-block-specific-qdict-code-to-block-qdic.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch66: kvm-block-Fix-blockdev-for-certain-non-string-scalars.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch67: kvm-block-Fix-drive-for-certain-non-string-scalars.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch68: kvm-block-Clean-up-a-misuse-of-qobject_to-in-.bdrv_co_cr.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch69: kvm-block-Factor-out-qobject_input_visitor_new_flat_conf.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch70: kvm-block-Make-remaining-uses-of-qobject-input-visitor-m.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch71: kvm-block-qdict-Simplify-qdict_flatten_qdict.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch72: kvm-block-qdict-Tweak-qdict_flatten_qdict-qdict_flatten_.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch73: kvm-block-qdict-Clean-up-qdict_crumple-a-bit.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch74: kvm-block-qdict-Simplify-qdict_is_list-some.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch75: kvm-check-block-qdict-Rename-qdict_flatten-s-variables-f.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch76: kvm-check-block-qdict-Cover-flattening-of-empty-lists-an.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch77: kvm-block-Fix-blockdev-blockdev-add-for-empty-objects-an.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch78: kvm-rbd-New-parameter-auth-client-required.patch +# For bz#1557995 - QAPI schema for RBD storage misses the 'password-secret' option +Patch79: kvm-rbd-New-parameter-key-secret.patch +# For bz#1572856 - 'block-job-cancel' can not cancel a "drive-mirror" job +Patch80: kvm-block-mirror-honor-ratelimit-again.patch +# For bz#1572856 - 'block-job-cancel' can not cancel a "drive-mirror" job +Patch81: kvm-block-mirror-Make-cancel-always-cancel-pre-READY.patch +# For bz#1572856 - 'block-job-cancel' can not cancel a "drive-mirror" job +Patch82: kvm-iotests-Add-test-for-cancelling-a-mirror-job.patch +# For bz#1518738 - Add 'copy-on-read' filter driver for use with blockdev-add +Patch83: kvm-iotests-Split-214-off-of-122.patch +# For bz#1518738 - Add 'copy-on-read' filter driver for use with blockdev-add +Patch84: kvm-block-Add-COR-filter-driver.patch +# For bz#1518738 - Add 'copy-on-read' filter driver for use with blockdev-add +Patch85: kvm-block-BLK_PERM_WRITE-includes-._UNCHANGED.patch +# For bz#1518738 - Add 'copy-on-read' filter driver for use with blockdev-add +Patch86: kvm-block-Add-BDRV_REQ_WRITE_UNCHANGED-flag.patch +# For bz#1518738 - Add 'copy-on-read' filter driver for use with blockdev-add +Patch87: kvm-block-Set-BDRV_REQ_WRITE_UNCHANGED-for-COR-writes.patch +# For bz#1518738 - Add 'copy-on-read' filter driver for use with blockdev-add +Patch88: kvm-block-quorum-Support-BDRV_REQ_WRITE_UNCHANGED.patch +# For bz#1518738 - Add 'copy-on-read' filter driver for use with blockdev-add +Patch89: kvm-block-Support-BDRV_REQ_WRITE_UNCHANGED-in-filters.patch +# For bz#1518738 - Add 'copy-on-read' filter driver for use with blockdev-add +Patch90: kvm-iotests-Clean-up-wrap-image-in-197.patch +# For bz#1518738 - Add 'copy-on-read' filter driver for use with blockdev-add +Patch91: kvm-iotests-Copy-197-for-COR-filter-driver.patch +# For bz#1518738 - Add 'copy-on-read' filter driver for use with blockdev-add +Patch92: kvm-iotests-Add-test-for-COR-across-nodes.patch +# For bz#1576598 - Segfault in qemu-io and qemu-img with -U --image-opts force-share=off +Patch93: kvm-qemu-io-Use-purely-string-blockdev-options.patch +# For bz#1576598 - Segfault in qemu-io and qemu-img with -U --image-opts force-share=off +Patch94: kvm-qemu-img-Use-only-string-options-in-img_open_opts.patch +# For bz#1576598 - Segfault in qemu-io and qemu-img with -U --image-opts force-share=off +Patch95: kvm-iotests-Add-test-for-U-force-share-conflicts.patch +# For bz#1519617 - The exit code should be non-zero when qemu-io reports an error +Patch96: kvm-qemu-io-Drop-command-functions-return-values.patch +# For bz#1519617 - The exit code should be non-zero when qemu-io reports an error +Patch97: kvm-qemu-io-Let-command-functions-return-error-code.patch +# For bz#1519617 - The exit code should be non-zero when qemu-io reports an error +Patch98: kvm-qemu-io-Exit-with-error-when-a-command-failed.patch +# For bz#1519617 - The exit code should be non-zero when qemu-io reports an error +Patch99: kvm-iotests.py-Add-qemu_io_silent.patch +# For bz#1519617 - The exit code should be non-zero when qemu-io reports an error +Patch100: kvm-iotests-Let-216-make-use-of-qemu-io-s-exit-code.patch +# For bz#1527085 - The copied flag should be updated during '-r leaks' +Patch101: kvm-qcow2-Repair-OFLAG_COPIED-when-fixing-leaks.patch +# For bz#1527085 - The copied flag should be updated during '-r leaks' +Patch102: kvm-iotests-Repairing-error-during-snapshot-deletion.patch +# For bz#1588039 - Possible assertion failure in qemu when a corrupted image is used during an incoming migration +Patch103: kvm-block-Make-bdrv_is_writable-public.patch +# For bz#1588039 - Possible assertion failure in qemu when a corrupted image is used during an incoming migration +Patch104: kvm-qcow2-Do-not-mark-inactive-images-corrupt.patch +# For bz#1588039 - Possible assertion failure in qemu when a corrupted image is used during an incoming migration +Patch105: kvm-iotests-Add-case-for-a-corrupted-inactive-image.patch +# For bz#1592327 - [RHEL-Alt-7.6 FEAT] KVM: CPU Model z14 ZR1 (qemu-kvm-ma) +Patch106: kvm-s390x-cpumodels-add-z14-Model-ZR1.patch +# For bz#1168213 - main-loop: WARNING: I/O thread spun for 1000 iterations while doing stream block device. +Patch107: kvm-main-loop-drop-spin_counter.patch +# For bz#1560847 - [Power8][FW b0320a_1812.861][rhel7.5rc2 3.10.0-861.el7.ppc64le][qemu-kvm-{ma,rhev}-2.10.0-21.el7_5.1.ppc64le] KVM guest does not default to ori type flush even with pseries-rhel7.5.0-sxxm +Patch108: kvm-target-ppc-Factor-out-the-parsing-in-kvmppc_get_cpu_.patch +# For bz#1560847 - [Power8][FW b0320a_1812.861][rhel7.5rc2 3.10.0-861.el7.ppc64le][qemu-kvm-{ma,rhev}-2.10.0-21.el7_5.1.ppc64le] KVM guest does not default to ori type flush even with pseries-rhel7.5.0-sxxm +Patch109: kvm-target-ppc-Don-t-require-private-l1d-cache-on-POWER8.patch +# For bz#1560847 - [Power8][FW b0320a_1812.861][rhel7.5rc2 3.10.0-861.el7.ppc64le][qemu-kvm-{ma,rhev}-2.10.0-21.el7_5.1.ppc64le] KVM guest does not default to ori type flush even with pseries-rhel7.5.0-sxxm +Patch110: kvm-ppc-spapr_caps-Don-t-disable-cap_cfpc-on-POWER8-by-d.patch +Patch111: kvm-Fix-permissions-for-iotest-218.patch +# For bz#1567733 - qemu abort when migrate during guest reboot +Patch112: kvm-qxl-fix-local-renderer-crash.patch +# For bz#1537956 - RFE: qemu-img amend should list the true supported options +Patch113: kvm-qemu-img-Amendment-support-implies-create_opts.patch +# For bz#1537956 - RFE: qemu-img amend should list the true supported options +Patch114: kvm-block-Add-Error-parameter-to-bdrv_amend_options.patch +# For bz#1537956 - RFE: qemu-img amend should list the true supported options +Patch115: kvm-qemu-option-Pull-out-Supported-options-print.patch +# For bz#1537956 - RFE: qemu-img amend should list the true supported options +Patch116: kvm-qemu-img-Add-print_amend_option_help.patch +# For bz#1537956 - RFE: qemu-img amend should list the true supported options +Patch117: kvm-qemu-img-Recognize-no-creation-support-in-o-help.patch +# For bz#1537956 - RFE: qemu-img amend should list the true supported options +Patch118: kvm-iotests-Test-help-option-for-unsupporting-formats.patch +# For bz#1537956 - RFE: qemu-img amend should list the true supported options +Patch119: kvm-iotests-Rework-113.patch +# For bz#1569835 - qemu-img get wrong backing file path after rebasing image with relative path +Patch120: kvm-qemu-img-Resolve-relative-backing-paths-in-rebase.patch +# For bz#1569835 - qemu-img get wrong backing file path after rebasing image with relative path +Patch121: kvm-iotests-Add-test-for-rebasing-with-relative-paths.patch +# For bz#1527898 - [RFE] qemu-img should leave cluster unallocated if it's read as zero throughout the backing chain +Patch122: kvm-qemu-img-Special-post-backing-convert-handling.patch +# For bz#1527898 - [RFE] qemu-img should leave cluster unallocated if it's read as zero throughout the backing chain +Patch123: kvm-iotests-Test-post-backing-convert-target-behavior.patch +# For bz#1564576 - Pegas 1.1 - Require to backport qemu-kvm patch that fixes expected_downtime calculation during migration +Patch124: kvm-migration-calculate-expected_downtime-with-ram_bytes.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch125: kvm-sheepdog-Fix-sd_co_create_opts-memory-leaks.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch126: kvm-qemu-iotests-reduce-chance-of-races-in-185.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch127: kvm-blockjob-do-not-cancel-timer-in-resume.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch128: kvm-nfs-Fix-error-path-in-nfs_options_qdict_to_qapi.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch129: kvm-nfs-Remove-processed-options-from-QDict.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch130: kvm-blockjob-drop-block_job_pause-resume_all.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch131: kvm-blockjob-expose-error-string-via-query.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch132: kvm-blockjob-Fix-assertion-in-block_job_finalize.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch133: kvm-blockjob-Wrappers-for-progress-counter-access.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch134: kvm-blockjob-Move-RateLimit-to-BlockJob.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch135: kvm-blockjob-Implement-block_job_set_speed-centrally.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch136: kvm-blockjob-Introduce-block_job_ratelimit_get_delay.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch137: kvm-blockjob-Add-block_job_driver.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch138: kvm-blockjob-Update-block-job-pause-resume-documentation.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch139: kvm-blockjob-Improve-BlockJobInfo.offset-len-documentati.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch140: kvm-job-Create-Job-JobDriver-and-job_create.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch141: kvm-job-Rename-BlockJobType-into-JobType.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch142: kvm-job-Add-JobDriver.job_type.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch143: kvm-job-Add-job_delete.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch144: kvm-job-Maintain-a-list-of-all-jobs.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch145: kvm-job-Move-state-transitions-to-Job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch146: kvm-job-Add-reference-counting.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch147: kvm-job-Move-cancelled-to-Job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch148: kvm-job-Add-Job.aio_context.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch149: kvm-job-Move-defer_to_main_loop-to-Job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch150: kvm-job-Move-coroutine-and-related-code-to-Job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch151: kvm-job-Add-job_sleep_ns.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch152: kvm-job-Move-pause-resume-functions-to-Job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch153: kvm-job-Replace-BlockJob.completed-with-job_is_completed.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch154: kvm-job-Move-BlockJobCreateFlags-to-Job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch155: kvm-blockjob-Split-block_job_event_pending.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch156: kvm-job-Add-job_event_.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch157: kvm-job-Move-single-job-finalisation-to-Job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch158: kvm-job-Convert-block_job_cancel_async-to-Job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch159: kvm-job-Add-job_drain.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch160: kvm-job-Move-.complete-callback-to-Job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch161: kvm-job-Move-job_finish_sync-to-Job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch162: kvm-job-Switch-transactions-to-JobTxn.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch163: kvm-job-Move-transactions-to-Job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch164: kvm-job-Move-completion-and-cancellation-to-Job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch165: kvm-block-Cancel-job-in-bdrv_close_all-callers.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch166: kvm-job-Add-job_yield.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch167: kvm-job-Add-job_dismiss.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch168: kvm-job-Add-job_is_ready.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch169: kvm-job-Add-job_transition_to_ready.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch170: kvm-job-Move-progress-fields-to-Job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch171: kvm-job-Introduce-qapi-job.json.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch172: kvm-job-Add-JOB_STATUS_CHANGE-QMP-event.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch173: kvm-job-Add-lifecycle-QMP-commands.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch174: kvm-job-Add-query-jobs-QMP-command.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch175: kvm-blockjob-Remove-BlockJob.driver.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch176: kvm-iotests-Move-qmp_to_opts-to-VM.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch177: kvm-qemu-iotests-Test-job-with-block-jobs.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch178: kvm-vdi-Fix-vdi_co_do_create-return-value.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch179: kvm-vhdx-Fix-vhdx_co_create-return-value.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch180: kvm-job-Add-error-message-for-failing-jobs.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch181: kvm-block-create-Make-x-blockdev-create-a-job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch182: kvm-qemu-iotests-Add-VM.get_qmp_events_filtered.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch183: kvm-qemu-iotests-Add-VM.qmp_log.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch184: kvm-qemu-iotests-Add-iotests.img_info_log.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch185: kvm-qemu-iotests-Add-VM.run_job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch186: kvm-qemu-iotests-iotests.py-helper-for-non-file-protocol.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch187: kvm-qemu-iotests-Rewrite-206-for-blockdev-create-job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch188: kvm-qemu-iotests-Rewrite-207-for-blockdev-create-job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch189: kvm-qemu-iotests-Rewrite-210-for-blockdev-create-job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch190: kvm-qemu-iotests-Rewrite-211-for-blockdev-create-job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch191: kvm-qemu-iotests-Rewrite-212-for-blockdev-create-job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch192: kvm-qemu-iotests-Rewrite-213-for-blockdev-create-job.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch193: kvm-block-create-Mark-blockdev-create-stable.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch194: kvm-jobs-fix-stale-wording.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch195: kvm-jobs-fix-verb-references-in-docs.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch196: kvm-iotests-Fix-219-s-timing.patch +# For bz#1513543 - [RFE] Add block job to create format on a storage device +Patch197: kvm-iotests-improve-pause_job.patch +# For bz#1583050 - Fails to start guest with Intel vGPU device +Patch198: kvm-vfio-pci-Default-display-option-to-off.patch +# For bz#1572851 - Core dumped after migration when with usb-host +Patch199: kvm-usb-host-skip-open-on-pending-postload-bh.patch +# For bz#1574216 - CVE-2018-3639 qemu-kvm-rhev: hw: cpu: speculative store bypass [rhel-7.6] +Patch200: kvm-i386-Define-the-Virt-SSBD-MSR-and-handling-of-it-CVE.patch +# For bz#1574216 - CVE-2018-3639 qemu-kvm-rhev: hw: cpu: speculative store bypass [rhel-7.6] +Patch201: kvm-i386-define-the-AMD-virt-ssbd-CPUID-feature-bit-CVE-.patch +# For bz#1519144 - qemu-img: image locking doesn't cover image creation +Patch202: kvm-block-file-posix-Pass-FD-to-locking-helpers.patch +# For bz#1519144 - qemu-img: image locking doesn't cover image creation +Patch203: kvm-block-file-posix-File-locking-during-creation.patch +# For bz#1519144 - qemu-img: image locking doesn't cover image creation +Patch204: kvm-iotests-Add-creation-test-to-153.patch +# For bz#1584139 - 2.12 migration fixes +Patch205: kvm-migration-stop-compressing-page-in-migration-thread.patch +# For bz#1584139 - 2.12 migration fixes +Patch206: kvm-migration-stop-compression-to-allocate-and-free-memo.patch +# For bz#1584139 - 2.12 migration fixes +Patch207: kvm-migration-stop-decompression-to-allocate-and-free-me.patch +# For bz#1584139 - 2.12 migration fixes +Patch208: kvm-migration-detect-compression-and-decompression-error.patch +# For bz#1584139 - 2.12 migration fixes +Patch209: kvm-migration-introduce-control_save_page.patch +# For bz#1584139 - 2.12 migration fixes +Patch210: kvm-migration-move-some-code-to-ram_save_host_page.patch +# For bz#1584139 - 2.12 migration fixes +Patch211: kvm-migration-move-calling-control_save_page-to-the-comm.patch +# For bz#1584139 - 2.12 migration fixes +Patch212: kvm-migration-move-calling-save_zero_page-to-the-common-.patch +# For bz#1584139 - 2.12 migration fixes +Patch213: kvm-migration-introduce-save_normal_page.patch +# For bz#1584139 - 2.12 migration fixes +Patch214: kvm-migration-remove-ram_save_compressed_page.patch +# For bz#1584139 - 2.12 migration fixes +Patch215: kvm-migration-block-dirty-bitmap-fix-memory-leak-in-dirt.patch +# For bz#1584139 - 2.12 migration fixes +Patch216: kvm-migration-fix-saving-normal-page-even-if-it-s-been-c.patch +# For bz#1584139 - 2.12 migration fixes +Patch217: kvm-migration-update-index-field-when-delete-or-qsort-RD.patch +# For bz#1584139 - 2.12 migration fixes +Patch218: kvm-Migration-TLS-Fix-crash-due-to-double-cleanup.patch +# For bz#1584139 - 2.12 migration fixes +Patch219: kvm-migration-introduce-decompress-error-check.patch +# For bz#1560854 - Guest is left paused on source host sometimes if kill source libvirtd during live migration due to QEMU image locking +Patch220: kvm-migration-Don-t-activate-block-devices-if-using-S.patch +# For bz#1584139 - 2.12 migration fixes +Patch221: kvm-migration-not-wait-RDMA_CM_EVENT_DISCONNECTED-event-.patch +# For bz#1584139 - 2.12 migration fixes +Patch222: kvm-migration-block-dirty-bitmap-fix-dirty_bitmap_load.patch +# For bz#1526645 - [Intel 7.6 FEAT] vHost Data Plane Acceleration (vDPA) - vhost user client - qemu-kvm-rhev +Patch223: kvm-vhost-user-add-Net-prefix-to-internal-state-structur.patch +# For bz#1526645 - [Intel 7.6 FEAT] vHost Data Plane Acceleration (vDPA) - vhost user client - qemu-kvm-rhev +Patch224: kvm-virtio-support-setting-memory-region-based-host-noti.patch +# For bz#1526645 - [Intel 7.6 FEAT] vHost Data Plane Acceleration (vDPA) - vhost user client - qemu-kvm-rhev +Patch225: kvm-vhost-user-support-receiving-file-descriptors-in-sla.patch +# For bz#1526645 - [Intel 7.6 FEAT] vHost Data Plane Acceleration (vDPA) - vhost user client - qemu-kvm-rhev +Patch226: kvm-osdep-add-wait.h-compat-macros.patch +# For bz#1526645 - [Intel 7.6 FEAT] vHost Data Plane Acceleration (vDPA) - vhost user client - qemu-kvm-rhev +Patch227: kvm-vhost-user-bridge-support-host-notifier.patch +# For bz#1526645 - [Intel 7.6 FEAT] vHost Data Plane Acceleration (vDPA) - vhost user client - qemu-kvm-rhev +Patch228: kvm-vhost-allow-backends-to-filter-memory-sections.patch +# For bz#1526645 - [Intel 7.6 FEAT] vHost Data Plane Acceleration (vDPA) - vhost user client - qemu-kvm-rhev +Patch229: kvm-vhost-user-allow-slave-to-send-fds-via-slave-channel.patch +# For bz#1526645 - [Intel 7.6 FEAT] vHost Data Plane Acceleration (vDPA) - vhost user client - qemu-kvm-rhev +Patch230: kvm-vhost-user-introduce-shared-vhost-user-state.patch +# For bz#1526645 - [Intel 7.6 FEAT] vHost Data Plane Acceleration (vDPA) - vhost user client - qemu-kvm-rhev +Patch231: kvm-vhost-user-support-registering-external-host-notifie.patch +# For bz#1526645 - [Intel 7.6 FEAT] vHost Data Plane Acceleration (vDPA) - vhost user client - qemu-kvm-rhev +Patch232: kvm-libvhost-user-support-host-notifier.patch +# For bz#1482537 - [RFE] qemu-img copy-offloading (convert command) +Patch233: kvm-block-Introduce-API-for-copy-offloading.patch +# For bz#1482537 - [RFE] qemu-img copy-offloading (convert command) +Patch234: kvm-raw-Check-byte-range-uniformly.patch +# For bz#1482537 - [RFE] qemu-img copy-offloading (convert command) +Patch235: kvm-raw-Implement-copy-offloading.patch +# For bz#1482537 - [RFE] qemu-img copy-offloading (convert command) +Patch236: kvm-qcow2-Implement-copy-offloading.patch +# For bz#1482537 - [RFE] qemu-img copy-offloading (convert command) +Patch237: kvm-file-posix-Implement-bdrv_co_copy_range.patch +# For bz#1482537 - [RFE] qemu-img copy-offloading (convert command) +Patch238: kvm-iscsi-Query-and-save-device-designator-when-opening.patch +# For bz#1482537 - [RFE] qemu-img copy-offloading (convert command) +Patch239: kvm-iscsi-Create-and-use-iscsi_co_wait_for_task.patch +# For bz#1482537 - [RFE] qemu-img copy-offloading (convert command) +Patch240: kvm-iscsi-Implement-copy-offloading.patch +# For bz#1482537 - [RFE] qemu-img copy-offloading (convert command) +Patch241: kvm-block-backend-Add-blk_co_copy_range.patch +# For bz#1482537 - [RFE] qemu-img copy-offloading (convert command) +Patch242: kvm-qemu-img-Convert-with-copy-offloading.patch +# For bz#1482537 - [RFE] qemu-img copy-offloading (convert command) +Patch243: kvm-qcow2-Fix-src_offset-in-copy-offloading.patch +# For bz#1482537 - [RFE] qemu-img copy-offloading (convert command) +Patch244: kvm-iscsi-Don-t-blindly-use-designator-length-in-respons.patch +# For bz#1482537 - [RFE] qemu-img copy-offloading (convert command) +Patch245: kvm-file-posix-Fix-EINTR-handling.patch +# For bz#1557051 - pc-i440fx-rhel7.6.0 and pc-q35-rhel7.6.0 machine types (x86) +Patch246: kvm-pc-Add-rhel7.6.0-machine-types.patch +# For bz#1595180 - Can't set rerror/werror with usb-storage +Patch247: kvm-usb-storage-Add-rerror-werror-properties.patch +# For bz#1578381 - Error message need update when specify numa distance with node index >=128 +Patch248: kvm-numa-clarify-error-message-when-node-index-is-out-of.patch +# For bz#1528541 - qemu-img check reports tons of leaked clusters after re-start nfs service to resume writing data in guest +Patch249: kvm-qemu-iotests-Update-026.out.nocache-reference-output.patch +# For bz#1528541 - qemu-img check reports tons of leaked clusters after re-start nfs service to resume writing data in guest +Patch250: kvm-qcow2-Free-allocated-clusters-on-write-error.patch +# For bz#1528541 - qemu-img check reports tons of leaked clusters after re-start nfs service to resume writing data in guest +Patch251: kvm-qemu-iotests-Test-qcow2-not-leaking-clusters-on-writ.patch +# For bz#1595715 - Add ppa15/bpb to the default cpu model for z196 and higher in the 7.6 s390-ccw-virtio machine +Patch252: kvm-s390x-cpumodel-default-enable-bpb-and-ppa15-for-z196.patch +# For bz#1586313 - -smp option is not easily found in the output of qemu help +Patch253: kvm-qemu-options-Add-missing-newline-to-accel-help-text.patch +# For bz#1592648 - Create pseries-rhel7.6.0-sxxm machine type +Patch254: kvm-RHEL-7.6-Add-pseries-rhel7.6.0-sxxm-machine-type.patch +# For bz#1481253 - AMD EPYC/Zen SMT support for KVM / QEMU guest (qemu-kvm-rhev) +Patch255: kvm-i386-Helpers-to-encode-cache-information-consistentl.patch +# For bz#1481253 - AMD EPYC/Zen SMT support for KVM / QEMU guest (qemu-kvm-rhev) +Patch256: kvm-i386-Add-cache-information-in-X86CPUDefinition.patch +# For bz#1481253 - AMD EPYC/Zen SMT support for KVM / QEMU guest (qemu-kvm-rhev) +Patch257: kvm-i386-Initialize-cache-information-for-EPYC-family-pr.patch +# For bz#1481253 - AMD EPYC/Zen SMT support for KVM / QEMU guest (qemu-kvm-rhev) +Patch258: kvm-i386-Add-new-property-to-control-cache-info.patch +# For bz#1481253 - AMD EPYC/Zen SMT support for KVM / QEMU guest (qemu-kvm-rhev) +Patch259: kvm-i386-Clean-up-cache-CPUID-code.patch +# For bz#1481253 - AMD EPYC/Zen SMT support for KVM / QEMU guest (qemu-kvm-rhev) +Patch260: kvm-i386-Populate-AMD-Processor-Cache-Information-for-cp.patch +# For bz#1481253 - AMD EPYC/Zen SMT support for KVM / QEMU guest (qemu-kvm-rhev) +Patch261: kvm-i386-Add-support-for-CPUID_8000_001E-for-AMD.patch +# For bz#1481253 - AMD EPYC/Zen SMT support for KVM / QEMU guest (qemu-kvm-rhev) +Patch262: kvm-i386-Fix-up-the-Node-id-for-CPUID_8000_001E.patch +# For bz#1481253 - AMD EPYC/Zen SMT support for KVM / QEMU guest (qemu-kvm-rhev) +Patch263: kvm-i386-Enable-TOPOEXT-feature-on-AMD-EPYC-CPU.patch +# For bz#1481253 - AMD EPYC/Zen SMT support for KVM / QEMU guest (qemu-kvm-rhev) +Patch264: kvm-i386-Remove-generic-SMT-thread-check.patch +# For bz#1594135 - system_reset many times linux guests cause qemu process Aborted +Patch265: kvm-xhci-fix-guest-triggerable-assert.patch +# For bz#1589634 - Migration failed when rebooting guest with multiple virtio videos +Patch266: kvm-virtio-gpu-tweak-scanout-disable.patch +# For bz#1589634 - Migration failed when rebooting guest with multiple virtio videos +Patch267: kvm-virtio-gpu-update-old-resource-too.patch +# For bz#1589634 - Migration failed when rebooting guest with multiple virtio videos +Patch268: kvm-virtio-gpu-disable-scanout-when-backing-resource-is-.patch +# For bz#1549654 - Reject node-names which would be truncated by the block layer commands +Patch269: kvm-block-Don-t-silently-truncate-node-names.patch +# For bz#1533158 - QEMU support for libvirtd restarting qemu-pr-helper +Patch270: kvm-pr-helper-fix-socket-path-default-in-help.patch +# For bz#1533158 - QEMU support for libvirtd restarting qemu-pr-helper +Patch271: kvm-pr-helper-fix-assertion-failure-on-failed-multipath-.patch +# For bz#1533158 - QEMU support for libvirtd restarting qemu-pr-helper +Patch272: kvm-pr-manager-helper-avoid-SIGSEGV-when-writing-to-the-.patch +# For bz#1533158 - QEMU support for libvirtd restarting qemu-pr-helper +Patch273: kvm-pr-manager-put-stubs-in-.c-file.patch +# For bz#1533158 - QEMU support for libvirtd restarting qemu-pr-helper +Patch274: kvm-pr-manager-add-query-pr-managers-QMP-command.patch +# For bz#1533158 - QEMU support for libvirtd restarting qemu-pr-helper +Patch275: kvm-pr-manager-helper-report-event-on-connection-disconn.patch +# For bz#1533158 - QEMU support for libvirtd restarting qemu-pr-helper +Patch276: kvm-pr-helper-avoid-error-on-PR-IN-command-with-zero-req.patch +# For bz#1533158 - QEMU support for libvirtd restarting qemu-pr-helper +Patch277: kvm-pr-helper-Rework-socket-path-handling.patch +# For bz#1533158 - QEMU support for libvirtd restarting qemu-pr-helper +Patch278: kvm-pr-manager-helper-fix-memory-leak-on-event.patch +# For bz#1556678 - Hot plug usb-ccid for the 2nd time with the same ID as the 1st time failed +Patch279: kvm-object-fix-OBJ_PROP_LINK_UNREF_ON_RELEASE-ambivalenc.patch +# For bz#1556678 - Hot plug usb-ccid for the 2nd time with the same ID as the 1st time failed +Patch280: kvm-usb-hcd-xhci-test-add-a-test-for-ccid-hotplug.patch +# For bz#1556678 - Hot plug usb-ccid for the 2nd time with the same ID as the 1st time failed +Patch281: kvm-Revert-usb-release-the-created-buses.patch +# For bz#1599335 - Image creation locking is too tight and is not properly released +Patch282: kvm-file-posix-Fix-creation-locking.patch +# For bz#1599335 - Image creation locking is too tight and is not properly released +Patch283: kvm-file-posix-Unlock-FD-after-creation.patch +# For bz#1584914 - SATA emulator lags and hangs +Patch284: kvm-ahci-trim-signatures-on-raise-lower.patch +# For bz#1584914 - SATA emulator lags and hangs +Patch285: kvm-ahci-fix-PxCI-register-race.patch +# For bz#1584914 - SATA emulator lags and hangs +Patch286: kvm-ahci-don-t-schedule-unnecessary-BH.patch +# For bz#1595173 - blockdev-create is blocking +Patch287: kvm-qcow2-Fix-qcow2_truncate-error-return-value.patch +# For bz#1595173 - blockdev-create is blocking +Patch288: kvm-block-Convert-.bdrv_truncate-callback-to-coroutine_f.patch +# For bz#1595173 - blockdev-create is blocking +Patch289: kvm-qcow2-Remove-coroutine-trampoline-for-preallocate_co.patch +# For bz#1595173 - blockdev-create is blocking +Patch290: kvm-block-Move-bdrv_truncate-implementation-to-io.c.patch +# For bz#1595173 - blockdev-create is blocking +Patch291: kvm-block-Use-tracked-request-for-truncate.patch +# For bz#1595173 - blockdev-create is blocking +Patch292: kvm-file-posix-Make-.bdrv_co_truncate-asynchronous.patch +# For bz#1590640 - qemu-kvm: block/io.c:1098: bdrv_co_do_copy_on_readv: Assertion `skip_bytes < pnum' failed. +Patch293: kvm-block-Fix-copy-on-read-crash-with-partial-final-clus.patch +# For bz#1599515 - qemu core-dump with aio_read via hmp (util/qemu-thread-posix.c:64: qemu_mutex_lock_impl: Assertion `mutex->initialized' failed) +Patch294: kvm-block-fix-QEMU-crash-with-scsi-hd-and-drive_del.patch +# For bz#1576743 - virtio-rng hangs when running on recent (2.x) QEMU versions +Patch295: kvm-virtio-rng-process-pending-requests-on-DRIVER_OK.patch +# For bz#1525829 - can not boot up a scsi-block passthrough disk via -blockdev with error "cannot get SG_IO version number: Operation not supported. Is this a SCSI device?" +Patch296: kvm-file-posix-specify-expected-filetypes.patch +# For bz#1525829 - can not boot up a scsi-block passthrough disk via -blockdev with error "cannot get SG_IO version number: Operation not supported. Is this a SCSI device?" +Patch297: kvm-iotests-add-test-226-for-file-driver-types.patch +# For bz#1600797 - RHEL7.5/RHV4.2 - qemu patch to align memory to allow 2MB THP +Patch298: kvm-osdep-powerpc64-align-memory-to-allow-2MB-radix-THP-.patch +# For bz#1598287 - After rebooting guest,all the hot plug memory will be assigned to the 1st numa node. +Patch299: kvm-spapr-Correct-inverted-test-in-spapr_pc_dimm_node.patch +Patch300: kvm-Add-functional-acceptance-tests-infrastructure.patch +Patch301: kvm-scripts-qemu.py-allow-adding-to-the-list-of-extra-ar.patch +Patch302: kvm-Acceptance-tests-add-quick-VNC-tests.patch +Patch303: kvm-scripts-qemu.py-introduce-set_console-method.patch +Patch304: kvm-Acceptance-tests-add-Linux-kernel-boot-and-console-c.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch305: kvm-block-dirty-bitmap-add-lock-to-bdrv_enable-disable_d.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch306: kvm-qapi-add-x-block-dirty-bitmap-enable-disable.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch307: kvm-qmp-transaction-support-for-x-block-dirty-bitmap-ena.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch308: kvm-qapi-add-x-block-dirty-bitmap-merge.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch309: kvm-qapi-add-disabled-parameter-to-block-dirty-bitmap-ad.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch310: kvm-block-dirty-bitmap-add-bdrv_enable_dirty_bitmap_lock.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch311: kvm-dirty-bitmap-fix-double-lock-on-bitmap-enabling.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch312: kvm-block-qcow2-bitmap-fix-free_bitmap_clusters.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch313: kvm-qcow2-add-overlap-check-for-bitmap-directory.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch314: kvm-blockdev-enable-non-root-nodes-for-backup-source.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch315: kvm-iotests-add-222-to-test-basic-fleecing.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch316: kvm-qcow2-Remove-dead-check-on-ret.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch317: kvm-block-Move-request-tracking-to-children-in-copy-offl.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch318: kvm-block-Fix-parameter-checking-in-bdrv_co_copy_range_i.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch319: kvm-block-Honour-BDRV_REQ_NO_SERIALISING-in-copy-range.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch320: kvm-backup-Use-copy-offloading.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch321: kvm-block-backup-disable-copy-offloading-for-backup.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch322: kvm-iotests-222-Don-t-run-with-luks.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch323: kvm-block-io-fix-copy_range.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch324: kvm-block-split-flags-in-copy_range.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch325: kvm-block-add-BDRV_REQ_SERIALISING-flag.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch326: kvm-block-backup-fix-fleecing-scheme-use-serialized-writ.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch327: kvm-nbd-server-Reject-0-length-block-status-request.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch328: kvm-nbd-server-fix-trace.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch329: kvm-nbd-server-refactor-NBDExportMetaContexts.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch330: kvm-nbd-server-add-nbd_meta_empty_or_pattern-helper.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch331: kvm-nbd-server-implement-dirty-bitmap-export.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch332: kvm-qapi-new-qmp-command-nbd-server-add-bitmap.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch333: kvm-docs-interop-add-nbd.txt.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch334: kvm-nbd-server-introduce-NBD_CMD_CACHE.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch335: kvm-nbd-server-Silence-gcc-false-positive.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch336: kvm-nbd-server-Fix-dirty-bitmap-logic-regression.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch337: kvm-nbd-server-fix-nbd_co_send_block_status.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch338: kvm-nbd-client-Add-x-dirty-bitmap-to-query-bitmap-from-s.patch +# For bz#1207657 - RFE: QEMU Incremental live backup - push and pull modes +Patch339: kvm-iotests-New-test-223-for-exporting-dirty-bitmap-over.patch +# For bz#1592817 - Retrying on serial_xmit if the pipe is broken may compromise the Guest +Patch340: kvm-hw-char-serial-Only-retry-if-qemu_chr_fe_write-retur.patch +# For bz#1592817 - Retrying on serial_xmit if the pipe is broken may compromise the Guest +Patch341: kvm-hw-char-serial-retry-write-if-EAGAIN.patch +# For bz#1535914 - Disable io throttling for one member disk of a group during io will induce the other one hang with io +Patch342: kvm-throttle-groups-fix-hang-when-group-member-leaves.patch +# For bz#1586357 - Disable new devices in 2.12 +Patch343: kvm-Disable-aarch64-devices-reappeared-after-2.12-rebase.patch +# For bz#1586357 - Disable new devices in 2.12 +Patch344: kvm-Disable-split-irq-device.patch +# For bz#1586357 - Disable new devices in 2.12 +Patch345: kvm-Disable-AT24Cx-i2c-eeprom.patch +# For bz#1586357 - Disable new devices in 2.12 +Patch346: kvm-Disable-CAN-bus-devices.patch +# For bz#1586357 - Disable new devices in 2.12 +Patch347: kvm-Disable-new-superio-devices.patch +# For bz#1586357 - Disable new devices in 2.12 +Patch348: kvm-Disable-new-pvrdma-device.patch +# For bz#1586357 - Disable new devices in 2.12 +Patch349: kvm-Disable-PCIe-to-PCI-bridge-device.patch +# For bz#1607891 - Hotplug events are sometimes lost with virtio-scsi + iothread +Patch350: kvm-qdev-add-HotplugHandler-post_plug-callback.patch +# For bz#1607891 - Hotplug events are sometimes lost with virtio-scsi + iothread +Patch351: kvm-virtio-scsi-fix-hotplug-reset-vs-event-race.patch +# For bz#1608698 - topoext support should not require kernel support +Patch352: kvm-i386-Allow-TOPOEXT-to-be-enabled-on-older-kernels.patch +# For bz#1608778 - qemu/migration: migrate failed from RHEL.7.6 to RHEL.7.5 with e1000-82540em +Patch353: kvm-e1000-Fix-tso_props-compat-for-82540em.patch +# For bz#1586255 - CVE-2018-11806 qemu-kvm-rhev: QEMU: slirp: heap buffer overflow while reassembling fragmented datagrams [rhel-7.6] +Patch354: kvm-slirp-correct-size-computation-while-concatenating-m.patch +# For bz#1595740 - RHEL-Alt-7.6 - qemu has error during migration of larger guests +Patch355: kvm-s390x-sclp-fix-maxram-calculation.patch +# For bz#1607774 - Target files for 'qemu-img convert' do not support thin_provisoning with iscsi/nfs backend +Patch356: kvm-qemu-img-Add-C-option-for-convert-with-copy-offloadi.patch +# For bz#1607774 - Target files for 'qemu-img convert' do not support thin_provisoning with iscsi/nfs backend +Patch357: kvm-iotests-Add-test-for-qemu-img-convert-C-compatibilit.patch +# For bz#1601310 - qemu-img map 'Aborted (core dumped)' when specifying a plain file +Patch358: kvm-qemu-img-Fix-assert-when-mapping-unaligned-raw-file.patch +# For bz#1601310 - qemu-img map 'Aborted (core dumped)' when specifying a plain file +Patch359: kvm-iotests-Add-test-221-to-catch-qemu-img-map-regressio.patch +# For bz#1609234 - Win2016 guest can't recognize pc-dimm hotplugged to node 0 +Patch360: kvm-pc-acpi-fix-memory-hotplug-regression-by-reducing-st.patch +# For bz#1612114 - Anonymous BlockBackends are missing in query-blockstats +Patch361: kvm-block-qapi-Add-qdev-field-to-query-blockstats-result.patch +# For bz#1612114 - Anonymous BlockBackends are missing in query-blockstats +Patch362: kvm-block-qapi-Include-anonymous-BBs-in-query-blockstats.patch +# For bz#1612114 - Anonymous BlockBackends are missing in query-blockstats +Patch363: kvm-qemu-iotests-Test-query-blockstats-with-drive-and-bl.patch +# For bz#1586255 - CVE-2018-11806 qemu-kvm-rhev: QEMU: slirp: heap buffer overflow while reassembling fragmented datagrams [rhel-7.6] +Patch364: kvm-slirp-reformat-m_inc-routine.patch +# For bz#1586255 - CVE-2018-11806 qemu-kvm-rhev: QEMU: slirp: heap buffer overflow while reassembling fragmented datagrams [rhel-7.6] +Patch365: kvm-slirp-Correct-size-check-in-m_inc.patch +# For bz#1390329 - PCIe: Add Generic PCIe-PCI bridge +Patch366: kvm-Revert-Disable-PCIe-to-PCI-bridge-device.patch +# For bz#1562750 - VM doesn't boot from HD +Patch367: kvm-aio-posix-Don-t-count-ctx-notifier-as-progress-when-.patch +# For bz#1562750 - VM doesn't boot from HD +Patch368: kvm-aio-Do-aio_notify_accept-only-during-blocking-aio_po.patch +# For bz#1596010 - The network link can't be detected on guest when the guest uses e1000e model type +Patch369: kvm-e1000e-Do-not-auto-clear-ICR-bits-which-aren-t-set-i.patch +# For bz#1596010 - The network link can't be detected on guest when the guest uses e1000e model type +Patch370: kvm-e1000e-Prevent-MSI-MSI-X-storms.patch +# For bz#1589147 - Handle 64 B USB packets for Smart Card redirection +Patch371: kvm-hw-usb-dev-smartcard-reader-Handle-64-B-USB-packets.patch +# For bz#1613277 - kernel panic in init_amd_cacheinfo +Patch372: kvm-i386-Disable-TOPOEXT-by-default-on-cpu-host.patch +# For bz#1622962 - Add etoken support to qemu-kvm for s390x KVM guests +Patch373: kvm-linux-headers-asm-s390-kvm.h-header-sync.patch +# For bz#1622962 - Add etoken support to qemu-kvm for s390x KVM guests +Patch374: kvm-s390x-kvm-add-etoken-facility.patch +# For bz#1605026 - Quitting VM causes qemu core dump once the block mirror job paused for no enough target space +Patch375: kvm-block-for-jobs-do-not-clear-user_paused-until-after-.patch +# For bz#1605026 - Quitting VM causes qemu core dump once the block mirror job paused for no enough target space +Patch376: kvm-iotests-Add-failure-matching-to-common.qemu.patch +# For bz#1605026 - Quitting VM causes qemu core dump once the block mirror job paused for no enough target space +Patch377: kvm-block-iotest-to-catch-abort-on-forced-blockjob-cance.patch +# For bz#1574216 - CVE-2018-3639 qemu-kvm-rhev: hw: cpu: speculative store bypass [rhel-7.6] +Patch378: kvm-i386-define-the-ssbd-CPUID-feature-bit-CVE-2018-3639.patch +# For bz#1624390 - Memory leaks +Patch379: kvm-target-i386-sev-fix-memory-leaks.patch +# For bz#1624390 - Memory leaks +Patch380: kvm-i386-Fix-arch_query_cpu_model_expansion-leak.patch +# For bz#1539280 - [Intel 7.6 Bug] [KVM][Crystal Ridge] Lack of data persistence guarantee of QEMU writes to host PMEM +Patch381: kvm-migration-discard-non-migratable-RAMBlocks.patch +# For bz#1539280 - [Intel 7.6 Bug] [KVM][Crystal Ridge] Lack of data persistence guarantee of QEMU writes to host PMEM +Patch382: kvm-vfio-pci-do-not-set-the-PCIDevice-has_rom-attribute.patch +# For bz#1539280 - [Intel 7.6 Bug] [KVM][Crystal Ridge] Lack of data persistence guarantee of QEMU writes to host PMEM +Patch383: kvm-memory-exec-Expose-all-memory-block-related-flags.patch +# For bz#1539280 - [Intel 7.6 Bug] [KVM][Crystal Ridge] Lack of data persistence guarantee of QEMU writes to host PMEM +Patch384: kvm-memory-exec-switch-file-ram-allocation-functions-to-.patch +# For bz#1539280 - [Intel 7.6 Bug] [KVM][Crystal Ridge] Lack of data persistence guarantee of QEMU writes to host PMEM +Patch385: kvm-configure-add-libpmem-support.patch +# For bz#1539280 - [Intel 7.6 Bug] [KVM][Crystal Ridge] Lack of data persistence guarantee of QEMU writes to host PMEM +Patch386: kvm-hostmem-file-add-the-pmem-option.patch +# For bz#1539280 - [Intel 7.6 Bug] [KVM][Crystal Ridge] Lack of data persistence guarantee of QEMU writes to host PMEM +Patch387: kvm-mem-nvdimm-ensure-write-persistence-to-PMEM-in-label.patch +# For bz#1539280 - [Intel 7.6 Bug] [KVM][Crystal Ridge] Lack of data persistence guarantee of QEMU writes to host PMEM +Patch388: kvm-migration-ram-Add-check-and-info-message-to-nvdimm-p.patch +# For bz#1539280 - [Intel 7.6 Bug] [KVM][Crystal Ridge] Lack of data persistence guarantee of QEMU writes to host PMEM +Patch389: kvm-migration-ram-ensure-write-persistence-on-loading-al.patch +# For bz#1623859 - Guest "Detected Tx unit Hang" and host "DMAR: fault addr" when booting guest with ixgbe device assignment, +Patch390: kvm-intel-iommu-send-PSI-always-even-if-across-PDEs.patch +# For bz#1623859 - Guest "Detected Tx unit Hang" and host "DMAR: fault addr" when booting guest with ixgbe device assignment, +Patch391: kvm-intel-iommu-remove-IntelIOMMUNotifierNode.patch +# For bz#1623859 - Guest "Detected Tx unit Hang" and host "DMAR: fault addr" when booting guest with ixgbe device assignment, +Patch392: kvm-intel-iommu-add-iommu-lock.patch +# For bz#1623859 - Guest "Detected Tx unit Hang" and host "DMAR: fault addr" when booting guest with ixgbe device assignment, +Patch393: kvm-intel-iommu-only-do-page-walk-for-MAP-notifiers.patch +# For bz#1623859 - Guest "Detected Tx unit Hang" and host "DMAR: fault addr" when booting guest with ixgbe device assignment, +Patch394: kvm-intel-iommu-introduce-vtd_page_walk_info.patch +# For bz#1623859 - Guest "Detected Tx unit Hang" and host "DMAR: fault addr" when booting guest with ixgbe device assignment, +Patch395: kvm-intel-iommu-pass-in-address-space-when-page-walk.patch +# For bz#1623859 - Guest "Detected Tx unit Hang" and host "DMAR: fault addr" when booting guest with ixgbe device assignment, +Patch396: kvm-intel-iommu-trace-domain-id-during-page-walk.patch +# For bz#1623859 - Guest "Detected Tx unit Hang" and host "DMAR: fault addr" when booting guest with ixgbe device assignment, +Patch397: kvm-util-implement-simple-iova-tree.patch +# For bz#1623859 - Guest "Detected Tx unit Hang" and host "DMAR: fault addr" when booting guest with ixgbe device assignment, +Patch398: kvm-intel-iommu-rework-the-page-walk-logic.patch +# For bz#1575578 - Failed to convert a source image to the qcow2 image encrypted by luks +Patch399: kvm-qemu-img-fix-regression-copying-secrets-during-conve.patch +# For bz#1582042 - Segfault on 'blockdev-mirror' with same node as source and target +Patch400: kvm-mirror-Fail-gracefully-for-source-target.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch401: kvm-jobs-change-start-callback-to-run-callback.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch402: kvm-jobs-canonize-Error-object.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch403: kvm-jobs-add-exit-shim.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch404: kvm-block-commit-utilize-job_exit-shim.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch405: kvm-block-mirror-utilize-job_exit-shim.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch406: kvm-jobs-utilize-job_exit-shim.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch407: kvm-block-backup-make-function-variables-consistently-na.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch408: kvm-jobs-remove-ret-argument-to-job_completed-privatize-.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch409: kvm-jobs-remove-job_defer_to_main_loop.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch410: kvm-block-commit-add-block-job-creation-flags.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch411: kvm-block-mirror-add-block-job-creation-flags.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch412: kvm-block-stream-add-block-job-creation-flags.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch413: kvm-block-commit-refactor-commit-to-use-job-callbacks.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch414: kvm-block-mirror-don-t-install-backing-chain-on-abort.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch415: kvm-block-mirror-conservative-mirror_exit-refactor.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch416: kvm-block-stream-refactor-stream-to-use-job-callbacks.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch417: kvm-tests-blockjob-replace-Blockjob-with-Job.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch418: kvm-tests-test-blockjob-remove-exit-callback.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch419: kvm-tests-test-blockjob-txn-move-.exit-to-.clean.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch420: kvm-jobs-remove-.exit-callback.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch421: kvm-qapi-block-commit-expose-new-job-properties.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch422: kvm-qapi-block-mirror-expose-new-job-properties.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch423: kvm-qapi-block-stream-expose-new-job-properties.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch424: kvm-block-backup-qapi-documentation-fixup.patch +# For bz#1626061 - qemu blockjobs other than backup do not support job-finalize or job-dismiss +Patch425: kvm-blockdev-document-transactional-shortcomings.patch +# For bz#1624012 - allow using node-names with block-commit +Patch426: kvm-commit-Add-top-node-base-node-options.patch +# For bz#1624012 - allow using node-names with block-commit +Patch427: kvm-qemu-iotests-Test-commit-with-top-node-base-node.patch +# For bz#1610605 - rbd json format of 7.6 is incompatible with 7.5 +Patch428: kvm-block-rbd-pull-out-qemu_rbd_convert_options.patch +# For bz#1610605 - rbd json format of 7.6 is incompatible with 7.5 +Patch429: kvm-block-rbd-Attempt-to-parse-legacy-filenames.patch +# For bz#1610605 - rbd json format of 7.6 is incompatible with 7.5 +Patch430: kvm-block-rbd-add-iotest-for-rbd-legacy-keyvalue-filenam.patch +# For bz#1626059 - RHEL6 guest panics on boot if hotpluggable memory (pc-dimm) is present at boot time +Patch431: kvm-pc-acpi-revert-back-to-1-SRAT-entry-for-hotpluggable.patch +# For bz#1628373 - blockdev-backup does not accept bitmap parameter +Patch432: kvm-blockdev-backup-add-bitmap-argument.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch433: kvm-test-bdrv-drain-bdrv_drain-works-with-cross-AioConte.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch434: kvm-block-Use-bdrv_do_drain_begin-end-in-bdrv_drain_all.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch435: kvm-block-Remove-recursive-parameter-from-bdrv_drain_inv.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch436: kvm-block-Don-t-manually-poll-in-bdrv_drain_all.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch437: kvm-tests-test-bdrv-drain-bdrv_drain_all-works-in-corout.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch438: kvm-block-Avoid-unnecessary-aio_poll-in-AIO_WAIT_WHILE.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch439: kvm-block-Really-pause-block-jobs-on-drain.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch440: kvm-block-Remove-bdrv_drain_recurse.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch441: kvm-test-bdrv-drain-Add-test-for-node-deletion.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch442: kvm-block-Drain-recursively-with-a-single-BDRV_POLL_WHIL.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch443: kvm-test-bdrv-drain-Test-node-deletion-in-subtree-recurs.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch444: kvm-block-Don-t-poll-in-parent-drain-callbacks.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch445: kvm-test-bdrv-drain-Graph-change-through-parent-callback.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch446: kvm-block-Defer-.bdrv_drain_begin-callback-to-polling-ph.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch447: kvm-test-bdrv-drain-Test-that-bdrv_drain_invoke-doesn-t-.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch448: kvm-block-Allow-AIO_WAIT_WHILE-with-NULL-ctx.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch449: kvm-block-Move-bdrv_drain_all_begin-out-of-coroutine-con.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch450: kvm-block-ignore_bds_parents-parameter-for-drain-functio.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch451: kvm-block-Allow-graph-changes-in-bdrv_drain_all_begin-en.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch452: kvm-test-bdrv-drain-Test-graph-changes-in-drain_all-sect.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch453: kvm-block-Poll-after-drain-on-attaching-a-node.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch454: kvm-test-bdrv-drain-Test-bdrv_append-to-drained-node.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch455: kvm-block-linux-aio-acquire-AioContext-before-qemu_laio_.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch456: kvm-util-async-use-qemu_aio_coroutine_enter-in-co_schedu.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch457: kvm-job-Fix-nested-aio_poll-hanging-in-job_txn_apply.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch458: kvm-job-Fix-missing-locking-due-to-mismerge.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch459: kvm-blockjob-Wake-up-BDS-when-job-becomes-idle.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch460: kvm-aio-wait-Increase-num_waiters-even-in-home-thread.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch461: kvm-test-bdrv-drain-Drain-with-block-jobs-in-an-I-O-thre.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch462: kvm-test-blockjob-Acquire-AioContext-around-job_cancel_s.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch463: kvm-job-Use-AIO_WAIT_WHILE-in-job_finish_sync.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch464: kvm-test-bdrv-drain-Test-AIO_WAIT_WHILE-in-completion-ca.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch465: kvm-block-Add-missing-locking-in-bdrv_co_drain_bh_cb.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch466: kvm-block-backend-Add-.drained_poll-callback.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch467: kvm-block-backend-Fix-potential-double-blk_delete.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch468: kvm-block-backend-Decrease-in_flight-only-after-callback.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch469: kvm-mirror-Fix-potential-use-after-free-in-active-commit.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch470: kvm-blockjob-Lie-better-in-child_job_drained_poll.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch471: kvm-block-Remove-aio_poll-in-bdrv_drain_poll-variants.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch472: kvm-test-bdrv-drain-Test-nested-poll-in-bdrv_drain_poll_.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch473: kvm-job-Avoid-deadlocks-in-job_completed_txn_abort.patch +# For bz#1601212 - qemu coredumps on block-commit +Patch474: kvm-test-bdrv-drain-AIO_WAIT_WHILE-in-job-.commit-.abort.patch +# For bz#1628191 - ~40% virtio_blk disk performance drop for win2012r2 guest when comparing qemu-kvm-rhev-2.12.0-9 with qemu-kvm-rhev-2.12.0-12 +Patch475: kvm-aio-posix-fix-concurrent-access-to-poll_disable_cnt.patch +# For bz#1628191 - ~40% virtio_blk disk performance drop for win2012r2 guest when comparing qemu-kvm-rhev-2.12.0-9 with qemu-kvm-rhev-2.12.0-12 +Patch476: kvm-aio-posix-compute-timeout-before-polling.patch +# For bz#1628191 - ~40% virtio_blk disk performance drop for win2012r2 guest when comparing qemu-kvm-rhev-2.12.0-9 with qemu-kvm-rhev-2.12.0-12 +Patch477: kvm-aio-posix-do-skip-system-call-if-ctx-notifier-pollin.patch +# For bz#1618584 - block commit aborts with "Co-routine was already scheduled" +Patch478: kvm-test-bdrv-drain-Fix-outdated-comments.patch +# For bz#1618584 - block commit aborts with "Co-routine was already scheduled" +Patch479: kvm-block-Use-a-single-global-AioWait.patch +# For bz#1618584 - block commit aborts with "Co-routine was already scheduled" +Patch480: kvm-test-bdrv-drain-Test-draining-job-source-child-and-p.patch +# For bz#1638077 - Cross migration from RHEL7.5 to RHEL7.6 fails with cpu flag stibp [rhel-7.6.z] +Patch481: kvm-target-i386-cpu-Add-downstream-only-STIBP-CPUID-flag.patch +# For bz#1638835 - High Host CPU load for Windows 10 Guests (Update 1803) when idle [rhel-7.6.z] +Patch482: kvm-Re-enable-disabled-Hyper-V-enlightenments.patch +# For bz#1636148 - qemu NBD_CMD_CACHE flaws impacting non-qemu NBD clients +Patch483: kvm-nbd-server-fix-NBD_CMD_CACHE.patch +# For bz#1636148 - qemu NBD_CMD_CACHE flaws impacting non-qemu NBD clients +Patch484: kvm-nbd-fix-NBD_FLAG_SEND_CACHE-value.patch +# For bz#1629720 - [Intel 7.6 BUG][Crystal Ridge] pc_dimm_get_free_addr: assertion failed: (QEMU_ALIGN_UP(address_space_start, align) == address_space_start) +Patch485: kvm-pc-dimm-turn-alignment-assert-into-check.patch +# For bz#1629717 - qemu_ram_mmap: Assertion `is_power_of_2(align)' failed +Patch486: kvm-exec-check-that-alignment-is-a-power-of-two.patch +# For bz#1620373 - Failed to do migration after hotplug and hotunplug the ivshmem device +Patch487: kvm-nvdimm-no-need-to-overwrite-get_vmstate_memory_regio.patch +# For bz#1620373 - Failed to do migration after hotplug and hotunplug the ivshmem device +Patch488: kvm-hostmem-drop-error-variable-from-host_memory_backend.patch +# For bz#1620373 - Failed to do migration after hotplug and hotunplug the ivshmem device +Patch489: kvm-ivshmem-Fix-unplug-of-device-ivshmem-plain.patch +# For bz#1627272 - boot guest with q35+vIOMMU+ device assignment, qemu crash when return assigned network devices from vfio driver to ixgbe in guest +Patch490: kvm-qemu-error-introduce-error-warn-_report_once.patch +# For bz#1627272 - boot guest with q35+vIOMMU+ device assignment, qemu crash when return assigned network devices from vfio driver to ixgbe in guest +Patch491: kvm-intel-iommu-start-to-use-error_report_once.patch +# For bz#1627272 - boot guest with q35+vIOMMU+ device assignment, qemu crash when return assigned network devices from vfio driver to ixgbe in guest +Patch492: kvm-intel-iommu-replace-more-vtd_err_-traces.patch +# For bz#1627272 - boot guest with q35+vIOMMU+ device assignment, qemu crash when return assigned network devices from vfio driver to ixgbe in guest +Patch493: kvm-intel_iommu-introduce-vtd_reset_caches.patch +# For bz#1627272 - boot guest with q35+vIOMMU+ device assignment, qemu crash when return assigned network devices from vfio driver to ixgbe in guest +Patch494: kvm-intel_iommu-better-handling-of-dmar-state-switch.patch +# For bz#1627272 - boot guest with q35+vIOMMU+ device assignment, qemu crash when return assigned network devices from vfio driver to ixgbe in guest +Patch495: kvm-intel_iommu-move-ce-fetching-out-when-sync-shadow.patch +# For bz#1627272 - boot guest with q35+vIOMMU+ device assignment, qemu crash when return assigned network devices from vfio driver to ixgbe in guest +Patch496: kvm-intel_iommu-handle-invalid-ce-for-shadow-sync.patch +# For bz#1607406 - [RHEL 7.7] RFE: Define firmware metadata format +Patch497: kvm-qapi-fill-in-CpuInfoFast.arch-in-query-cpus-fast.patch +# For bz#1607406 - [RHEL 7.7] RFE: Define firmware metadata format +Patch498: kvm-qapi-add-SysEmuTarget-to-common.json.patch +# For bz#1607406 - [RHEL 7.7] RFE: Define firmware metadata format +Patch499: kvm-qapi-change-the-type-of-TargetInfo.arch-from-string-.patch +# For bz#1607406 - [RHEL 7.7] RFE: Define firmware metadata format +Patch500: kvm-qapi-discriminate-CpuInfoFast-on-SysEmuTarget-not-Cp.patch +# For bz#1607406 - [RHEL 7.7] RFE: Define firmware metadata format +Patch501: kvm-qapi-deprecate-CpuInfoFast.arch.patch +# For bz#1607406 - [RHEL 7.7] RFE: Define firmware metadata format +Patch502: kvm-docs-interop-add-firmware.json.patch +# For bz#1608877 - After postcopy migration, do savevm and loadvm, guest hang and call trace +Patch503: kvm-migration-postcopy-Clear-have_listen_thread.patch +# For bz#1608877 - After postcopy migration, do savevm and loadvm, guest hang and call trace +Patch504: kvm-migration-cleanup-in-error-paths-in-loadvm.patch +# For bz#1614302 - qemu-kvm: Could not find keytab file: /etc/qemu/krb5.tab: No such file or directory +Patch505: kvm-vnc-call-sasl_server_init-only-when-required.patch +# For bz#1623986 - block-commit can't be used with -blockdev +Patch506: kvm-block-Update-flags-in-bdrv_set_read_only.patch +# For bz#1623986 - block-commit can't be used with -blockdev +Patch507: kvm-block-Add-auto-read-only-option.patch +# For bz#1623986 - block-commit can't be used with -blockdev +Patch508: kvm-rbd-Close-image-in-qemu_rbd_open-error-path.patch +# For bz#1623986 - block-commit can't be used with -blockdev +Patch509: kvm-block-Require-auto-read-only-for-existing-fallbacks.patch +# For bz#1623986 - block-commit can't be used with -blockdev +Patch510: kvm-nbd-Support-auto-read-only-option.patch +# For bz#1623986 - block-commit can't be used with -blockdev +Patch511: kvm-file-posix-Support-auto-read-only-option.patch +# For bz#1623986 - block-commit can't be used with -blockdev +Patch512: kvm-curl-Support-auto-read-only-option.patch +# For bz#1623986 - block-commit can't be used with -blockdev +Patch513: kvm-gluster-Support-auto-read-only-option.patch +# For bz#1623986 - block-commit can't be used with -blockdev +Patch514: kvm-iscsi-Support-auto-read-only-option.patch +# For bz#1623986 - block-commit can't be used with -blockdev +Patch515: kvm-block-Make-auto-read-only-on-default-for-drive.patch +# For bz#1623986 - block-commit can't be used with -blockdev +Patch516: kvm-qemu-iotests-Test-auto-read-only-with-drive-and-bloc.patch +# For bz#1623986 - block-commit can't be used with -blockdev +Patch517: kvm-block-Fix-update-of-BDRV_O_AUTO_RDONLY-in-update_fla.patch +# For bz#1585155 - QEMU core dumped when hotplug memory exceeding host hugepages and with discard-data=yes +Patch518: kvm-memory-cleanup-side-effects-of-memory_region_init_fo.patch +# For bz#1633536 - Qemu core dump when do migration after hot plugging a backend image with 'blockdev-add'(without the frontend) +Patch519: kvm-block-Don-t-inactivate-children-before-parents.patch +# For bz#1633536 - Qemu core dump when do migration after hot plugging a backend image with 'blockdev-add'(without the frontend) +Patch520: kvm-iotests-Test-migration-with-blockdev.patch +# For bz#1607768 - qemu aborted when start guest with a big iothreads +Patch521: kvm-iothread-fix-crash-with-invalid-properties.patch +# For bz#1619778 - Ballooning is incompatible with vfio assigned devices, but not prevented +Patch522: kvm-balloon-Allow-multiple-inhibit-users.patch +# For bz#1619778 - Ballooning is incompatible with vfio assigned devices, but not prevented +Patch523: kvm-Use-inhibit-to-prevent-ballooning-without-synchr.patch +# For bz#1619778 - Ballooning is incompatible with vfio assigned devices, but not prevented +Patch524: kvm-vfio-Inhibit-ballooning-based-on-group-attachment-to.patch +# For bz#1619778 - Ballooning is incompatible with vfio assigned devices, but not prevented +Patch525: kvm-vfio-ccw-pci-Allow-devices-to-opt-in-for-ballooning.patch +# For bz#1619778 - Ballooning is incompatible with vfio assigned devices, but not prevented +Patch526: kvm-vfio-pci-Handle-subsystem-realpath-returning-NULL.patch +# For bz#1619778 - Ballooning is incompatible with vfio assigned devices, but not prevented +Patch527: kvm-vfio-pci-Fix-failure-to-close-file-descriptor-on-err.patch +# For bz#1619778 - Ballooning is incompatible with vfio assigned devices, but not prevented +Patch528: kvm-postcopy-Synchronize-usage-of-the-balloon-inhibitor.patch +# For bz#1566195 - Guests don't always see correct transfer limits for passed through scsi devices (e.g. raid0 controlled by sas 9361-8i) +Patch529: kvm-include-Add-IEC-binary-prefixes-in-qemu-units.h.patch +# For bz#1566195 - Guests don't always see correct transfer limits for passed through scsi devices (e.g. raid0 controlled by sas 9361-8i) +Patch530: kvm-hw-scsi-cleanups-before-VPD-BL-emulation.patch +# For bz#1566195 - Guests don't always see correct transfer limits for passed through scsi devices (e.g. raid0 controlled by sas 9361-8i) +Patch531: kvm-hw-scsi-centralize-SG_IO-calls-into-single-function.patch +# For bz#1566195 - Guests don't always see correct transfer limits for passed through scsi devices (e.g. raid0 controlled by sas 9361-8i) +Patch532: kvm-hw-scsi-add-VPD-Block-Limits-emulation.patch +# For bz#1566195 - Guests don't always see correct transfer limits for passed through scsi devices (e.g. raid0 controlled by sas 9361-8i) +Patch533: kvm-scsi-disk-Block-Device-Characteristics-emulation-fix.patch +# For bz#1566195 - Guests don't always see correct transfer limits for passed through scsi devices (e.g. raid0 controlled by sas 9361-8i) +Patch534: kvm-scsi-generic-keep-VPD-page-list-sorted.patch +# For bz#1566195 - Guests don't always see correct transfer limits for passed through scsi devices (e.g. raid0 controlled by sas 9361-8i) +Patch535: kvm-scsi-generic-avoid-out-of-bounds-access-to-VPD-page-.patch +# For bz#1566195 - Guests don't always see correct transfer limits for passed through scsi devices (e.g. raid0 controlled by sas 9361-8i) +Patch536: kvm-scsi-generic-avoid-invalid-access-to-struct-when-emu.patch +# For bz#1566195 - Guests don't always see correct transfer limits for passed through scsi devices (e.g. raid0 controlled by sas 9361-8i) +Patch537: kvm-scsi-generic-do-not-do-VPD-emulation-for-sense-other.patch +# For bz#1626347 - RHEL7.6 - [Power8][Host: 3.10.0-933.el7.ppc64le][Guest: 3.10.0-933.el7.ppc64le] [qemu-kvm-ma-2.12.0-8.el7.ppc64le] Guest VM crashes during vcpu hotplug with specific numa configuration (kvm) +Patch538: kvm-spapr-Fix-ibm-max-associativity-domains-property-num.patch +# For bz#1626347 - RHEL7.6 - [Power8][Host: 3.10.0-933.el7.ppc64le][Guest: 3.10.0-933.el7.ppc64le] [qemu-kvm-ma-2.12.0-8.el7.ppc64le] Guest VM crashes during vcpu hotplug with specific numa configuration (kvm) +Patch539: kvm-spapr-Add-H-Call-H_HOME_NODE_ASSOCIATIVITY.patch +# For bz#1628098 - [Intel 7.7 BUG][KVM][Crystal Ridge]object_get_canonical_path_component: assertion failed: (obj->parent != NULL) +Patch540: kvm-hostmem-file-remove-object-id-from-pmem-error-messag.patch +# For bz#1614610 - Guest quit with error when hotunplug cpu +Patch541: kvm-cpus-ignore-ESRCH-in-qemu_cpu_kick_thread.patch +# For bz#1658426 - qemu core dumped when doing incremental live backup without "bitmap" parameter by mistake in a transaction mode((blockdev-backup/block-dirty-bitmap-add/x-block-dirty-bitmap-merge) +Patch542: kvm-blockdev-abort-transactions-in-reverse-order.patch +# For bz#1658426 - qemu core dumped when doing incremental live backup without "bitmap" parameter by mistake in a transaction mode((blockdev-backup/block-dirty-bitmap-add/x-block-dirty-bitmap-merge) +Patch543: kvm-block-dirty-bitmap-remove-assertion-from-restore.patch +# For bz#1531888 - Local VM and migrated VM on the same host can run with same RAW file as visual disk source while without shareable configured or lock manager enabled +Patch544: kvm-block-Fix-invalidate_cache-error-path-for-parent-act.patch +# For bz#1598119 - "share-rw=on" does not work for luks format image +Patch545: kvm-luks-Allow-share-rw-on.patch +# For bz#1551486 - QEMU image locking needn't double open fd number (i.e. drop file-posix.c:s->lock_fd) +Patch546: kvm-iotests-153-Fix-dead-code.patch +# For bz#1551486 - QEMU image locking needn't double open fd number (i.e. drop file-posix.c:s->lock_fd) +Patch547: kvm-file-posix-Include-filename-in-locking-error-message.patch +# For bz#1551486 - QEMU image locking needn't double open fd number (i.e. drop file-posix.c:s->lock_fd) +Patch548: kvm-file-posix-Skip-effectiveless-OFD-lock-operations.patch +# For bz#1551486 - QEMU image locking needn't double open fd number (i.e. drop file-posix.c:s->lock_fd) +Patch549: kvm-file-posix-Drop-s-lock_fd.patch +# For bz#1551486 - QEMU image locking needn't double open fd number (i.e. drop file-posix.c:s->lock_fd) +Patch550: kvm-tests-Add-unit-tests-for-image-locking.patch +# For bz#1551486 - QEMU image locking needn't double open fd number (i.e. drop file-posix.c:s->lock_fd) +Patch551: kvm-file-posix-Fix-shared-locks-on-reopen-commit.patch +# For bz#1551486 - QEMU image locking needn't double open fd number (i.e. drop file-posix.c:s->lock_fd) +Patch552: kvm-iotests-Test-file-posix-locking-and-reopen.patch +# For bz#1673080 - "An unknown error has occurred" when using cdrom to install the system with two blockdev disks.(when choose installation destination) +Patch553: kvm-scsi-disk-Don-t-use-empty-string-as-device-id.patch +# For bz#1673080 - "An unknown error has occurred" when using cdrom to install the system with two blockdev disks.(when choose installation destination) +Patch554: kvm-scsi-disk-Add-device_id-property.patch +# For bz#1668999 - CVE-2019-6501 qemu-kvm-rhev: QEMU: scsi-generic: possible OOB access while handling inquiry request [rhel-7] +Patch555: kvm-scsi-generic-avoid-possible-out-of-bounds-access-to-.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch556: kvm-block-remove-bdrv_dirty_bitmap_make_anon.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch557: kvm-block-simplify-code-around-releasing-bitmaps.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch558: kvm-hbitmap-Add-advance-param-to-hbitmap_iter_next.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch559: kvm-test-hbitmap-Add-non-advancing-iter_next-tests.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch560: kvm-block-dirty-bitmap-Add-bdrv_dirty_iter_next_area.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch561: kvm-dirty-bitmap-switch-assert-fails-to-errors-in-bdrv_m.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch562: kvm-dirty-bitmap-rename-bdrv_undo_clear_dirty_bitmap.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch563: kvm-dirty-bitmap-make-it-possible-to-restore-bitmap-afte.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch564: kvm-blockdev-rename-block-dirty-bitmap-clear-transaction.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch565: kvm-qapi-add-transaction-support-for-x-block-dirty-bitma.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch566: kvm-block-dirty-bitmaps-add-user_locked-status-checker.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch567: kvm-block-dirty-bitmaps-fix-merge-permissions.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch568: kvm-block-dirty-bitmaps-allow-clear-on-disabled-bitmaps.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch569: kvm-block-dirty-bitmaps-prohibit-enable-disable-on-locke.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch570: kvm-block-backup-prohibit-backup-from-using-in-use-bitma.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch571: kvm-nbd-forbid-use-of-frozen-bitmaps.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch572: kvm-bitmap-Update-count-after-a-merge.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch573: kvm-iotests-169-drop-deprecated-autoload-parameter.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch574: kvm-block-qcow2-improve-error-message-in-qcow2_inactivat.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch575: kvm-bloc-qcow2-drop-dirty_bitmaps_loaded-state-variable.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch576: kvm-dirty-bitmaps-clean-up-bitmaps-loading-and-migration.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch577: kvm-iotests-improve-169.patch +# For bz#1658343 - QEMU incremental backup fixes and improvements (from upstream and in RHEL8) +Patch578: kvm-iotests-169-add-cases-for-source-vm-resuming.patch +# For bz#1597482 - qemu crashed when disk enable the IOMMU +Patch579: kvm-exec-move-memory-access-declarations-to-a-common-hea.patch +# For bz#1597482 - qemu crashed when disk enable the IOMMU +Patch580: kvm-exec-small-changes-to-flatview_do_translate.patch +# For bz#1597482 - qemu crashed when disk enable the IOMMU +Patch581: kvm-exec-extract-address_space_translate_iommu-fix-page_.patch +# For bz#1597482 - qemu crashed when disk enable the IOMMU +Patch582: kvm-exec-reintroduce-MemoryRegion-caching.patch +# For bz#1597482 - qemu crashed when disk enable the IOMMU +Patch583: kvm-virtio-update-MemoryRegionCaches-when-guest-negotiat.patch +# For bz#1597482 - qemu crashed when disk enable the IOMMU +Patch584: kvm-exec-Fix-MAP_RAM-for-cached-access.patch +# For bz#1597482 - qemu crashed when disk enable the IOMMU +Patch585: kvm-virtio-Return-true-from-virtio_queue_empty-if-broken.patch +# For bz#1631615 - Wrong werror default for -device drive= +Patch586: kvm-block-backend-Set-werror-rerror-defaults-in-blk_new.patch +# For bz#1667320 - -blockdev: auto-read-only is ineffective for drivers on read-only whitelist +Patch587: kvm-block-Apply-auto-read-only-for-ro-whitelist-drivers.patch +# For bz#1656913 - qcow2 cache is too small +Patch588: kvm-qcow2-Give-the-refcount-cache-the-minimum-possible-s.patch +# For bz#1656913 - qcow2 cache is too small +Patch589: kvm-docs-Document-the-new-default-sizes-of-the-qcow2-cac.patch +# For bz#1656913 - qcow2 cache is too small +Patch590: kvm-qcow2-Fix-Coverity-warning-when-calculating-the-refc.patch +# For bz#1656913 - qcow2 cache is too small +Patch591: kvm-qcow2-Options-documentation-fixes.patch +# For bz#1656913 - qcow2 cache is too small +Patch592: kvm-include-Add-a-lookup-table-of-sizes.patch +# For bz#1656913 - qcow2 cache is too small +Patch593: kvm-qcow2-Make-sizes-more-humanly-readable.patch +# For bz#1656913 - qcow2 cache is too small +Patch594: kvm-qcow2-Avoid-duplication-in-setting-the-refcount-cach.patch +# For bz#1656913 - qcow2 cache is too small +Patch595: kvm-qcow2-Assign-the-L2-cache-relatively-to-the-image-si.patch +# For bz#1656913 - qcow2 cache is too small +Patch596: kvm-qcow2-Increase-the-default-upper-limit-on-the-L2-cac.patch +# For bz#1656913 - qcow2 cache is too small +Patch597: kvm-qcow2-Resize-the-cache-upon-image-resizing.patch +# For bz#1656913 - qcow2 cache is too small +Patch598: kvm-qcow2-Set-the-default-cache-clean-interval-to-10-min.patch +# For bz#1656913 - qcow2 cache is too small +Patch599: kvm-qcow2-Explicit-number-replaced-by-a-constant.patch +# For bz#1656913 - qcow2 cache is too small +Patch600: kvm-qcow2-Fix-cache-clean-interval-documentation.patch +# For bz#1671173 - qemu with iothreads enabled crashes on resume after enospc pause for disk extension +Patch601: kvm-block-backend-Make-blk_inc-dec_in_flight-public.patch +# For bz#1671173 - qemu with iothreads enabled crashes on resume after enospc pause for disk extension +Patch602: kvm-virtio-blk-Increase-in_flight-for-request-restart-BH.patch +# For bz#1671173 - qemu with iothreads enabled crashes on resume after enospc pause for disk extension +Patch603: kvm-block-Fix-AioContext-switch-for-drained-node.patch +# For bz#1671173 - qemu with iothreads enabled crashes on resume after enospc pause for disk extension +Patch604: kvm-test-bdrv-drain-AioContext-switch-in-drained-section.patch +# For bz#1671173 - qemu with iothreads enabled crashes on resume after enospc pause for disk extension +Patch605: kvm-block-Use-normal-drain-for-bdrv_set_aio_context.patch +# For bz#1648236 - QEMU doesn't expose rendernode option for egl-headless display type +Patch606: kvm-qapi-Add-rendernode-display-option-for-egl-headless.patch +# For bz#1648236 - QEMU doesn't expose rendernode option for egl-headless display type +Patch607: kvm-ui-Allow-specifying-rendernode-display-option-for-eg.patch +# For bz#1648236 - QEMU doesn't expose rendernode option for egl-headless display type +Patch608: kvm-qapi-add-query-display-options-command.patch +# For bz#1648236 - QEMU doesn't expose rendernode option for egl-headless display type +Patch609: kvm-egl-headless-parse-rendernode.patch +# For bz#1685989 - Add facility to use block jobs with backing images without write permission +Patch610: kvm-tests-virtio-blk-test-Disable-auto-read-only.patch +# For bz#1685989 - Add facility to use block jobs with backing images without write permission +Patch611: kvm-block-Avoid-useless-local_err.patch +# For bz#1685989 - Add facility to use block jobs with backing images without write permission +Patch612: kvm-block-Simplify-bdrv_reopen_abort.patch +# For bz#1685989 - Add facility to use block jobs with backing images without write permission +Patch613: kvm-block-Always-abort-reopen-after-prepare-succeeded.patch +# For bz#1685989 - Add facility to use block jobs with backing images without write permission +Patch614: kvm-block-Make-permission-changes-in-reopen-less-wrong.patch +# For bz#1685989 - Add facility to use block jobs with backing images without write permission +Patch615: kvm-block-Fix-use-after-free-error-in-bdrv_open_inherit.patch +# For bz#1685989 - Add facility to use block jobs with backing images without write permission +Patch616: kvm-qemu-iotests-Test-snapshot-on-with-nonexistent-TMPDI.patch +# For bz#1685989 - Add facility to use block jobs with backing images without write permission +Patch617: kvm-file-posix-Fix-bdrv_open_flags-for-snapshot-on.patch +# For bz#1685989 - Add facility to use block jobs with backing images without write permission +Patch618: kvm-file-posix-Use-error-API-properly.patch +# For bz#1685989 - Add facility to use block jobs with backing images without write permission +Patch619: kvm-file-posix-Factor-out-raw_reconfigure_getfd.patch +# For bz#1685989 - Add facility to use block jobs with backing images without write permission +Patch620: kvm-file-posix-Store-BDRVRawState.reopen_state-during-re.patch +# For bz#1685989 - Add facility to use block jobs with backing images without write permission +Patch621: kvm-file-posix-Lock-new-fd-in-raw_reopen_prepare.patch +# For bz#1685989 - Add facility to use block jobs with backing images without write permission +Patch622: kvm-file-posix-Prepare-permission-code-for-fd-switching.patch +# For bz#1685989 - Add facility to use block jobs with backing images without write permission +Patch623: kvm-file-posix-Make-auto-read-only-dynamic.patch +# For bz#1668956 - incremental backup bitmap API needs a finalized interface +Patch624: kvm-qapi-bitmap-merge-document-name-change.patch +# For bz#1668956 - incremental backup bitmap API needs a finalized interface +Patch625: kvm-iotests-Enhance-223-to-cover-multiple-bitmap-granula.patch +# For bz#1668956 - incremental backup bitmap API needs a finalized interface +Patch626: kvm-iotests-Unify-log-outputs-between-Python-2-and-3.patch +# For bz#1668956 - incremental backup bitmap API needs a finalized interface +Patch627: kvm-blockdev-n-ary-bitmap-merge.patch +# For bz#1668956 - incremental backup bitmap API needs a finalized interface +Patch628: kvm-block-remove-x-prefix-from-experimental-bitmap-APIs.patch +# For bz#1668956 - incremental backup bitmap API needs a finalized interface +Patch629: kvm-iotests.py-don-t-abort-if-IMGKEYSECRET-is-undefined.patch +# For bz#1668956 - incremental backup bitmap API needs a finalized interface +Patch630: kvm-iotests-add-filter_generated_node_ids.patch +# For bz#1668956 - incremental backup bitmap API needs a finalized interface +Patch631: kvm-iotests-add-qmp-recursive-sorting-function.patch +# For bz#1668956 - incremental backup bitmap API needs a finalized interface +Patch632: kvm-iotests-remove-default-filters-from-qmp_log.patch +# For bz#1668956 - incremental backup bitmap API needs a finalized interface +Patch633: kvm-iotests-change-qmp_log-filters-to-expect-QMP-objects.patch +# For bz#1668956 - incremental backup bitmap API needs a finalized interface +Patch634: kvm-iotests-implement-pretty-print-for-log-and-qmp_log.patch +# For bz#1668956 - incremental backup bitmap API needs a finalized interface +Patch635: kvm-iotests-add-iotest-236-for-testing-bitmap-merge.patch +# For bz#1668956 - incremental backup bitmap API needs a finalized interface +Patch636: kvm-iotests-236-fix-transaction-kwarg-order.patch +# For bz#1668956 - incremental backup bitmap API needs a finalized interface +Patch637: kvm-iotests-Re-add-filename-filters.patch +# For bz#1668956 - incremental backup bitmap API needs a finalized interface +Patch638: kvm-iotests-Remove-superfluous-rm-from-232.patch +# For bz#1668956 - incremental backup bitmap API needs a finalized interface +Patch639: kvm-iotests-Fix-232-for-LUKS.patch +# For bz#1668956 - incremental backup bitmap API needs a finalized interface +Patch640: kvm-iotests-Fix-207-to-use-QMP-filters-for-qmp_log.patch +# For bz#1668956 - incremental backup bitmap API needs a finalized interface +Patch641: kvm-iotests.py-Add-is_str.patch +# For bz#1668956 - incremental backup bitmap API needs a finalized interface +Patch642: kvm-iotests.py-Filter-filename-in-any-string-value.patch +# For bz#1691018 - Fix iotest 226 for local development builds +Patch643: kvm-iotest-Fix-filtering-order-in-226.patch +# For bz#1691018 - Fix iotest 226 for local development builds +Patch644: kvm-iotests-Don-t-lock-dev-null-in-226.patch +# For bz#1691048 - Add qemu-img info support for querying bitmaps offline +Patch645: kvm-dirty-bitmap-improve-bdrv_dirty_bitmap_next_zero.patch +# For bz#1691048 - Add qemu-img info support for querying bitmaps offline +Patch646: kvm-tests-add-tests-for-hbitmap_next_zero-with-specified.patch +# For bz#1691048 - Add qemu-img info support for querying bitmaps offline +Patch647: kvm-dirty-bitmap-add-bdrv_dirty_bitmap_next_dirty_area.patch +# For bz#1691048 - Add qemu-img info support for querying bitmaps offline +Patch648: kvm-tests-add-tests-for-hbitmap_next_dirty_area.patch +# For bz#1691048 - Add qemu-img info support for querying bitmaps offline +Patch649: kvm-Revert-block-dirty-bitmap-Add-bdrv_dirty_iter_next_a.patch +# For bz#1691048 - Add qemu-img info support for querying bitmaps offline +Patch650: kvm-Revert-test-hbitmap-Add-non-advancing-iter_next-test.patch +# For bz#1691048 - Add qemu-img info support for querying bitmaps offline +Patch651: kvm-Revert-hbitmap-Add-advance-param-to-hbitmap_iter_nex.patch +# For bz#1691048 - Add qemu-img info support for querying bitmaps offline +Patch652: kvm-bdrv_query_image_info-Error-parameter-added.patch +# For bz#1691048 - Add qemu-img info support for querying bitmaps offline +Patch653: kvm-qcow2-Add-list-of-bitmaps-to-ImageInfoSpecificQCow2.patch +# For bz#1691048 - Add qemu-img info support for querying bitmaps offline +Patch654: kvm-qcow2-list-of-bitmaps-new-test-242.patch +# For bz#1672010 - [RHEL7]Qemu coredump when remove a persistent bitmap after vm re-start(dataplane enabled) +Patch655: kvm-blockdev-acquire-aio_context-for-bitmap-add-remove.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch656: kvm-nbd-client-fix-nbd_negotiate_simple_meta_context.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch657: kvm-nbd-client-Fix-error-messages-during-NBD_INFO_BLOCK_.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch658: kvm-nbd-client-Relax-handling-of-large-NBD_CMD_BLOCK_STA.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch659: kvm-tests-Simplify-.gitignore.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch660: kvm-iotests-nbd-Stop-qemu-nbd-before-remaking-image.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch661: kvm-iotests-Disallow-compat-0.10-in-223.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch662: kvm-nbd-server-fix-bitmap-export.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch663: kvm-nbd-server-send-more-than-one-extent-of-base-allocat.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch664: kvm-nbd-Don-t-take-address-of-fields-in-packed-structs.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch665: kvm-qemu-nbd-Document-tls-creds.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch666: kvm-qemu-nbd-drop-old-style-negotiation.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch667: kvm-nbd-server-drop-old-style-negotiation.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch668: kvm-qemu-iotests-remove-unused-variable-here.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch669: kvm-qemu-iotests-convert-pwd-and-pwd-to-PWD.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch670: kvm-qemu-iotests-Modern-shell-scripting-use-instead-of.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch671: kvm-nbd-fix-whitespace-in-server-error-message.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch672: kvm-nbd-server-Ignore-write-errors-when-replying-to-NBD_.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch673: kvm-io-return-0-for-EOF-in-TLS-session-read-after-shutdo.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch674: kvm-tests-pull-qemu-nbd-iotest-helpers-into-common.nbd-f.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch675: kvm-tests-check-if-qemu-nbd-is-still-alive-before-waitin.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch676: kvm-tests-add-iotests-helpers-for-dealing-with-TLS-certi.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch677: kvm-tests-exercise-NBD-server-in-TLS-mode.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch678: kvm-iotests-Also-test-I-O-over-NBD-TLS.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch679: kvm-iotests-Drop-use-of-bash-keyword-function.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch680: kvm-nbd-server-Advertise-all-contexts-in-response-to-bar.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch681: kvm-nbd-client-Make-x-dirty-bitmap-more-reliable.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch682: kvm-nbd-client-Send-NBD_CMD_DISC-if-open-fails-after-con.patch +# For bz#1691563 - QEMU NBD Feature parity roundup (QEMU 3.1.0) +Patch683: kvm-iotests-fix-nbd-test-233-to-work-correctly-with-raw-.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch684: kvm-iotests-Skip-233-if-certtool-not-installed.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch685: kvm-iotests-Make-nbd-fault-injector-flush.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch686: kvm-iotests-make-083-specific-to-raw.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch687: kvm-nbd-publish-_lookup-functions.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch688: kvm-nbd-client-Trace-all-server-option-error-messages.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch689: kvm-block-nbd-client-use-traces-instead-of-noisy-error_r.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch690: kvm-qemu-nbd-Use-program-name-in-error-messages.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch691: kvm-nbd-Document-timeline-of-various-features.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch692: kvm-nbd-client-More-consistent-error-messages.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch693: kvm-qemu-nbd-Fail-earlier-for-c-d-on-non-linux.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch694: kvm-nbd-client-Drop-pointless-buf-variable.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch695: kvm-qemu-nbd-Rename-exp-variable-clashing-with-math-exp-.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch696: kvm-nbd-Add-some-error-case-testing-to-iotests-223.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch697: kvm-nbd-Forbid-nbd-server-stop-when-server-is-not-runnin.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch698: kvm-nbd-Only-require-disabled-bitmap-for-read-only-expor.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch699: kvm-nbd-Merge-nbd_export_set_name-into-nbd_export_new.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch700: kvm-nbd-Allow-bitmap-export-during-QMP-nbd-server-add.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch701: kvm-nbd-Remove-x-nbd-server-add-bitmap.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch702: kvm-nbd-Merge-nbd_export_bitmap-into-nbd_export_new.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch703: kvm-qemu-nbd-Add-bitmap-NAME-option.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch704: kvm-qom-Clean-up-error-reporting-in-user_creatable_add_o.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch705: kvm-qemu-img-fix-error-reporting-for-object.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch706: kvm-iotests-Make-233-output-more-reliable.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch707: kvm-maint-Allow-for-EXAMPLES-in-texi2pod.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch708: kvm-qemu-nbd-Enhance-man-page.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch709: kvm-qemu-nbd-Sanity-check-partition-bounds.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch710: kvm-nbd-server-Hoist-length-check-to-qmp_nbd_server_add.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch711: kvm-nbd-server-Favor-u-int64_t-over-off_t.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch712: kvm-qemu-nbd-Avoid-strtol-open-coding.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch713: kvm-nbd-client-Refactor-nbd_receive_list.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch714: kvm-nbd-client-Move-export-name-into-NBDExportInfo.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch715: kvm-nbd-client-Change-signature-of-nbd_negotiate_simple_.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch716: kvm-nbd-client-Split-out-nbd_send_meta_query.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch717: kvm-nbd-client-Split-out-nbd_receive_one_meta_context.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch718: kvm-nbd-client-Refactor-return-of-nbd_receive_negotiate.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch719: kvm-nbd-client-Split-handshake-into-two-functions.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch720: kvm-nbd-client-Pull-out-oldstyle-size-determination.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch721: kvm-nbd-client-Refactor-nbd_opt_go-to-support-NBD_OPT_IN.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch722: kvm-nbd-client-Add-nbd_receive_export_list.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch723: kvm-nbd-client-Add-meta-contexts-to-nbd_receive_export_l.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch724: kvm-qemu-nbd-Add-list-option.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch725: kvm-nbd-client-Work-around-3.0-bug-for-listing-meta-cont.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch726: kvm-iotests-Enhance-223-233-to-cover-qemu-nbd-list.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch727: kvm-qemu-nbd-Deprecate-qemu-nbd-partition.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch728: kvm-nbd-generalize-usage-of-nbd_read.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch729: kvm-block-nbd-client-split-channel-errors-from-export-er.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch730: kvm-block-nbd-move-connection-code-from-block-nbd-to-blo.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch731: kvm-block-nbd-client-split-connection-from-initializatio.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch732: kvm-block-nbd-client-fix-nbd_reply_chunk_iter_receive.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch733: kvm-block-nbd-client-don-t-check-ioc.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch734: kvm-block-nbd-client-rename-read_reply_co-to-connection_.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch735: kvm-nbd-server-Kill-pointless-shadowed-variable.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch736: kvm-iotests-ensure-we-print-nbd-server-log-on-error.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch737: kvm-iotests-avoid-broken-pipe-with-certtool.patch +# For bz#1691009 - NBD pull mode incremental backup API needs a finalized interface +Patch738: kvm-iotests-Wait-for-qemu-to-end-in-223.patch +# For bz#1689793 - CVE-2019-9824 qemu-kvm-rhev: QEMU: Slirp: information leakage in tcp_emu() due to uninitialized stack variables [rhel-7] +Patch740: kvm-slirp-check-sscanf-result-when-emulating-ident.patch +# For bz#1677073 - Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7 +Patch741: kvm-dirty-bitmap-Expose-persistent-flag-to-query-block.patch +# For bz#1677073 - Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7 +Patch742: kvm-block-dirty-bitmap-Documentation-and-Comment-fixups.patch +# For bz#1677073 - Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7 +Patch743: kvm-block-dirty-bitmap-add-recording-and-busy-properties.patch +# For bz#1677073 - Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7 +Patch744: kvm-block-dirty-bitmaps-rename-frozen-predicate-helper.patch +# For bz#1677073 - Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7 +Patch745: kvm-block-dirty-bitmap-remove-set-reset-assertions-again.patch +# For bz#1677073 - Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7 +Patch746: kvm-block-dirty-bitmap-change-semantics-of-enabled-predi.patch +# For bz#1677073 - Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7 +Patch747: kvm-nbd-change-error-checking-order-for-bitmaps.patch +# For bz#1677073 - Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7 +Patch748: kvm-block-dirty-bitmap-explicitly-lock-bitmaps-with-succ.patch +# For bz#1677073 - Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7 +Patch749: kvm-block-dirty-bitmaps-unify-qmp_locked-and-user_locked.patch +# For bz#1677073 - Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7 +Patch750: kvm-block-dirty-bitmaps-move-comment-block.patch +# For bz#1677073 - Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7 +Patch751: kvm-blockdev-remove-unused-paio-parameter-documentation.patch +# For bz#1677073 - Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7 +Patch752: kvm-iotests-add-busy-recording-bit-test-to-124.patch +# For bz#1677073 - Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7 +Patch753: kvm-block-dirty-bitmaps-add-inconsistent-bit.patch +# For bz#1677073 - Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7 +Patch754: kvm-block-dirty-bitmap-add-inconsistent-status.patch +# For bz#1677073 - Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7 +Patch755: kvm-block-dirty-bitmaps-add-block_dirty_bitmap_check-fun.patch +# For bz#1677073 - Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7 +Patch756: kvm-block-dirty-bitmaps-prohibit-readonly-bitmaps-for-ba.patch +# For bz#1677073 - Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7 +Patch757: kvm-block-dirty-bitmaps-prohibit-removing-readonly-bitma.patch +# For bz#1677073 - Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7 +Patch758: kvm-block-dirty-bitmaps-disallow-busy-bitmaps-as-merge-s.patch +# For bz#1677073 - Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7 +Patch759: kvm-block-dirty-bitmaps-implement-inconsistent-bit.patch +# For bz#1677073 - Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7 +Patch760: kvm-bitmaps-Fix-typo-in-function-name.patch +# For bz#1603104 - Qemu Aborted (core dumped) for 'qemu-kvm: Failed to lock byte 100' when remote NFS or GlusterFS volume stopped during the block mirror(or block commit/stream) process +Patch762: kvm-block-file-posix-do-not-fail-on-unlock-bytes.patch +# For bz#1666884 - persistent bitmaps prevent qcow2 image resize +Patch763: kvm-docs-interop-qcow2-Improve-bitmap-flag-in_use-specif.patch +# For bz#1666884 - persistent bitmaps prevent qcow2 image resize +Patch764: kvm-block-qcow2-bitmap-Don-t-check-size-for-IN_USE-bitma.patch +# For bz#1666884 - persistent bitmaps prevent qcow2 image resize +Patch765: kvm-block-qcow2-bitmap-Allow-resizes-with-persistent-bit.patch +# For bz#1666884 - persistent bitmaps prevent qcow2 image resize +Patch766: kvm-tests-qemu-iotests-add-bitmap-resize-test-246.patch +# For bz#1691519 - physical bits should <= 48 when host with 5level paging &EPT5 and qemu command with "-cpu qemu64" parameters. +Patch768: kvm-x86-host-phys-bits-limit-option.patch +# For bz#1691519 - physical bits should <= 48 when host with 5level paging &EPT5 and qemu command with "-cpu qemu64" parameters. +Patch769: kvm-rhel-Set-host-phys-bits-limit-48-on-rhel-machine-typ.patch +# For bz#1693115 - CVE-2018-20815 qemu-kvm-rhev: QEMU: device_tree: heap buffer overflow while loading device tree blob [rhel-7.7] +Patch770: kvm-device_tree-Fix-integer-overflowing-in-load_device_t.patch +# For bz#1672819 - RHEL7.6/RHV4.2 - qemu doesn't free up hugepage memory when hotplug/hotunplug using memory-backend-file (qemu-kvm-rhev) +Patch771: kvm-mmap-alloc-unfold-qemu_ram_mmap.patch +# For bz#1672819 - RHEL7.6/RHV4.2 - qemu doesn't free up hugepage memory when hotplug/hotunplug using memory-backend-file (qemu-kvm-rhev) +Patch772: kvm-mmap-alloc-fix-hugetlbfs-misaligned-length-in-ppc64.patch +# For bz#1537776 - [Intel 7.7 Feat] KVM Enabling SnowRidge new NIs - qemu-kvm-rhev +Patch773: kvm-x86-cpu-Enable-CLDEMOTE-Demote-Cache-Line-cpu-featur.patch +# For bz#1693879 - RHV VM pauses when 'dd' issued inside guest to a direct lun configured as virtio-scsi with scsi-passthrough +Patch774: kvm-scsi-generic-prevent-guest-from-exceeding-SG_IO-limi.patch +# For bz#1666336 - severe performance impact using encrypted Cinder volume (QEMU luks) +Patch775: kvm-tests-crypto-Use-the-IEC-binary-prefix-definitions.patch +# For bz#1666336 - severe performance impact using encrypted Cinder volume (QEMU luks) +Patch776: kvm-crypto-expand-algorithm-coverage-for-cipher-benchmar.patch +# For bz#1666336 - severe performance impact using encrypted Cinder volume (QEMU luks) +Patch777: kvm-crypto-remove-code-duplication-in-tweak-encrypt-decr.patch +# For bz#1666336 - severe performance impact using encrypted Cinder volume (QEMU luks) +Patch778: kvm-crypto-introduce-a-xts_uint128-data-type.patch +# For bz#1666336 - severe performance impact using encrypted Cinder volume (QEMU luks) +Patch779: kvm-crypto-convert-xts_tweak_encdec-to-use-xts_uint128-t.patch +# For bz#1666336 - severe performance impact using encrypted Cinder volume (QEMU luks) +Patch780: kvm-crypto-convert-xts_mult_x-to-use-xts_uint128-type.patch +# For bz#1666336 - severe performance impact using encrypted Cinder volume (QEMU luks) +Patch781: kvm-crypto-annotate-xts_tweak_encdec-as-inlineable.patch +# For bz#1666336 - severe performance impact using encrypted Cinder volume (QEMU luks) +Patch782: kvm-crypto-refactor-XTS-cipher-mode-test-suite.patch +# For bz#1666336 - severe performance impact using encrypted Cinder volume (QEMU luks) +Patch783: kvm-crypto-add-testing-for-unaligned-buffers-with-XTS-ci.patch +# For bz#1631227 - Qemu Core dump when quit vm that's in status "paused(io-error)" with data plane enabled +Patch784: kvm-block-Fix-AioContext-switch-for-bs-drv-NULL.patch +# For bz#1692018 - qemu-img: Protocol error: simple reply when structured reply chunk was expected +Patch785: kvm-qemu-img-Report-bdrv_block_status-failures.patch +# For bz#1692018 - qemu-img: Protocol error: simple reply when structured reply chunk was expected +Patch786: kvm-nbd-Tolerate-some-server-non-compliance-in-NBD_CMD_B.patch +# For bz#1692018 - qemu-img: Protocol error: simple reply when structured reply chunk was expected +Patch787: kvm-nbd-Don-t-lose-server-s-error-to-NBD_CMD_BLOCK_STATU.patch +# For bz#1692018 - qemu-img: Protocol error: simple reply when structured reply chunk was expected +Patch788: kvm-nbd-Permit-simple-error-to-NBD_CMD_BLOCK_STATUS.patch +# For bz#1692018 - qemu-img: Protocol error: simple reply when structured reply chunk was expected +Patch789: kvm-qemu-img-Gracefully-shutdown-when-map-can-t-finish.patch +# For bz#1692018 - qemu-img: Protocol error: simple reply when structured reply chunk was expected +Patch790: kvm-nbd-client-Work-around-server-BLOCK_STATUS-misalignm.patch +# For bz#1692018 - qemu-img: Protocol error: simple reply when structured reply chunk was expected +Patch791: kvm-iotests-Add-241-to-test-NBD-on-unaligned-images.patch +# For bz#1692018 - qemu-img: Protocol error: simple reply when structured reply chunk was expected +Patch792: kvm-nbd-client-Lower-min_block-for-block-status-unaligne.patch +# For bz#1692018 - qemu-img: Protocol error: simple reply when structured reply chunk was expected +Patch793: kvm-nbd-client-Report-offsets-in-bdrv_block_status.patch +# For bz#1692018 - qemu-img: Protocol error: simple reply when structured reply chunk was expected +Patch794: kvm-nbd-client-Reject-inaccessible-tail-of-inconsistent-.patch +# For bz#1692018 - qemu-img: Protocol error: simple reply when structured reply chunk was expected +Patch795: kvm-nbd-client-Support-qemu-img-convert-from-unaligned-s.patch +# For bz#1692018 - qemu-img: Protocol error: simple reply when structured reply chunk was expected +Patch796: kvm-block-Add-bdrv_get_request_alignment.patch +# For bz#1692018 - qemu-img: Protocol error: simple reply when structured reply chunk was expected +Patch797: kvm-nbd-server-Advertise-actual-minimum-block-size.patch +# For bz#1692018 - qemu-img: Protocol error: simple reply when structured reply chunk was expected +Patch798: kvm-nbd-client-Trace-server-noncompliance-on-structured-.patch +# For bz#1692018 - qemu-img: Protocol error: simple reply when structured reply chunk was expected +Patch799: kvm-nbd-server-Fix-blockstatus-trace.patch +# For bz#1692018 - qemu-img: Protocol error: simple reply when structured reply chunk was expected +Patch800: kvm-nbd-server-Trace-client-noncompliance-on-unaligned-r.patch +# For bz#1692018 - qemu-img: Protocol error: simple reply when structured reply chunk was expected +Patch801: kvm-nbd-server-Don-t-fail-NBD_OPT_INFO-for-byte-aligned-.patch +# For bz#1692018 - qemu-img: Protocol error: simple reply when structured reply chunk was expected +Patch802: kvm-nbd-client-Fix-error-message-for-server-with-unusabl.patch +# For bz#1692018 - qemu-img: Protocol error: simple reply when structured reply chunk was expected +Patch803: kvm-iotest-Fix-241-to-run-in-generic-directory.patch +# For bz#1673397 - [RHEL.7] qemu-kvm core dumped after hotplug the deleted disk with iothread parameter +# For bz#1673402 - Qemu core dump when start guest with two disks using same drive +Patch804: kvm-virtio-scsi-Move-BlockBackend-back-to-the-main-AioCo.patch +# For bz#1673397 - [RHEL.7] qemu-kvm core dumped after hotplug the deleted disk with iothread parameter +# For bz#1673402 - Qemu core dump when start guest with two disks using same drive +Patch805: kvm-scsi-disk-Acquire-the-AioContext-in-scsi_-_realize.patch +# For bz#1673397 - [RHEL.7] qemu-kvm core dumped after hotplug the deleted disk with iothread parameter +# For bz#1673402 - Qemu core dump when start guest with two disks using same drive +Patch806: kvm-virtio-scsi-Forbid-devices-with-different-iothreads-.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch807: kvm-monitor-move-init-global-earlier.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch808: kvm-block-pflash_cfi02-Fix-memory-leak-and-potential-use.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch809: kvm-pflash-Rename-pflash_t-to-PFlashCFI01-PFlashCFI02.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch810: kvm-pflash_cfi01-Do-not-exit-on-guest-aborting-write-to-.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch811: kvm-pflash_cfi01-Log-use-of-flawed-write-to-buffer.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch812: kvm-pflash-Rename-CFI_PFLASH-to-PFLASH_CFI.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch813: kvm-hw-Use-PFLASH_CFI0-1-2-and-TYPE_PFLASH_CFI0-1-2.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch814: kvm-sam460ex-Don-t-size-flash-memory-to-match-backing-im.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch815: kvm-ppc405_boards-Delete-stale-disabled-DEBUG_BOARD_INIT.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch816: kvm-ppc405_boards-Don-t-size-flash-memory-to-match-backi.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch817: kvm-r2d-Fix-flash-memory-size-sector-size-width-device-I.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch818: kvm-mips_malta-Delete-disabled-broken-DEBUG_BOARD_INIT-c.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch819: kvm-hw-mips-malta-Remove-fl_sectors-variable.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch820: kvm-hw-mips-malta-Restrict-bios_size-variable-scope.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch821: kvm-mips_malta-Clean-up-definition-of-flash-memory-size-.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch822: kvm-pflash-Clean-up-after-commit-368a354f02b-part-1.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch823: kvm-pflash-Clean-up-after-commit-368a354f02b-part-2.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch824: kvm-qdev-Loosen-coupling-between-compat-and-other-global.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch825: kvm-vl-Prepare-fix-of-latent-bug-with-global-and-onboard.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch826: kvm-vl-Fix-latent-bug-with-global-and-onboard-devices.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch827: kvm-sysbus-Fix-latent-bug-with-onboard-devices.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch828: kvm-vl-Improve-legibility-of-BlockdevOptions-queue.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch829: kvm-vl-Factor-configure_blockdev-out-of-main.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch830: kvm-vl-Create-block-backends-before-setting-machine-prop.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch831: kvm-pflash_cfi01-Add-pflash_cfi01_get_blk-helper.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch832: kvm-pc_sysfw-Remove-unused-PcSysFwDevice.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch833: kvm-pc_sysfw-Pass-PCMachineState-to-pc_system_firmware_i.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch834: kvm-pc-Support-firmware-configuration-with-blockdev.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch835: kvm-docs-interop-firmware.json-Prefer-machine-to-if-pfla.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch836: kvm-Revert-migration-move-only_migratable-to-MigrationSt.patch +# For bz#1624009 - allow backing of pflash via -blockdev +Patch837: kvm-migration-Support-adding-migration-blockers-earlier.patch +# For bz#1669071 - CVE-2019-6778 qemu-kvm-rhev: QEMU: slirp: heap buffer overflow in tcp_emu() [rhel-7.7] +Patch838: kvm-slirp-fix-big-little-endian-conversion-in-ident-prot.patch +# For bz#1669071 - CVE-2019-6778 qemu-kvm-rhev: QEMU: slirp: heap buffer overflow in tcp_emu() [rhel-7.7] +Patch839: kvm-slirp-ensure-there-is-enough-space-in-mbuf-to-null-t.patch +# For bz#1669071 - CVE-2019-6778 qemu-kvm-rhev: QEMU: slirp: heap buffer overflow in tcp_emu() [rhel-7.7] +Patch840: kvm-slirp-don-t-manipulate-so_rcv-in-tcp_emu.patch +# For bz#1636727 - CVE-2018-17958 qemu-kvm: Qemu: rtl8139: integer overflow leads to buffer overflow [rhel-7] +# For bz#1636780 - CVE-2018-17963 qemu-kvm-rhev: Qemu: net: ignore packets with large size [rhel-7] +Patch841: kvm-rtl8139-fix-possible-out-of-bound-access.patch +# For bz#1636779 - CVE-2018-17963 qemu-kvm: Qemu: net: ignore packets with large size [rhel-7] +# For bz#1636780 - CVE-2018-17963 qemu-kvm-rhev: Qemu: net: ignore packets with large size [rhel-7] +Patch842: kvm-net-ignore-packet-size-greater-than-INT_MAX.patch +# For bz#1636779 - CVE-2018-17963 qemu-kvm: Qemu: net: ignore packets with large size [rhel-7] +# For bz#1636780 - CVE-2018-17963 qemu-kvm-rhev: Qemu: net: ignore packets with large size [rhel-7] +Patch843: kvm-net-drop-too-large-packet-early.patch +# For bz#1710861 - Detached device when trying to upgrade USB device firmware when in doing USB Passthrough via QEMU +Patch844: kvm-Introduce-new-no_guest_reset-parameter-for-usb-host-.patch +# For bz#1710861 - Detached device when trying to upgrade USB device firmware when in doing USB Passthrough via QEMU +Patch845: kvm-usb-call-reset-handler-before-updating-state.patch +# For bz#1710861 - Detached device when trying to upgrade USB device firmware when in doing USB Passthrough via QEMU +Patch846: kvm-usb-host-skip-reset-for-untouched-devices.patch +# For bz#1710861 - Detached device when trying to upgrade USB device firmware when in doing USB Passthrough via QEMU +Patch847: kvm-usb-host-avoid-libusb_set_configuration-calls.patch +# For bz#1703916 - Qemu core dump when quit vm after forbidden to do backup with a read-only bitmap +Patch848: kvm-blockdev-fix-missed-target-unref-for-drive-backup.patch +# For bz#1714160 - Guest with 'reservations' for a disk start failed +Patch849: kvm-vl-Fix-drive-blockdev-persistent-reservation-managem.patch +# For bz#1608226 - [virtual-network][mq] prompt warning "qemu-kvm: unable to start vhost net: 14: falling back on userspace virtio" when boot with win8+ guests with multi-queue +Patch850: kvm-vhost_net-don-t-set-backend-for-the-uninitialized-vi.patch +# For bz#1734753 - CVE-2019-14378 qemu-kvm-rhev: QEMU: slirp: heap buffer overflow during packet reassembly [rhel-7.8] +# For bz#1735653 - CVE-2019-14378 qemu-kvm-ma: QEMU: slirp: heap buffer overflow during packet reassembly [rhel-7.8] +Patch851: kvm-Fix-heap-overflow-in-ip_reass-on-big-packet-input.patch +# For bz#1709972 - [Intel 7.8 Bug] [KVM][CLX] CPUID_7_0_EDX_ARCH_CAPABILITIES is not enabled in VM qemu-kvm-rhev +Patch852: kvm-i386-Add-new-MSR-indices-for-IA32_PRED_CMD-and-IA32_.patch +# For bz#1709972 - [Intel 7.8 Bug] [KVM][CLX] CPUID_7_0_EDX_ARCH_CAPABILITIES is not enabled in VM qemu-kvm-rhev +Patch853: kvm-i386-Add-CPUID-bit-and-feature-words-for-IA32_ARCH_C.patch +# For bz#1709972 - [Intel 7.8 Bug] [KVM][CLX] CPUID_7_0_EDX_ARCH_CAPABILITIES is not enabled in VM qemu-kvm-rhev +Patch854: kvm-Add-support-to-KVM_GET_MSR_FEATURE_INDEX_LIST-an.patch +# For bz#1709972 - [Intel 7.8 Bug] [KVM][CLX] CPUID_7_0_EDX_ARCH_CAPABILITIES is not enabled in VM qemu-kvm-rhev +Patch855: kvm-x86-Data-structure-changes-to-support-MSR-based-feat.patch +# For bz#1709972 - [Intel 7.8 Bug] [KVM][CLX] CPUID_7_0_EDX_ARCH_CAPABILITIES is not enabled in VM qemu-kvm-rhev +Patch856: kvm-x86-define-a-new-MSR-based-feature-word-FEATURE_WORD.patch +# For bz#1709972 - [Intel 7.8 Bug] [KVM][CLX] CPUID_7_0_EDX_ARCH_CAPABILITIES is not enabled in VM qemu-kvm-rhev +Patch857: kvm-Use-KVM_GET_MSR_INDEX_LIST-for-MSR_IA32_ARCH_CAP.patch +# For bz#1709972 - [Intel 7.8 Bug] [KVM][CLX] CPUID_7_0_EDX_ARCH_CAPABILITIES is not enabled in VM qemu-kvm-rhev +Patch858: kvm-i386-kvm-Disable-arch_capabilities-if-MSR-can-t-be-s.patch +# For bz#1709972 - [Intel 7.8 Bug] [KVM][CLX] CPUID_7_0_EDX_ARCH_CAPABILITIES is not enabled in VM qemu-kvm-rhev +Patch859: kvm-i386-Make-arch_capabilities-migratable.patch +# For bz#1648622 - [v2v] Migration performance regression +Patch860: kvm-block-Remove-error-messages-in-bdrv_make_zero.patch +# For bz#1648622 - [v2v] Migration performance regression +Patch861: kvm-block-Add-BDRV_REQ_NO_FALLBACK.patch +# For bz#1648622 - [v2v] Migration performance regression +Patch862: kvm-block-Advertise-BDRV_REQ_NO_FALLBACK-in-filter-drive.patch +# For bz#1648622 - [v2v] Migration performance regression +Patch863: kvm-file-posix-Fix-write_zeroes-with-unmap-on-block-devi.patch +# For bz#1648622 - [v2v] Migration performance regression +Patch864: kvm-file-posix-Factor-out-raw_thread_pool_submit.patch +# For bz#1648622 - [v2v] Migration performance regression +Patch865: kvm-file-posix-Avoid-aio_worker-for-QEMU_AIO_WRITE_ZEROE.patch +# For bz#1648622 - [v2v] Migration performance regression +Patch866: kvm-file-posix-Support-BDRV_REQ_NO_FALLBACK-for-zero-wri.patch +# For bz#1648622 - [v2v] Migration performance regression +Patch867: kvm-qemu-img-Use-BDRV_REQ_NO_FALLBACK-for-pre-zeroing.patch +# For bz#1648622 - [v2v] Migration performance regression +Patch868: kvm-qemu-io-Add-write-n-for-BDRV_REQ_NO_FALLBACK.patch +# For bz#1712704 - CVE-2019-12155 qemu-kvm-rhev: QEMU: qxl: null pointer dereference while releasing spice resources [rhel-7] +Patch869: kvm-qxl-check-release-info-object.patch +# For bz#1721522 - ccid: Fix incorrect dwProtocol advertisement of T=0 +Patch870: kvm-ccid-Fix-dwProtocols-advertisement-of-T-0.patch +# For bz#1673546 - QEMU gets stuck on resume/cont call from libvirt +Patch871: kvm-vl-add-qemu_add_vm_change_state_handler_prio.patch +# For bz#1673546 - QEMU gets stuck on resume/cont call from libvirt +Patch872: kvm-qdev-add-qdev_add_vm_change_state_handler.patch +# For bz#1673546 - QEMU gets stuck on resume/cont call from libvirt +Patch873: kvm-virtio-scsi-restart-DMA-after-iothread.patch +# For bz#1711643 - qemu aborts in blockCommit: qemu-kvm: block.c:3486: bdrv_replace_node: Assertion `!({ _Static_assert(!(sizeof(*&from->in_flight) > 8), "not expecting: " "sizeof(*&from->in_flight) > ATOMIC_REG_SIZE"); __atomic_load_n(&from->in_flight, 0); })' failed. +Patch874: kvm-block-Drain-source-node-in-bdrv_replace_node.patch +# For bz#1749723 - CVE-2019-15890 qemu-kvm-ma: QEMU: Slirp: use-after-free during packet reassembly [rhel-7] +Patch875: kvm-Using-ip_deq-after-m_free-might-read-pointers-from-a.patch +# For bz#1746224 - qemu coredump: qemu-kvm: block/create.c:68: qmp_blockdev_create: Assertion `drv' failed +Patch876: kvm-block-create-Do-not-abort-if-a-block-driver-is-not-a.patch +# For bz#1743508 - ISST-LTE:RHV4.3 on RHEL7.6 kvm host:Power8:Tuleta-L:lotg7: call traces dumped on guest while performing guest migration (qemu-kvm-rhev) +Patch877: kvm-migration-Do-not-re-read-the-clock-on-pre_save-in-ca.patch +# For bz#1665256 - Live storage migration fails with: TimeoutError: Timed out during operation: cannot acquire state change lock (held by monitor=remoteDispatchConnectGetAllDomainStats) and the VM becomes 'Not Responding' +Patch878: kvm-mirror-Confirm-we-re-quiesced-only-if-the-job-is-pau.patch +# For bz#1734502 - qemu-kvm: backport cpuidle-haltpoll support +Patch879: kvm-i386-halt-poll-control-MSR-support.patch +# For bz#1716726 - [Intel 7.8 FEAT] MDS_NO exposure to guest - qemu-kvm-rhev +Patch880: kvm-target-i386-add-MDS-NO-feature.patch +# For bz#1743365 - qemu, qemu-img fail to detect alignment with XFS and Gluster/XFS on 4k block device +Patch881: kvm-file-posix-Handle-undetectable-alignment.patch +# For bz#1648622 - [v2v] Migration performance regression +Patch882: kvm-qemu-img-Enable-BDRV_REQ_MAY_UNMAP-in-convert.patch + +BuildRequires: zlib-devel +BuildRequires: glib2-devel +BuildRequires: which +BuildRequires: gnutls-devel +BuildRequires: cyrus-sasl-devel +BuildRequires: libtool +BuildRequires: libaio-devel +BuildRequires: rsync +BuildRequires: python +BuildRequires: pciutils-devel +BuildRequires: libiscsi-devel +BuildRequires: ncurses-devel +BuildRequires: libattr-devel +BuildRequires: libusbx-devel >= 1.0.19 +%if %{have_usbredir} +BuildRequires: usbredir-devel >= 0.7.1 +%endif +BuildRequires: texinfo +%if %{have_spice} +BuildRequires: spice-protocol >= 0.12.12 +BuildRequires: spice-server-devel >= 0.12.8 +BuildRequires: libcacard-devel +# For smartcard NSS support +BuildRequires: nss-devel +%endif +%if %{have_seccomp} +BuildRequires: libseccomp-devel >= 2.3.0 +%endif +# For network block driver +BuildRequires: libcurl-devel +BuildRequires: libssh2-devel +BuildRequires: librados2-devel +BuildRequires: librbd1-devel +%if %{have_gluster} +# For gluster block driver +BuildRequires: glusterfs-api-devel >= 3.6.0 +BuildRequires: glusterfs-devel +%endif +# We need both because the 'stap' binary is probed for by configure +BuildRequires: systemtap +BuildRequires: systemtap-sdt-devel +# For XFS discard support in raw-posix.c +# For VNC JPEG support +BuildRequires: libjpeg-devel +# For VNC PNG support +BuildRequires: libpng-devel +# For uuid generation +BuildRequires: libuuid-devel +# For BlueZ device support +BuildRequires: bluez-libs-devel +# For Braille device support +BuildRequires: brlapi-devel +# For test suite +BuildRequires: check-devel +# For virtfs +BuildRequires: libcap-devel +# Hard requirement for version >= 1.3 +BuildRequires: pixman-devel +# Documentation requirement +BuildRequires: perl-podlators +BuildRequires: texinfo +# For rdma +%if 0%{?have_librdma} +BuildRequires: rdma-core-devel +%endif +%if 0%{?have_tcmalloc} +BuildRequires: gperftools-devel +%endif +%if %{have_fdt} +BuildRequires: libfdt-devel >= 1.4.3 +%endif +# iasl and cpp for acpi generation (not a hard requirement as we can use +# pre-compiled files, but it's better to use this) +%ifarch %{ix86} x86_64 +BuildRequires: iasl +BuildRequires: cpp +%endif +# For compressed guest memory dumps +BuildRequires: lzo-devel snappy-devel +# For NUMA memory binding +%ifnarch s390x +BuildRequires: numactl-devel +%endif +BuildRequires: libgcrypt-devel +# qemu-pr-helper multipath support (requires libudev too) +BuildRequires: device-mapper-multipath-devel +BuildRequires: systemd-devel +# used by qemu-bridge-helper and qemu-pr-helper +BuildRequires: libcap-ng-devel + +# For kvm-unit-tests +%ifarch x86_64 +BuildRequires: binutils +BuildRequires: kernel-devel +%endif + +BuildRequires: diffutils + +# For s390-pgste flag +%ifarch s390x +BuildRequires: binutils >= 2.27-16 +%endif + +# qemu-keymap +BuildRequires: pkgconfig(xkbcommon) + +%if %{have_opengl} +BuildRequires: pkgconfig(epoxy) +BuildRequires: pkgconfig(libdrm) +BuildRequires: pkgconfig(gbm) +Requires: mesa-libGL +Requires: mesa-libEGL +Requires: mesa-dri-drivers +%endif + +Requires: qemu-img%{?pkgsuffix} = %{epoch}:%{version}-%{release} + +# RHEV-specific changes: +# We provide special suffix for qemu-kvm so the conflit is easy +# In addition, RHEV version should obsolete all RHEL version in case both +# RHEL and RHEV channels are used +%rhel_rhev_conflicts qemu-kvm + + +%define qemudocdir %{_docdir}/%{pkgname} + +%description +qemu-kvm%{?pkgsuffix} is an open source virtualizer that provides hardware +emulation for the KVM hypervisor. qemu-kvm%{?pkgsuffix} acts as a virtual +machine monitor together with the KVM kernel modules, and emulates the +hardware for a full system such as a PC and its associated peripherals. + +%package -n qemu-img%{?pkgsuffix} +Summary: QEMU command line tool for manipulating disk images +Group: Development/Tools + +%rhel_rhev_conflicts qemu-img + +%description -n qemu-img%{?pkgsuffix} +This package provides a command line tool for manipulating disk images. + +%package -n qemu-kvm-common%{?pkgsuffix} +Summary: QEMU common files needed by all QEMU targets +Group: Development/Tools +Requires(post): /usr/bin/getent +Requires(post): /usr/sbin/groupadd +Requires(post): /usr/sbin/useradd +Requires(post): systemd-units +Requires(preun): systemd-units +Requires(postun): systemd-units + +%rhel_rhev_conflicts qemu-kvm-common + +%description -n qemu-kvm-common%{?pkgsuffix} +qemu-kvm is an open source virtualizer that provides hardware emulation for +the KVM hypervisor. + +This package provides documentation and auxiliary programs used with qemu-kvm. + +%package -n qemu-kvm-tools%{?pkgsuffix} +Summary: KVM debugging and diagnostics tools +Group: Development/Tools + +# Needed due to coloring hack +Requires: libxkbcommon + +%rhel_rhev_conflicts qemu-kvm-tools + +%description -n qemu-kvm-tools%{?pkgsuffix} +This package contains some diagnostics and debugging tools for KVM, such as kvm_stat. + +%prep +%setup -q -n qemu-%{version} + +# Copy bios files to allow 'make check' pass +cp %{SOURCE14} pc-bios +cp %{SOURCE15} pc-bios +cp %{SOURCE16} pc-bios +cp %{SOURCE17} pc-bios +cp %{SOURCE18} pc-bios +cp %{SOURCE20} pc-bios +cp %{SOURCE29} pc-bios + +# if patch fuzzy patch applying will be forbidden +%define with_fuzzy_patches 0 +%if %{with_fuzzy_patches} + patch_command='patch -p1 -s' +%else + patch_command='patch -p1 -F1 -s' +%endif + +ApplyPatch() +{ + local patch=$1 + shift + if [ ! -f $RPM_SOURCE_DIR/$patch ]; then + exit 1 + fi + case "$patch" in + *.bz2) bunzip2 < "$RPM_SOURCE_DIR/$patch" | $patch_command ${1+"$@"} ;; + *.gz) gunzip < "$RPM_SOURCE_DIR/$patch" | $patch_command ${1+"$@"} ;; + *) $patch_command ${1+"$@"} < "$RPM_SOURCE_DIR/$patch" ;; + esac +} + +# don't apply patch if it's empty or does not exist +ApplyOptionalPatch() +{ + local patch=$1 + shift + if [ ! -f $RPM_SOURCE_DIR/$patch ]; then + return 0 + fi + local C=$(wc -l $RPM_SOURCE_DIR/$patch | awk '{print $1}') + if [ "$C" -gt 9 ]; then + ApplyPatch $patch ${1+"$@"} + fi +} + + +%patch0001 -p1 +%patch0003 -p1 +%patch0004 -p1 +%patch0005 -p1 +%patch0006 -p1 +%patch0007 -p1 +%patch0008 -p1 +%patch0009 -p1 +%patch0010 -p1 +%patch0011 -p1 +%patch0012 -p1 +%patch0013 -p1 +%patch0014 -p1 +%patch0015 -p1 +%patch0016 -p1 +%patch0017 -p1 +%patch0018 -p1 +%patch0019 -p1 +%patch0020 -p1 +%patch0021 -p1 +%patch0022 -p1 +%patch0023 -p1 +%patch0024 -p1 +%patch0025 -p1 +%patch0026 -p1 +%patch0027 -p1 +%patch0028 -p1 +%patch0029 -p1 +%patch0030 -p1 +%patch0031 -p1 +%patch0032 -p1 +%patch0033 -p1 +%patch0034 -p1 +%patch35 -p1 +%patch36 -p1 +%patch37 -p1 +%patch38 -p1 +%patch39 -p1 +%patch40 -p1 +%patch41 -p1 +%patch42 -p1 +%patch43 -p1 +%patch44 -p1 +%patch45 -p1 +%patch46 -p1 +%patch47 -p1 +%patch48 -p1 +%patch49 -p1 +%patch50 -p1 +%patch51 -p1 +%patch52 -p1 +%patch53 -p1 +%patch54 -p1 +%patch55 -p1 +%patch56 -p1 +%patch57 -p1 +%patch58 -p1 +%patch59 -p1 +%patch60 -p1 +%patch61 -p1 +%patch62 -p1 +%patch63 -p1 +%patch64 -p1 +%patch65 -p1 +%patch66 -p1 +%patch67 -p1 +%patch68 -p1 +%patch69 -p1 +%patch70 -p1 +%patch71 -p1 +%patch72 -p1 +%patch73 -p1 +%patch74 -p1 +%patch75 -p1 +%patch76 -p1 +%patch77 -p1 +%patch78 -p1 +%patch79 -p1 +%patch80 -p1 +%patch81 -p1 +%patch82 -p1 +%patch83 -p1 +%patch84 -p1 +%patch85 -p1 +%patch86 -p1 +%patch87 -p1 +%patch88 -p1 +%patch89 -p1 +%patch90 -p1 +%patch91 -p1 +%patch92 -p1 +%patch93 -p1 +%patch94 -p1 +%patch95 -p1 +%patch96 -p1 +%patch97 -p1 +%patch98 -p1 +%patch99 -p1 +%patch100 -p1 +%patch101 -p1 +%patch102 -p1 +%patch103 -p1 +%patch104 -p1 +%patch105 -p1 +%patch106 -p1 +%patch107 -p1 +%patch108 -p1 +%patch109 -p1 +%patch110 -p1 +%patch111 -p1 +%patch112 -p1 +%patch113 -p1 +%patch114 -p1 +%patch115 -p1 +%patch116 -p1 +%patch117 -p1 +%patch118 -p1 +%patch119 -p1 +%patch120 -p1 +%patch121 -p1 +%patch122 -p1 +%patch123 -p1 +%patch124 -p1 +%patch125 -p1 +%patch126 -p1 +%patch127 -p1 +%patch128 -p1 +%patch129 -p1 +%patch130 -p1 +%patch131 -p1 +%patch132 -p1 +%patch133 -p1 +%patch134 -p1 +%patch135 -p1 +%patch136 -p1 +%patch137 -p1 +%patch138 -p1 +%patch139 -p1 +%patch140 -p1 +%patch141 -p1 +%patch142 -p1 +%patch143 -p1 +%patch144 -p1 +%patch145 -p1 +%patch146 -p1 +%patch147 -p1 +%patch148 -p1 +%patch149 -p1 +%patch150 -p1 +%patch151 -p1 +%patch152 -p1 +%patch153 -p1 +%patch154 -p1 +%patch155 -p1 +%patch156 -p1 +%patch157 -p1 +%patch158 -p1 +%patch159 -p1 +%patch160 -p1 +%patch161 -p1 +%patch162 -p1 +%patch163 -p1 +%patch164 -p1 +%patch165 -p1 +%patch166 -p1 +%patch167 -p1 +%patch168 -p1 +%patch169 -p1 +%patch170 -p1 +%patch171 -p1 +%patch172 -p1 +%patch173 -p1 +%patch174 -p1 +%patch175 -p1 +%patch176 -p1 +%patch177 -p1 +%patch178 -p1 +%patch179 -p1 +%patch180 -p1 +%patch181 -p1 +%patch182 -p1 +%patch183 -p1 +%patch184 -p1 +%patch185 -p1 +%patch186 -p1 +%patch187 -p1 +%patch188 -p1 +%patch189 -p1 +%patch190 -p1 +%patch191 -p1 +%patch192 -p1 +%patch193 -p1 +%patch194 -p1 +%patch195 -p1 +%patch196 -p1 +%patch197 -p1 +%patch198 -p1 +%patch199 -p1 +%patch200 -p1 +%patch201 -p1 +%patch202 -p1 +%patch203 -p1 +%patch204 -p1 +%patch205 -p1 +%patch206 -p1 +%patch207 -p1 +%patch208 -p1 +%patch209 -p1 +%patch210 -p1 +%patch211 -p1 +%patch212 -p1 +%patch213 -p1 +%patch214 -p1 +%patch215 -p1 +%patch216 -p1 +%patch217 -p1 +%patch218 -p1 +%patch219 -p1 +%patch220 -p1 +%patch221 -p1 +%patch222 -p1 +%patch223 -p1 +%patch224 -p1 +%patch225 -p1 +%patch226 -p1 +%patch227 -p1 +%patch228 -p1 +%patch229 -p1 +%patch230 -p1 +%patch231 -p1 +%patch232 -p1 +%patch233 -p1 +%patch234 -p1 +%patch235 -p1 +%patch236 -p1 +%patch237 -p1 +%patch238 -p1 +%patch239 -p1 +%patch240 -p1 +%patch241 -p1 +%patch242 -p1 +%patch243 -p1 +%patch244 -p1 +%patch245 -p1 +%patch246 -p1 +%patch247 -p1 +%patch248 -p1 +%patch249 -p1 +%patch250 -p1 +%patch251 -p1 +%patch252 -p1 +%patch253 -p1 +%patch254 -p1 +%patch255 -p1 +%patch256 -p1 +%patch257 -p1 +%patch258 -p1 +%patch259 -p1 +%patch260 -p1 +%patch261 -p1 +%patch262 -p1 +%patch263 -p1 +%patch264 -p1 +%patch265 -p1 +%patch266 -p1 +%patch267 -p1 +%patch268 -p1 +%patch269 -p1 +%patch270 -p1 +%patch271 -p1 +%patch272 -p1 +%patch273 -p1 +%patch274 -p1 +%patch275 -p1 +%patch276 -p1 +%patch277 -p1 +%patch278 -p1 +%patch279 -p1 +%patch280 -p1 +%patch281 -p1 +%patch282 -p1 +%patch283 -p1 +%patch284 -p1 +%patch285 -p1 +%patch286 -p1 +%patch287 -p1 +%patch288 -p1 +%patch289 -p1 +%patch290 -p1 +%patch291 -p1 +%patch292 -p1 +%patch293 -p1 +%patch294 -p1 +%patch295 -p1 +%patch296 -p1 +%patch297 -p1 +%patch298 -p1 +%patch299 -p1 +%patch300 -p1 +%patch301 -p1 +%patch302 -p1 +%patch303 -p1 +%patch304 -p1 +%patch305 -p1 +%patch306 -p1 +%patch307 -p1 +%patch308 -p1 +%patch309 -p1 +%patch310 -p1 +%patch311 -p1 +%patch312 -p1 +%patch313 -p1 +%patch314 -p1 +%patch315 -p1 +%patch316 -p1 +%patch317 -p1 +%patch318 -p1 +%patch319 -p1 +%patch320 -p1 +%patch321 -p1 +%patch322 -p1 +%patch323 -p1 +%patch324 -p1 +%patch325 -p1 +%patch326 -p1 +%patch327 -p1 +%patch328 -p1 +%patch329 -p1 +%patch330 -p1 +%patch331 -p1 +%patch332 -p1 +%patch333 -p1 +%patch334 -p1 +%patch335 -p1 +%patch336 -p1 +%patch337 -p1 +%patch338 -p1 +%patch339 -p1 +%patch340 -p1 +%patch341 -p1 +%patch342 -p1 +%patch343 -p1 +%patch344 -p1 +%patch345 -p1 +%patch346 -p1 +%patch347 -p1 +%patch348 -p1 +%patch349 -p1 +%patch350 -p1 +%patch351 -p1 +%patch352 -p1 +%patch353 -p1 +%patch354 -p1 +%patch355 -p1 +%patch356 -p1 +%patch357 -p1 +%patch358 -p1 +%patch359 -p1 +%patch360 -p1 +%patch361 -p1 +%patch362 -p1 +%patch363 -p1 +%patch364 -p1 +%patch365 -p1 +%patch366 -p1 +%patch367 -p1 +%patch368 -p1 +%patch369 -p1 +%patch370 -p1 +%patch371 -p1 +%patch372 -p1 +%patch373 -p1 +%patch374 -p1 +%patch375 -p1 +%patch376 -p1 +%patch377 -p1 +%patch378 -p1 +%patch379 -p1 +%patch380 -p1 +%patch381 -p1 +%patch382 -p1 +%patch383 -p1 +%patch384 -p1 +%patch385 -p1 +%patch386 -p1 +%patch387 -p1 +%patch388 -p1 +%patch389 -p1 +%patch390 -p1 +%patch391 -p1 +%patch392 -p1 +%patch393 -p1 +%patch394 -p1 +%patch395 -p1 +%patch396 -p1 +%patch397 -p1 +%patch398 -p1 +%patch399 -p1 +%patch400 -p1 +%patch401 -p1 +%patch402 -p1 +%patch403 -p1 +%patch404 -p1 +%patch405 -p1 +%patch406 -p1 +%patch407 -p1 +%patch408 -p1 +%patch409 -p1 +%patch410 -p1 +%patch411 -p1 +%patch412 -p1 +%patch413 -p1 +%patch414 -p1 +%patch415 -p1 +%patch416 -p1 +%patch417 -p1 +%patch418 -p1 +%patch419 -p1 +%patch420 -p1 +%patch421 -p1 +%patch422 -p1 +%patch423 -p1 +%patch424 -p1 +%patch425 -p1 +%patch426 -p1 +%patch427 -p1 +%patch428 -p1 +%patch429 -p1 +%patch430 -p1 +%patch431 -p1 +%patch432 -p1 +%patch433 -p1 +%patch434 -p1 +%patch435 -p1 +%patch436 -p1 +%patch437 -p1 +%patch438 -p1 +%patch439 -p1 +%patch440 -p1 +%patch441 -p1 +%patch442 -p1 +%patch443 -p1 +%patch444 -p1 +%patch445 -p1 +%patch446 -p1 +%patch447 -p1 +%patch448 -p1 +%patch449 -p1 +%patch450 -p1 +%patch451 -p1 +%patch452 -p1 +%patch453 -p1 +%patch454 -p1 +%patch455 -p1 +%patch456 -p1 +%patch457 -p1 +%patch458 -p1 +%patch459 -p1 +%patch460 -p1 +%patch461 -p1 +%patch462 -p1 +%patch463 -p1 +%patch464 -p1 +%patch465 -p1 +%patch466 -p1 +%patch467 -p1 +%patch468 -p1 +%patch469 -p1 +%patch470 -p1 +%patch471 -p1 +%patch472 -p1 +%patch473 -p1 +%patch474 -p1 +%patch475 -p1 +%patch476 -p1 +%patch477 -p1 +%patch478 -p1 +%patch479 -p1 +%patch480 -p1 +%patch481 -p1 +%patch482 -p1 +%patch483 -p1 +%patch484 -p1 +%patch485 -p1 +%patch486 -p1 +%patch487 -p1 +%patch488 -p1 +%patch489 -p1 +%patch490 -p1 +%patch491 -p1 +%patch492 -p1 +%patch493 -p1 +%patch494 -p1 +%patch495 -p1 +%patch496 -p1 +%patch497 -p1 +%patch498 -p1 +%patch499 -p1 +%patch500 -p1 +%patch501 -p1 +%patch502 -p1 +%patch503 -p1 +%patch504 -p1 +%patch505 -p1 +%patch506 -p1 +%patch507 -p1 +%patch508 -p1 +%patch509 -p1 +%patch510 -p1 +%patch511 -p1 +%patch512 -p1 +%patch513 -p1 +%patch514 -p1 +%patch515 -p1 +%patch516 -p1 +%patch517 -p1 +%patch518 -p1 +%patch519 -p1 +%patch520 -p1 +%patch521 -p1 +%patch522 -p1 +%patch523 -p1 +%patch524 -p1 +%patch525 -p1 +%patch526 -p1 +%patch527 -p1 +%patch528 -p1 +%patch529 -p1 +%patch530 -p1 +%patch531 -p1 +%patch532 -p1 +%patch533 -p1 +%patch534 -p1 +%patch535 -p1 +%patch536 -p1 +%patch537 -p1 +%patch538 -p1 +%patch539 -p1 +%patch540 -p1 +%patch541 -p1 +%patch542 -p1 +%patch543 -p1 +%patch544 -p1 +%patch545 -p1 +%patch546 -p1 +%patch547 -p1 +%patch548 -p1 +%patch549 -p1 +%patch550 -p1 +%patch551 -p1 +%patch552 -p1 +%patch553 -p1 +%patch554 -p1 +%patch555 -p1 +%patch556 -p1 +%patch557 -p1 +%patch558 -p1 +%patch559 -p1 +%patch560 -p1 +%patch561 -p1 +%patch562 -p1 +%patch563 -p1 +%patch564 -p1 +%patch565 -p1 +%patch566 -p1 +%patch567 -p1 +%patch568 -p1 +%patch569 -p1 +%patch570 -p1 +%patch571 -p1 +%patch572 -p1 +%patch573 -p1 +%patch574 -p1 +%patch575 -p1 +%patch576 -p1 +%patch577 -p1 +%patch578 -p1 +%patch579 -p1 +%patch580 -p1 +%patch581 -p1 +%patch582 -p1 +%patch583 -p1 +%patch584 -p1 +%patch585 -p1 +%patch586 -p1 +%patch587 -p1 +%patch588 -p1 +%patch589 -p1 +%patch590 -p1 +%patch591 -p1 +%patch592 -p1 +%patch593 -p1 +%patch594 -p1 +%patch595 -p1 +%patch596 -p1 +%patch597 -p1 +%patch598 -p1 +%patch599 -p1 +%patch600 -p1 +%patch601 -p1 +%patch602 -p1 +%patch603 -p1 +%patch604 -p1 +%patch605 -p1 +%patch606 -p1 +%patch607 -p1 +%patch608 -p1 +%patch609 -p1 +%patch610 -p1 +%patch611 -p1 +%patch612 -p1 +%patch613 -p1 +%patch614 -p1 +%patch615 -p1 +%patch616 -p1 +%patch617 -p1 +%patch618 -p1 +%patch619 -p1 +%patch620 -p1 +%patch621 -p1 +%patch622 -p1 +%patch623 -p1 +%patch624 -p1 +%patch625 -p1 +%patch626 -p1 +%patch627 -p1 +%patch628 -p1 +%patch629 -p1 +%patch630 -p1 +%patch631 -p1 +%patch632 -p1 +%patch633 -p1 +%patch634 -p1 +%patch635 -p1 +%patch636 -p1 +%patch637 -p1 +%patch638 -p1 +%patch639 -p1 +%patch640 -p1 +%patch641 -p1 +%patch642 -p1 +%patch643 -p1 +%patch644 -p1 +%patch645 -p1 +%patch646 -p1 +%patch647 -p1 +%patch648 -p1 +%patch649 -p1 +%patch650 -p1 +%patch651 -p1 +%patch652 -p1 +%patch653 -p1 +%patch654 -p1 +%patch655 -p1 +%patch656 -p1 +%patch657 -p1 +%patch658 -p1 +%patch659 -p1 +%patch660 -p1 +%patch661 -p1 +%patch662 -p1 +%patch663 -p1 +%patch664 -p1 +%patch665 -p1 +%patch666 -p1 +%patch667 -p1 +%patch668 -p1 +%patch669 -p1 +%patch670 -p1 +%patch671 -p1 +%patch672 -p1 +%patch673 -p1 +%patch674 -p1 +%patch675 -p1 +%patch676 -p1 +%patch677 -p1 +%patch678 -p1 +%patch679 -p1 +%patch680 -p1 +%patch681 -p1 +%patch682 -p1 +%patch683 -p1 +%patch684 -p1 +%patch685 -p1 +%patch686 -p1 +%patch687 -p1 +%patch688 -p1 +%patch689 -p1 +%patch690 -p1 +%patch691 -p1 +%patch692 -p1 +%patch693 -p1 +%patch694 -p1 +%patch695 -p1 +%patch696 -p1 +%patch697 -p1 +%patch698 -p1 +%patch699 -p1 +%patch700 -p1 +%patch701 -p1 +%patch702 -p1 +%patch703 -p1 +%patch704 -p1 +%patch705 -p1 +%patch706 -p1 +%patch707 -p1 +%patch708 -p1 +%patch709 -p1 +%patch710 -p1 +%patch711 -p1 +%patch712 -p1 +%patch713 -p1 +%patch714 -p1 +%patch715 -p1 +%patch716 -p1 +%patch717 -p1 +%patch718 -p1 +%patch719 -p1 +%patch720 -p1 +%patch721 -p1 +%patch722 -p1 +%patch723 -p1 +%patch724 -p1 +%patch725 -p1 +%patch726 -p1 +%patch727 -p1 +%patch728 -p1 +%patch729 -p1 +%patch730 -p1 +%patch731 -p1 +%patch732 -p1 +%patch733 -p1 +%patch734 -p1 +%patch735 -p1 +%patch736 -p1 +%patch737 -p1 +%patch738 -p1 +%patch740 -p1 +%patch741 -p1 +%patch742 -p1 +%patch743 -p1 +%patch744 -p1 +%patch745 -p1 +%patch746 -p1 +%patch747 -p1 +%patch748 -p1 +%patch749 -p1 +%patch750 -p1 +%patch751 -p1 +%patch752 -p1 +%patch753 -p1 +%patch754 -p1 +%patch755 -p1 +%patch756 -p1 +%patch757 -p1 +%patch758 -p1 +%patch759 -p1 +%patch760 -p1 +%patch762 -p1 +%patch763 -p1 +%patch764 -p1 +%patch765 -p1 +%patch766 -p1 +%patch768 -p1 +%patch769 -p1 +%patch770 -p1 +%patch771 -p1 +%patch772 -p1 +%patch773 -p1 +%patch774 -p1 +%patch775 -p1 +%patch776 -p1 +%patch777 -p1 +%patch778 -p1 +%patch779 -p1 +%patch780 -p1 +%patch781 -p1 +%patch782 -p1 +%patch783 -p1 +%patch784 -p1 +%patch785 -p1 +%patch786 -p1 +%patch787 -p1 +%patch788 -p1 +%patch789 -p1 +%patch790 -p1 +%patch791 -p1 +%patch792 -p1 +%patch793 -p1 +%patch794 -p1 +%patch795 -p1 +%patch796 -p1 +%patch797 -p1 +%patch798 -p1 +%patch799 -p1 +%patch800 -p1 +%patch801 -p1 +%patch802 -p1 +%patch803 -p1 +%patch804 -p1 +%patch805 -p1 +%patch806 -p1 +%patch807 -p1 +%patch808 -p1 +%patch809 -p1 +%patch810 -p1 +%patch811 -p1 +%patch812 -p1 +%patch813 -p1 +%patch814 -p1 +%patch815 -p1 +%patch816 -p1 +%patch817 -p1 +%patch818 -p1 +%patch819 -p1 +%patch820 -p1 +%patch821 -p1 +%patch822 -p1 +%patch823 -p1 +%patch824 -p1 +%patch825 -p1 +%patch826 -p1 +%patch827 -p1 +%patch828 -p1 +%patch829 -p1 +%patch830 -p1 +%patch831 -p1 +%patch832 -p1 +%patch833 -p1 +%patch834 -p1 +%patch835 -p1 +%patch836 -p1 +%patch837 -p1 +%patch838 -p1 +%patch839 -p1 +%patch840 -p1 +%patch841 -p1 +%patch842 -p1 +%patch843 -p1 +%patch844 -p1 +%patch845 -p1 +%patch846 -p1 +%patch847 -p1 +%patch848 -p1 +%patch849 -p1 +%patch850 -p1 +%patch851 -p1 +%patch852 -p1 +%patch853 -p1 +%patch854 -p1 +%patch855 -p1 +%patch856 -p1 +%patch857 -p1 +%patch858 -p1 +%patch859 -p1 +%patch860 -p1 +%patch861 -p1 +%patch862 -p1 +%patch863 -p1 +%patch864 -p1 +%patch865 -p1 +%patch866 -p1 +%patch867 -p1 +%patch868 -p1 +%patch869 -p1 +%patch870 -p1 +%patch871 -p1 +%patch872 -p1 +%patch873 -p1 +%patch874 -p1 +%patch875 -p1 +%patch876 -p1 +%patch877 -p1 +%patch878 -p1 +%patch879 -p1 +%patch880 -p1 +%patch881 -p1 +%patch882 -p1 + +# Fix executable permission for iotests +chmod 755 $(ls tests/qemu-iotests/???) + +ApplyOptionalPatch qemu-kvm-test.patch + +# for tscdeadline_latency.flat +%ifarch x86_64 + tar -xf %{SOURCE25} +%endif + +%build +buildarch="%{kvm_target}-softmmu" + +# --build-id option is used for giving info to the debug packages. +extraldflags="-Wl,--build-id"; +buildldflags="VL_LDFLAGS=-Wl,--build-id" + +# QEMU already knows how to set _FORTIFY_SOURCE +%global qemuoptflags %(echo %{optflags} | sed 's/-Wp,-D_FORTIFY_SOURCE=2//') + +%ifarch s390 + # drop -g flag to prevent memory exhaustion by linker + %global qemuoptflags %(echo %{qemuoptflags} | sed 's/-g//') + sed -i.debug 's/"-g $CFLAGS"/"$CFLAGS"/g' configure +%endif + +cp %{SOURCE24} build_configure.sh + +./build_configure.sh \ + "%{_prefix}" \ + "%{_libdir}" \ + "%{_sysconfdir}" \ + "%{_localstatedir}" \ + "%{_libexecdir}" \ + "%{qemudocdir}" \ + "%{pkgname}" \ + "%{kvm_target}" \ + "%{name}-%{version}-%{release}" \ + "%{qemuoptflags}" \ +%if 0%{have_fdt} + enable \ +%else + disable \ + %endif +%if 0%{have_gluster} + enable \ +%else + disable \ +%endif + disable \ +%ifnarch s390x + enable \ +%else + disable \ +%endif + enable \ +%if 0%{have_librdma} + enable \ +%else + disable \ +%endif +%if 0%{have_seccomp} + enable \ +%else + disable \ +%endif +%if 0%{have_spice} + enable \ +%else + disable \ +%endif +%if 0%{have_opengl} + enable \ +%else + disable \ +%endif +%if 0%{have_usbredir} + enable \ +%else + disable \ +%endif +%if 0%{have_tcmalloc} + enable \ +%else + disable \ +%endif +%if 0%{have_vxhs} + enable \ +%else + disable \ +%endif +%if 0%{have_vtd} + enable \ +%else + disable \ +%endif +%if 0%{have_live_block_ops} + enable \ +%else + disable \ +%endif +%if 0%{have_vhost_user} + enable \ +%else + disable \ +%endif +%if 0%{rhev} + enable \ +%else + disable \ +%endif +%if 0%{have_tcmalloc} + disable \ +%else + enable \ +%endif + --target-list="$buildarch" + +echo "config-host.mak contents:" +echo "===" +cat config-host.mak +echo "===" + +make V=1 %{?_smp_mflags} $buildldflags + +# Setup back compat qemu-kvm binary +./scripts/tracetool.py --backend dtrace --format stap --group=all \ + --binary %{_libexecdir}/qemu-kvm --target-name %{kvm_target} \ + --target-type system --probe-prefix \ + qemu.kvm trace-events-all > qemu-kvm.stp + +./scripts/tracetool.py --backend dtrace --format simpletrace-stap \ + --group=all --binary %{_libexecdir}/qemu-kvm --target-name %{kvm_target} \ + --target-type system --probe-prefix \ + qemu.kvm trace-events-all > qemu-kvm-simpletrace.stp + +cp -a %{kvm_target}-softmmu/qemu-system-%{kvm_target} qemu-kvm + +gcc %{SOURCE6} $RPM_OPT_FLAGS $RPM_LD_FLAGS -o ksmctl + +# build tscdeadline_latency.flat +%ifarch x86_64 + (cd kvm-unit-tests && ./configure) + make -C kvm-unit-tests +%endif + +%install +%define _udevdir %(pkg-config --variable=udevdir udev)/rules.d + +install -D -p -m 0644 %{SOURCE4} $RPM_BUILD_ROOT%{_unitdir}/ksm.service +install -D -p -m 0644 %{SOURCE5} $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/ksm +install -D -p -m 0755 ksmctl $RPM_BUILD_ROOT%{_libexecdir}/ksmctl + +install -D -p -m 0644 %{SOURCE7} $RPM_BUILD_ROOT%{_unitdir}/ksmtuned.service +install -D -p -m 0755 %{SOURCE8} $RPM_BUILD_ROOT%{_sbindir}/ksmtuned +install -D -p -m 0644 %{SOURCE9} $RPM_BUILD_ROOT%{_sysconfdir}/ksmtuned.conf +install -D -p -m 0644 %{SOURCE26} $RPM_BUILD_ROOT%{_sysconfdir}/modprobe.d/vhost.conf +%ifarch s390x s390 + install -D -p -m 0644 %{SOURCE30} $RPM_BUILD_ROOT%{_sysconfdir}/modprobe.d/kvm.conf +%else +%ifarch %{ix86} x86_64 + install -D -p -m 0644 %{SOURCE31} $RPM_BUILD_ROOT%{_sysconfdir}/modprobe.d/kvm.conf +%else + install -D -p -m 0644 %{SOURCE27} $RPM_BUILD_ROOT%{_sysconfdir}/modprobe.d/kvm.conf +%endif +%endif + +mkdir -p $RPM_BUILD_ROOT%{_bindir}/ +mkdir -p $RPM_BUILD_ROOT%{_udevdir} + +install -m 0755 scripts/kvm/kvm_stat $RPM_BUILD_ROOT%{_bindir}/ +mkdir -p ${RPM_BUILD_ROOT}%{_mandir}/man1/ +install -m 0644 kvm_stat.1 ${RPM_BUILD_ROOT}%{_mandir}/man1/ +install -m 0644 %{SOURCE3} $RPM_BUILD_ROOT%{_udevdir} + +mkdir -p $RPM_BUILD_ROOT%{_datadir}/%{pkgname} +install -m 0644 scripts/dump-guest-memory.py \ + $RPM_BUILD_ROOT%{_datadir}/%{pkgname} +%ifarch x86_64 + install -m 0644 kvm-unit-tests/x86/tscdeadline_latency.flat \ + $RPM_BUILD_ROOT%{_datadir}/%{pkgname} +%endif + +make DESTDIR=$RPM_BUILD_ROOT \ + sharedir="%{_datadir}/%{pkgname}" \ + datadir="%{_datadir}/%{pkgname}" \ + install + +mkdir -p $RPM_BUILD_ROOT%{_datadir}/systemtap/tapset + +# Install compatibility roms +install %{SOURCE14} $RPM_BUILD_ROOT%{_datadir}/%{pkgname}/ +install %{SOURCE15} $RPM_BUILD_ROOT%{_datadir}/%{pkgname}/ +install %{SOURCE16} $RPM_BUILD_ROOT%{_datadir}/%{pkgname}/ +install %{SOURCE17} $RPM_BUILD_ROOT%{_datadir}/%{pkgname}/ +install %{SOURCE20} $RPM_BUILD_ROOT%{_datadir}/%{pkgname}/ + +install -m 0755 qemu-kvm $RPM_BUILD_ROOT%{_libexecdir}/ +install -m 0644 qemu-kvm.stp $RPM_BUILD_ROOT%{_datadir}/systemtap/tapset/ +install -m 0644 qemu-kvm-simpletrace.stp $RPM_BUILD_ROOT%{_datadir}/systemtap/tapset/ + +rm $RPM_BUILD_ROOT%{_bindir}/qemu-system-%{kvm_target} +rm $RPM_BUILD_ROOT%{_datadir}/systemtap/tapset/qemu-system-%{kvm_target}.stp +rm $RPM_BUILD_ROOT%{_datadir}/systemtap/tapset/qemu-system-%{kvm_target}-simpletrace.stp + +# Install simpletrace +install -m 0755 scripts/simpletrace.py $RPM_BUILD_ROOT%{_datadir}/%{pkgname}/simpletrace.py +mkdir -p $RPM_BUILD_ROOT%{_datadir}/%{pkgname}/tracetool +install -m 0644 -t $RPM_BUILD_ROOT%{_datadir}/%{pkgname}/tracetool scripts/tracetool/*.py +mkdir -p $RPM_BUILD_ROOT%{_datadir}/%{pkgname}/tracetool/backend +install -m 0644 -t $RPM_BUILD_ROOT%{_datadir}/%{pkgname}/tracetool/backend scripts/tracetool/backend/*.py +mkdir -p $RPM_BUILD_ROOT%{_datadir}/%{pkgname}/tracetool/format +install -m 0644 -t $RPM_BUILD_ROOT%{_datadir}/%{pkgname}/tracetool/format scripts/tracetool/format/*.py + +mkdir -p $RPM_BUILD_ROOT%{qemudocdir} +install -p -m 0644 -t ${RPM_BUILD_ROOT}%{qemudocdir} Changelog README README.systemtap COPYING COPYING.LIB LICENSE %{SOURCE19} docs/interop/qmp-spec.txt +chmod -x ${RPM_BUILD_ROOT}%{_mandir}/man1/* +chmod -x ${RPM_BUILD_ROOT}%{_mandir}/man8/* + +install -D -p -m 0644 qemu.sasl $RPM_BUILD_ROOT%{_sysconfdir}/sasl2/%{pkgname}.conf + +# Provided by package openbios +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/openbios-ppc +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/openbios-sparc32 +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/openbios-sparc64 +# Provided by package SLOF +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/slof.bin + +# Remove unpackaged files. +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/palcode-clipper +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/petalogix*.dtb +rm -f ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/bamboo.dtb +rm -f ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/ppc_rom.bin +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/s390-zipl.rom +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/u-boot.e500 +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/qemu_vga.ndrv +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/skiboot.lid +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/s390-ccw.img +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/hppa-firmware.img +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/canyonlands.dtb +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/u-boot-sam460-20100605.bin + +%ifarch s390x + # Use the s390-ccw.img that we've just built, not the pre-built one + install -m 0644 pc-bios/s390-ccw/s390-ccw.img $RPM_BUILD_ROOT%{_datadir}/%{pkgname}/ +%else + rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/s390-netboot.img +%endif + +%ifnarch %{power64} + rm -f ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/spapr-rtas.bin +%endif + +%ifnarch x86_64 + rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/kvmvapic.bin + rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/linuxboot.bin + rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/multiboot.bin +%endif + +# Remove sparc files +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/QEMU,tcx.bin +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/QEMU,cgthree.bin + +# Remove ivshmem example programs +rm -rf ${RPM_BUILD_ROOT}%{_bindir}/ivshmem-client +rm -rf ${RPM_BUILD_ROOT}%{_bindir}/ivshmem-server + +# Remove efi roms +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/efi*.rom + +# Provided by package ipxe +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/pxe*rom +# Provided by package vgabios +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/vgabios*bin +# Provided by package seabios +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/bios*.bin +# Provided by package sgabios +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{pkgname}/sgabios.bin + +# the pxe gpxe images will be symlinks to the images on +# /usr/share/ipxe, as QEMU doesn't know how to look +# for other paths, yet. +pxe_link() { + ln -s ../ipxe/$2.rom %{buildroot}%{_datadir}/%{pkgname}/pxe-$1.rom +} + +%ifnarch aarch64 s390x +pxe_link e1000 8086100e +pxe_link ne2k_pci 10ec8029 +pxe_link pcnet 10222000 +pxe_link rtl8139 10ec8139 +pxe_link virtio 1af41000 +pxe_link e1000e 808610d3 +%endif + +rom_link() { + ln -s $1 %{buildroot}%{_datadir}/%{pkgname}/$2 +} + +%ifnarch aarch64 s390x + rom_link ../seavgabios/vgabios-isavga.bin vgabios.bin + rom_link ../seavgabios/vgabios-cirrus.bin vgabios-cirrus.bin + rom_link ../seavgabios/vgabios-qxl.bin vgabios-qxl.bin + rom_link ../seavgabios/vgabios-stdvga.bin vgabios-stdvga.bin + rom_link ../seavgabios/vgabios-vmware.bin vgabios-vmware.bin + rom_link ../seavgabios/vgabios-virtio.bin vgabios-virtio.bin +%endif +%ifarch x86_64 + rom_link ../seabios/bios.bin bios.bin + rom_link ../seabios/bios-256k.bin bios-256k.bin + rom_link ../sgabios/sgabios.bin sgabios.bin +%endif + +%if 0%{have_kvm_setup} + install -D -p -m 755 %{SOURCE21} $RPM_BUILD_ROOT%{_prefix}/lib/systemd/kvm-setup + install -D -p -m 644 %{SOURCE22} $RPM_BUILD_ROOT%{_unitdir}/kvm-setup.service + install -D -p -m 644 %{SOURCE23} $RPM_BUILD_ROOT%{_presetdir}/85-kvm.preset + %ifarch %{power64} + install -D -p -m 0644 %{SOURCE34} $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/kvm + %endif +%endif + +%if 0%{have_memlock_limits} + install -D -p -m 644 %{SOURCE28} $RPM_BUILD_ROOT%{_sysconfdir}/security/limits.d/95-kvm-memlock.conf +%endif + +# Install rules to use the bridge helper with libvirt's virbr0 +install -D -m 0644 %{SOURCE12} $RPM_BUILD_ROOT%{_sysconfdir}/%{pkgname}/bridge.conf + +# Install qemu-pr-helper service +install -m 0644 %{_sourcedir}/qemu-pr-helper.service %{buildroot}%{_unitdir} +install -m 0644 %{_sourcedir}/qemu-pr-helper.socket %{buildroot}%{_unitdir} + +%if 0 +make %{?_smp_mflags} $buildldflags DESTDIR=$RPM_BUILD_ROOT install-libcacard + +find $RPM_BUILD_ROOT -name "libcacard.so*" -exec chmod +x \{\} \; +%endif + +find $RPM_BUILD_ROOT -name '*.la' -or -name '*.a' | xargs rm -f + +# Hack to prevent coloring tools package +chmod 0644 $RPM_BUILD_ROOT%{_bindir}/qemu-keymap + +%check +export DIFF=diff; make check V=1 + +%post +# load kvm modules now, so we can make sure no reboot is needed. +# If there's already a kvm module installed, we don't mess with it +%udev_rules_update +sh %{_sysconfdir}/sysconfig/modules/kvm.modules &> /dev/null || : + udevadm trigger --subsystem-match=misc --sysname-match=kvm --action=add || : +%if %{have_kvm_setup} + systemctl daemon-reload # Make sure it sees the new presets and unitfile + %systemd_post kvm-setup.service + if systemctl is-enabled kvm-setup.service > /dev/null; then + systemctl start kvm-setup.service + fi +%endif + +%post -n qemu-kvm-common%{?pkgsuffix} +%systemd_post ksm.service +%systemd_post ksmtuned.service + +getent group kvm >/dev/null || groupadd -g 36 -r kvm +getent group qemu >/dev/null || groupadd -g 107 -r qemu +getent passwd qemu >/dev/null || \ +useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \ + -c "qemu user" qemu + +%preun -n qemu-kvm-common%{?pkgsuffix} +%systemd_preun ksm.service +%systemd_preun ksmtuned.service + +%postun -n qemu-kvm-common%{?pkgsuffix} +%systemd_postun_with_restart ksm.service +%systemd_postun_with_restart ksmtuned.service + +%global kvm_files \ +%{_udevdir}/80-kvm.rules + +%global qemu_kvm_files \ +%{_libexecdir}/qemu-kvm \ +%{_datadir}/systemtap/tapset/qemu-kvm.stp \ +%{_datadir}/%{pkgname}/trace-events-all \ +%{_datadir}/systemtap/tapset/qemu-kvm-simpletrace.stp \ +%{_datadir}/%{pkgname}/systemtap/script.d/qemu_kvm.stp \ +%{_datadir}/%{pkgname}/systemtap/conf.d/qemu_kvm.conf + +%files -n qemu-kvm-common%{?pkgsuffix} +%defattr(-,root,root) +%dir %{qemudocdir} +%doc %{qemudocdir}/Changelog +%doc %{qemudocdir}/README +%doc %{qemudocdir}/qemu-doc.html +%doc %{qemudocdir}/COPYING +%doc %{qemudocdir}/COPYING.LIB +%doc %{qemudocdir}/LICENSE +%doc %{qemudocdir}/README.rhel6-gpxe-source +%doc %{qemudocdir}/README.systemtap +%doc %{qemudocdir}/qmp-spec.txt +%doc %{qemudocdir}/qemu-doc.txt +%doc %{qemudocdir}/qemu-qmp-ref.html +%doc %{qemudocdir}/qemu-qmp-ref.txt +%{_mandir}/man7/qemu-qmp-ref.7* +%{_bindir}/qemu-pr-helper +%{_unitdir}/qemu-pr-helper.service +%{_unitdir}/qemu-pr-helper.socket + +%dir %{_datadir}/%{pkgname}/ +%{_datadir}/%{pkgname}/keymaps/ +%{_mandir}/man1/%{pkgname}.1* +%{_mandir}/man7/qemu-block-drivers.7* +%attr(4755, -, -) %{_libexecdir}/qemu-bridge-helper +%config(noreplace) %{_sysconfdir}/sasl2/%{pkgname}.conf +%{_unitdir}/ksm.service +%{_libexecdir}/ksmctl +%config(noreplace) %{_sysconfdir}/sysconfig/ksm +%{_unitdir}/ksmtuned.service +%{_sbindir}/ksmtuned +%config(noreplace) %{_sysconfdir}/ksmtuned.conf +%dir %{_sysconfdir}/%{pkgname} +%config(noreplace) %{_sysconfdir}/%{pkgname}/bridge.conf +%config(noreplace) %{_sysconfdir}/modprobe.d/vhost.conf +%config(noreplace) %{_sysconfdir}/modprobe.d/kvm.conf +%{_datadir}/%{pkgname}/simpletrace.py* +%{_datadir}/%{pkgname}/tracetool/*.py* +%{_datadir}/%{pkgname}/tracetool/backend/*.py* +%{_datadir}/%{pkgname}/tracetool/format/*.py* + +%files +%defattr(-,root,root) +%ifarch x86_64 + %{_datadir}/%{pkgname}/bios.bin + %{_datadir}/%{pkgname}/bios-256k.bin + %{_datadir}/%{pkgname}/linuxboot.bin + %{_datadir}/%{pkgname}/multiboot.bin + %{_datadir}/%{pkgname}/kvmvapic.bin + %{_datadir}/%{pkgname}/sgabios.bin +%endif +%ifarch s390x + %{_datadir}/%{pkgname}/s390-ccw.img + %{_datadir}/%{pkgname}/s390-netboot.img +%endif +%ifnarch aarch64 s390x + %{_datadir}/%{pkgname}/vgabios.bin + %{_datadir}/%{pkgname}/vgabios-cirrus.bin + %{_datadir}/%{pkgname}/vgabios-qxl.bin + %{_datadir}/%{pkgname}/vgabios-stdvga.bin + %{_datadir}/%{pkgname}/vgabios-vmware.bin + %{_datadir}/%{pkgname}/vgabios-virtio.bin + %{_datadir}/%{pkgname}/pxe-e1000.rom + %{_datadir}/%{pkgname}/pxe-e1000e.rom + %{_datadir}/%{pkgname}/pxe-virtio.rom + %{_datadir}/%{pkgname}/pxe-pcnet.rom + %{_datadir}/%{pkgname}/pxe-rtl8139.rom + %{_datadir}/%{pkgname}/pxe-ne2k_pci.rom +%endif +%{_datadir}/%{pkgname}/qemu-icon.bmp +%{_datadir}/%{pkgname}/qemu_logo_no_text.svg +%{_datadir}/%{pkgname}/rhel6-virtio.rom +%{_datadir}/%{pkgname}/rhel6-pcnet.rom +%{_datadir}/%{pkgname}/rhel6-rtl8139.rom +%{_datadir}/%{pkgname}/rhel6-ne2k_pci.rom +%{_datadir}/%{pkgname}/rhel6-e1000.rom +%{_datadir}/%{pkgname}/linuxboot_dma.bin +%{_datadir}/%{pkgname}/dump-guest-memory.py* +%ifarch %{power64} + %{_datadir}/%{pkgname}/spapr-rtas.bin +%endif +%{?kvm_files:} +%{?qemu_kvm_files:} +%if 0%{have_kvm_setup} + %{_prefix}/lib/systemd/kvm-setup + %{_unitdir}/kvm-setup.service + %{_presetdir}/85-kvm.preset + %ifarch %{power64} + %config(noreplace) %{_sysconfdir}/sysconfig/kvm + %endif +%endif +%if 0%{have_memlock_limits} + %{_sysconfdir}/security/limits.d/95-kvm-memlock.conf +%endif + +%files -n qemu-kvm-tools%{?pkgsuffix} +%defattr(-,root,root,-) +%{_bindir}/kvm_stat +%attr(0755, -, -) %{_bindir}/qemu-keymap +%{_mandir}/man1/kvm_stat.1* +%ifarch x86_64 +%{_datadir}/%{pkgname}/tscdeadline_latency.flat +%endif + +%files -n qemu-img%{?pkgsuffix} +%defattr(-,root,root) +%{_bindir}/qemu-img +%{_bindir}/qemu-io +%{_bindir}/qemu-nbd +%{_mandir}/man1/qemu-img.1* +%{_mandir}/man8/qemu-nbd.8* + +%if 0 +%files -n libcacard%{?pkgsuffix} +%defattr(-,root,root,-) +%{_libdir}/libcacard.so.* + +%files -n libcacard-tools%{?pkgsuffix} +%defattr(-,root,root,-) +%{_bindir}/vscclient + +%files -n libcacard-devel%{?pkgsuffix} +%defattr(-,root,root,-) +%{_includedir}/cacard +%{_libdir}/libcacard.so +%{_libdir}/pkgconfig/libcacard.pc +%endif + +%changelog +* Thu Sep 26 2019 Miroslav Rezanina - 2.12.0-37.el7 +- kvm-i386-halt-poll-control-MSR-support.patch [bz#1734502] +- kvm-target-i386-add-MDS-NO-feature.patch [bz#1716726] +- kvm-file-posix-Handle-undetectable-alignment.patch [bz#1743365] +- kvm-qemu-img-Enable-BDRV_REQ_MAY_UNMAP-in-convert.patch [bz#1648622] +- Resolves: bz#1648622 + ([v2v] Migration performance regression) +- Resolves: bz#1716726 + ([Intel 7.8 FEAT] MDS_NO exposure to guest - qemu-kvm-rhev) +- Resolves: bz#1734502 + (qemu-kvm: backport cpuidle-haltpoll support) +- Resolves: bz#1743365 + (qemu, qemu-img fail to detect alignment with XFS and Gluster/XFS on 4k block device) + +* Tue Sep 24 2019 Miroslav Rezanina - 2.12.0-36.el7 +- kvm-Using-ip_deq-after-m_free-might-read-pointers-from-a.patch [bz#1749723] +- kvm-block-create-Do-not-abort-if-a-block-driver-is-not-a.patch [bz#1746224] +- kvm-migration-Do-not-re-read-the-clock-on-pre_save-in-ca.patch [bz#1743508] +- kvm-mirror-Confirm-we-re-quiesced-only-if-the-job-is-pau.patch [bz#1665256] +- Resolves: bz#1665256 + (Live storage migration fails with: TimeoutError: Timed out during operation: cannot acquire state change lock (held by monitor=remoteDispatchConnectGetAllDomainStats) and the VM becomes 'Not Responding') +- Resolves: bz#1743508 + (ISST-LTE:RHV4.3 on RHEL7.6 kvm host:Power8:Tuleta-L:lotg7: call traces dumped on guest while performing guest migration (qemu-kvm-rhev)) +- Resolves: bz#1746224 + (qemu coredump: qemu-kvm: block/create.c:68: qmp_blockdev_create: Assertion `drv' failed) +- Resolves: bz#1749723 + (CVE-2019-15890 qemu-kvm-ma: QEMU: Slirp: use-after-free during packet reassembly [rhel-7]) + +* Tue Sep 17 2019 Miroslav Rezanina - 2.12.0-35.el7 +- kvm-i386-Add-new-MSR-indices-for-IA32_PRED_CMD-and-IA32_.patch [bz#1709972] +- kvm-i386-Add-CPUID-bit-and-feature-words-for-IA32_ARCH_C.patch [bz#1709972] +- kvm-Add-support-to-KVM_GET_MSR_FEATURE_INDEX_LIST-an.patch [bz#1709972] +- kvm-x86-Data-structure-changes-to-support-MSR-based-feat.patch [bz#1709972] +- kvm-x86-define-a-new-MSR-based-feature-word-FEATURE_WORD.patch [bz#1709972] +- kvm-Use-KVM_GET_MSR_INDEX_LIST-for-MSR_IA32_ARCH_CAP.patch [bz#1709972] +- kvm-i386-kvm-Disable-arch_capabilities-if-MSR-can-t-be-s.patch [bz#1709972] +- kvm-i386-Make-arch_capabilities-migratable.patch [bz#1709972] +- kvm-block-Remove-error-messages-in-bdrv_make_zero.patch [bz#1648622] +- kvm-block-Add-BDRV_REQ_NO_FALLBACK.patch [bz#1648622] +- kvm-block-Advertise-BDRV_REQ_NO_FALLBACK-in-filter-drive.patch [bz#1648622] +- kvm-file-posix-Fix-write_zeroes-with-unmap-on-block-devi.patch [bz#1648622] +- kvm-file-posix-Factor-out-raw_thread_pool_submit.patch [bz#1648622] +- kvm-file-posix-Avoid-aio_worker-for-QEMU_AIO_WRITE_ZEROE.patch [bz#1648622] +- kvm-file-posix-Support-BDRV_REQ_NO_FALLBACK-for-zero-wri.patch [bz#1648622] +- kvm-qemu-img-Use-BDRV_REQ_NO_FALLBACK-for-pre-zeroing.patch [bz#1648622] +- kvm-qemu-io-Add-write-n-for-BDRV_REQ_NO_FALLBACK.patch [bz#1648622] +- kvm-qxl-check-release-info-object.patch [bz#1712704] +- kvm-ccid-Fix-dwProtocols-advertisement-of-T-0.patch [bz#1721522] +- kvm-vl-add-qemu_add_vm_change_state_handler_prio.patch [bz#1673546] +- kvm-qdev-add-qdev_add_vm_change_state_handler.patch [bz#1673546] +- kvm-virtio-scsi-restart-DMA-after-iothread.patch [bz#1673546] +- kvm-block-Drain-source-node-in-bdrv_replace_node.patch [bz#1711643] +- Resolves: bz#1648622 + ([v2v] Migration performance regression) +- Resolves: bz#1673546 + (QEMU gets stuck on resume/cont call from libvirt) +- Resolves: bz#1709972 + ([Intel 7.8 Bug] [KVM][CLX] CPUID_7_0_EDX_ARCH_CAPABILITIES is not enabled in VM qemu-kvm-rhev) +- Resolves: bz#1711643 + (qemu aborts in blockCommit: qemu-kvm: block.c:3486: bdrv_replace_node: Assertion `!({ _Static_assert(!(sizeof(*&from->in_flight) > 8), "not expecting: " "sizeof(*&from->in_flight) > ATOMIC_REG_SIZE"); __atomic_load_n(&from->in_flight, 0); })' failed.) +- Resolves: bz#1712704 + (CVE-2019-12155 qemu-kvm-rhev: QEMU: qxl: null pointer dereference while releasing spice resources [rhel-7]) +- Resolves: bz#1721522 + (ccid: Fix incorrect dwProtocol advertisement of T=0) + +* Thu Sep 05 2019 Miroslav Rezanina - 2.12.0-34.el7 +- kvm-Fix-heap-overflow-in-ip_reass-on-big-packet-input.patch [bz#1734753 bz#1735653] +- Resolves: bz#1734753 + (CVE-2019-14378 qemu-kvm-rhev: QEMU: slirp: heap buffer overflow during packet reassembly [rhel-7.8]) +- Resolves: bz#1735653 + (CVE-2019-14378 qemu-kvm-ma: QEMU: slirp: heap buffer overflow during packet reassembly [rhel-7.8]) + +* Thu Jun 20 2019 Miroslav Rezanina - 2.12.0-33.el7 +- kvm-vhost_net-don-t-set-backend-for-the-uninitialized-vi.patch [bz#1608226] +- Resolves: bz#1608226 + ([virtual-network][mq] prompt warning "qemu-kvm: unable to start vhost net: 14: falling back on userspace virtio" when boot with win8+ guests with multi-queue) + +* Tue Jun 11 2019 Miroslav Rezanina - 2.12.0-32.el7 +- kvm-rtl8139-fix-possible-out-of-bound-access.patch [bz#1636727 bz#1636780] +- kvm-net-ignore-packet-size-greater-than-INT_MAX.patch [bz#1636779 bz#1636780] +- kvm-net-drop-too-large-packet-early.patch [bz#1636779 bz#1636780] +- kvm-Introduce-new-no_guest_reset-parameter-for-usb-host-.patch [bz#1710861] +- kvm-usb-call-reset-handler-before-updating-state.patch [bz#1710861] +- kvm-usb-host-skip-reset-for-untouched-devices.patch [bz#1710861] +- kvm-usb-host-avoid-libusb_set_configuration-calls.patch [bz#1710861] +- kvm-blockdev-fix-missed-target-unref-for-drive-backup.patch [bz#1703916] +- kvm-vl-Fix-drive-blockdev-persistent-reservation-managem.patch [bz#1714160] +- Resolves: bz#1636727 + (CVE-2018-17958 qemu-kvm: Qemu: rtl8139: integer overflow leads to buffer overflow [rhel-7]) +- Resolves: bz#1636779 + (CVE-2018-17963 qemu-kvm: Qemu: net: ignore packets with large size [rhel-7]) +- Resolves: bz#1636780 + (CVE-2018-17963 qemu-kvm-rhev: Qemu: net: ignore packets with large size [rhel-7]) +- Resolves: bz#1703916 + (Qemu core dump when quit vm after forbidden to do backup with a read-only bitmap) +- Resolves: bz#1710861 + (Detached device when trying to upgrade USB device firmware when in doing USB Passthrough via QEMU) +- Resolves: bz#1714160 + (Guest with 'reservations' for a disk start failed) + +* Tue Jun 04 2019 Miroslav Rezanina - 2.12.0-31.el7 +- kvm-seccomp-set-the-seccomp-filter-to-all-threads.patch [bz#1618504] +- Resolves: bz#1618504 + (qemu-kvm-rhev: Qemu: seccomp: blacklist is not applied to all threads [rhel-7]) + +* Tue May 28 2019 Miroslav Rezanina - 2.12.0-30.el7 +- kvm-slirp-fix-big-little-endian-conversion-in-ident-prot.patch [bz#1669071] +- kvm-slirp-ensure-there-is-enough-space-in-mbuf-to-null-t.patch [bz#1669071] +- kvm-slirp-don-t-manipulate-so_rcv-in-tcp_emu.patch [bz#1669071] +- Resolves: bz#1669071 + (CVE-2019-6778 qemu-kvm-rhev: QEMU: slirp: heap buffer overflow in tcp_emu() [rhel-7.7]) + +* Fri May 17 2019 Miroslav Rezanina - 2.12.0-29.el7 +- kvm-qemu-img-Report-bdrv_block_status-failures.patch [bz#1692018] +- kvm-nbd-Tolerate-some-server-non-compliance-in-NBD_CMD_B.patch [bz#1692018] +- kvm-nbd-Don-t-lose-server-s-error-to-NBD_CMD_BLOCK_STATU.patch [bz#1692018] +- kvm-nbd-Permit-simple-error-to-NBD_CMD_BLOCK_STATUS.patch [bz#1692018] +- kvm-qemu-img-Gracefully-shutdown-when-map-can-t-finish.patch [bz#1692018] +- kvm-nbd-client-Work-around-server-BLOCK_STATUS-misalignm.patch [bz#1692018] +- kvm-iotests-Add-241-to-test-NBD-on-unaligned-images.patch [bz#1692018] +- kvm-nbd-client-Lower-min_block-for-block-status-unaligne.patch [bz#1692018] +- kvm-nbd-client-Report-offsets-in-bdrv_block_status.patch [bz#1692018] +- kvm-nbd-client-Reject-inaccessible-tail-of-inconsistent-.patch [bz#1692018] +- kvm-nbd-client-Support-qemu-img-convert-from-unaligned-s.patch [bz#1692018] +- kvm-block-Add-bdrv_get_request_alignment.patch [bz#1692018] +- kvm-nbd-server-Advertise-actual-minimum-block-size.patch [bz#1692018] +- kvm-nbd-client-Trace-server-noncompliance-on-structured-.patch [bz#1692018] +- kvm-nbd-server-Fix-blockstatus-trace.patch [bz#1692018] +- kvm-nbd-server-Trace-client-noncompliance-on-unaligned-r.patch [bz#1692018] +- kvm-nbd-server-Don-t-fail-NBD_OPT_INFO-for-byte-aligned-.patch [bz#1692018] +- kvm-nbd-client-Fix-error-message-for-server-with-unusabl.patch [bz#1692018] +- kvm-iotest-Fix-241-to-run-in-generic-directory.patch [bz#1692018] +- kvm-virtio-scsi-Move-BlockBackend-back-to-the-main-AioCo.patch [bz#1673397 bz#1673402] +- kvm-scsi-disk-Acquire-the-AioContext-in-scsi_-_realize.patch [bz#1673397 bz#1673402] +- kvm-virtio-scsi-Forbid-devices-with-different-iothreads-.patch [bz#1673397 bz#1673402] +- kvm-monitor-move-init-global-earlier.patch [bz#1624009] +- kvm-block-pflash_cfi02-Fix-memory-leak-and-potential-use.patch [bz#1624009] +- kvm-pflash-Rename-pflash_t-to-PFlashCFI01-PFlashCFI02.patch [bz#1624009] +- kvm-pflash_cfi01-Do-not-exit-on-guest-aborting-write-to-.patch [bz#1624009] +- kvm-pflash_cfi01-Log-use-of-flawed-write-to-buffer.patch [bz#1624009] +- kvm-pflash-Rename-CFI_PFLASH-to-PFLASH_CFI.patch [bz#1624009] +- kvm-hw-Use-PFLASH_CFI0-1-2-and-TYPE_PFLASH_CFI0-1-2.patch [bz#1624009] +- kvm-sam460ex-Don-t-size-flash-memory-to-match-backing-im.patch [bz#1624009] +- kvm-ppc405_boards-Delete-stale-disabled-DEBUG_BOARD_INIT.patch [bz#1624009] +- kvm-ppc405_boards-Don-t-size-flash-memory-to-match-backi.patch [bz#1624009] +- kvm-r2d-Fix-flash-memory-size-sector-size-width-device-I.patch [bz#1624009] +- kvm-mips_malta-Delete-disabled-broken-DEBUG_BOARD_INIT-c.patch [bz#1624009] +- kvm-hw-mips-malta-Remove-fl_sectors-variable.patch [bz#1624009] +- kvm-hw-mips-malta-Restrict-bios_size-variable-scope.patch [bz#1624009] +- kvm-mips_malta-Clean-up-definition-of-flash-memory-size-.patch [bz#1624009] +- kvm-pflash-Clean-up-after-commit-368a354f02b-part-1.patch [bz#1624009] +- kvm-pflash-Clean-up-after-commit-368a354f02b-part-2.patch [bz#1624009] +- kvm-qdev-Loosen-coupling-between-compat-and-other-global.patch [bz#1624009] +- kvm-vl-Prepare-fix-of-latent-bug-with-global-and-onboard.patch [bz#1624009] +- kvm-vl-Fix-latent-bug-with-global-and-onboard-devices.patch [bz#1624009] +- kvm-sysbus-Fix-latent-bug-with-onboard-devices.patch [bz#1624009] +- kvm-vl-Improve-legibility-of-BlockdevOptions-queue.patch [bz#1624009] +- kvm-vl-Factor-configure_blockdev-out-of-main.patch [bz#1624009] +- kvm-vl-Create-block-backends-before-setting-machine-prop.patch [bz#1624009] +- kvm-pflash_cfi01-Add-pflash_cfi01_get_blk-helper.patch [bz#1624009] +- kvm-pc_sysfw-Remove-unused-PcSysFwDevice.patch [bz#1624009] +- kvm-pc_sysfw-Pass-PCMachineState-to-pc_system_firmware_i.patch [bz#1624009] +- kvm-pc-Support-firmware-configuration-with-blockdev.patch [bz#1624009] +- kvm-docs-interop-firmware.json-Prefer-machine-to-if-pfla.patch [bz#1624009] +- kvm-Revert-migration-move-only_migratable-to-MigrationSt.patch [bz#1624009] +- kvm-migration-Support-adding-migration-blockers-earlier.patch [bz#1624009] +- Resolves: bz#1624009 + (allow backing of pflash via -blockdev) +- Resolves: bz#1673397 + ([RHEL.7] qemu-kvm core dumped after hotplug the deleted disk with iothread parameter) +- Resolves: bz#1673402 + (Qemu core dump when start guest with two disks using same drive) +- Resolves: bz#1692018 + (qemu-img: Protocol error: simple reply when structured reply chunk was expected) + +* Mon May 13 2019 Miroslav Rezanina - 2.12.0-28.el7 +- kvm-x86-cpu-Enable-CLDEMOTE-Demote-Cache-Line-cpu-featur.patch [bz#1537776] +- kvm-scsi-generic-prevent-guest-from-exceeding-SG_IO-limi.patch [bz#1693879] +- kvm-tests-crypto-Use-the-IEC-binary-prefix-definitions.patch [bz#1666336] +- kvm-crypto-expand-algorithm-coverage-for-cipher-benchmar.patch [bz#1666336] +- kvm-crypto-remove-code-duplication-in-tweak-encrypt-decr.patch [bz#1666336] +- kvm-crypto-introduce-a-xts_uint128-data-type.patch [bz#1666336] +- kvm-crypto-convert-xts_tweak_encdec-to-use-xts_uint128-t.patch [bz#1666336] +- kvm-crypto-convert-xts_mult_x-to-use-xts_uint128-type.patch [bz#1666336] +- kvm-crypto-annotate-xts_tweak_encdec-as-inlineable.patch [bz#1666336] +- kvm-crypto-refactor-XTS-cipher-mode-test-suite.patch [bz#1666336] +- kvm-crypto-add-testing-for-unaligned-buffers-with-XTS-ci.patch [bz#1666336] +- kvm-block-Fix-AioContext-switch-for-bs-drv-NULL.patch [bz#1631227] +- Resolves: bz#1537776 + ([Intel 7.7 Feat] KVM Enabling SnowRidge new NIs - qemu-kvm-rhev) +- Resolves: bz#1631227 + (Qemu Core dump when quit vm that's in status "paused(io-error)" with data plane enabled) +- Resolves: bz#1666336 + (severe performance impact using encrypted Cinder volume (QEMU luks)) +- Resolves: bz#1693879 + (RHV VM pauses when 'dd' issued inside guest to a direct lun configured as virtio-scsi with scsi-passthrough) + +* Wed Apr 24 2019 Miroslav Rezanina - 2.12.0-27.el7 +- kvm-tests-virtio-blk-test-Disable-auto-read-only.patch [bz#1685989] +- kvm-block-Avoid-useless-local_err.patch [bz#1685989] +- kvm-block-Simplify-bdrv_reopen_abort.patch [bz#1685989] +- kvm-block-Always-abort-reopen-after-prepare-succeeded.patch [bz#1685989] +- kvm-block-Make-permission-changes-in-reopen-less-wrong.patch [bz#1685989] +- kvm-block-Fix-use-after-free-error-in-bdrv_open_inherit.patch [bz#1685989] +- kvm-qemu-iotests-Test-snapshot-on-with-nonexistent-TMPDI.patch [bz#1685989] +- kvm-file-posix-Fix-bdrv_open_flags-for-snapshot-on.patch [bz#1685989] +- kvm-file-posix-Use-error-API-properly.patch [bz#1685989] +- kvm-file-posix-Factor-out-raw_reconfigure_getfd.patch [bz#1685989] +- kvm-file-posix-Store-BDRVRawState.reopen_state-during-re.patch [bz#1685989] +- kvm-file-posix-Lock-new-fd-in-raw_reopen_prepare.patch [bz#1685989] +- kvm-file-posix-Prepare-permission-code-for-fd-switching.patch [bz#1685989] +- kvm-file-posix-Make-auto-read-only-dynamic.patch [bz#1685989] +- kvm-qapi-bitmap-merge-document-name-change.patch [bz#1668956] +- kvm-iotests-Enhance-223-to-cover-multiple-bitmap-granula.patch [bz#1668956] +- kvm-iotests-Unify-log-outputs-between-Python-2-and-3.patch [bz#1668956] +- kvm-blockdev-n-ary-bitmap-merge.patch [bz#1668956] +- kvm-block-remove-x-prefix-from-experimental-bitmap-APIs.patch [bz#1668956] +- kvm-iotests.py-don-t-abort-if-IMGKEYSECRET-is-undefined.patch [bz#1668956] +- kvm-iotests-add-filter_generated_node_ids.patch [bz#1668956] +- kvm-iotests-add-qmp-recursive-sorting-function.patch [bz#1668956] +- kvm-iotests-remove-default-filters-from-qmp_log.patch [bz#1668956] +- kvm-iotests-change-qmp_log-filters-to-expect-QMP-objects.patch [bz#1668956] +- kvm-iotests-implement-pretty-print-for-log-and-qmp_log.patch [bz#1668956] +- kvm-iotests-add-iotest-236-for-testing-bitmap-merge.patch [bz#1668956] +- kvm-iotests-236-fix-transaction-kwarg-order.patch [bz#1668956] +- kvm-iotests-Re-add-filename-filters.patch [bz#1668956] +- kvm-iotests-Remove-superfluous-rm-from-232.patch [bz#1668956] +- kvm-iotests-Fix-232-for-LUKS.patch [bz#1668956] +- kvm-iotests-Fix-207-to-use-QMP-filters-for-qmp_log.patch [bz#1668956] +- kvm-iotests.py-Add-is_str.patch [bz#1668956] +- kvm-iotests.py-Filter-filename-in-any-string-value.patch [bz#1668956] +- kvm-iotest-Fix-filtering-order-in-226.patch [bz#1691018] +- kvm-iotests-Don-t-lock-dev-null-in-226.patch [bz#1691018] +- kvm-dirty-bitmap-improve-bdrv_dirty_bitmap_next_zero.patch [bz#1691048] +- kvm-tests-add-tests-for-hbitmap_next_zero-with-specified.patch [bz#1691048] +- kvm-dirty-bitmap-add-bdrv_dirty_bitmap_next_dirty_area.patch [bz#1691048] +- kvm-tests-add-tests-for-hbitmap_next_dirty_area.patch [bz#1691048] +- kvm-Revert-block-dirty-bitmap-Add-bdrv_dirty_iter_next_a.patch [bz#1691048] +- kvm-Revert-test-hbitmap-Add-non-advancing-iter_next-test.patch [bz#1691048] +- kvm-Revert-hbitmap-Add-advance-param-to-hbitmap_iter_nex.patch [bz#1691048] +- kvm-bdrv_query_image_info-Error-parameter-added.patch [bz#1691048] +- kvm-qcow2-Add-list-of-bitmaps-to-ImageInfoSpecificQCow2.patch [bz#1691048] +- kvm-qcow2-list-of-bitmaps-new-test-242.patch [bz#1691048] +- kvm-blockdev-acquire-aio_context-for-bitmap-add-remove.patch [bz#1672010] +- kvm-nbd-client-fix-nbd_negotiate_simple_meta_context.patch [bz#1691563] +- kvm-nbd-client-Fix-error-messages-during-NBD_INFO_BLOCK_.patch [bz#1691563] +- kvm-nbd-client-Relax-handling-of-large-NBD_CMD_BLOCK_STA.patch [bz#1691563] +- kvm-tests-Simplify-.gitignore.patch [bz#1691563] +- kvm-iotests-nbd-Stop-qemu-nbd-before-remaking-image.patch [bz#1691563] +- kvm-iotests-Disallow-compat-0.10-in-223.patch [bz#1691563] +- kvm-nbd-server-fix-bitmap-export.patch [bz#1691563] +- kvm-nbd-server-send-more-than-one-extent-of-base-allocat.patch [bz#1691563] +- kvm-nbd-Don-t-take-address-of-fields-in-packed-structs.patch [bz#1691563] +- kvm-qemu-nbd-Document-tls-creds.patch [bz#1691563] +- kvm-qemu-nbd-drop-old-style-negotiation.patch [bz#1691563] +- kvm-nbd-server-drop-old-style-negotiation.patch [bz#1691563] +- kvm-qemu-iotests-remove-unused-variable-here.patch [bz#1691563] +- kvm-qemu-iotests-convert-pwd-and-pwd-to-PWD.patch [bz#1691563] +- kvm-qemu-iotests-Modern-shell-scripting-use-instead-of.patch [bz#1691563] +- kvm-nbd-fix-whitespace-in-server-error-message.patch [bz#1691563] +- kvm-nbd-server-Ignore-write-errors-when-replying-to-NBD_.patch [bz#1691563] +- kvm-io-return-0-for-EOF-in-TLS-session-read-after-shutdo.patch [bz#1691563] +- kvm-tests-pull-qemu-nbd-iotest-helpers-into-common.nbd-f.patch [bz#1691563] +- kvm-tests-check-if-qemu-nbd-is-still-alive-before-waitin.patch [bz#1691563] +- kvm-tests-add-iotests-helpers-for-dealing-with-TLS-certi.patch [bz#1691563] +- kvm-tests-exercise-NBD-server-in-TLS-mode.patch [bz#1691563] +- kvm-iotests-Also-test-I-O-over-NBD-TLS.patch [bz#1691563] +- kvm-iotests-Drop-use-of-bash-keyword-function.patch [bz#1691563] +- kvm-nbd-server-Advertise-all-contexts-in-response-to-bar.patch [bz#1691563] +- kvm-nbd-client-Make-x-dirty-bitmap-more-reliable.patch [bz#1691563] +- kvm-nbd-client-Send-NBD_CMD_DISC-if-open-fails-after-con.patch [bz#1691563] +- kvm-iotests-fix-nbd-test-233-to-work-correctly-with-raw-.patch [bz#1691563] +- kvm-iotests-Skip-233-if-certtool-not-installed.patch [bz#1691009] +- kvm-iotests-Make-nbd-fault-injector-flush.patch [bz#1691009] +- kvm-iotests-make-083-specific-to-raw.patch [bz#1691009] +- kvm-nbd-publish-_lookup-functions.patch [bz#1691009] +- kvm-nbd-client-Trace-all-server-option-error-messages.patch [bz#1691009] +- kvm-block-nbd-client-use-traces-instead-of-noisy-error_r.patch [bz#1691009] +- kvm-qemu-nbd-Use-program-name-in-error-messages.patch [bz#1691009] +- kvm-nbd-Document-timeline-of-various-features.patch [bz#1691009] +- kvm-nbd-client-More-consistent-error-messages.patch [bz#1691009] +- kvm-qemu-nbd-Fail-earlier-for-c-d-on-non-linux.patch [bz#1691009] +- kvm-nbd-client-Drop-pointless-buf-variable.patch [bz#1691009] +- kvm-qemu-nbd-Rename-exp-variable-clashing-with-math-exp-.patch [bz#1691009] +- kvm-nbd-Add-some-error-case-testing-to-iotests-223.patch [bz#1691009] +- kvm-nbd-Forbid-nbd-server-stop-when-server-is-not-runnin.patch [bz#1691009] +- kvm-nbd-Only-require-disabled-bitmap-for-read-only-expor.patch [bz#1691009] +- kvm-nbd-Merge-nbd_export_set_name-into-nbd_export_new.patch [bz#1691009] +- kvm-nbd-Allow-bitmap-export-during-QMP-nbd-server-add.patch [bz#1691009] +- kvm-nbd-Remove-x-nbd-server-add-bitmap.patch [bz#1691009] +- kvm-nbd-Merge-nbd_export_bitmap-into-nbd_export_new.patch [bz#1691009] +- kvm-qemu-nbd-Add-bitmap-NAME-option.patch [bz#1691009] +- kvm-qom-Clean-up-error-reporting-in-user_creatable_add_o.patch [bz#1691009] +- kvm-qemu-img-fix-error-reporting-for-object.patch [bz#1691009] +- kvm-iotests-Make-233-output-more-reliable.patch [bz#1691009] +- kvm-maint-Allow-for-EXAMPLES-in-texi2pod.patch [bz#1691009] +- kvm-qemu-nbd-Enhance-man-page.patch [bz#1691009] +- kvm-qemu-nbd-Sanity-check-partition-bounds.patch [bz#1691009] +- kvm-nbd-server-Hoist-length-check-to-qmp_nbd_server_add.patch [bz#1691009] +- kvm-nbd-server-Favor-u-int64_t-over-off_t.patch [bz#1691009] +- kvm-qemu-nbd-Avoid-strtol-open-coding.patch [bz#1691009] +- kvm-nbd-client-Refactor-nbd_receive_list.patch [bz#1691009] +- kvm-nbd-client-Move-export-name-into-NBDExportInfo.patch [bz#1691009] +- kvm-nbd-client-Change-signature-of-nbd_negotiate_simple_.patch [bz#1691009] +- kvm-nbd-client-Split-out-nbd_send_meta_query.patch [bz#1691009] +- kvm-nbd-client-Split-out-nbd_receive_one_meta_context.patch [bz#1691009] +- kvm-nbd-client-Refactor-return-of-nbd_receive_negotiate.patch [bz#1691009] +- kvm-nbd-client-Split-handshake-into-two-functions.patch [bz#1691009] +- kvm-nbd-client-Pull-out-oldstyle-size-determination.patch [bz#1691009] +- kvm-nbd-client-Refactor-nbd_opt_go-to-support-NBD_OPT_IN.patch [bz#1691009] +- kvm-nbd-client-Add-nbd_receive_export_list.patch [bz#1691009] +- kvm-nbd-client-Add-meta-contexts-to-nbd_receive_export_l.patch [bz#1691009] +- kvm-qemu-nbd-Add-list-option.patch [bz#1691009] +- kvm-nbd-client-Work-around-3.0-bug-for-listing-meta-cont.patch [bz#1691009] +- kvm-iotests-Enhance-223-233-to-cover-qemu-nbd-list.patch [bz#1691009] +- kvm-qemu-nbd-Deprecate-qemu-nbd-partition.patch [bz#1691009] +- kvm-nbd-generalize-usage-of-nbd_read.patch [bz#1691009] +- kvm-block-nbd-client-split-channel-errors-from-export-er.patch [bz#1691009] +- kvm-block-nbd-move-connection-code-from-block-nbd-to-blo.patch [bz#1691009] +- kvm-block-nbd-client-split-connection-from-initializatio.patch [bz#1691009] +- kvm-block-nbd-client-fix-nbd_reply_chunk_iter_receive.patch [bz#1691009] +- kvm-block-nbd-client-don-t-check-ioc.patch [bz#1691009] +- kvm-block-nbd-client-rename-read_reply_co-to-connection_.patch [bz#1691009] +- kvm-nbd-server-Kill-pointless-shadowed-variable.patch [bz#1691009] +- kvm-iotests-ensure-we-print-nbd-server-log-on-error.patch [bz#1691009] +- kvm-iotests-avoid-broken-pipe-with-certtool.patch [bz#1691009] +- kvm-iotests-Wait-for-qemu-to-end-in-223.patch [bz#1691009] +- kvm-qemu-kvm-rhev.spec-add-iotest-233.patch [bz#1691009] +- kvm-slirp-check-sscanf-result-when-emulating-ident.patch [bz#1689793] +- kvm-dirty-bitmap-Expose-persistent-flag-to-query-block.patch [bz#1677073] +- kvm-block-dirty-bitmap-Documentation-and-Comment-fixups.patch [bz#1677073] +- kvm-block-dirty-bitmap-add-recording-and-busy-properties.patch [bz#1677073] +- kvm-block-dirty-bitmaps-rename-frozen-predicate-helper.patch [bz#1677073] +- kvm-block-dirty-bitmap-remove-set-reset-assertions-again.patch [bz#1677073] +- kvm-block-dirty-bitmap-change-semantics-of-enabled-predi.patch [bz#1677073] +- kvm-nbd-change-error-checking-order-for-bitmaps.patch [bz#1677073] +- kvm-block-dirty-bitmap-explicitly-lock-bitmaps-with-succ.patch [bz#1677073] +- kvm-block-dirty-bitmaps-unify-qmp_locked-and-user_locked.patch [bz#1677073] +- kvm-block-dirty-bitmaps-move-comment-block.patch [bz#1677073] +- kvm-blockdev-remove-unused-paio-parameter-documentation.patch [bz#1677073] +- kvm-iotests-add-busy-recording-bit-test-to-124.patch [bz#1677073] +- kvm-block-dirty-bitmaps-add-inconsistent-bit.patch [bz#1677073] +- kvm-block-dirty-bitmap-add-inconsistent-status.patch [bz#1677073] +- kvm-block-dirty-bitmaps-add-block_dirty_bitmap_check-fun.patch [bz#1677073] +- kvm-block-dirty-bitmaps-prohibit-readonly-bitmaps-for-ba.patch [bz#1677073] +- kvm-block-dirty-bitmaps-prohibit-removing-readonly-bitma.patch [bz#1677073] +- kvm-block-dirty-bitmaps-disallow-busy-bitmaps-as-merge-s.patch [bz#1677073] +- kvm-block-dirty-bitmaps-implement-inconsistent-bit.patch [bz#1677073] +- kvm-bitmaps-Fix-typo-in-function-name.patch [bz#1677073] +- kvm-qemu-kvm-rhev.spec-add-bitmap-related-tests.patch [bz#1677073] +- kvm-block-file-posix-do-not-fail-on-unlock-bytes.patch [bz#1603104] +- kvm-docs-interop-qcow2-Improve-bitmap-flag-in_use-specif.patch [bz#1666884] +- kvm-block-qcow2-bitmap-Don-t-check-size-for-IN_USE-bitma.patch [bz#1666884] +- kvm-block-qcow2-bitmap-Allow-resizes-with-persistent-bit.patch [bz#1666884] +- kvm-tests-qemu-iotests-add-bitmap-resize-test-246.patch [bz#1666884] +- kvm-qemu-kvm-rhev.spec-add-iotest-246.patch [bz#1666884] +- kvm-x86-host-phys-bits-limit-option.patch [bz#1691519] +- kvm-rhel-Set-host-phys-bits-limit-48-on-rhel-machine-typ.patch [bz#1691519] +- kvm-device_tree-Fix-integer-overflowing-in-load_device_t.patch [bz#1693115] +- kvm-mmap-alloc-unfold-qemu_ram_mmap.patch [bz#1672819] +- kvm-mmap-alloc-fix-hugetlbfs-misaligned-length-in-ppc64.patch [bz#1672819] +- Resolves: bz#1603104 + (Qemu Aborted (core dumped) for 'qemu-kvm: Failed to lock byte 100' when remote NFS or GlusterFS volume stopped during the block mirror(or block commit/stream) process) +- Resolves: bz#1666884 + (persistent bitmaps prevent qcow2 image resize) +- Resolves: bz#1668956 + (incremental backup bitmap API needs a finalized interface) +- Resolves: bz#1672010 + ([RHEL7]Qemu coredump when remove a persistent bitmap after vm re-start(dataplane enabled)) +- Resolves: bz#1672819 + (RHEL7.6/RHV4.2 - qemu doesn't free up hugepage memory when hotplug/hotunplug using memory-backend-file (qemu-kvm-rhev)) +- Resolves: bz#1677073 + (Backport additional QEMU 4.0 Bitmap API changes to RHEL 7.7) +- Resolves: bz#1685989 + (Add facility to use block jobs with backing images without write permission) +- Resolves: bz#1689793 + (CVE-2019-9824 qemu-kvm-rhev: QEMU: Slirp: information leakage in tcp_emu() due to uninitialized stack variables [rhel-7]) +- Resolves: bz#1691009 + (NBD pull mode incremental backup API needs a finalized interface) +- Resolves: bz#1691018 + (Fix iotest 226 for local development builds) +- Resolves: bz#1691048 + (Add qemu-img info support for querying bitmaps offline) +- Resolves: bz#1691519 + (physical bits should <= 48 when host with 5level paging &EPT5 and qemu command with "-cpu qemu64" parameters.) +- Resolves: bz#1691563 + (QEMU NBD Feature parity roundup (QEMU 3.1.0)) +- Resolves: bz#1693115 + (CVE-2018-20815 qemu-kvm-rhev: QEMU: device_tree: heap buffer overflow while loading device tree blob [rhel-7.7]) + +* Tue Mar 19 2019 Miroslav Rezanina - 2.12.0-25.el7 +- kvm-block-backend-Make-blk_inc-dec_in_flight-public.patch [bz#1671173] +- kvm-virtio-blk-Increase-in_flight-for-request-restart-BH.patch [bz#1671173] +- kvm-block-Fix-AioContext-switch-for-drained-node.patch [bz#1671173] +- kvm-test-bdrv-drain-AioContext-switch-in-drained-section.patch [bz#1671173] +- kvm-block-Use-normal-drain-for-bdrv_set_aio_context.patch [bz#1671173] +- kvm-qapi-Add-rendernode-display-option-for-egl-headless.patch [bz#1648236] +- kvm-ui-Allow-specifying-rendernode-display-option-for-eg.patch [bz#1648236] +- kvm-qapi-add-query-display-options-command.patch [bz#1648236] +- kvm-egl-headless-parse-rendernode.patch [bz#1648236] +- Resolves: bz#1648236 + (QEMU doesn't expose rendernode option for egl-headless display type) +- Resolves: bz#1671173 + (qemu with iothreads enabled crashes on resume after enospc pause for disk extension) + +* Tue Feb 26 2019 Miroslav Rezanina - 2.12.0-24.el7 +- kvm-exec-move-memory-access-declarations-to-a-common-hea.patch [bz#1597482] +- kvm-exec-small-changes-to-flatview_do_translate.patch [bz#1597482] +- kvm-exec-extract-address_space_translate_iommu-fix-page_.patch [bz#1597482] +- kvm-exec-reintroduce-MemoryRegion-caching.patch [bz#1597482] +- kvm-virtio-update-MemoryRegionCaches-when-guest-negotiat.patch [bz#1597482] +- kvm-exec-Fix-MAP_RAM-for-cached-access.patch [bz#1597482] +- kvm-virtio-Return-true-from-virtio_queue_empty-if-broken.patch [bz#1597482] +- kvm-block-backend-Set-werror-rerror-defaults-in-blk_new.patch [bz#1631615] +- kvm-block-Apply-auto-read-only-for-ro-whitelist-drivers.patch [bz#1667320] +- kvm-qcow2-Give-the-refcount-cache-the-minimum-possible-s.patch [bz#1656913] +- kvm-docs-Document-the-new-default-sizes-of-the-qcow2-cac.patch [bz#1656913] +- kvm-qcow2-Fix-Coverity-warning-when-calculating-the-refc.patch [bz#1656913] +- kvm-qcow2-Options-documentation-fixes.patch [bz#1656913] +- kvm-include-Add-a-lookup-table-of-sizes.patch [bz#1656913] +- kvm-qcow2-Make-sizes-more-humanly-readable.patch [bz#1656913] +- kvm-qcow2-Avoid-duplication-in-setting-the-refcount-cach.patch [bz#1656913] +- kvm-qcow2-Assign-the-L2-cache-relatively-to-the-image-si.patch [bz#1656913] +- kvm-qcow2-Increase-the-default-upper-limit-on-the-L2-cac.patch [bz#1656913] +- kvm-qcow2-Resize-the-cache-upon-image-resizing.patch [bz#1656913] +- kvm-qcow2-Set-the-default-cache-clean-interval-to-10-min.patch [bz#1656913] +- kvm-qcow2-Explicit-number-replaced-by-a-constant.patch [bz#1656913] +- kvm-qcow2-Fix-cache-clean-interval-documentation.patch [bz#1656913] +- kvm-Add-raw-qcow2-nbd-and-luks-iotests-to-run-during-the.patch [bz#1676728 bz#1678898] +- Resolves: bz#1597482 + (qemu crashed when disk enable the IOMMU) +- Resolves: bz#1631615 + (Wrong werror default for -device drive=) +- Resolves: bz#1656913 + (qcow2 cache is too small) +- Resolves: bz#1667320 + (-blockdev: auto-read-only is ineffective for drivers on read-only whitelist) +- Resolves: bz#1676728 + (RHEL77: Run iotests as part of build process) +- Resolves: bz#1678898 + (Run iotests in rhel77 qemu-kvm-ma build %check phase) + +* Tue Feb 12 2019 Miroslav Rezanina - 2.12.0-23.el7 +- kvm-iotests-153-Fix-dead-code.patch [bz#1551486] +- kvm-file-posix-Include-filename-in-locking-error-message.patch [bz#1551486] +- kvm-file-posix-Skip-effectiveless-OFD-lock-operations.patch [bz#1551486] +- kvm-file-posix-Drop-s-lock_fd.patch [bz#1551486] +- kvm-tests-Add-unit-tests-for-image-locking.patch [bz#1551486] +- kvm-file-posix-Fix-shared-locks-on-reopen-commit.patch [bz#1551486] +- kvm-iotests-Test-file-posix-locking-and-reopen.patch [bz#1551486] +- kvm-scsi-disk-Don-t-use-empty-string-as-device-id.patch [bz#1673080] +- kvm-scsi-disk-Add-device_id-property.patch [bz#1673080] +- kvm-scsi-generic-avoid-possible-out-of-bounds-access-to-.patch [bz#1668999] +- kvm-block-remove-bdrv_dirty_bitmap_make_anon.patch [bz#1658343] +- kvm-block-simplify-code-around-releasing-bitmaps.patch [bz#1658343] +- kvm-hbitmap-Add-advance-param-to-hbitmap_iter_next.patch [bz#1658343] +- kvm-test-hbitmap-Add-non-advancing-iter_next-tests.patch [bz#1658343] +- kvm-block-dirty-bitmap-Add-bdrv_dirty_iter_next_area.patch [bz#1658343] +- kvm-dirty-bitmap-switch-assert-fails-to-errors-in-bdrv_m.patch [bz#1658343] +- kvm-dirty-bitmap-rename-bdrv_undo_clear_dirty_bitmap.patch [bz#1658343] +- kvm-dirty-bitmap-make-it-possible-to-restore-bitmap-afte.patch [bz#1658343] +- kvm-blockdev-rename-block-dirty-bitmap-clear-transaction.patch [bz#1658343] +- kvm-qapi-add-transaction-support-for-x-block-dirty-bitma.patch [bz#1658343] +- kvm-block-dirty-bitmaps-add-user_locked-status-checker.patch [bz#1658343] +- kvm-block-dirty-bitmaps-fix-merge-permissions.patch [bz#1658343] +- kvm-block-dirty-bitmaps-allow-clear-on-disabled-bitmaps.patch [bz#1658343] +- kvm-block-dirty-bitmaps-prohibit-enable-disable-on-locke.patch [bz#1658343] +- kvm-block-backup-prohibit-backup-from-using-in-use-bitma.patch [bz#1658343] +- kvm-nbd-forbid-use-of-frozen-bitmaps.patch [bz#1658343] +- kvm-bitmap-Update-count-after-a-merge.patch [bz#1658343] +- kvm-iotests-169-drop-deprecated-autoload-parameter.patch [bz#1658343] +- kvm-block-qcow2-improve-error-message-in-qcow2_inactivat.patch [bz#1658343] +- kvm-bloc-qcow2-drop-dirty_bitmaps_loaded-state-variable.patch [bz#1658343] +- kvm-dirty-bitmaps-clean-up-bitmaps-loading-and-migration.patch [bz#1658343] +- kvm-iotests-improve-169.patch [bz#1658343] +- kvm-iotests-169-add-cases-for-source-vm-resuming.patch [bz#1658343] +- Resolves: bz#1551486 + (QEMU image locking needn't double open fd number (i.e. drop file-posix.c:s->lock_fd)) +- Resolves: bz#1658343 + (QEMU incremental backup fixes and improvements (from upstream and in RHEL8)) +- Resolves: bz#1668999 + (CVE-2019-6501 qemu-kvm-rhev: QEMU: scsi-generic: possible OOB access while handling inquiry request [rhel-7]) +- Resolves: bz#1673080 + ("An unknown error has occurred" when using cdrom to install the system with two blockdev disks.(when choose installation destination)) + +* Tue Feb 05 2019 Miroslav Rezanina - 2.12.0-22.el7 +- kvm-spapr-Fix-ibm-max-associativity-domains-property-num.patch [bz#1626347] +- kvm-spapr-Add-H-Call-H_HOME_NODE_ASSOCIATIVITY.patch [bz#1626347] +- kvm-hostmem-file-remove-object-id-from-pmem-error-messag.patch [bz#1628098] +- kvm-cpus-ignore-ESRCH-in-qemu_cpu_kick_thread.patch [bz#1614610] +- kvm-blockdev-abort-transactions-in-reverse-order.patch [bz#1658426] +- kvm-block-dirty-bitmap-remove-assertion-from-restore.patch [bz#1658426] +- kvm-block-Fix-invalidate_cache-error-path-for-parent-act.patch [bz#1531888] +- kvm-luks-Allow-share-rw-on.patch [bz#1598119] +- Resolves: bz#1531888 + (Local VM and migrated VM on the same host can run with same RAW file as visual disk source while without shareable configured or lock manager enabled) +- Resolves: bz#1598119 + ("share-rw=on" does not work for luks format image) +- Resolves: bz#1614610 + (Guest quit with error when hotunplug cpu) +- Resolves: bz#1626347 + (RHEL7.6 - [Power8][Host: 3.10.0-933.el7.ppc64le][Guest: 3.10.0-933.el7.ppc64le] [qemu-kvm-ma-2.12.0-8.el7.ppc64le] Guest VM crashes during vcpu hotplug with specific numa configuration (kvm)) +- Resolves: bz#1628098 + ([Intel 7.7 BUG][KVM][Crystal Ridge]object_get_canonical_path_component: assertion failed: (obj->parent != NULL)) +- Resolves: bz#1658426 + (qemu core dumped when doing incremental live backup without "bitmap" parameter by mistake in a transaction mode((blockdev-backup/block-dirty-bitmap-add/x-block-dirty-bitmap-merge)) + +* Wed Jan 02 2019 Miroslav Rezanina - 2.12.0-21.el7 +- kvm-Add-dependency-to-libxkbcommon.patch [bz#1642551] +- Resolves: bz#1642551 + (qemu-kvm-tools-rhev depends on libxkbcommon, but the RPM-level dependency is missing) + +* Thu Dec 06 2018 Miroslav Rezanina - 2.12.0-20.el7 +- kvm-vnc-call-sasl_server_init-only-when-required.patch [bz#1614302] +- kvm-block-Update-flags-in-bdrv_set_read_only.patch [bz#1623986] +- kvm-block-Add-auto-read-only-option.patch [bz#1623986] +- kvm-rbd-Close-image-in-qemu_rbd_open-error-path.patch [bz#1623986] +- kvm-block-Require-auto-read-only-for-existing-fallbacks.patch [bz#1623986] +- kvm-nbd-Support-auto-read-only-option.patch [bz#1623986] +- kvm-file-posix-Support-auto-read-only-option.patch [bz#1623986] +- kvm-curl-Support-auto-read-only-option.patch [bz#1623986] +- kvm-gluster-Support-auto-read-only-option.patch [bz#1623986] +- kvm-iscsi-Support-auto-read-only-option.patch [bz#1623986] +- kvm-block-Make-auto-read-only-on-default-for-drive.patch [bz#1623986] +- kvm-qemu-iotests-Test-auto-read-only-with-drive-and-bloc.patch [bz#1623986] +- kvm-block-Fix-update-of-BDRV_O_AUTO_RDONLY-in-update_fla.patch [bz#1623986] +- kvm-memory-cleanup-side-effects-of-memory_region_init_fo.patch [bz#1585155] +- kvm-block-Don-t-inactivate-children-before-parents.patch [bz#1633536] +- kvm-iotests-Test-migration-with-blockdev.patch [bz#1633536] +- kvm-iothread-fix-crash-with-invalid-properties.patch [bz#1607768] +- kvm-balloon-Allow-multiple-inhibit-users.patch [bz#1619778] +- kvm-Use-inhibit-to-prevent-ballooning-without-synchr.patch [bz#1619778] +- kvm-vfio-Inhibit-ballooning-based-on-group-attachment-to.patch [bz#1619778] +- kvm-vfio-ccw-pci-Allow-devices-to-opt-in-for-ballooning.patch [bz#1619778] +- kvm-vfio-pci-Handle-subsystem-realpath-returning-NULL.patch [bz#1619778] +- kvm-vfio-pci-Fix-failure-to-close-file-descriptor-on-err.patch [bz#1619778] +- kvm-postcopy-Synchronize-usage-of-the-balloon-inhibitor.patch [bz#1619778] +- kvm-include-Add-IEC-binary-prefixes-in-qemu-units.h.patch [bz#1566195] +- kvm-hw-scsi-cleanups-before-VPD-BL-emulation.patch [bz#1566195] +- kvm-hw-scsi-centralize-SG_IO-calls-into-single-function.patch [bz#1566195] +- kvm-hw-scsi-add-VPD-Block-Limits-emulation.patch [bz#1566195] +- kvm-scsi-disk-Block-Device-Characteristics-emulation-fix.patch [bz#1566195] +- kvm-scsi-generic-keep-VPD-page-list-sorted.patch [bz#1566195] +- kvm-scsi-generic-avoid-out-of-bounds-access-to-VPD-page-.patch [bz#1566195] +- kvm-scsi-generic-avoid-invalid-access-to-struct-when-emu.patch [bz#1566195] +- kvm-scsi-generic-do-not-do-VPD-emulation-for-sense-other.patch [bz#1566195] +- kvm-redhat-add-opengl-runtime-dependencies.patch [bz#1628455] +- Resolves: bz#1566195 + (Guests don't always see correct transfer limits for passed through scsi devices (e.g. raid0 controlled by sas 9361-8i)) +- Resolves: bz#1585155 + (QEMU core dumped when hotplug memory exceeding host hugepages and with discard-data=yes) +- Resolves: bz#1607768 + (qemu aborted when start guest with a big iothreads) +- Resolves: bz#1614302 + (qemu-kvm: Could not find keytab file: /etc/qemu/krb5.tab: No such file or directory) +- Resolves: bz#1619778 + (Ballooning is incompatible with vfio assigned devices, but not prevented) +- Resolves: bz#1623986 + (block-commit can't be used with -blockdev) +- Resolves: bz#1628455 + (qemu-kvm-rhev should add opengl packages as dependencies) +- Resolves: bz#1633536 + (Qemu core dump when do migration after hot plugging a backend image with 'blockdev-add'(without the frontend)) + +* Wed Nov 21 2018 Miroslav Rezanina - 2.12.0-19.el7 +- kvm-nbd-server-fix-NBD_CMD_CACHE.patch [bz#1636148] +- kvm-nbd-fix-NBD_FLAG_SEND_CACHE-value.patch [bz#1636148] +- kvm-pc-dimm-turn-alignment-assert-into-check.patch [bz#1629720] +- kvm-exec-check-that-alignment-is-a-power-of-two.patch [bz#1629717] +- kvm-nvdimm-no-need-to-overwrite-get_vmstate_memory_regio.patch [bz#1620373] +- kvm-hostmem-drop-error-variable-from-host_memory_backend.patch [bz#1620373] +- kvm-ivshmem-Fix-unplug-of-device-ivshmem-plain.patch [bz#1620373] +- kvm-qemu-error-introduce-error-warn-_report_once.patch [bz#1627272] +- kvm-intel-iommu-start-to-use-error_report_once.patch [bz#1627272] +- kvm-intel-iommu-replace-more-vtd_err_-traces.patch [bz#1627272] +- kvm-intel_iommu-introduce-vtd_reset_caches.patch [bz#1627272] +- kvm-intel_iommu-better-handling-of-dmar-state-switch.patch [bz#1627272] +- kvm-intel_iommu-move-ce-fetching-out-when-sync-shadow.patch [bz#1627272] +- kvm-intel_iommu-handle-invalid-ce-for-shadow-sync.patch [bz#1627272] +- kvm-qapi-fill-in-CpuInfoFast.arch-in-query-cpus-fast.patch [bz#1607406] +- kvm-qapi-add-SysEmuTarget-to-common.json.patch [bz#1607406] +- kvm-qapi-change-the-type-of-TargetInfo.arch-from-string-.patch [bz#1607406] +- kvm-qapi-discriminate-CpuInfoFast-on-SysEmuTarget-not-Cp.patch [bz#1607406] +- kvm-qapi-deprecate-CpuInfoFast.arch.patch [bz#1607406] +- kvm-docs-interop-add-firmware.json.patch [bz#1607406] +- kvm-migration-postcopy-Clear-have_listen_thread.patch [bz#1608877] +- kvm-migration-cleanup-in-error-paths-in-loadvm.patch [bz#1608877] +- Resolves: bz#1607406 + ([RHEL 7.7] RFE: Define firmware metadata format) +- Resolves: bz#1608877 + (After postcopy migration, do savevm and loadvm, guest hang and call trace) +- Resolves: bz#1620373 + (Failed to do migration after hotplug and hotunplug the ivshmem device) +- Resolves: bz#1627272 + (boot guest with q35+vIOMMU+ device assignment, qemu crash when return assigned network devices from vfio driver to ixgbe in guest) +- Resolves: bz#1629717 + (qemu_ram_mmap: Assertion `is_power_of_2(align)' failed) +- Resolves: bz#1629720 + ([Intel 7.6 BUG][Crystal Ridge] pc_dimm_get_free_addr: assertion failed: (QEMU_ALIGN_UP(address_space_start, align) == address_space_start)) +- Resolves: bz#1636148 + (qemu NBD_CMD_CACHE flaws impacting non-qemu NBD clients) + +* Fri Sep 21 2018 Miroslav Rezanina - 2.12.0-18.el7 +- kvm-test-bdrv-drain-Fix-outdated-comments.patch [bz#1618584] +- kvm-block-Use-a-single-global-AioWait.patch [bz#1618584] +- kvm-test-bdrv-drain-Test-draining-job-source-child-and-p.patch [bz#1618584] +- Resolves: bz#1618584 + (block commit aborts with "Co-routine was already scheduled") + +* Wed Sep 19 2018 Miroslav Rezanina - 2.12.0-17.el7 +- kvm-aio-posix-fix-concurrent-access-to-poll_disable_cnt.patch [bz#1628191] +- kvm-aio-posix-compute-timeout-before-polling.patch [bz#1628191] +- kvm-aio-posix-do-skip-system-call-if-ctx-notifier-pollin.patch [bz#1628191] +- Resolves: bz#1628191 + (~40% virtio_blk disk performance drop for win2012r2 guest when comparing qemu-kvm-rhev-2.12.0-9 with qemu-kvm-rhev-2.12.0-12) + +* Mon Sep 17 2018 Miroslav Rezanina - 2.12.0-16.el7 +- kvm-commit-Add-top-node-base-node-options.patch [bz#1624012] +- kvm-qemu-iotests-Test-commit-with-top-node-base-node.patch [bz#1624012] +- kvm-block-rbd-pull-out-qemu_rbd_convert_options.patch [bz#1610605] +- kvm-block-rbd-Attempt-to-parse-legacy-filenames.patch [bz#1610605] +- kvm-block-rbd-add-iotest-for-rbd-legacy-keyvalue-filenam.patch [bz#1610605] +- kvm-pc-acpi-revert-back-to-1-SRAT-entry-for-hotpluggable.patch [bz#1626059] +- kvm-blockdev-backup-add-bitmap-argument.patch [bz#1628373] +- kvm-test-bdrv-drain-bdrv_drain-works-with-cross-AioConte.patch [bz#1601212] +- kvm-block-Use-bdrv_do_drain_begin-end-in-bdrv_drain_all.patch [bz#1601212] +- kvm-block-Remove-recursive-parameter-from-bdrv_drain_inv.patch [bz#1601212] +- kvm-block-Don-t-manually-poll-in-bdrv_drain_all.patch [bz#1601212] +- kvm-tests-test-bdrv-drain-bdrv_drain_all-works-in-corout.patch [bz#1601212] +- kvm-block-Avoid-unnecessary-aio_poll-in-AIO_WAIT_WHILE.patch [bz#1601212] +- kvm-block-Really-pause-block-jobs-on-drain.patch [bz#1601212] +- kvm-block-Remove-bdrv_drain_recurse.patch [bz#1601212] +- kvm-test-bdrv-drain-Add-test-for-node-deletion.patch [bz#1601212] +- kvm-block-Drain-recursively-with-a-single-BDRV_POLL_WHIL.patch [bz#1601212] +- kvm-test-bdrv-drain-Test-node-deletion-in-subtree-recurs.patch [bz#1601212] +- kvm-block-Don-t-poll-in-parent-drain-callbacks.patch [bz#1601212] +- kvm-test-bdrv-drain-Graph-change-through-parent-callback.patch [bz#1601212] +- kvm-block-Defer-.bdrv_drain_begin-callback-to-polling-ph.patch [bz#1601212] +- kvm-test-bdrv-drain-Test-that-bdrv_drain_invoke-doesn-t-.patch [bz#1601212] +- kvm-block-Allow-AIO_WAIT_WHILE-with-NULL-ctx.patch [bz#1601212] +- kvm-block-Move-bdrv_drain_all_begin-out-of-coroutine-con.patch [bz#1601212] +- kvm-block-ignore_bds_parents-parameter-for-drain-functio.patch [bz#1601212] +- kvm-block-Allow-graph-changes-in-bdrv_drain_all_begin-en.patch [bz#1601212] +- kvm-test-bdrv-drain-Test-graph-changes-in-drain_all-sect.patch [bz#1601212] +- kvm-block-Poll-after-drain-on-attaching-a-node.patch [bz#1601212] +- kvm-test-bdrv-drain-Test-bdrv_append-to-drained-node.patch [bz#1601212] +- kvm-block-linux-aio-acquire-AioContext-before-qemu_laio_.patch [bz#1601212] +- kvm-util-async-use-qemu_aio_coroutine_enter-in-co_schedu.patch [bz#1601212] +- kvm-job-Fix-nested-aio_poll-hanging-in-job_txn_apply.patch [bz#1601212] +- kvm-job-Fix-missing-locking-due-to-mismerge.patch [bz#1601212] +- kvm-blockjob-Wake-up-BDS-when-job-becomes-idle.patch [bz#1601212] +- kvm-aio-wait-Increase-num_waiters-even-in-home-thread.patch [bz#1601212] +- kvm-test-bdrv-drain-Drain-with-block-jobs-in-an-I-O-thre.patch [bz#1601212] +- kvm-test-blockjob-Acquire-AioContext-around-job_cancel_s.patch [bz#1601212] +- kvm-job-Use-AIO_WAIT_WHILE-in-job_finish_sync.patch [bz#1601212] +- kvm-test-bdrv-drain-Test-AIO_WAIT_WHILE-in-completion-ca.patch [bz#1601212] +- kvm-block-Add-missing-locking-in-bdrv_co_drain_bh_cb.patch [bz#1601212] +- kvm-block-backend-Add-.drained_poll-callback.patch [bz#1601212] +- kvm-block-backend-Fix-potential-double-blk_delete.patch [bz#1601212] +- kvm-block-backend-Decrease-in_flight-only-after-callback.patch [bz#1601212] +- kvm-mirror-Fix-potential-use-after-free-in-active-commit.patch [bz#1601212] +- kvm-blockjob-Lie-better-in-child_job_drained_poll.patch [bz#1601212] +- kvm-block-Remove-aio_poll-in-bdrv_drain_poll-variants.patch [bz#1601212] +- kvm-test-bdrv-drain-Test-nested-poll-in-bdrv_drain_poll_.patch [bz#1601212] +- kvm-job-Avoid-deadlocks-in-job_completed_txn_abort.patch [bz#1601212] +- kvm-test-bdrv-drain-AIO_WAIT_WHILE-in-job-.commit-.abort.patch [bz#1601212] +- Resolves: bz#1601212 + (qemu coredumps on block-commit) +- Resolves: bz#1610605 + (rbd json format of 7.6 is incompatible with 7.5) +- Resolves: bz#1624012 + (allow using node-names with block-commit) +- Resolves: bz#1626059 + (RHEL6 guest panics on boot if hotpluggable memory (pc-dimm) is present at boot time) +- Resolves: bz#1628373 + (blockdev-backup does not accept bitmap parameter) + +* Wed Sep 12 2018 Miroslav Rezanina - 2.12.0-15.el7 +- kvm-jobs-change-start-callback-to-run-callback.patch [bz#1626061] +- kvm-jobs-canonize-Error-object.patch [bz#1626061] +- kvm-jobs-add-exit-shim.patch [bz#1626061] +- kvm-block-commit-utilize-job_exit-shim.patch [bz#1626061] +- kvm-block-mirror-utilize-job_exit-shim.patch [bz#1626061] +- kvm-jobs-utilize-job_exit-shim.patch [bz#1626061] +- kvm-block-backup-make-function-variables-consistently-na.patch [bz#1626061] +- kvm-jobs-remove-ret-argument-to-job_completed-privatize-.patch [bz#1626061] +- kvm-jobs-remove-job_defer_to_main_loop.patch [bz#1626061] +- kvm-block-commit-add-block-job-creation-flags.patch [bz#1626061] +- kvm-block-mirror-add-block-job-creation-flags.patch [bz#1626061] +- kvm-block-stream-add-block-job-creation-flags.patch [bz#1626061] +- kvm-block-commit-refactor-commit-to-use-job-callbacks.patch [bz#1626061] +- kvm-block-mirror-don-t-install-backing-chain-on-abort.patch [bz#1626061] +- kvm-block-mirror-conservative-mirror_exit-refactor.patch [bz#1626061] +- kvm-block-stream-refactor-stream-to-use-job-callbacks.patch [bz#1626061] +- kvm-tests-blockjob-replace-Blockjob-with-Job.patch [bz#1626061] +- kvm-tests-test-blockjob-remove-exit-callback.patch [bz#1626061] +- kvm-tests-test-blockjob-txn-move-.exit-to-.clean.patch [bz#1626061] +- kvm-jobs-remove-.exit-callback.patch [bz#1626061] +- kvm-qapi-block-commit-expose-new-job-properties.patch [bz#1626061] +- kvm-qapi-block-mirror-expose-new-job-properties.patch [bz#1626061] +- kvm-qapi-block-stream-expose-new-job-properties.patch [bz#1626061] +- kvm-block-backup-qapi-documentation-fixup.patch [bz#1626061] +- kvm-blockdev-document-transactional-shortcomings.patch [bz#1626061] +- Resolves: bz#1626061 + (qemu blockjobs other than backup do not support job-finalize or job-dismiss) + +* Tue Sep 11 2018 Miroslav Rezanina - 2.12.0-14.el7 +- kvm-mirror-Fail-gracefully-for-source-target.patch [bz#1582042] +- Resolves: bz#1582042 + (Segfault on 'blockdev-mirror' with same node as source and target) + +* Tue Sep 04 2018 Miroslav Rezanina - 2.12.0-13.el7 +- kvm-linux-headers-asm-s390-kvm.h-header-sync.patch [bz#1622962] +- kvm-s390x-kvm-add-etoken-facility.patch [bz#1622962] +- kvm-block-for-jobs-do-not-clear-user_paused-until-after-.patch [bz#1605026] +- kvm-iotests-Add-failure-matching-to-common.qemu.patch [bz#1605026] +- kvm-block-iotest-to-catch-abort-on-forced-blockjob-cance.patch [bz#1605026] +- kvm-i386-define-the-ssbd-CPUID-feature-bit-CVE-2018-3639.patch [bz#1574216] +- kvm-target-i386-sev-fix-memory-leaks.patch [bz#1624390] +- kvm-i386-Fix-arch_query_cpu_model_expansion-leak.patch [bz#1624390] +- kvm-migration-discard-non-migratable-RAMBlocks.patch [bz#1539280] +- kvm-vfio-pci-do-not-set-the-PCIDevice-has_rom-attribute.patch [bz#1539280] +- kvm-memory-exec-Expose-all-memory-block-related-flags.patch [bz#1539280] +- kvm-memory-exec-switch-file-ram-allocation-functions-to-.patch [bz#1539280] +- kvm-configure-add-libpmem-support.patch [bz#1539280] +- kvm-hostmem-file-add-the-pmem-option.patch [bz#1539280] +- kvm-mem-nvdimm-ensure-write-persistence-to-PMEM-in-label.patch [bz#1539280] +- kvm-migration-ram-Add-check-and-info-message-to-nvdimm-p.patch [bz#1539280] +- kvm-migration-ram-ensure-write-persistence-on-loading-al.patch [bz#1539280] +- kvm-intel-iommu-send-PSI-always-even-if-across-PDEs.patch [bz#1623859] +- kvm-intel-iommu-remove-IntelIOMMUNotifierNode.patch [bz#1623859] +- kvm-intel-iommu-add-iommu-lock.patch [bz#1623859] +- kvm-intel-iommu-only-do-page-walk-for-MAP-notifiers.patch [bz#1623859] +- kvm-intel-iommu-introduce-vtd_page_walk_info.patch [bz#1623859] +- kvm-intel-iommu-pass-in-address-space-when-page-walk.patch [bz#1623859] +- kvm-intel-iommu-trace-domain-id-during-page-walk.patch [bz#1623859] +- kvm-util-implement-simple-iova-tree.patch [bz#1623859] +- kvm-intel-iommu-rework-the-page-walk-logic.patch [bz#1623859] +- kvm-qemu-img-fix-regression-copying-secrets-during-conve.patch [bz#1575578] +- kvm-redhat-rewrap-build_configure.sh-cmdline-for-the-rh-.patch [bz#1500753] +- kvm-redhat-enable-opengl-for-x86_64.patch [bz#1500753] +- Resolves: bz#1500753 + ([RFE] Support console on Intel vGPU - qemu) +- Resolves: bz#1539280 + ([Intel 7.6 Bug] [KVM][Crystal Ridge] Lack of data persistence guarantee of QEMU writes to host PMEM) +- Resolves: bz#1574216 + (CVE-2018-3639 qemu-kvm-rhev: hw: cpu: speculative store bypass [rhel-7.6]) +- Resolves: bz#1575578 + (Failed to convert a source image to the qcow2 image encrypted by luks) +- Resolves: bz#1605026 + (Quitting VM causes qemu core dump once the block mirror job paused for no enough target space) +- Resolves: bz#1622962 + (Add etoken support to qemu-kvm for s390x KVM guests) +- Resolves: bz#1623859 + (Guest "Detected Tx unit Hang" and host "DMAR: fault addr" when booting guest with ixgbe device assignment,) +- Resolves: bz#1624390 + (Memory leaks) + +* Tue Aug 28 2018 Miroslav Rezanina - 2.12.0-12.el7 +- kvm-e1000e-Do-not-auto-clear-ICR-bits-which-aren-t-set-i.patch [bz#1596010] +- kvm-e1000e-Prevent-MSI-MSI-X-storms.patch [bz#1596010] +- kvm-hw-usb-dev-smartcard-reader-Handle-64-B-USB-packets.patch [bz#1589147] +- kvm-i386-Disable-TOPOEXT-by-default-on-cpu-host.patch [bz#1613277] +- kvm-RHEL-Disable-transparent-hugepages-on-POWER8-KVM-hos.patch [bz#1620378] +- Resolves: bz#1589147 + (Handle 64 B USB packets for Smart Card redirection) +- Resolves: bz#1596010 + (The network link can't be detected on guest when the guest uses e1000e model type) +- Resolves: bz#1613277 + (kernel panic in init_amd_cacheinfo) +- Resolves: bz#1620378 + (qemu-kvm-rhev: Failed to allocate KVM HPT with vfio device enabled - disable THP for POWER8 guests) + +* Mon Aug 20 2018 Miroslav Rezanina - 2.12.0-11.el7 +- kvm-slirp-reformat-m_inc-routine.patch [bz#1586255] +- kvm-slirp-Correct-size-check-in-m_inc.patch [bz#1586255] +- kvm-Revert-Disable-PCIe-to-PCI-bridge-device.patch [bz#1390329] +- kvm-aio-posix-Don-t-count-ctx-notifier-as-progress-when-.patch [bz#1562750] +- kvm-aio-Do-aio_notify_accept-only-during-blocking-aio_po.patch [bz#1562750] +- Resolves: bz#1390329 + (PCIe: Add Generic PCIe-PCI bridge) +- Resolves: bz#1562750 + (VM doesn't boot from HD) +- Resolves: bz#1586255 + (CVE-2018-11806 qemu-kvm-rhev: QEMU: slirp: heap buffer overflow while reassembling fragmented datagrams [rhel-7.6]) + +* Fri Aug 10 2018 Miroslav Rezanina - 2.12.0-10.el7 +- kvm-qemu-img-Add-C-option-for-convert-with-copy-offloadi.patch [bz#1607774] +- kvm-iotests-Add-test-for-qemu-img-convert-C-compatibilit.patch [bz#1607774] +- kvm-qemu-img-Fix-assert-when-mapping-unaligned-raw-file.patch [bz#1601310] +- kvm-iotests-Add-test-221-to-catch-qemu-img-map-regressio.patch [bz#1601310] +- kvm-pc-acpi-fix-memory-hotplug-regression-by-reducing-st.patch [bz#1609234] +- kvm-block-qapi-Add-qdev-field-to-query-blockstats-result.patch [bz#1612114] +- kvm-block-qapi-Include-anonymous-BBs-in-query-blockstats.patch [bz#1612114] +- kvm-qemu-iotests-Test-query-blockstats-with-drive-and-bl.patch [bz#1612114] +- kvm-Add-local-build-configure-for-s390x.patch [bz#1573135] +- kvm-Update-build-configuration.patch [bz#1573135] +- kvm-Update-local-build-to-work-with-2.12.0-configuration.patch [bz#1573135] +- kvm-Ensure-all-iotests-are-executable-on-build-from-srpm.patch [bz#1608229] +- Resolves: bz#1573135 + (Update build configure for QEMU 2.12.0) +- Resolves: bz#1601310 + (qemu-img map 'Aborted (core dumped)' when specifying a plain file) +- Resolves: bz#1607774 + (Target files for 'qemu-img convert' do not support thin_provisoning with iscsi/nfs backend) +- Resolves: bz#1608229 + (Parts of iotest cases in SRPM don't have execute permission) +- Resolves: bz#1609234 + (Win2016 guest can't recognize pc-dimm hotplugged to node 0) +- Resolves: bz#1612114 + (Anonymous BlockBackends are missing in query-blockstats) + +* Wed Aug 01 2018 Miroslav Rezanina - 2.12.0-9.el7 +- kvm-Disable-aarch64-devices-reappeared-after-2.12-rebase.patch [bz#1586357] +- kvm-Disable-split-irq-device.patch [bz#1586357] +- kvm-Disable-AT24Cx-i2c-eeprom.patch [bz#1586357] +- kvm-Disable-CAN-bus-devices.patch [bz#1586357] +- kvm-Disable-new-superio-devices.patch [bz#1586357] +- kvm-Disable-new-pvrdma-device.patch [bz#1586357] +- kvm-Disable-PCIe-to-PCI-bridge-device.patch [bz#1586357] +- kvm-qdev-add-HotplugHandler-post_plug-callback.patch [bz#1607891] +- kvm-virtio-scsi-fix-hotplug-reset-vs-event-race.patch [bz#1607891] +- kvm-i386-Allow-TOPOEXT-to-be-enabled-on-older-kernels.patch [bz#1608698] +- kvm-e1000-Fix-tso_props-compat-for-82540em.patch [bz#1608778] +- kvm-Revert-redhat-support-POWER8-on-POWER9-settings-in-k.patch [bz#1607443] +- kvm-slirp-correct-size-computation-while-concatenating-m.patch [bz#1586255] +- kvm-s390x-sclp-fix-maxram-calculation.patch [bz#1595740] +- kvm-Fix-update-of-qemu-kvm-tools.patch [bz#1598104] +- Resolves: bz#1586255 + (CVE-2018-11806 qemu-kvm-rhev: QEMU: slirp: heap buffer overflow while reassembling fragmented datagrams [rhel-7.6]) +- Resolves: bz#1586357 + (Disable new devices in 2.12) +- Resolves: bz#1595740 + (RHEL-Alt-7.6 - qemu has error during migration of larger guests) +- Resolves: bz#1598104 + (Upgrade failed from qemu-kvm-tools-rhev-2.12.0-6.el7 to qemu-kvm-tools-rhev-2.12.0-7.el7) +- Resolves: bz#1607443 + (qemu-kvm-rhev: Remove POWER8-on-POWER9 settings from kvm-setup) +- Resolves: bz#1607891 + (Hotplug events are sometimes lost with virtio-scsi + iothread) +- Resolves: bz#1608698 + (topoext support should not require kernel support) +- Resolves: bz#1608778 + (qemu/migration: migrate failed from RHEL.7.6 to RHEL.7.5 with e1000-82540em) + +* Tue Jul 24 2018 Miroslav Rezanina - 2.12.0-8.el7 +- kvm-RHEL-7.6-Add-pseries-rhel7.6.0-sxxm-machine-type.patch [bz#1592648] +- kvm-i386-Helpers-to-encode-cache-information-consistentl.patch [bz#1481253] +- kvm-i386-Add-cache-information-in-X86CPUDefinition.patch [bz#1481253] +- kvm-i386-Initialize-cache-information-for-EPYC-family-pr.patch [bz#1481253] +- kvm-i386-Add-new-property-to-control-cache-info.patch [bz#1481253] +- kvm-i386-Clean-up-cache-CPUID-code.patch [bz#1481253] +- kvm-i386-Populate-AMD-Processor-Cache-Information-for-cp.patch [bz#1481253] +- kvm-i386-Add-support-for-CPUID_8000_001E-for-AMD.patch [bz#1481253] +- kvm-i386-Fix-up-the-Node-id-for-CPUID_8000_001E.patch [bz#1481253] +- kvm-i386-Enable-TOPOEXT-feature-on-AMD-EPYC-CPU.patch [bz#1481253] +- kvm-i386-Remove-generic-SMT-thread-check.patch [bz#1481253] +- kvm-xhci-fix-guest-triggerable-assert.patch [bz#1594135] +- kvm-virtio-gpu-tweak-scanout-disable.patch [bz#1589634] +- kvm-virtio-gpu-update-old-resource-too.patch [bz#1589634] +- kvm-virtio-gpu-disable-scanout-when-backing-resource-is-.patch [bz#1589634] +- kvm-block-Don-t-silently-truncate-node-names.patch [bz#1549654] +- kvm-pr-helper-fix-socket-path-default-in-help.patch [bz#1533158] +- kvm-pr-helper-fix-assertion-failure-on-failed-multipath-.patch [bz#1533158] +- kvm-pr-manager-helper-avoid-SIGSEGV-when-writing-to-the-.patch [bz#1533158] +- kvm-pr-manager-put-stubs-in-.c-file.patch [bz#1533158] +- kvm-pr-manager-add-query-pr-managers-QMP-command.patch [bz#1533158] +- kvm-pr-manager-helper-report-event-on-connection-disconn.patch [bz#1533158] +- kvm-pr-helper-avoid-error-on-PR-IN-command-with-zero-req.patch [bz#1533158] +- kvm-pr-helper-Rework-socket-path-handling.patch [bz#1533158] +- kvm-pr-manager-helper-fix-memory-leak-on-event.patch [bz#1533158] +- kvm-object-fix-OBJ_PROP_LINK_UNREF_ON_RELEASE-ambivalenc.patch [bz#1556678] +- kvm-usb-hcd-xhci-test-add-a-test-for-ccid-hotplug.patch [bz#1556678] +- kvm-Revert-usb-release-the-created-buses.patch [bz#1556678] +- kvm-file-posix-Fix-creation-locking.patch [bz#1599335] +- kvm-file-posix-Unlock-FD-after-creation.patch [bz#1599335] +- kvm-ahci-trim-signatures-on-raise-lower.patch [bz#1584914] +- kvm-ahci-fix-PxCI-register-race.patch [bz#1584914] +- kvm-ahci-don-t-schedule-unnecessary-BH.patch [bz#1584914] +- kvm-qcow2-Fix-qcow2_truncate-error-return-value.patch [bz#1595173] +- kvm-block-Convert-.bdrv_truncate-callback-to-coroutine_f.patch [bz#1595173] +- kvm-qcow2-Remove-coroutine-trampoline-for-preallocate_co.patch [bz#1595173] +- kvm-block-Move-bdrv_truncate-implementation-to-io.c.patch [bz#1595173] +- kvm-block-Use-tracked-request-for-truncate.patch [bz#1595173] +- kvm-file-posix-Make-.bdrv_co_truncate-asynchronous.patch [bz#1595173] +- kvm-block-Fix-copy-on-read-crash-with-partial-final-clus.patch [bz#1590640] +- kvm-block-fix-QEMU-crash-with-scsi-hd-and-drive_del.patch [bz#1599515] +- kvm-virtio-rng-process-pending-requests-on-DRIVER_OK.patch [bz#1576743] +- kvm-file-posix-specify-expected-filetypes.patch [bz#1525829] +- kvm-iotests-add-test-226-for-file-driver-types.patch [bz#1525829] +- kvm-osdep-powerpc64-align-memory-to-allow-2MB-radix-THP-.patch [bz#1600797] +- kvm-spapr-Correct-inverted-test-in-spapr_pc_dimm_node.patch [bz#1598287] +- kvm-block-dirty-bitmap-add-lock-to-bdrv_enable-disable_d.patch [bz#1207657] +- kvm-qapi-add-x-block-dirty-bitmap-enable-disable.patch [bz#1207657] +- kvm-qmp-transaction-support-for-x-block-dirty-bitmap-ena.patch [bz#1207657] +- kvm-qapi-add-x-block-dirty-bitmap-merge.patch [bz#1207657] +- kvm-qapi-add-disabled-parameter-to-block-dirty-bitmap-ad.patch [bz#1207657] +- kvm-block-dirty-bitmap-add-bdrv_enable_dirty_bitmap_lock.patch [bz#1207657] +- kvm-dirty-bitmap-fix-double-lock-on-bitmap-enabling.patch [bz#1207657] +- kvm-block-qcow2-bitmap-fix-free_bitmap_clusters.patch [bz#1207657] +- kvm-qcow2-add-overlap-check-for-bitmap-directory.patch [bz#1207657] +- kvm-blockdev-enable-non-root-nodes-for-backup-source.patch [bz#1207657] +- kvm-iotests-add-222-to-test-basic-fleecing.patch [bz#1207657] +- kvm-qcow2-Remove-dead-check-on-ret.patch [bz#1207657] +- kvm-block-Move-request-tracking-to-children-in-copy-offl.patch [bz#1207657] +- kvm-block-Fix-parameter-checking-in-bdrv_co_copy_range_i.patch [bz#1207657] +- kvm-block-Honour-BDRV_REQ_NO_SERIALISING-in-copy-range.patch [bz#1207657] +- kvm-backup-Use-copy-offloading.patch [bz#1207657] +- kvm-block-backup-disable-copy-offloading-for-backup.patch [bz#1207657] +- kvm-iotests-222-Don-t-run-with-luks.patch [bz#1207657] +- kvm-block-io-fix-copy_range.patch [bz#1207657] +- kvm-block-split-flags-in-copy_range.patch [bz#1207657] +- kvm-block-add-BDRV_REQ_SERIALISING-flag.patch [bz#1207657] +- kvm-block-backup-fix-fleecing-scheme-use-serialized-writ.patch [bz#1207657] +- kvm-nbd-server-Reject-0-length-block-status-request.patch [bz#1207657] +- kvm-nbd-server-fix-trace.patch [bz#1207657] +- kvm-nbd-server-refactor-NBDExportMetaContexts.patch [bz#1207657] +- kvm-nbd-server-add-nbd_meta_empty_or_pattern-helper.patch [bz#1207657] +- kvm-nbd-server-implement-dirty-bitmap-export.patch [bz#1207657] +- kvm-qapi-new-qmp-command-nbd-server-add-bitmap.patch [bz#1207657] +- kvm-docs-interop-add-nbd.txt.patch [bz#1207657] +- kvm-nbd-server-introduce-NBD_CMD_CACHE.patch [bz#1207657] +- kvm-nbd-server-Silence-gcc-false-positive.patch [bz#1207657] +- kvm-nbd-server-Fix-dirty-bitmap-logic-regression.patch [bz#1207657] +- kvm-nbd-server-fix-nbd_co_send_block_status.patch [bz#1207657] +- kvm-nbd-client-Add-x-dirty-bitmap-to-query-bitmap-from-s.patch [bz#1207657] +- kvm-iotests-New-test-223-for-exporting-dirty-bitmap-over.patch [bz#1207657] +- kvm-hw-char-serial-Only-retry-if-qemu_chr_fe_write-retur.patch [bz#1592817] +- kvm-hw-char-serial-retry-write-if-EAGAIN.patch [bz#1592817] +- kvm-throttle-groups-fix-hang-when-group-member-leaves.patch [bz#1535914] +- Resolves: bz#1207657 + (RFE: QEMU Incremental live backup - push and pull modes) +- Resolves: bz#1481253 + (AMD EPYC/Zen SMT support for KVM / QEMU guest (qemu-kvm-rhev)) +- Resolves: bz#1525829 + (can not boot up a scsi-block passthrough disk via -blockdev with error "cannot get SG_IO version number: Operation not supported. Is this a SCSI device?") +- Resolves: bz#1533158 + (QEMU support for libvirtd restarting qemu-pr-helper) +- Resolves: bz#1535914 + (Disable io throttling for one member disk of a group during io will induce the other one hang with io) +- Resolves: bz#1549654 + (Reject node-names which would be truncated by the block layer commands) +- Resolves: bz#1556678 + (Hot plug usb-ccid for the 2nd time with the same ID as the 1st time failed) +- Resolves: bz#1576743 + (virtio-rng hangs when running on recent (2.x) QEMU versions) +- Resolves: bz#1584914 + (SATA emulator lags and hangs) +- Resolves: bz#1589634 + (Migration failed when rebooting guest with multiple virtio videos) +- Resolves: bz#1590640 + (qemu-kvm: block/io.c:1098: bdrv_co_do_copy_on_readv: Assertion `skip_bytes < pnum' failed.) +- Resolves: bz#1592648 + (Create pseries-rhel7.6.0-sxxm machine type) +- Resolves: bz#1592817 + (Retrying on serial_xmit if the pipe is broken may compromise the Guest) +- Resolves: bz#1594135 + (system_reset many times linux guests cause qemu process Aborted) +- Resolves: bz#1595173 + (blockdev-create is blocking) +- Resolves: bz#1598287 + (After rebooting guest,all the hot plug memory will be assigned to the 1st numa node.) +- Resolves: bz#1599335 + (Image creation locking is too tight and is not properly released) +- Resolves: bz#1599515 + (qemu core-dump with aio_read via hmp (util/qemu-thread-posix.c:64: qemu_mutex_lock_impl: Assertion `mutex->initialized' failed)) +- Resolves: bz#1600797 + (RHEL7.5/RHV4.2 - qemu patch to align memory to allow 2MB THP) + +* Wed Jul 04 2018 Miroslav Rezanina - 2.12.0-7.el7 +- kvm-vfio-pci-Default-display-option-to-off.patch [bz#1583050] +- kvm-Add-qemu-keymap-to-qemu-kvm-tools.patch [bz#1590756] +- kvm-usb-host-skip-open-on-pending-postload-bh.patch [bz#1572851] +- kvm-i386-Define-the-Virt-SSBD-MSR-and-handling-of-it-CVE.patch [bz#1574216] +- kvm-i386-define-the-AMD-virt-ssbd-CPUID-feature-bit-CVE-.patch [bz#1574216] +- kvm-block-file-posix-Pass-FD-to-locking-helpers.patch [bz#1519144] +- kvm-block-file-posix-File-locking-during-creation.patch [bz#1519144] +- kvm-iotests-Add-creation-test-to-153.patch [bz#1519144] +- kvm-migration-stop-compressing-page-in-migration-thread.patch [bz#1584139] +- kvm-migration-stop-compression-to-allocate-and-free-memo.patch [bz#1584139] +- kvm-migration-stop-decompression-to-allocate-and-free-me.patch [bz#1584139] +- kvm-migration-detect-compression-and-decompression-error.patch [bz#1584139] +- kvm-migration-introduce-control_save_page.patch [bz#1584139] +- kvm-migration-move-some-code-to-ram_save_host_page.patch [bz#1584139] +- kvm-migration-move-calling-control_save_page-to-the-comm.patch [bz#1584139] +- kvm-migration-move-calling-save_zero_page-to-the-common-.patch [bz#1584139] +- kvm-migration-introduce-save_normal_page.patch [bz#1584139] +- kvm-migration-remove-ram_save_compressed_page.patch [bz#1584139] +- kvm-migration-block-dirty-bitmap-fix-memory-leak-in-dirt.patch [bz#1584139] +- kvm-migration-fix-saving-normal-page-even-if-it-s-been-c.patch [bz#1584139] +- kvm-migration-update-index-field-when-delete-or-qsort-RD.patch [bz#1584139] +- kvm-Migration-TLS-Fix-crash-due-to-double-cleanup.patch [bz#1584139] +- kvm-migration-introduce-decompress-error-check.patch [bz#1584139] +- kvm-migration-Don-t-activate-block-devices-if-using-S.patch [bz#1560854] +- kvm-migration-not-wait-RDMA_CM_EVENT_DISCONNECTED-event-.patch [bz#1584139] +- kvm-migration-block-dirty-bitmap-fix-dirty_bitmap_load.patch [bz#1584139] +- kvm-vhost-user-add-Net-prefix-to-internal-state-structur.patch [bz#1526645] +- kvm-virtio-support-setting-memory-region-based-host-noti.patch [bz#1526645] +- kvm-vhost-user-support-receiving-file-descriptors-in-sla.patch [bz#1526645] +- kvm-osdep-add-wait.h-compat-macros.patch [bz#1526645] +- kvm-vhost-user-bridge-support-host-notifier.patch [bz#1526645] +- kvm-vhost-allow-backends-to-filter-memory-sections.patch [bz#1526645] +- kvm-vhost-user-allow-slave-to-send-fds-via-slave-channel.patch [bz#1526645] +- kvm-vhost-user-introduce-shared-vhost-user-state.patch [bz#1526645] +- kvm-vhost-user-support-registering-external-host-notifie.patch [bz#1526645] +- kvm-libvhost-user-support-host-notifier.patch [bz#1526645] +- kvm-block-Introduce-API-for-copy-offloading.patch [bz#1482537] +- kvm-raw-Check-byte-range-uniformly.patch [bz#1482537] +- kvm-raw-Implement-copy-offloading.patch [bz#1482537] +- kvm-qcow2-Implement-copy-offloading.patch [bz#1482537] +- kvm-file-posix-Implement-bdrv_co_copy_range.patch [bz#1482537] +- kvm-iscsi-Query-and-save-device-designator-when-opening.patch [bz#1482537] +- kvm-iscsi-Create-and-use-iscsi_co_wait_for_task.patch [bz#1482537] +- kvm-iscsi-Implement-copy-offloading.patch [bz#1482537] +- kvm-block-backend-Add-blk_co_copy_range.patch [bz#1482537] +- kvm-qemu-img-Convert-with-copy-offloading.patch [bz#1482537] +- kvm-qcow2-Fix-src_offset-in-copy-offloading.patch [bz#1482537] +- kvm-iscsi-Don-t-blindly-use-designator-length-in-respons.patch [bz#1482537] +- kvm-file-posix-Fix-EINTR-handling.patch [bz#1482537] +- kvm-pc-Add-rhel7.6.0-machine-types.patch [bz#1557051] +- kvm-usb-storage-Add-rerror-werror-properties.patch [bz#1595180] +- kvm-numa-clarify-error-message-when-node-index-is-out-of.patch [bz#1578381] +- kvm-qemu-iotests-Update-026.out.nocache-reference-output.patch [bz#1528541] +- kvm-qcow2-Free-allocated-clusters-on-write-error.patch [bz#1528541] +- kvm-qemu-iotests-Test-qcow2-not-leaking-clusters-on-writ.patch [bz#1528541] +- kvm-s390x-cpumodel-default-enable-bpb-and-ppa15-for-z196.patch [bz#1595715] +- kvm-qemu-options-Add-missing-newline-to-accel-help-text.patch [bz#1586313] +- Resolves: bz#1482537 + ([RFE] qemu-img copy-offloading (convert command)) +- Resolves: bz#1519144 + (qemu-img: image locking doesn't cover image creation) +- Resolves: bz#1526645 + ([Intel 7.6 FEAT] vHost Data Plane Acceleration (vDPA) - vhost user client - qemu-kvm-rhev) +- Resolves: bz#1528541 + (qemu-img check reports tons of leaked clusters after re-start nfs service to resume writing data in guest) +- Resolves: bz#1557051 + (pc-i440fx-rhel7.6.0 and pc-q35-rhel7.6.0 machine types (x86)) +- Resolves: bz#1560854 + (Guest is left paused on source host sometimes if kill source libvirtd during live migration due to QEMU image locking) +- Resolves: bz#1572851 + (Core dumped after migration when with usb-host) +- Resolves: bz#1574216 + (CVE-2018-3639 qemu-kvm-rhev: hw: cpu: speculative store bypass [rhel-7.6]) +- Resolves: bz#1578381 + (Error message need update when specify numa distance with node index >=128) +- Resolves: bz#1583050 + (Fails to start guest with Intel vGPU device) +- Resolves: bz#1584139 + (2.12 migration fixes) +- Resolves: bz#1586313 + (-smp option is not easily found in the output of qemu help) +- Resolves: bz#1590756 + (add qemu-keymap utility) +- Resolves: bz#1595180 + (Can't set rerror/werror with usb-storage) +- Resolves: bz#1595715 + (Add ppa15/bpb to the default cpu model for z196 and higher in the 7.6 s390-ccw-virtio machine) + +* Sat Jun 30 2018 Miroslav Rezanina - 2.12.0-6.el7 +- kvm-Fix-permissions-for-iotest-218.patch [] +- kvm-qxl-fix-local-renderer-crash.patch [bz#1567733] +- kvm-qemu-img-Amendment-support-implies-create_opts.patch [bz#1537956] +- kvm-block-Add-Error-parameter-to-bdrv_amend_options.patch [bz#1537956] +- kvm-qemu-option-Pull-out-Supported-options-print.patch [bz#1537956] +- kvm-qemu-img-Add-print_amend_option_help.patch [bz#1537956] +- kvm-qemu-img-Recognize-no-creation-support-in-o-help.patch [bz#1537956] +- kvm-iotests-Test-help-option-for-unsupporting-formats.patch [bz#1537956] +- kvm-iotests-Rework-113.patch [bz#1537956] +- kvm-qemu-img-Resolve-relative-backing-paths-in-rebase.patch [bz#1569835] +- kvm-iotests-Add-test-for-rebasing-with-relative-paths.patch [bz#1569835] +- kvm-qemu-img-Special-post-backing-convert-handling.patch [bz#1527898] +- kvm-iotests-Test-post-backing-convert-target-behavior.patch [bz#1527898] +- kvm-migration-calculate-expected_downtime-with-ram_bytes.patch [bz#1564576] +- kvm-sheepdog-Fix-sd_co_create_opts-memory-leaks.patch [bz#1513543] +- kvm-qemu-iotests-reduce-chance-of-races-in-185.patch [bz#1513543] +- kvm-blockjob-do-not-cancel-timer-in-resume.patch [bz#1513543] +- kvm-nfs-Fix-error-path-in-nfs_options_qdict_to_qapi.patch [bz#1513543] +- kvm-nfs-Remove-processed-options-from-QDict.patch [bz#1513543] +- kvm-blockjob-drop-block_job_pause-resume_all.patch [bz#1513543] +- kvm-blockjob-expose-error-string-via-query.patch [bz#1513543] +- kvm-blockjob-Fix-assertion-in-block_job_finalize.patch [bz#1513543] +- kvm-blockjob-Wrappers-for-progress-counter-access.patch [bz#1513543] +- kvm-blockjob-Move-RateLimit-to-BlockJob.patch [bz#1513543] +- kvm-blockjob-Implement-block_job_set_speed-centrally.patch [bz#1513543] +- kvm-blockjob-Introduce-block_job_ratelimit_get_delay.patch [bz#1513543] +- kvm-blockjob-Add-block_job_driver.patch [bz#1513543] +- kvm-blockjob-Update-block-job-pause-resume-documentation.patch [bz#1513543] +- kvm-blockjob-Improve-BlockJobInfo.offset-len-documentati.patch [bz#1513543] +- kvm-job-Create-Job-JobDriver-and-job_create.patch [bz#1513543] +- kvm-job-Rename-BlockJobType-into-JobType.patch [bz#1513543] +- kvm-job-Add-JobDriver.job_type.patch [bz#1513543] +- kvm-job-Add-job_delete.patch [bz#1513543] +- kvm-job-Maintain-a-list-of-all-jobs.patch [bz#1513543] +- kvm-job-Move-state-transitions-to-Job.patch [bz#1513543] +- kvm-job-Add-reference-counting.patch [bz#1513543] +- kvm-job-Move-cancelled-to-Job.patch [bz#1513543] +- kvm-job-Add-Job.aio_context.patch [bz#1513543] +- kvm-job-Move-defer_to_main_loop-to-Job.patch [bz#1513543] +- kvm-job-Move-coroutine-and-related-code-to-Job.patch [bz#1513543] +- kvm-job-Add-job_sleep_ns.patch [bz#1513543] +- kvm-job-Move-pause-resume-functions-to-Job.patch [bz#1513543] +- kvm-job-Replace-BlockJob.completed-with-job_is_completed.patch [bz#1513543] +- kvm-job-Move-BlockJobCreateFlags-to-Job.patch [bz#1513543] +- kvm-blockjob-Split-block_job_event_pending.patch [bz#1513543] +- kvm-job-Add-job_event_.patch [bz#1513543] +- kvm-job-Move-single-job-finalisation-to-Job.patch [bz#1513543] +- kvm-job-Convert-block_job_cancel_async-to-Job.patch [bz#1513543] +- kvm-job-Add-job_drain.patch [bz#1513543] +- kvm-job-Move-.complete-callback-to-Job.patch [bz#1513543] +- kvm-job-Move-job_finish_sync-to-Job.patch [bz#1513543] +- kvm-job-Switch-transactions-to-JobTxn.patch [bz#1513543] +- kvm-job-Move-transactions-to-Job.patch [bz#1513543] +- kvm-job-Move-completion-and-cancellation-to-Job.patch [bz#1513543] +- kvm-block-Cancel-job-in-bdrv_close_all-callers.patch [bz#1513543] +- kvm-job-Add-job_yield.patch [bz#1513543] +- kvm-job-Add-job_dismiss.patch [bz#1513543] +- kvm-job-Add-job_is_ready.patch [bz#1513543] +- kvm-job-Add-job_transition_to_ready.patch [bz#1513543] +- kvm-job-Move-progress-fields-to-Job.patch [bz#1513543] +- kvm-job-Introduce-qapi-job.json.patch [bz#1513543] +- kvm-job-Add-JOB_STATUS_CHANGE-QMP-event.patch [bz#1513543] +- kvm-job-Add-lifecycle-QMP-commands.patch [bz#1513543] +- kvm-job-Add-query-jobs-QMP-command.patch [bz#1513543] +- kvm-blockjob-Remove-BlockJob.driver.patch [bz#1513543] +- kvm-iotests-Move-qmp_to_opts-to-VM.patch [bz#1513543] +- kvm-qemu-iotests-Test-job-with-block-jobs.patch [bz#1513543] +- kvm-vdi-Fix-vdi_co_do_create-return-value.patch [bz#1513543] +- kvm-vhdx-Fix-vhdx_co_create-return-value.patch [bz#1513543] +- kvm-job-Add-error-message-for-failing-jobs.patch [bz#1513543] +- kvm-block-create-Make-x-blockdev-create-a-job.patch [bz#1513543] +- kvm-qemu-iotests-Add-VM.get_qmp_events_filtered.patch [bz#1513543] +- kvm-qemu-iotests-Add-VM.qmp_log.patch [bz#1513543] +- kvm-qemu-iotests-Add-iotests.img_info_log.patch [bz#1513543] +- kvm-qemu-iotests-Add-VM.run_job.patch [bz#1513543] +- kvm-qemu-iotests-iotests.py-helper-for-non-file-protocol.patch [bz#1513543] +- kvm-qemu-iotests-Rewrite-206-for-blockdev-create-job.patch [bz#1513543] +- kvm-qemu-iotests-Rewrite-207-for-blockdev-create-job.patch [bz#1513543] +- kvm-qemu-iotests-Rewrite-210-for-blockdev-create-job.patch [bz#1513543] +- kvm-qemu-iotests-Rewrite-211-for-blockdev-create-job.patch [bz#1513543] +- kvm-qemu-iotests-Rewrite-212-for-blockdev-create-job.patch [bz#1513543] +- kvm-qemu-iotests-Rewrite-213-for-blockdev-create-job.patch [bz#1513543] +- kvm-block-create-Mark-blockdev-create-stable.patch [bz#1513543] +- kvm-jobs-fix-stale-wording.patch [bz#1513543] +- kvm-jobs-fix-verb-references-in-docs.patch [bz#1513543] +- kvm-iotests-Fix-219-s-timing.patch [bz#1513543] +- kvm-iotests-improve-pause_job.patch [bz#1513543] +- kvm-rpm-Whitelist-copy-on-read-block-driver.patch [bz#1518738] +- kvm-rpm-add-throttle-driver-to-rw-whitelist.patch [bz#1591076] +- Resolves: bz#1513543 + ([RFE] Add block job to create format on a storage device) +- Resolves: bz#1518738 + (Add 'copy-on-read' filter driver for use with blockdev-add) +- Resolves: bz#1527898 + ([RFE] qemu-img should leave cluster unallocated if it's read as zero throughout the backing chain) +- Resolves: bz#1537956 + (RFE: qemu-img amend should list the true supported options) +- Resolves: bz#1564576 + (Pegas 1.1 - Require to backport qemu-kvm patch that fixes expected_downtime calculation during migration) +- Resolves: bz#1567733 + (qemu abort when migrate during guest reboot) +- Resolves: bz#1569835 + (qemu-img get wrong backing file path after rebasing image with relative path) +- Resolves: bz#1591076 + (The driver of 'throttle' is not whitelisted) + +* Mon Jun 25 2018 Miroslav Rezanina - 2.12.0-5.el7 +- kvm-qobject-Use-qobject_to-instead-of-type-cast.patch [bz#1557995] +- kvm-qobject-Ensure-base-is-at-offset-0.patch [bz#1557995] +- kvm-qobject-use-a-QObjectBase_-struct.patch [bz#1557995] +- kvm-qobject-Replace-qobject_incref-QINCREF-qobject_decre.patch [bz#1557995] +- kvm-qobject-Modify-qobject_ref-to-return-obj.patch [bz#1557995] +- kvm-rbd-Drop-deprecated-drive-parameter-filename.patch [bz#1557995] +- kvm-iscsi-Drop-deprecated-drive-parameter-filename.patch [bz#1557995] +- kvm-block-Add-block-specific-QDict-header.patch [bz#1557995] +- kvm-qobject-Move-block-specific-qdict-code-to-block-qdic.patch [bz#1557995] +- kvm-block-Fix-blockdev-for-certain-non-string-scalars.patch [bz#1557995] +- kvm-block-Fix-drive-for-certain-non-string-scalars.patch [bz#1557995] +- kvm-block-Clean-up-a-misuse-of-qobject_to-in-.bdrv_co_cr.patch [bz#1557995] +- kvm-block-Factor-out-qobject_input_visitor_new_flat_conf.patch [bz#1557995] +- kvm-block-Make-remaining-uses-of-qobject-input-visitor-m.patch [bz#1557995] +- kvm-block-qdict-Simplify-qdict_flatten_qdict.patch [bz#1557995] +- kvm-block-qdict-Tweak-qdict_flatten_qdict-qdict_flatten_.patch [bz#1557995] +- kvm-block-qdict-Clean-up-qdict_crumple-a-bit.patch [bz#1557995] +- kvm-block-qdict-Simplify-qdict_is_list-some.patch [bz#1557995] +- kvm-check-block-qdict-Rename-qdict_flatten-s-variables-f.patch [bz#1557995] +- kvm-check-block-qdict-Cover-flattening-of-empty-lists-an.patch [bz#1557995] +- kvm-block-Fix-blockdev-blockdev-add-for-empty-objects-an.patch [bz#1557995] +- kvm-rbd-New-parameter-auth-client-required.patch [bz#1557995] +- kvm-rbd-New-parameter-key-secret.patch [bz#1557995] +- kvm-block-mirror-honor-ratelimit-again.patch [bz#1572856] +- kvm-block-mirror-Make-cancel-always-cancel-pre-READY.patch [bz#1572856] +- kvm-iotests-Add-test-for-cancelling-a-mirror-job.patch [bz#1572856] +- kvm-iotests-Split-214-off-of-122.patch [bz#1518738] +- kvm-block-Add-COR-filter-driver.patch [bz#1518738] +- kvm-block-BLK_PERM_WRITE-includes-._UNCHANGED.patch [bz#1518738] +- kvm-block-Add-BDRV_REQ_WRITE_UNCHANGED-flag.patch [bz#1518738] +- kvm-block-Set-BDRV_REQ_WRITE_UNCHANGED-for-COR-writes.patch [bz#1518738] +- kvm-block-quorum-Support-BDRV_REQ_WRITE_UNCHANGED.patch [bz#1518738] +- kvm-block-Support-BDRV_REQ_WRITE_UNCHANGED-in-filters.patch [bz#1518738] +- kvm-iotests-Clean-up-wrap-image-in-197.patch [bz#1518738] +- kvm-iotests-Copy-197-for-COR-filter-driver.patch [bz#1518738] +- kvm-iotests-Add-test-for-COR-across-nodes.patch [bz#1518738] +- kvm-qemu-io-Use-purely-string-blockdev-options.patch [bz#1576598] +- kvm-qemu-img-Use-only-string-options-in-img_open_opts.patch [bz#1576598] +- kvm-iotests-Add-test-for-U-force-share-conflicts.patch [bz#1576598] +- kvm-qemu-io-Drop-command-functions-return-values.patch [bz#1519617] +- kvm-qemu-io-Let-command-functions-return-error-code.patch [bz#1519617] +- kvm-qemu-io-Exit-with-error-when-a-command-failed.patch [bz#1519617] +- kvm-iotests.py-Add-qemu_io_silent.patch [bz#1519617] +- kvm-iotests-Let-216-make-use-of-qemu-io-s-exit-code.patch [bz#1519617] +- kvm-qcow2-Repair-OFLAG_COPIED-when-fixing-leaks.patch [bz#1527085] +- kvm-iotests-Repairing-error-during-snapshot-deletion.patch [bz#1527085] +- kvm-block-Make-bdrv_is_writable-public.patch [bz#1588039] +- kvm-qcow2-Do-not-mark-inactive-images-corrupt.patch [bz#1588039] +- kvm-iotests-Add-case-for-a-corrupted-inactive-image.patch [bz#1588039] +- kvm-s390x-cpumodels-add-z14-Model-ZR1.patch [bz#1592327] +- kvm-main-loop-drop-spin_counter.patch [bz#1168213] +- kvm-target-ppc-Factor-out-the-parsing-in-kvmppc_get_cpu_.patch [bz#1560847] +- kvm-target-ppc-Don-t-require-private-l1d-cache-on-POWER8.patch [bz#1560847] +- kvm-ppc-spapr_caps-Don-t-disable-cap_cfpc-on-POWER8-by-d.patch [bz#1560847] +- Resolves: bz#1168213 + (main-loop: WARNING: I/O thread spun for 1000 iterations while doing stream block device.) +- Resolves: bz#1518738 + (Add 'copy-on-read' filter driver for use with blockdev-add) +- Resolves: bz#1519617 + (The exit code should be non-zero when qemu-io reports an error) +- Resolves: bz#1527085 + (The copied flag should be updated during '-r leaks') +- Resolves: bz#1557995 + (QAPI schema for RBD storage misses the 'password-secret' option) +- Resolves: bz#1560847 + ([Power8][FW b0320a_1812.861][rhel7.5rc2 3.10.0-861.el7.ppc64le][qemu-kvm-{ma,rhev}-2.10.0-21.el7_5.1.ppc64le] KVM guest does not default to ori type flush even with pseries-rhel7.5.0-sxxm) +- Resolves: bz#1572856 + ('block-job-cancel' can not cancel a "drive-mirror" job) +- Resolves: bz#1576598 + (Segfault in qemu-io and qemu-img with -U --image-opts force-share=off) +- Resolves: bz#1588039 + (Possible assertion failure in qemu when a corrupted image is used during an incoming migration) +- Resolves: bz#1592327 + ([RHEL-Alt-7.6 FEAT] KVM: CPU Model z14 ZR1 (qemu-kvm-ma)) + +* Tue Jun 19 2018 Miroslav Rezanina - 2.12.0-4.el7 +- kvm-Fix-x-hv-max-vps-compat-value-for-7.4-machine-type.patch [bz#1583959] +- kvm-ccid-card-passthru-fix-regression-in-realize.patch [bz#1584984] +- kvm-remove-duplicate-HW_COMPAT_RHEL7_5.patch [bz#1542080] +- kvm-Use-4-MB-vram-for-cirrus.patch [bz#1542080] +- kvm-spapr_pci-Remove-unhelpful-pagesize-warning.patch [bz#1505664] +- kvm-redhat-cleanup-redhat-kvm-setup.patch [bz#1580297] +- kvm-redhat-add-a-configuration-file-for-kvm-setup.patch [bz#1580297] +- kvm-redhat-support-POWER8-on-POWER9-settings-in-kvm-setu.patch [bz#1580297] +- kvm-rpm-Add-nvme-VFIO-driver-to-rw-whitelist.patch [bz#1416180] +- Resolves: bz#1416180 + (QEMU VFIO based block driver for NVMe devices) +- Resolves: bz#1505664 + ("qemu-kvm: System page size 0x1000000 is not enabled in page_size_mask (0x11000). Performance may be slow" show up while using hugepage as guest's memory) +- Resolves: bz#1542080 + (Qemu core dump at cirrus_invalidate_region) +- Resolves: bz#1580297 + (qemu-kvm-rhev: Support POWER8-on-POWER9 settings in kvm-setup) +- Resolves: bz#1583959 + (Incorrect vcpu count limit for 7.4 machine types for windows guests) +- Resolves: bz#1584984 + (Vm starts failed with 'passthrough' smartcard) + +* Fri Jun 01 2018 Miroslav Rezanina - 2.12.0-3.el7 +- kvm-qemu-img-Check-post-truncation-size.patch [bz#1523065] +- kvm-s390x-add-RHEL-7.6-machine-type-for-ccw.patch [bz#1562138] +- kvm-redhat-define-HW_COMPAT_RHEL7_5.patch [bz#1557054] +- kvm-redhat-define-pseries-rhel7.6.0-machine-types.patch [bz#1557054] +- kvm-pc-pc-rhel75.5.0-compat-code.patch [bz#1578068] +- kvm-vga-catch-depth-0.patch [bz#1575541] +- kvm-spec-Enable-Native-Ceph-support-on-all-architectures.patch [bz#1578664] +- kvm-spec-Use-hardening-flags-for-ksmctl-build.patch [bz#1558516] +- Resolves: bz#1523065 + ("qemu-img resize" should fail to decrease the size of logical partition/lvm/iSCSI image with raw format) +- Resolves: bz#1557054 + (RHEL 7.6 new pseries machine type (ppc64le)) +- Resolves: bz#1558516 + (ksmctl is built without any hardening flags set [rhel-7.6]) +- Resolves: bz#1562138 + (RHEL 7.6 new qemu-kvm-ma machine type (s390x)) +- Resolves: bz#1575541 + (qemu core dump while installing win10 guest) +- Resolves: bz#1578068 + (Backwards compatibility of pc-*-rhel7.5.0 and older machine-types) +- Resolves: bz#1578664 + (Enable Native Ceph support on non x86_64 CPUs) + +* Wed May 16 2018 Miroslav Rezanina - 2.12.0-2.el7 +- kvm-AArch64-Add-virt-rhel7.6-machine-type.patch [bz#1558723] +- kvm-configuration-Use-gcrypt-instead-of-nettle.patch [bz#1549543] +- kvm-spec-Change-License-line.patch [bz#1549106] +- kvm-spapr-Add-ibm-max-associativity-domains-property.patch [bz#1570525] +- kvm-Revert-spapr-Don-t-allow-memory-hotplug-to-memory-le.patch [bz#1570525] +- kvm-tcg-workaround-branch-instruction-overflow-in-tcg_ou.patch [bz#1574577] +- kvm-s390-ccw-force-diag-308-subcode-to-unsigned-long.patch [bz#1523857] +- kvm-pc-bios-s390-ccw-size_t-should-be-unsigned.patch [bz#1523857] +- kvm-pc-bios-s390-ccw-rename-MAX_TABLE_ENTRIES-to-MAX_BOO.patch [bz#1523857] +- kvm-pc-bios-s390-ccw-fix-loadparm-initialization-and-int.patch [bz#1523857] +- kvm-pc-bios-s390-ccw-fix-non-sequential-boot-entries-eck.patch [bz#1523857] +- kvm-pc-bios-s390-ccw-fix-non-sequential-boot-entries-enu.patch [bz#1523857] +- kvm-cpus-Fix-event-order-on-resume-of-stopped-guest.patch [bz#1566153] +- Resolves: bz#1523857 + ([Pegas1.2 FEAT] KVM: Interactive Bootloader - qemu part) +- Resolves: bz#1549106 + (Incorrect License information in RPM specfile) +- Resolves: bz#1549543 + (Use of Nettle crypto library prevents FIPS compliance, need to go back to libgcrypt) +- Resolves: bz#1558723 + (Create RHEL-7.6 QEMU machine type for AArch64) +- Resolves: bz#1566153 + (IOERROR pause code lost after resuming a VM while I/O error is still present) +- Resolves: bz#1570525 + ([POWER][QEMU-KVM] Can't hotplug memory to initially memory-less NUMA node) +- Resolves: bz#1574577 + (qemu-kvm segfaults when run guestfsd under TCG) + +* Thu Apr 26 2018 Miroslav Rezanina - 2.12.0-1.el7 +- Rebase to QEMU 2.12.0 [bz#1562219] +- Resolves: bz#1562219 + (Rebase qemu-kvm-ma for RHEL-7.6) + +* Tue Feb 20 2018 Miroslav Rezanina - 2.10.0-21.el7 +- kvm-migration-Recover-block-devices-if-failure-in-device.patch [bz#1538494] +- kvm-migration-savevm.c-set-MAX_VM_CMD_PACKAGED_SIZE-to-1.patch [bz#1540003] +- kvm-pci-bus-let-it-has-higher-migration-priority.patch [bz#1538953] +- kvm-spapr-set-vsmt-to-MAX-8-smp_threads.patch [bz#1542421] +- kvm-target-ppc-spapr_caps-Add-macro-to-generate-spapr_ca.patch [bz#1532050] +- kvm-target-ppc-kvm-Add-cap_ppc_safe_-cache-bounds_check-.patch [bz#1532050] +- kvm-target-ppc-spapr_caps-Add-support-for-tristate-spapr.patch [bz#1532050] +- kvm-target-ppc-spapr_caps-Add-new-tristate-cap-safe_cach.patch [bz#1532050] +- kvm-target-ppc-spapr_caps-Add-new-tristate-cap-safe_boun.patch [bz#1532050] +- kvm-target-ppc-spapr_caps-Add-new-tristate-cap-safe_indi.patch [bz#1532050] +- kvm-target-ppc-introduce-the-PPC_BIT-macro.patch [bz#1532050] +- kvm-target-ppc-spapr-Add-H-Call-H_GET_CPU_CHARACTERISTIC.patch [bz#1532050] +- kvm-spapr-add-missing-break-in-h_get_cpu_characteristics.patch [bz#1532050] +- kvm-vfio-pci-Add-option-to-disable-GeForce-quirks.patch [bz#1508330] +- kvm-Disable-GeForce-quirks-in-vfio-pci-for-RHEL-machines.patch [bz#1508330] +- Resolves: bz#1508330 + (Interrupt latency issues with vGPU on KVM hypervisor.) +- Resolves: bz#1532050 + ([CVE-2017-5754] Variant3: POWER {qemu-kvm-rhev} (rhel 7.5)) +- Resolves: bz#1538494 + (Guest crashed on the source host when cancel migration by virDomainMigrateBegin3Params sometimes) +- Resolves: bz#1538953 + (IOTLB entry size mismatch before/after migration during DPDK PVP testing) +- Resolves: bz#1540003 + (Postcopy migration failed with "Unreasonably large packaged state") +- Resolves: bz#1542421 + (Pegas1.1 Snapshot1 [4.14.0-35.el7a.ppc64le] [qemu-kvm-ma-2.10.0-18.el7.ppc64le] qemu-kvm behaves incorrectly for guest boot with invalid threads) + +* Wed Feb 07 2018 Miroslav Rezanina - 2.10.0-20.el7 +- kvm-console-fix-dpy_gfx_replace_surface-assert.patch [bz#1505696] +- kvm-ui-add-tracing-of-VNC-operations-related-to-QIOChann.patch [bz#1527404] +- kvm-ui-add-tracing-of-VNC-authentication-process.patch [bz#1527404] +- kvm-ui-remove-sync-parameter-from-vnc_update_client.patch [bz#1527404] +- kvm-ui-remove-unreachable-code-in-vnc_update_client.patch [bz#1527404] +- kvm-ui-remove-redundant-indentation-in-vnc_client_update.patch [bz#1527404] +- kvm-ui-avoid-pointless-VNC-updates-if-framebuffer-isn-t-.patch [bz#1527404] +- kvm-ui-track-how-much-decoded-data-we-consumed-when-doin.patch [bz#1527404] +- kvm-ui-introduce-enum-to-track-VNC-client-framebuffer-up.patch [bz#1527404] +- kvm-ui-correctly-reset-framebuffer-update-state-after-pr.patch [bz#1527404] +- kvm-ui-refactor-code-for-determining-if-an-update-should.patch [bz#1527404] +- kvm-ui-fix-VNC-client-throttling-when-audio-capture-is-a.patch [bz#1527404] +- kvm-ui-fix-VNC-client-throttling-when-forced-update-is-r.patch [bz#1527404] +- kvm-ui-place-a-hard-cap-on-VNC-server-output-buffer-size.patch [bz#1527404] +- kvm-ui-add-trace-events-related-to-VNC-client-throttling.patch [bz#1527404] +- kvm-ui-mix-misleading-comments-return-types-of-VNC-I-O-h.patch [bz#1527404] +- kvm-ui-avoid-sign-extension-using-client-width-height.patch [bz#1527404] +- kvm-ui-correctly-advance-output-buffer-when-writing-SASL.patch [bz#1527404] +- kvm-dump-guest-memory.py-skip-vmcoreinfo-section-if-not-.patch [bz#1398633] +- kvm-virtio-gpu-disallow-vIOMMU.patch [bz#1540182] +- Resolves: bz#1398633 + ([RFE] Kernel address space layout randomization [KASLR] support (qemu-kvm-rhev)) +- Resolves: bz#1505696 + (Qemu crashed when open the second display of virtio video) +- Resolves: bz#1527404 + (CVE-2017-15124 qemu-kvm-rhev: Qemu: memory exhaustion through framebuffer update request message in VNC server [rhel-7.5]) +- Resolves: bz#1540182 + (QEMU: disallow virtio-gpu to boot with vIOMMU) + +* Fri Feb 02 2018 Miroslav Rezanina - 2.10.0-19.el7 +- kvm-Drop-105th-key-from-en-us-keymap.patch [bz#1513870] +- kvm-linux-headers-update.patch [bz#1535606] +- kvm-s390x-kvm-Handle-bpb-feature.patch [bz#1535606] +- kvm-s390x-kvm-provide-stfle.81.patch [bz#1535606] +- kvm-osdep-Retry-SETLK-upon-EINTR.patch [bz#1529053] +- kvm-vga-check-the-validation-of-memory-addr-when-draw-te.patch [bz#1534682] +- kvm-usb-storage-Fix-share-rw-option-parsing.patch [bz#1525324] +- kvm-spapr-disable-memory-hotplug.patch [bz#1535952] +- Resolves: bz#1513870 + (For VNC connection, characters '|' and '<' are both recognized as '>' in linux guests, while '<' and '>' are both recognized as '|' in windows guest) +- Resolves: bz#1525324 + (2 VMs both with 'share-rw=on' appending on '-device usb-storage' for the same source image can not be started at the same time) +- Resolves: bz#1529053 + (Miss the handling of EINTR in the fcntl calls made by QEMU) +- Resolves: bz#1534682 + (CVE-2018-5683 qemu-kvm-rhev: Qemu: Out-of-bounds read in vga_draw_text routine [rhel-7.5]) +- Resolves: bz#1535606 + (Spectre mitigation patches for qemu-kvm-ma on z Systems) +- Resolves: bz#1535952 + (qemu-kvm-ma differentiation - memory hotplug) + +* Tue Jan 23 2018 Miroslav Rezanina - 2.10.0-18.el7 +- kvm-serial-always-transmit-send-receive-buffers-on-migra.patch [bz#1459945] +- kvm-hw-acpi-Move-acpi_set_pci_info-to-pcihp.patch [bz#1507693] +- kvm-scsi-block-Add-share-rw-option.patch [bz#1518482] +- kvm-scsi-generic-Add-share-rw-option.patch [bz#1518482] +- kvm-target-i386-sanitize-x86-MSR_PAT-loaded-from-another.patch [bz#1529461] +- kvm-scsi-disk-release-AioContext-in-unaligned-WRITE-SAME.patch [bz#1526423] +- kvm-hw-pci-bridge-fix-QEMU-crash-because-of-pcie-root-po.patch [bz#1520858] +- kvm-spapr-Capabilities-infrastructure.patch [bz#1523414] +- kvm-spapr-Treat-Hardware-Transactional-Memory-HTM-as-an-.patch [bz#1523414] +- kvm-spapr-Validate-capabilities-on-migration.patch [bz#1523414] +- kvm-spapr-Handle-VMX-VSX-presence-as-an-spapr-capability.patch [bz#1523414] +- kvm-spapr-Handle-Decimal-Floating-Point-DFP-as-an-option.patch [bz#1523414] +- kvm-hw-ppc-spapr_caps-Rework-spapr_caps-to-use-uint8-int.patch [bz#1523414] +- kvm-spapr-Remove-unnecessary-options-field-from-sPAPRCap.patch [bz#1523414] +- kvm-ppc-Change-Power9-compat-table-to-support-at-most-8-.patch [bz#1529243] +- kvm-target-ppc-Clarify-compat-mode-max_threads-value.patch [bz#1529243] +- kvm-spapr-Allow-some-cases-where-we-can-t-set-VSMT-mode-.patch [bz#1529243] +- kvm-spapr-Adjust-default-VSMT-value-for-better-migration.patch [bz#1529243] +- kvm-qemu-img-info-Force-U-downstream.patch [bz#1535992] +- kvm-dump-guest-memory.py-fix-python-2-support.patch [bz#1398633] +- kvm-spapr-fix-device-tree-properties-when-using-compatib.patch [bz#1535752] +- Resolves: bz#1398633 + ([RFE] Kernel address space layout randomization [KASLR] support (qemu-kvm-rhev)) +- Resolves: bz#1459945 + (migration fails with hungup serial console reader on -M pc-i440fx-rhel7.0.0 and pc-i440fx-rhel7.1.0) +- Resolves: bz#1507693 + (Unable to hot plug device to VM reporting libvirt errors.) +- Resolves: bz#1518482 + ("share-rw" property is unavailable on scsi passthrough devices) +- Resolves: bz#1520858 + (qemu-kvm core dumped when booting guest with more pcie-root-ports than available slots and io-reserve=0) +- Resolves: bz#1523414 + ([POWER guests] Verify compatible CPU & hypervisor capabilities across migration) +- Resolves: bz#1526423 + (QEMU hang with data plane enabled after some sg_write_same operations in guest) +- Resolves: bz#1529243 + (Migration from P9 to P8, migration failed and qemu quit on dst end with "error while loading state for instance 0x0 of device 'ics'") +- Resolves: bz#1529461 + (On amd hosts, after migration from rhel6.9.z to rhel7.5, CPU utilization of qemu-kvm is always more than 100% on destination rhel7.5 host) +- Resolves: bz#1535752 + (Device tree incorrectly advertises compatibility modes for secondary CPUs) +- Resolves: bz#1535992 + (Set force shared option "-U" as default option for "qemu-img info") + +* Tue Jan 16 2018 Miroslav Rezanina - 2.10.0-17.el7 +- kvm-tools-kvm_stat-fix-command-line-option-g.patch [bz#1529676] +- kvm-redhat-globally-limit-the-maximum-number-of-CPUs.patch [bz#1527449] +- kvm-redhat-remove-manual-max_cpus-limitations-for-ppc.patch [bz#1527449] +- kvm-dump-guest-memory.py-fix-You-can-t-do-that-without-a.patch [bz#1398633] +- kvm-hw-ppc-spapr.c-abort-unplug_request-if-previous-unpl.patch [bz#1528173] +- kvm-spapr-Correct-compatibility-mode-setting-for-hotplug.patch [bz#1528234] +- kvm-ui-fix-dcl-unregister.patch [bz#1510809] +- kvm-block-Open-backing-image-in-force-share-mode-for-siz.patch [bz#1526212] +- kvm-fw_cfg-fix-memory-corruption-when-all-fw_cfg-slots-a.patch [bz#1462145] +- kvm-block-Don-t-use-BLK_PERM_CONSISTENT_READ-for-format-.patch [bz#1515604] +- kvm-block-Don-t-request-I-O-permission-with-BDRV_O_NO_IO.patch [bz#1515604] +- kvm-block-Formats-don-t-need-CONSISTENT_READ-with-NO_IO.patch [bz#1515604] +- Resolves: bz#1398633 + ([RFE] Kernel address space layout randomization [KASLR] support (qemu-kvm-rhev)) +- Resolves: bz#1462145 + (Qemu crashes when all fw_cfg slots are used) +- Resolves: bz#1510809 + (qemu-kvm core dumped when booting up guest using both virtio-vga and VGA) +- Resolves: bz#1515604 + (qemu-img info: failed to get "consistent read" lock on a mirroring image) +- Resolves: bz#1526212 + (qemu-img should not need a write lock for creating the overlay image) +- Resolves: bz#1527449 + (qemu-kvm-ma: vCPU count should be limited to 240 on all arches) +- Resolves: bz#1528173 + (Hot-unplug memory during booting early stage induced qemu-kvm coredump) +- Resolves: bz#1528234 + (Pegas1.1 Alpha: Hotplugged vcpu does not guarantee CPU P8compat mode on POWER9 host (qemu-kvm)) +- Resolves: bz#1529676 + (kvm_stat: option '--guest' doesn't work) + +* Mon Jan 08 2018 Miroslav Rezanina - 2.10.0-16.el7 +- kvm-gicv3-Convert-to-DEFINE_PROP_LINK.patch [bz#1513323] +- kvm-hw-intc-arm_gicv3_its-Fix-the-VM-termination-in-vm_c.patch [bz#1513323] +- kvm-hw-intc-arm_gicv3_its-Don-t-abort-on-table-save-fail.patch [bz#1513323] +- kvm-hw-intc-arm_gicv3_its-Don-t-call-post_load-on-reset.patch [bz#1513323] +- kvm-hw-intc-arm_gicv3_its-Implement-a-minimalist-reset.patch [bz#1513323] +- kvm-linux-headers-Partial-header-update-against-v4.15-rc.patch [bz#1513323] +- kvm-hw-intc-arm_gicv3_its-Implement-full-reset.patch [bz#1513323] +- kvm-block-throttle-groups.c-allocate-RestartData-on-the-.patch [bz#1525868] +- kvm-redhat-Fix-permissions-of-dev-kvm-on-a-freshly-boote.patch [bz#1527947] +- Resolves: bz#1513323 + (vITS reset) +- Resolves: bz#1525868 + (Guest hit core dump with both IO throttling and data plane) +- Resolves: bz#1527947 + (Pegas1.1 - virsh domcapabilities doesn't report KVM capabilities on s390x) + +* Thu Jan 04 2018 Miroslav Rezanina - 2.10.0-15.el7 +- kvm-target-i386-add-support-for-SPEC_CTRL-MSR.patch [CVE-2017-5715] +- kvm-target-i386-cpu-add-new-CPUID-bits-for-indirect-bran.patch [CVE-2017-5715] +- kvm-target-i386-cpu-add-new-CPU-models-for-indirect-bran.patch [CVE-2017-5715] + +* Tue Jan 02 2018 Miroslav Rezanina - 2.10.0-14.el7 +- kvm-spapr-don-t-initialize-PATB-entry-if-max-cpu-compat-.patch [bz#1525866] +- kvm-block-avoid-recursive-AioContext-acquire-in-bdrv_ina.patch [bz#1520824] +- kvm-io-send-proper-HTTP-response-for-websocket-errors.patch [bz#1518649] +- kvm-io-include-full-error-message-in-websocket-handshake.patch [bz#1518649] +- kvm-io-use-case-insensitive-check-for-Connection-Upgrade.patch [bz#1518649] +- kvm-ui-Always-remove-an-old-VNC-channel-watch-before-add.patch [bz#1518649] +- kvm-io-Small-updates-in-preparation-for-websocket-change.patch [bz#1518649] +- kvm-io-Add-support-for-fragmented-websocket-binary-frame.patch [bz#1518649] +- kvm-io-Allow-empty-websocket-payload.patch [bz#1518649] +- kvm-io-Ignore-websocket-PING-and-PONG-frames.patch [bz#1518649] +- kvm-io-Reply-to-ping-frames.patch [bz#1518649] +- kvm-io-Attempt-to-send-websocket-close-messages-to-clien.patch [bz#1518649] +- kvm-io-add-trace-events-for-websockets-frame-handling.patch [bz#1518649] +- kvm-io-monitor-encoutput-buffer-size-from-websocket-GSou.patch [bz#1518650] +- kvm-io-simplify-websocket-ping-reply-handling.patch [bz#1518649] +- kvm-io-get-rid-of-qio_channel_websock_encode-helper-meth.patch [bz#1518649] +- kvm-io-pass-a-struct-iovec-into-qio_channel_websock_enco.patch [bz#1518649] +- kvm-io-get-rid-of-bounce-buffering-in-websock-write-path.patch [bz#1518649] +- kvm-io-cope-with-websock-Connection-header-having-multip.patch [bz#1518649] +- kvm-io-add-trace-points-for-websocket-HTTP-protocol-head.patch [bz#1518649] +- kvm-io-fix-mem-leak-in-websock-error-path.patch [bz#1518649] +- kvm-io-Add-missing-GCC_FMT_ATTR-fix-Werror-suggest-attri.patch [bz#1518649] +- kvm-qemu.py-make-VM-a-context-manager.patch [bz#1519721] +- kvm-iotests.py-add-FilePath-context-manager.patch [bz#1519721] +- kvm-qemu-iothread-IOThread-supports-the-GMainContext-eve.patch [bz#1519721] +- kvm-qom-provide-root-container-for-internal-objs.patch [bz#1519721] +- kvm-iothread-provide-helpers-for-internal-use.patch [bz#1519721] +- kvm-iothread-export-iothread_stop.patch [bz#1519721] +- kvm-iothread-delay-the-context-release-to-finalize.patch [bz#1519721] +- kvm-aio-fix-assert-when-remove-poll-during-destroy.patch [bz#1519721] +- kvm-blockdev-hold-AioContext-for-bdrv_unref-in-external_.patch [bz#1519721] +- kvm-block-don-t-keep-AioContext-acquired-after-external_.patch [bz#1519721] +- kvm-block-don-t-keep-AioContext-acquired-after-drive_bac.patch [bz#1519721] +- kvm-block-don-t-keep-AioContext-acquired-after-blockdev_.patch [bz#1519721] +- kvm-block-don-t-keep-AioContext-acquired-after-internal_.patch [bz#1519721] +- kvm-iothread-add-iothread_by_id-API.patch [bz#1519721] +- kvm-blockdev-add-x-blockdev-set-iothread-testing-command.patch [bz#1519721] +- kvm-qemu-iotests-add-202-external-snapshots-IOThread-tes.patch [bz#1519721] +- kvm-blockdev-add-x-blockdev-set-iothread-force-boolean.patch [bz#1519721] +- kvm-iotests-add-VM.add_object.patch [bz#1519721] +- kvm-iothread-fix-iothread_stop-race-condition.patch [bz#1519721] +- kvm-qemu-iotests-add-203-savevm-with-IOThreads-test.patch [bz#1519721] +- Resolves: bz#1518649 + (Client compatibility flaws in VNC websockets server) +- Resolves: bz#1518650 + (CVE-2017-15268 qemu-kvm-rhev: Qemu: I/O: potential memory exhaustion via websock connection to VNC [rhel-7.5]) +- Resolves: bz#1519721 + (Both qemu and guest hang when performing live snapshot transaction with data-plane) +- Resolves: bz#1520824 + (Migration with dataplane, qemu processor hang, vm hang and migration can't finish) +- Resolves: bz#1525866 + (P9 to P8 guest migration fails when kernel is not started) + +* Tue Dec 19 2017 Miroslav Rezanina - 2.10.0-13.el7 +- kvm-target-ppc-Add-POWER9-DD2.0-model-information.patch [bz#1523235] +- kvm-block-vxhs-improve-error-message-for-missing-bad-vxh.patch [bz#1505654] +- kvm-qemu-img-Clarify-about-relative-backing-file-options.patch [bz#1451269] +- kvm-nbd-server-CVE-2017-15119-Reject-options-larger-than.patch [bz#1518529 bz#1518551] +- kvm-nbd-server-CVE-2017-15118-Stack-smash-on-large-expor.patch [bz#1516545 bz#1518548] +- kvm-vfio-Fix-vfio-kvm-group-registration.patch [bz#1520294] +- Resolves: bz#1451269 + (Clarify the relativity of backing file and created image in "qemu-img create") +- Resolves: bz#1505654 + (Missing libvxhs share-able object file when try to query vxhs protocol) +- Resolves: bz#1516545 + (CVE-2017-15118 qemu-kvm-rhev: qemu NBD server vulnerable to stack smash from client requesting long export name [rhel-7.5]) +- Resolves: bz#1518529 + (CVE-2017-15119 qemu-kvm-rhev: qemu: DoS via large option request [rhel-7.5]) +- Resolves: bz#1518548 + (CVE-2017-15118 qemu-kvm-ma: Qemu: stack buffer overflow in NBD server triggered via long export name [rhel-7.5]) +- Resolves: bz#1518551 + (CVE-2017-15119 qemu-kvm-ma: qemu: DoS via large option request [rhel-7.5]) +- Resolves: bz#1520294 + (Hot-unplug the second pf cause qemu promote " Failed to remove group $iommu_group_num from KVM VFIO device:") +- Resolves: bz#1523235 + (Pegas1.0 - qemu cpu information is not up-to-date (qemu-kvm)) + +* Mon Dec 11 2017 Miroslav Rezanina - 2.10.0-12.el7 +- kvm-block-add-bdrv_co_drain_end-callback.patch [bz#1506531] +- kvm-block-rename-bdrv_co_drain-to-bdrv_co_drain_begin.patch [bz#1506531] +- kvm-blockjob-do-not-allow-coroutine-double-entry-or-entr.patch [bz#1506531] +- kvm-coroutine-abort-if-we-try-to-schedule-or-enter-a-pen.patch [bz#1506531] +- kvm-qemu-iotests-add-option-in-common.qemu-for-mismatch-.patch [bz#1506531] +- kvm-qemu-iotest-add-test-for-blockjob-coroutine-race-con.patch [bz#1506531] +- kvm-blockjob-Remove-the-job-from-the-list-earlier-in-blo.patch [bz#1506531] +- kvm-block-Expect-graph-changes-in-bdrv_parent_drained_be.patch [bz#1506531] +- kvm-blockjob-remove-clock-argument-from-block_job_sleep_.patch [bz#1506531] +- kvm-blockjob-introduce-block_job_do_yield.patch [bz#1506531] +- kvm-blockjob-reimplement-block_job_sleep_ns-to-allow-can.patch [bz#1506531] +- kvm-blockjob-Make-block_job_pause_all-keep-a-reference-t.patch [bz#1506531] +- kvm-target-ppc-Move-setting-of-patb_entry-on-hash-table-.patch [bz#1517051] +- kvm-target-ppc-Fix-setting-of-cpu-compat_pvr-on-incoming.patch [bz#1517051] +- kvm-BZ1513294-spapr-Include-pre-plugged-DIMMS-in-ram-siz.patch [bz#1513294] +- kvm-virtio-Add-queue-interface-to-restore-avail-index-fr.patch [bz#1491909] +- kvm-vhost-restore-avail-index-from-vring-used-index-on-d.patch [bz#1491909] +- kvm-dump-guest-memory.py-fix-No-symbol-vmcoreinfo_find.patch [bz#1398633] +- kvm-ppc-fix-setting-of-compat-mode.patch [bz#1396119] +- kvm-pc-fix-crash-on-attempted-cpu-unplug.patch [bz#1506856] +- kvm-sockets-avoid-crash-when-cleaning-up-sockets-for-an-.patch [bz#1506218] +- Resolves: bz#1396119 + ([IBM 7.5 Feature] POWER9 - Virt: QEMU: POWER8/P8-Compat mode for POWER8 Guests on POWER9 platform) +- Resolves: bz#1398633 + ([RFE] Kernel address space layout randomization [KASLR] support (qemu-kvm-rhev)) +- Resolves: bz#1491909 + (IP network can not recover after several vhost-user reconnect) +- Resolves: bz#1506218 + (seg at exit - due to missing fd?) +- Resolves: bz#1506531 + ([data-plane] Qemu-kvm core dumped when hot-unplugging a block device with data-plane while the drive-mirror job is running) +- Resolves: bz#1506856 + ([abrt] qemu-kvm-rhev: object_get_class(): qemu-kvm killed by SIGSEGV) +- Resolves: bz#1513294 + (Guest got stuck when attached memory beforehand.[-device dimm and object memory-backend-ram]) +- Resolves: bz#1517051 + (POWER9 - Virt: QEMU: Migration of HPT guest on Radix host fails) + +* Tue Dec 05 2017 Miroslav Rezanina - ma-2.10.0-11.el7 +- kvm-qcow2-don-t-permit-changing-encryption-parameters.patch [bz#1406803] +- kvm-qcow2-fix-image-corruption-after-committing-qcow2-im.patch [bz#1406803] +- kvm-qemu-doc-Add-UUID-support-in-initiator-name.patch [bz#1494210] +- kvm-docs-add-qemu-block-drivers-7-man-page.patch [bz#1494210] +- kvm-docs-Add-image-locking-subsection.patch [bz#1494210] +- kvm-qemu-options-Mention-locking-option-of-file-driver.patch [bz#1494210] +- kvm-Package-qemu-block-drivers-manpage.patch [bz#1494210] +- kvm-block-don-t-add-driver-to-options-when-referring-to-.patch [bz#1505701] +- kvm-blockdev-Report-proper-error-class-in-__com.redhat.d.patch [bz#1487515] +- kvm-block-use-1-MB-bounce-buffers-for-crypto-instead-of-.patch [bz#1500334] +- kvm-io-add-new-qio_channel_-readv-writev-read-write-_all.patch [bz#1464908] +- kvm-io-Yield-rather-than-wait-when-already-in-coroutine.patch [bz#1464908] +- kvm-scsi-bus-correct-responses-for-INQUIRY-and-REQUEST-S.patch [bz#1464908] +- kvm-scsi-Refactor-scsi-sense-interpreting-code.patch [bz#1464908] +- kvm-scsi-Improve-scsi_sense_to_errno.patch [bz#1464908] +- kvm-scsi-Introduce-scsi_sense_buf_to_errno.patch [bz#1464908] +- kvm-scsi-rename-scsi_build_sense-to-scsi_convert_sense.patch [bz#1464908] +- kvm-scsi-move-non-emulation-specific-code-to-scsi.patch [bz#1464908] +- kvm-scsi-introduce-scsi_build_sense.patch [bz#1464908] +- kvm-scsi-introduce-sg_io_sense_from_errno.patch [bz#1464908] +- kvm-scsi-move-block-scsi.h-to-include-scsi-constants.h.patch [bz#1464908] +- kvm-scsi-file-posix-add-support-for-persistent-reservati.patch [bz#1464908] +- kvm-scsi-build-qemu-pr-helper.patch [bz#1464908] +- kvm-scsi-add-multipath-support-to-qemu-pr-helper.patch [bz#1464908] +- kvm-scsi-add-persistent-reservation-manager-using-qemu-p.patch [bz#1464908] +- kvm-update-spec-to-build-and-install-qemu-pr-helper.patch [bz#1464908] +- kvm-qemu-pr-helper-miscellaneous-fixes.patch [bz#1464908] +- kvm-Match-POWER-max-cpus-to-x86.patch [bz#1495456] +- kvm-qemu-io-Drop-write-permissions-before-read-only-reop.patch [bz#1492178] +- kvm-block-Add-reopen_queue-to-bdrv_child_perm.patch [bz#1492178] +- kvm-block-Add-reopen-queue-to-bdrv_check_perm.patch [bz#1492178] +- kvm-block-Base-permissions-on-rw-state-after-reopen.patch [bz#1492178] +- kvm-block-reopen-Queue-children-after-their-parents.patch [bz#1492178] +- kvm-block-Fix-permissions-after-bdrv_reopen.patch [bz#1492178] +- kvm-qemu-iotests-Test-change-backing-file-command.patch [bz#1492178] +- kvm-iotests-Fix-195-if-IMGFMT-is-part-of-TEST_DIR.patch [bz#1492178] +- Resolves: bz#1406803 + (RFE: native integration of LUKS and qcow2) +- Resolves: bz#1464908 + ([RFE] Add S3 PR support to qemu (similar to mpathpersist)) +- Resolves: bz#1487515 + (wrong error code is reported if __com.redhat.drive_del can't find the device to delete) +- Resolves: bz#1492178 + (Non-top-level change-backing-file causes assertion failure) +- Resolves: bz#1494210 + (Document image locking in the qemu-img manpage) +- Resolves: bz#1495456 + (Update downstream qemu's max supported cpus for pseries to the RHEL supported number) +- Resolves: bz#1500334 + (LUKS driver has poor performance compared to in-kernel driver) +- Resolves: bz#1505701 + (-blockdev fails if a qcow2 image has backing store format and backing store is referenced via node-name) + +* Thu Nov 30 2017 Miroslav Rezanina - ma-2.10.0-10.el7 +- kvm-qcow2-fix-return-error-code-in-qcow2_truncate.patch [bz#1414049] +- kvm-qcow2-Fix-unaligned-preallocated-truncation.patch [bz#1414049] +- kvm-qcow2-Always-execute-preallocate-in-a-coroutine.patch [bz#1414049] +- kvm-iotests-Add-cluster_size-64k-to-125.patch [bz#1414049] +- kvm-fw_cfg-rename-read-callback.patch [bz#1398633] +- kvm-fw_cfg-add-write-callback.patch [bz#1398633] +- kvm-hw-misc-add-vmcoreinfo-device.patch [bz#1398633] +- kvm-dump-add-guest-ELF-note.patch [bz#1398633] +- kvm-dump-update-phys_base-header-field-based-on-VMCOREIN.patch [bz#1398633] +- kvm-kdump-set-vmcoreinfo-location.patch [bz#1398633] +- kvm-scripts-dump-guest-memory.py-add-vmcoreinfo.patch [bz#1398633] +- kvm-vmcoreinfo-put-it-in-the-misc-device-category.patch [bz#1398633] +- kvm-build-sys-restrict-vmcoreinfo-to-fw_cfg-dma-capable-.patch [bz#1398633] +- kvm-slirp-fix-clearing-ifq_so-from-pending-packets.patch [bz#1508750] +- kvm-migration-ram.c-do-not-set-postcopy_running-in-POSTC.patch [bz#1516956] +- kvm-scsi-Fix-onboard-HBAs-to-pick-up-drive-if-scsi.patch [bz#1497740] +- kvm-virtio-net-don-t-touch-virtqueue-if-vm-is-stopped.patch [bz#1506151] +- kvm-scsi-disk-support-reporting-of-rotation-rate.patch [bz#1498042] +- kvm-ide-support-reporting-of-rotation-rate.patch [bz#1498042] +- kvm-ide-avoid-referencing-NULL-dev-in-rotational-rate-se.patch [bz#1498042] +- Resolves: bz#1398633 + ([RFE] Kernel address space layout randomization [KASLR] support (qemu-kvm-rhev)) +- Resolves: bz#1414049 + ([RFE] Add support to qemu-img for resizing with preallocation) +- Resolves: bz#1497740 + (-cdrom option is broken) +- Resolves: bz#1498042 + (RFE: option to mark virtual block device as rotational/non-rotational) +- Resolves: bz#1506151 + ([data-plane] Quitting qemu in destination side encounters "core dumped" when doing live migration) +- Resolves: bz#1508750 + (CVE-2017-13711 qemu-kvm-rhev: Qemu: Slirp: use-after-free when sending response [rhel-7.5]) +- Resolves: bz#1516956 + (Pegas1.0 - [qemu]: loadvm fails to restore VM snapshot saved using savevm in destination after postcopy migration (kvm)) + +* Tue Nov 28 2017 Miroslav Rezanina - 2.10.0-9.el7 +- kvm-spapr-Correct-RAM-size-calculation-for-HPT-resizing.patch [bz#1499647] +- kvm-migration-Reenable-incoming-live-block-migration.patch [bz#1515173] +- kvm-ppc-fix-VTB-migration.patch [bz#1506882] +- kvm-hw-ppc-spapr-Fix-virtio-scsi-bootindex-handling-for-.patch [bz#1515393] +- kvm-spapr-Implement-bug-in-spapr-vty-device-to-be-compat.patch [bz#1495090] +- kvm-spapr-reset-DRCs-after-devices.patch [bz#1516145] +- kvm-redhat-install-generic-kvm.conf-except-for-s390-and-.patch [bz#1517144] +- Resolves: bz#1495090 + (Transfer a file about 10M failed from host to guest through spapr-vty device) +- Resolves: bz#1499647 + (qemu miscalculates guest RAM size during HPT resizing) +- Resolves: bz#1506882 + (Call trace showed up in dmesg after migrating guest when "stress-ng --numa 2" was running inside guest) +- Resolves: bz#1515173 + (Cross migration from rhel6.9 to rhel7.5 failed) +- Resolves: bz#1515393 + (bootindex is not taken into account for virtio-scsi devices on ppc64 if the LUN is >= 256) +- Resolves: bz#1516145 + (Pegas1.0 - [memory hotplug/unplug] qemu crashes with assertion failed from hw/virtio/vhost.c:649 (qemu-kvm)) +- Resolves: bz#1517144 + (Provide a ppc64le specific /etc/modprobe.d/kvm.conf) + +* Mon Nov 27 2017 Miroslav Rezanina - 2.10.0-8.el7 +- kvm-block-move-ThrottleGroup-membership-to-ThrottleGroup.patch [bz#1492295] +- kvm-block-add-aio_context-field-in-ThrottleGroupMember.patch [bz#1492295] +- kvm-block-tidy-ThrottleGroupMember-initializations.patch [bz#1492295] +- kvm-block-all-I-O-should-be-completed-before-removing-th.patch [bz#1492295] +- kvm-throttle-groups-drain-before-detaching-ThrottleState.patch [bz#1492295] +- kvm-block-Check-for-inserted-BlockDriverState-in-blk_io_.patch [bz#1492295] +- kvm-block-Leave-valid-throttle-timers-when-removing-a-BD.patch [bz#1492295] +- kvm-qemu-iotests-Test-I-O-limits-with-removable-media.patch [bz#1492295] +- kvm-throttle-groups-forget-timer-and-schedule-next-TGM-o.patch [bz#1492295] +- kvm-i386-cpu-hyperv-support-over-64-vcpus-for-windows-gu.patch [bz#1451959] +- kvm-target-ppc-correct-htab-shift-for-hash-on-radix.patch [bz#1396120] +- kvm-target-ppc-Update-setting-of-cpu-features-to-account.patch [bz#1396120] +- kvm-s390-ccw-Fix-alignment-for-CCW1.patch [bz#1514352] +- kvm-pc-bios-s390-ccw-Fix-problem-with-invalid-virtio-scs.patch [bz#1514352] +- kvm-redhat-qemu-kvm.spec-Use-the-freshly-built-s390-ccw..patch [bz#1514352] +- Resolves: bz#1396120 + ([IBM 7.5 FEAT] POWER9 - Virt: QEMU: POWER8/P8-Compat mode - HPT to guest) +- Resolves: bz#1451959 + (Windows 2016 guest blue screen with page fault in nonpaged area when using hv flags) +- Resolves: bz#1492295 + (Guest hit call trace with iothrottling(iops) after the status from stop to cont during doing io testing) +- Resolves: bz#1514352 + ([RHEL-ALT][s390x] qemu process terminated after rebooting the guest) + +* Wed Nov 22 2017 Miroslav Rezanina - 2.10.0-7.el7 +- kvm-hw-pci-introduce-bridge-only-vendor-specific-capabil.patch [bz#1437113] +- kvm-hw-pci-add-QEMU-specific-PCI-capability-to-the-Gener.patch [bz#1437113] +- kvm-util-async-use-atomic_mb_set-in-qemu_bh_cancel.patch [bz#1508886] +- kvm-hw-gen_pcie_root_port-make-IO-RO-0-on-IO-disabled.patch [bz#1344299] +- kvm-pcie_root_port-Fix-x-migrate-msix-compat.patch [bz#1511312] +- kvm-q35-Fix-mismerge.patch [bz#1511312] +- kvm-virtio-pci-Replace-modern_as-with-direct-access-to-m.patch [bz#1481593] +- kvm-atomic-update-documentation.patch [bz#1481593] +- kvm-memory-avoid-resurrection-of-dead-FlatViews.patch [bz#1481593] +- kvm-exec-Explicitly-export-target-AS-from-address_space_.patch [bz#1481593] +- kvm-memory-Open-code-FlatView-rendering.patch [bz#1481593] +- kvm-memory-Move-FlatView-allocation-to-a-helper.patch [bz#1481593] +- kvm-memory-Move-AddressSpaceDispatch-from-AddressSpace-t.patch [bz#1481593] +- kvm-memory-Remove-AddressSpace-pointer-from-AddressSpace.patch [bz#1481593] +- kvm-memory-Switch-memory-from-using-AddressSpace-to-Flat.patch [bz#1481593] +- kvm-memory-Cleanup-after-switching-to-FlatView.patch [bz#1481593] +- kvm-memory-Rename-mem_begin-mem_commit-mem_add-helpers.patch [bz#1481593] +- kvm-memory-Store-physical-root-MR-in-FlatView.patch [bz#1481593] +- kvm-memory-Alloc-dispatch-tree-where-topology-is-generar.patch [bz#1481593] +- kvm-memory-Move-address_space_update_ioeventfds.patch [bz#1481593] +- kvm-memory-Share-FlatView-s-and-dispatch-trees-between-a.patch [bz#1481593] +- kvm-memory-Do-not-allocate-FlatView-in-address_space_ini.patch [bz#1481593] +- kvm-memory-Rework-info-mtree-to-print-flat-views-and-dis.patch [bz#1481593] +- kvm-memory-Get-rid-of-address_space_init_shareable.patch [bz#1481593] +- kvm-memory-Create-FlatView-directly.patch [bz#1481593] +- kvm-memory-trace-FlatView-creation-and-destruction.patch [bz#1481593] +- kvm-memory-seek-FlatView-sharing-candidates-among-childr.patch [bz#1481593] +- kvm-memory-Share-special-empty-FlatView.patch [bz#1481593] +- kvm-hw-pci-host-Fix-x86-Host-Bridges-64bit-PCI-hole.patch [bz#1390346] +- kvm-redhat-Provide-s390x-specific-etc-modprobe.d-kvm.con.patch [bz#1511990] +- Resolves: bz#1344299 + (PCIe: Add an option to PCIe ports to disable IO port space support) +- Resolves: bz#1390346 + (PCI: Reserve MMIO space over 4G for PCI hotplug) +- Resolves: bz#1437113 + (PCIe: Allow configuring Generic PCIe Root Ports MMIO Window) +- Resolves: bz#1481593 + (Boot guest failed with "src/central_freelist.cc:333] tcmalloc: allocation failed 196608" when 465 disks are attached to 465 pci-bridges) +- Resolves: bz#1508886 + (QEMU's AIO subsystem gets stuck inhibiting all I/O operations on virtio-blk-pci devices) +- Resolves: bz#1511312 + (Migrate an VM with pci-bridge or pcie-root-port failed) +- Resolves: bz#1511990 + (Provide a s390x specific /etc/modprobe.d/kvm.conf) + +* Mon Nov 13 2017 Miroslav Rezanina - 2.10.0-6.el7 +- kvm-multiboot-validate-multiboot-header-address-values.patch [bz#1501124] +- kvm-monitor-fix-dangling-CPU-pointer.patch [bz#1510001] +- kvm-qdev-store-DeviceState-s-canonical-path-to-use-when-.patch [bz#1445460] +- kvm-Revert-qdev-Free-QemuOpts-when-the-QOM-path-goes-awa.patch [bz#1445460] +- kvm-qdev-defer-DEVICE_DEL-event-until-instance_finalize.patch [bz#1445460] +- kvm-s390x-print-CPU-definitions-in-sorted-order.patch [bz#1504138] +- kvm-s390x-cpumodel-Disable-unsupported-CPU-models.patch [bz#1504138] +- Resolves: bz#1445460 + (EEH freeze up when reattaching an i40evf VF to host) +- Resolves: bz#1501124 + (CVE-2017-14167 qemu-kvm-rhev: Qemu: i386: multiboot OOB access while loading kernel image [rhel-7.5]) +- Resolves: bz#1504138 + (Disable older CPU models in qemu-kvm-ma on s390x) +- Resolves: bz#1510001 + (Pegas1.0 - qemu crashed during "info cpus" in monitor with change in default cpu in hotplug/unplug sequence (kvm)) + +* Wed Nov 08 2017 Miroslav Rezanina - ma-2.10.0-5.el7 +- kvm-qemu-kvm-rhev-only-allows-pseries-rhel7.5.0-machine-.patch [bz#1478469] +- kvm-pc-bios-keymaps-keymaps-update.patch [bz#1503128] +- kvm-migration-Reset-rather-than-destroy-main_thread_load.patch [bz#1508799] +- kvm-snapshot-tests-Try-loadvm-twice.patch [bz#1508799] +- kvm-machine-compat-pci_bridge-shpc-always-enable.patch [bz#1508271] +- kvm-hw-pci-host-gpex-Set-INTx-index-gsi-mapping.patch [bz#1460957] +- kvm-hw-arm-virt-Set-INTx-gsi-mapping.patch [bz#1460957] +- kvm-hw-pci-host-gpex-Implement-PCI-INTx-routing.patch [bz#1460957] +- kvm-hw-pci-host-gpex-Improve-INTX-to-gsi-routing-error-c.patch [bz#1460957] +- Resolves: bz#1460957 + (Implement INTx to GSI routing on ARM virt) +- Resolves: bz#1478469 + (RHEL 7.5 machine types for Power 8 and 9 - qemu-kvm-rhev) +- Resolves: bz#1503128 + (update reverse keymaps for qemu vnc server) +- Resolves: bz#1508271 + (Migration is failed from host RHEL7.4.z to host RHEL7.5 with "-machine pseries-rhel7.4.0 -device pci-bridge,id=pci_bridge,bus=pci.0,addr=03,chassis_nr=1") +- Resolves: bz#1508799 + (qemu-kvm core dumped when doing 'savevm/loadvm/delvm' for the second time) + +* Thu Nov 02 2017 Miroslav Rezanina - 2.10.0-4.el7 +- kvm-vga-drop-line_offset-variable.patch [bz#1501301] +- kvm-vga-handle-cirrus-vbe-mode-wraparounds.patch [bz#1501301] +- kvm-cirrus-fix-oob-access-in-mode4and5-write-functions.patch [bz#1501301] +- kvm-exec-add-page_mask-for-address_space_do_translate.patch [bz#1498817] +- kvm-exec-simplify-address_space_get_iotlb_entry.patch [bz#1498817] +- kvm-xio3130_downstream-Report-error-if-pcie_chassis_add_.patch [bz#1390348] +- kvm-pci-conventional-pci-device-and-pci-express-device-i.patch [bz#1390348] +- kvm-pci-Add-interface-names-to-hybrid-PCI-devices.patch [bz#1390348] +- kvm-pci-Add-INTERFACE_PCIE_DEVICE-to-all-PCIe-devices.patch [bz#1390348] +- kvm-pci-Add-INTERFACE_CONVENTIONAL_PCI_DEVICE-to-Convent.patch [bz#1390348] +- kvm-xen-pt-Mark-TYPE_XEN_PT_DEVICE-as-hybrid.patch [bz#1390348] +- kvm-pci-Validate-interfaces-on-base_class_init.patch [bz#1390348] +- kvm-migration-Add-pause-before-switchover-capability.patch [bz#1497120] +- kvm-migration-Add-pre-switchover-and-device-statuses.patch [bz#1497120] +- kvm-migration-Wait-for-semaphore-before-completing-migra.patch [bz#1497120] +- kvm-migration-migrate-continue.patch [bz#1497120] +- kvm-migrate-HMP-migate_continue.patch [bz#1497120] +- kvm-migration-allow-cancel-to-unpause.patch [bz#1497120] +- kvm-migration-pause-before-switchover-for-postcopy.patch [bz#1497120] +- Resolves: bz#1390348 + (PCI: Provide to libvirt a new query command whether a device is PCI/PCIe/hybrid) +- Resolves: bz#1497120 + (migration+new block migration race: bdrv_co_do_pwritev: Assertion `!(bs->open_flags & 0x0800)' failed) +- Resolves: bz#1498817 + (Vhost IOMMU support regression since qemu-kvm-rhev-2.9.0-16.el7_4.5) +- Resolves: bz#1501301 + (CVE-2017-15289 qemu-kvm-rhev: Qemu: cirrus: OOB access issue in mode4and5 write functions [rhel-7.5]) + +* Fri Oct 20 2017 Miroslav Rezanina - 2.10.0-3.el7 +- kvm-virtio-gpu-don-t-clear-QemuUIInfo-information-on-res.patch [bz#1460595] +- kvm-vga-fix-display-update-region-calculation-split-scre.patch [bz#1486648] +- kvm-target-i386-cpu-Add-new-EPYC-CPU-model.patch [bz#1445834] +- kvm-redhat-add-CONFIG_RHV-flag.patch [bz#1498865] +- kvm-intel_iommu-fix-missing-BQL-in-pt-fast-path.patch [bz#1449067] +- kvm-redhat-define-HW_COMPAT_RHEL7_4.patch [bz#1478478] +- kvm-redhat-define-pseries-rhel7.5.0-machine-type.patch [bz#1478478] +- kvm-qemu-kvm-ma-define-only-pseries-rhel7.5.0-machine-ty.patch [bz#1478478] +- kvm-Create-x86-7.5.0-machine-types.patch [bz#1499011] +- kvm-i386-kvm-use-a-switch-statement-for-MSR-detection.patch [bz#1500347] +- kvm-i386-kvm-set-tsc_khz-before-configuring-Hyper-V-CPUI.patch [bz#1500347] +- kvm-i386-kvm-introduce-tsc_is_stable_and_known.patch [bz#1500347] +- kvm-i386-kvm-advertise-Hyper-V-frequency-MSRs.patch [bz#1500347] +- kvm-acpi-Force-rev1-FADT-on-old-q35-machine-types.patch [bz#1489800] +- kvm-pc-make-pc_rom-RO-only-on-new-machine-types.patch [bz#1489800] +- kvm-osdep-Force-define-F_OFD_GETLK-RHEL-only.patch [bz#1378241] +- kvm-Disable-vhost-user-scsi-and-vhost-user-scsi-pci.patch [bz#1498496] +- kvm-Disable-sm501-and-sysbus-sm501-devices.patch [bz#1498496] +- kvm-configure-enable-s390-pgste-linker-option.patch [bz#1485399] +- kvm-s390x-vm.allocate_pgste-sysctl-is-no-longer-needed.patch [bz#1485399] +- kvm-arm-virt-Add-RHEL-7.5-machine-type.patch [bz#1498662] +- kvm-tools-kvm_stat-hide-cursor.patch [bz#1497137] +- kvm-tools-kvm_stat-catch-curses-exceptions-only.patch [bz#1497137] +- kvm-tools-kvm_stat-handle-SIGINT-in-log-and-batch-modes.patch [bz#1497137] +- kvm-tools-kvm_stat-fix-misc-glitches.patch [bz#1497137] +- kvm-tools-kvm_stat-fix-trace-setup-glitch-on-field-updat.patch [bz#1497137] +- kvm-tools-kvm_stat-full-PEP8-compliance.patch [bz#1497137] +- kvm-tools-kvm_stat-reduce-perceived-idle-time-on-filter-.patch [bz#1497137] +- kvm-tools-kvm_stat-document-list-of-interactive-commands.patch [bz#1497137] +- kvm-tools-kvm_stat-display-guest-name-when-using-pid-fil.patch [bz#1497137] +- kvm-tools-kvm_stat-remove-pid-filter-on-empty-input.patch [bz#1497137] +- kvm-tools-kvm_stat-print-error-messages-on-faulty-pid-fi.patch [bz#1497137] +- kvm-tools-kvm_stat-display-regex-when-set-to-non-default.patch [bz#1497137] +- kvm-tools-kvm_stat-remove-regex-filter-on-empty-input.patch [bz#1497137] +- kvm-tools-kvm_stat-add-option-guest.patch [bz#1497137] +- kvm-tools-kvm_stat-add-interactive-command-c.patch [bz#1497137] +- kvm-tools-kvm_stat-add-interactive-command-r.patch [bz#1497137] +- kvm-tools-kvm_stat-add-Total-column.patch [bz#1497137] +- kvm-tools-kvm_stat-fix-typo.patch [bz#1497137] +- kvm-tools-kvm_stat-fix-event-counts-display-for-interrup.patch [bz#1497137] +- kvm-tools-kvm_stat-fix-undue-use-of-initial-sleeptime.patch [bz#1497137] +- kvm-tools-kvm_stat-remove-unnecessary-header-redraws.patch [bz#1497137] +- kvm-tools-kvm_stat-simplify-line-print-logic.patch [bz#1497137] +- kvm-tools-kvm_stat-removed-unused-function.patch [bz#1497137] +- kvm-tools-kvm_stat-remove-extra-statement.patch [bz#1497137] +- kvm-tools-kvm_stat-simplify-initializers.patch [bz#1497137] +- kvm-tools-kvm_stat-move-functions-to-corresponding-class.patch [bz#1497137] +- kvm-tools-kvm_stat-show-cursor-in-selection-screens.patch [bz#1497137] +- kvm-tools-kvm_stat-display-message-indicating-lack-of-ev.patch [bz#1497137] +- kvm-tools-kvm_stat-make-heading-look-a-bit-more-like-top.patch [bz#1497137] +- kvm-tools-kvm_stat-rename-Current-column-to-CurAvg-s.patch [bz#1497137] +- kvm-tools-kvm_stat-add-new-interactive-command-h.patch [bz#1497137] +- kvm-tools-kvm_stat-add-new-interactive-command-s.patch [bz#1497137] +- kvm-tools-kvm_stat-add-new-interactive-command-o.patch [bz#1497137] +- kvm-tools-kvm_stat-display-guest-list-in-pid-guest-selec.patch [bz#1497137] +- kvm-tools-kvm_stat-display-guest-list-in-pid-guest-sele2.patch [bz#1497137] +- kvm-tools-kvm_stat-add-new-command-line-switch-i.patch [bz#1497137] +- kvm-tools-kvm_stat-add-new-interactive-command-b.patch [bz#1497137] +- kvm-tools-kvm_stat-use-variables-instead-of-hard-paths-i.patch [bz#1497137] +- kvm-tools-kvm_stat-add-f-help-to-get-the-available-event.patch [bz#1497137] +- kvm-iothread-Make-iothread_stop-idempotent.patch [bz#1460848] +- kvm-vl-Clean-up-user-creatable-objects-when-exiting.patch [bz#1460848] +- kvm-osdep-Define-QEMU_MADV_REMOVE.patch [bz#1460848] +- kvm-hostmem-file-Add-discard-data-option.patch [bz#1460848] +- kvm-hw-dma-i8257-Remove-redundant-downstream-user_creata.patch [bz#1503998] +- kvm-hw-pci-host-q35-Remove-redundant-downstream-user_cre.patch [bz#1503998] +- kvm-hw-Remove-the-redundant-user_creatable-false-from-SY.patch [bz#1503998] +- kvm-spapr-disable-cpu-hot-remove.patch [bz#1499320] +- kvm-Update-build_configure-for-2.10.0-options.patch [bz#1502949] +- Resolves: bz#1378241 + (QEMU image file locking) +- Resolves: bz#1445834 + (Add support for AMD EPYC processors) +- Resolves: bz#1449067 + ([RFE] Device passthrough support for VT-d emulation) +- Resolves: bz#1460595 + ([virtio-vga]Display 2 should be dropped when guest reboot) +- Resolves: bz#1460848 + (RFE: Enhance qemu to support freeing memory before exit when using memory-backend-file) +- Resolves: bz#1478478 + (RHEL 7.5 machine types for Power 8 and 9 - qemu-kvm-ma) +- Resolves: bz#1485399 + (Backport selective allocation of PGSTE to avoid global vm.allocate_pgste) +- Resolves: bz#1486648 + (CVE-2017-13673 qemu-kvm-rhev: Qemu: vga: reachable assert failure during during display update [rhel-7.5]) +- Resolves: bz#1489800 + (q35/ovmf: Machine type compat vs OVMF vs windows) +- Resolves: bz#1497137 + (Update kvm_stat) +- Resolves: bz#1498496 + (Handle device tree changes in QEMU 2.10.0) +- Resolves: bz#1498662 + (RHEL-7.5 machine machine type for aarch64 (qemu-kvm-ma)) +- Resolves: bz#1498865 + (There is no switch to build qemu-kvm-rhev or qemu-kvm-ma packages) +- Resolves: bz#1499011 + (7.5: x86 machine types for 7.5) +- Resolves: bz#1499320 + (qemu-kvm-ma differentiation - cpu unplug) +- Resolves: bz#1500347 + ([Hyper-V][RHEL-7.4]Nested virt: Windows guest doesn't use TSC page when Hyper-V role is enabled) +- Resolves: bz#1502949 + (Update configure parameters to cover changes in 2.10.0) +- Resolves: bz#1503998 + (Remove redundant "user_creatable = false" flags from the downstream qemu-kvm-rhev code) + +* Fri Oct 13 2017 Miroslav Rezanina - 2.10.0-2.el7 +- kvm-vhost-Release-memory-references-on-cleanup.patch [bz#1489670] +- kvm-configure-Allow-enable-seccomp-on-s390x-too.patch [bz#1491647] +- kvm-redhat-qemu-kvm.spec.template-Enable-seccomp-on-s390.patch [bz#1491647] +- kvm-hw-ppc-spapr_drc.c-change-spapr_drc_needed-to-use-dr.patch [bz#1448344] +- kvm-hw-ppc-clear-pending_events-on-machine-reset.patch [bz#1448344] +- kvm-hw-ppc-CAS-reset-on-early-device-hotplug.patch [bz#1448344] +- kvm-spapr-fix-CAS-generated-reset.patch [bz#1448344] +- kvm-redhat-Remove-qemu.binfmt-from-the-downstream-reposi.patch [bz#1498122] +- kvm-redhat-fix-HW_COMPAT_RHEL7_3.patch [bz#1498754] +- kvm-vga-stop-passing-pointers-to-vga_draw_line-functions.patch [bz#1486643] +- kvm-s390x-ais-for-2.10-stable-disable-ais-facility.patch [bz#1494548] +- kvm-s390x-cpumodel-remove-ais-from-z14-default-model-als.patch [bz#1494548] +- kvm-PPC-KVM-Support-machine-option-to-set-VSMT-mode.patch [bz#1479178] +- kvm-nbd-client-avoid-read_reply_co-entry-if-send-failed.patch [bz#1482478] +- kvm-qemu-iotests-improve-nbd-fault-injector.py-startup-p.patch [bz#1482478] +- kvm-qemu-iotests-test-NBD-over-UNIX-domain-sockets-in-08.patch [bz#1482478] +- kvm-block-nbd-client-nbd_co_send_request-fix-return-code.patch [bz#1482478] +- kvm-usb-drop-HOST_USB.patch [bz#1492033] +- kvm-usb-only-build-usb-host-with-CONFIG_USB-y.patch [bz#1492033] +- kvm-usb-fix-libusb-config-variable-name.patch [bz#1492033] +- kvm-usb-fix-host-stub.c-build-race.patch [bz#1492033] +- kvm-s390x-s390-stattrib-Mark-the-storage-attribute-as-no.patch [bz#1492033] +- kvm-s390x-s390-skeys-Mark-the-storage-key-devices-with-u.patch [bz#1492033] +- kvm-watchdog-wdt_diag288-Mark-diag288-watchdog-as-non-ho.patch [bz#1492033] +- kvm-s390x-ipl-The-s390-ipl-device-is-not-hot-pluggable.patch [bz#1492033] +- kvm-hw-s390x-Mark-the-sclpquiesce-device-with-user_creat.patch [bz#1492033] +- kvm-s390x-sclp-mark-sclp-cpu-hotplug-as-non-usercreatabl.patch [bz#1492033] +- kvm-s390x-sclp-Mark-the-sclp-device-with-user_creatable-.patch [bz#1492033] +- kvm-RHEL-Disable-vfio-ccw-and-x-terminal3270-devices.patch [bz#1492033] +- kvm-s390x-css-fix-css-migration-compat-handling.patch [bz#1473292] +- kvm-RHEL-Add-RHEL7-machine-type-for-qemu-on-s390x.patch [bz#1473292] +- kvm-hw-nvram-spapr_nvram-Device-can-not-be-created-by-th.patch [bz#1490869] +- kvm-vl-exit-if-maxcpus-is-negative.patch [bz#1491743] +- kvm-Disable-build-of-qemu-kvm-ma-for-x86_64.patch [bz#1501230] +- Resolves: bz#1448344 + (Failed to hot unplug cpu core which hotplugged in early boot stages) +- Resolves: bz#1473292 + (Need RHEL-specific machine types for qemu-kvm on s390x) +- Resolves: bz#1479178 + (QEMU does not yet have support for setting the virtual SMT mode on Power 9, which is required to run with KVM and more than one thread per core.) +- Resolves: bz#1482478 + (Fail to quit source qemu when do live migration after mirroring guest to NBD server) +- Resolves: bz#1486643 + (CVE-2017-13672 qemu-kvm-rhev: Qemu: vga: OOB read access during display update [rhel-7.5]) +- Resolves: bz#1489670 + (Hot-unplugging a vhost network device leaks references to VFIOPCIDevice's) +- Resolves: bz#1490869 + ([Pegas1.0] qemu device spapr-nvram crashes with SIGABRT (qemu-kvm)) +- Resolves: bz#1491647 + ([RFE] Enable seccomp (sandbox) support in QEMU for s390x) +- Resolves: bz#1491743 + (qemu crashes with 'Abort' when a negative number is used for 'maxcpus' argument (qemu-kvm)) +- Resolves: bz#1492033 + (Disable unwanted device in QEMU for s390x) +- Resolves: bz#1494548 + (Disable ais facility on s390x) +- Resolves: bz#1498122 + (Remove superfluous file qemu.binfmt from the qemu-kvm-rhev package) +- Resolves: bz#1498754 + (Definition of HW_COMPAT_RHEL7_3 is not correct) +- Resolves: bz#1501230 + (Disable x86_64 build for qemu-kvm-ma) + +* Fri Sep 29 2017 Miroslav Rezanina - 2.10.0-1.el7 +- Rebase to 2.10.0 [bz#1470749] +- Resolves: bz#1470749 + (Rebase qemu-kvm-rhev for RHEL-7.5) + +* Thu Sep 21 2017 Danilo Cesar Lemes de Paula - 2.9.0-23.el7a +- kvm-vfio-spapr-Fix-levels-calculation.patch [bz#1491749] +- Resolves: bz#1491749 + (Pegas1.0 - Guest crashes during boot with VF Pass-through and 129GB memory (qemu-kvm)) + +* Tue Aug 29 2017 Miroslav Rezanina - 2.9.0-22.el7a +- kvm-qemu-kvm.spec-Configure-vm.allocate_pgste-for-s390x.patch [bz#1454281] +- Resolves: bz#1454281 + (Enable vm.allocate_pgste sysctl before running qemu-kvm on s390x) + +* Tue Aug 15 2017 Miroslav Rezanina - 2.9.0-21.el7a +- kvm-target-ppc-Implement-TIDR.patch [bz#1478822] +- kvm-target-ppc-Add-stub-implementation-of-the-PSSCR.patch [bz#1478822] +- kvm-target-ppc-Fix-size-of-struct-PPCElfPrstatus.patch [bz#1480418] +- Resolves: bz#1478822 + (The KVM guest SPRs TIDR (144) and PSSCR (823) are currently not migrated right on POWER9) +- Resolves: bz#1480418 + ([guest memory dump] Dump guest's memory to file and GDB fails to process the core file) + +* Tue Aug 08 2017 Miroslav Rezanina - 2.9.0-20.el7a +- kvm-Downstream-Update-pseries-machine-types-for-RHEL-ALT.patch [bz#1473518] +- kvm-cpu-don-t-allow-negative-core-id.patch [bz#1476181] +- kvm-pegas-add-disable-vhost-user.patch [bz#1455269] +- kvm-pegas-add-rpm-spec-options-for-vhost-user.patch [bz#1455269] +- Resolves: bz#1455269 + ([Pegas 1.0] qemu-kvm differentiation patches for Power9 - vhost-user) +- Resolves: bz#1473518 + (Need to remove (or not?) pseries-rhel7.2.0, pseries-rhel7.3.0 machine types for RHEL-ALT qemu-kvm) +- Resolves: bz#1476181 + (qemu core dumped after hotplug one cpu core with a negative core id) + +* Tue Aug 01 2017 Miroslav Rezanina - 2.9.0-19.el7a +- kvm-AArch64-remove-mach-virt-7.3-machine-type.patch [bz#1473548] +- kvm-nbd-strict-nbd_wr_syncv.patch [bz#1473638] +- kvm-nbd-read_sync-and-friends-return-0-on-success.patch [bz#1473638] +- kvm-nbd-make-nbd_drop-public.patch [bz#1473638] +- kvm-nbd-server-get-rid-of-nbd_negotiate_read-and-friends.patch [bz#1473638] +- kvm-spapr-htab-fix-savevm.patch [bz#1470035] +- kvm-migration-rdma-Fix-race-on-source.patch [bz#1475751] +- kvm-migration-rdma-fix-qemu_rdma_block_for_wrid-error-pa.patch [bz#1475751] +- kvm-migration-rdma-Allow-cancelling-while-waiting-for-wr.patch [bz#1475751] +- kvm-migration-rdma-Safely-convert-control-types.patch [bz#1475751] +- kvm-migration-rdma-Send-error-during-cancelling.patch [bz#1475751] +- kvm-configure-allow-to-disable-VT-d-emulation.patch [bz#1465450] +- kvm-Disable-VT-d-for-rhel-builds.patch [bz#1465450] +- kvm-RHEL-Diff.-Add-option-in-configure-to-disable-live-b.patch [bz#1418532] +- kvm-RHEL-Diff.-Unregister-live-block-operations.patch [bz#1418532] +- kvm-RHEL-Diff.-Disable-live-block-operations-in-HMP-moni.patch [bz#1418532] +- kvm-RHEL-Diff.-Add-rpm-spec-options-for-live-block-ops.patch [bz#1418532] +- Resolves: bz#1418532 + ([Pegas 1.0] qemu-kvm differentiation patches for Power9 - block) +- Resolves: bz#1465450 + ([Pegas 1.0] qemu-kvm differentiation - vIOMMU) +- Resolves: bz#1470035 + ([qmp] Load internal snapshot failed on Power9) +- Resolves: bz#1473548 + (AArch64: remove 7.3 machine type) +- Resolves: bz#1473638 + (CVE-2017-7539 qemu-kvm-rhev: Qemu: qemu-nbd crashes due to undefined I/O coroutine [rhel-alt-7.4]) +- Resolves: bz#1475751 + (migration/RDMA: backport fixes) + +* Tue Jul 18 2017 Miroslav Rezanina - 2.9.0-18.el7a +- kvm-ppc-kvm-have-the-family-CPU-alias-to-point-to-TYPE_H.patch [bz#1460908] +- kvm-Disable-virtio-pci-for-s390x-builds.patch [bz#1469000] +- kvm-target-ppc-Implement-ISA-V3.00-radix-page-fault-hand.patch [bz#1470558] +- kvm-target-ppc-Fix-return-value-in-tcg-radix-mmu-fault-h.patch [bz#1470558] +- kvm-target-ppc-Refactor-tcg-radix-mmu-code.patch [bz#1470558] +- kvm-target-ppc-Add-debug-function-for-radix-mmu-translat.patch [bz#1470558] +- Resolves: bz#1460908 + (qemu-kvm: POWER9 CPU model not usable on POWER9 machine) +- Resolves: bz#1469000 + (Disable virtio-pci devices in qemu-kvm on s390x) +- Resolves: bz#1470558 + ([qmp] qemu-kvm process aborted after issuing QMP 'memsave' command on Power9) + +* Tue Jul 11 2017 Miroslav Rezanina - 2.9.0-17.el7a +- kvm-spapr-Consolidate-HPT-freeing-code-into-a-routine.patch [bz#1456287] +- kvm-spapr-Add-a-no-HPT-encoding-to-HTAB-migration-stream.patch [bz#1456287] +- kvm-spapr-Fix-migration-of-Radix-guests.patch [bz#1456287] +- kvm-qemu-nbd-Ignore-SIGPIPE.patch [bz#1469463] +- Resolves: bz#1456287 + ([Pegas1.0 EA2] [qemu-kvm-rhev-2.9] After 'virsh managedsave', domain not starting) +- Resolves: bz#1469463 + (CVE-2017-10664 qemu-kvm: Qemu: qemu-nbd: server breaks with SIGPIPE upon client abort [rhel-7.4-Alt]) + +* Tue Jul 04 2017 Miroslav Rezanina - 2.9.0-16.el7a +- kvm-AArch64-Add-pci-testdev.patch [bz#1465048] +- Resolves: bz#1465048 + (AArch64: Add pci-testdev) + +* Tue Jun 27 2017 Miroslav Rezanina - 2.9.0-15.el7a +- kvm-hw-ppc-spapr-Adjust-firmware-name-for-PCI-bridges.patch [bz#1459170] +- Resolves: bz#1459170 + (SLOF: Can't boot from virtio-scsi disk behind pci-bridge: E3405: No such device) + +* Fri Jun 23 2017 Miroslav Rezanina - rhev-2.9.0-14.el7 +- kvm-sockets-ensure-we-can-bind-to-both-ipv4-ipv6-separat.patch [bz#1446003] +- Resolves: bz#1446003 + (vnc cannot find a free port to use) + +* Tue Jun 20 2017 Miroslav Rezanina - 2.9.0-13.el7 +- kvm-linux-headers-update.patch [bz#1462061] +- kvm-all-Pass-an-error-object-to-kvm_device_access.patch [bz#1462061] +- kvm-hw-intc-arm_gicv3_its-Implement-state-save-restore.patch [bz#1462061] +- kvm-hw-intc-arm_gicv3_kvm-Implement-pending-table-save.patch [bz#1462061] +- kvm-hw-intc-arm_gicv3_its-Allow-save-restore.patch [bz#1462061] +- Resolves: bz#1462061 + (Backport QEMU ITS migration series) + +* Tue Jun 20 2017 Miroslav Rezanina - rhev-2.9.0-12.el7 +- kvm-pseries-Correct-panic-behaviour-for-pseries-machine-.patch [bz#1458705] +- kvm-virtio-scsi-Reject-scsi-cd-if-data-plane-enabled-RHE.patch [bz#1378816] +- kvm-block-rbd-enable-filename-option-and-parsing.patch [bz#1457088] +- kvm-block-iscsi-enable-filename-option-and-parsing.patch [bz#1457088] +- kvm-nbd-fix-NBD-over-TLS-bz1461827.patch [bz#1461827] +- kvm-monitor-add-handle_hmp_command-trace-event.patch [bz#1457740] +- kvm-monitor-resurrect-handle_qmp_command-trace-event.patch [bz#1457740] +- kvm-hw-pcie-fix-the-generic-pcie-root-port-to-support-mi.patch [bz#1455150] +- Resolves: bz#1378816 + (Core dump when use "data-plane" and execute change cd) +- Resolves: bz#1455150 + (Unable to detach virtio disk from pcie-root-port after migration) +- Resolves: bz#1457088 + (rbd/iscsi: json: pseudo-protocol format is incompatible with 7.3) +- Resolves: bz#1457740 + ([Tracing] compling qemu-kvm failed through systemtap) +- Resolves: bz#1458705 + (pvdump: QMP reports "GUEST_PANICKED" event but HMP still shows VM running after guest crashed) +- Resolves: bz#1461827 + (QEMU hangs in aio wait when trying to access NBD volume over TLS) + +* Fri Jun 16 2017 Miroslav Rezanina - rhev-2.9.0-11.el7 +- kvm-Enable-USB_CONFIG-for-aarch64.patch [bz#1460010] +- Resolves: bz#1460010 + (USB HID (keyboard and tablet) missing [aarch64]) + +* Tue Jun 13 2017 Miroslav Rezanina - rhev-2.9.0-10.el7 +- kvm-Revert-Change-net-socket.c-to-use-socket_-functions-.patch [bz#1451629] +- kvm-nbd-Fully-initialize-client-in-case-of-failed-negoti.patch [bz#1447948] +- kvm-nbd-Fix-regression-on-resiliency-to-port-scan.patch [bz#1447948] +- kvm-nbd-make-it-thread-safe-fix-qcow2-over-nbd.patch [bz#1454582] +- kvm-commit-Fix-use-after-free-in-completion.patch [bz#1452048] +- kvm-qemu-iotests-Test-automatic-commit-job-cancel-on-hot.patch [bz#1452048] +- kvm-commit-Fix-completion-with-extra-reference.patch [bz#1453169] +- kvm-qemu-iotests-Allow-starting-new-qemu-after-cleanup.patch [bz#1453169] +- kvm-qemu-iotests-Test-exiting-qemu-with-running-job.patch [bz#1453169] +- kvm-virtio-serial-fix-segfault-on-disconnect.patch [bz#1447257] +- kvm-block-fix-external-snapshot-abort-permission-error.patch [bz#1447184] +- kvm-xhci-only-update-dequeue-ptr-on-completed-transfers.patch [bz#1451631] +- kvm-virtio-scsi-Unset-hotplug-handler-when-unrealize.patch [bz#1449031] +- Resolves: bz#1447184 + (qemu abort when live snapshot for multiple block device simultaneously with transaction and one is to a non-exist path) +- Resolves: bz#1447257 + (QEMU coredump while doing hexdump test onto virtio serial ports) +- Resolves: bz#1447948 + (qemu-nbd segment fault when nmap sweeps its port [rhel-7.4]) +- Resolves: bz#1449031 + (qemu core dump when hot-unplug/hot-plug scsi controller in turns) +- Resolves: bz#1451629 + (TCP tunnel network: the guest with interface type=client can not start) +- Resolves: bz#1451631 + (Keyboard does not work after migration) +- Resolves: bz#1452048 + (qemu abort when hot unplug block device during live commit) +- Resolves: bz#1453169 + (qemu aborts if quit during live commit process) +- Resolves: bz#1454582 + (Qemu crashes when start guest with qcow2 nbd image) + +* Thu Jun 08 2017 Miroslav Rezanina - rhev-2.9.0-9.el7 +- kvm-shutdown-Simplify-shutdown_signal.patch [bz#1418927] +- kvm-shutdown-Prepare-for-use-of-an-enum-in-reset-shutdow.patch [bz#1418927] +- kvm-shutdown-Preserve-shutdown-cause-through-replay.patch [bz#1418927] +- kvm-shutdown-Add-source-information-to-SHUTDOWN-and-RESE.patch [bz#1418927] +- kvm-shutdown-Expose-bool-cause-in-SHUTDOWN-and-RESET-eve.patch [bz#1418927] +- kvm-irqchip-trace-changes-on-msi-add-remove.patch [bz#1448813] +- kvm-msix-trace-control-bit-write-op.patch [bz#1448813] +- kvm-irqchip-skip-update-msi-when-disabled.patch [bz#1448813] +- kvm-vhost-propagate-errors-in-vhost_device_iotlb_miss.patch [bz#1451862] +- kvm-vhost-rework-IOTLB-messaging.patch [bz#1451862] +- kvm-vhost-user-add-vhost_user-to-hold-the-chr.patch [bz#1451862] +- kvm-vhost-user-add-slave-req-fd-support.patch [bz#1451862] +- kvm-spec-vhost-user-spec-Add-IOMMU-support.patch [bz#1451862] +- kvm-pc-Use-min-x-level-on-compat_props-on-RHEL-machine-t.patch [bz#1454641] +- kvm-usb-don-t-wakeup-during-coldplug.patch [bz#1452512] +- kvm-ehci-fix-overflow-in-frame-timer-code.patch [bz#1449609] +- kvm-ehci-fix-frame-timer-invocation.patch [bz#1449609] +- Resolves: bz#1418927 + (The lifecycle event for Guest OS Shutdown is not distinguishable from a qemu process that was quit with SIG_TERM) +- Resolves: bz#1448813 + (qemu crash when shutdown guest with '-device intel-iommu' and '-device vfio-pci') +- Resolves: bz#1449609 + (qemu coredump when dd on multiple usb-storage devices concurrently in guest) +- Resolves: bz#1451862 + (IOMMU support in QEMU for Vhost-user backend) +- Resolves: bz#1452512 + (qemu coredump when add more than 12 usb-storage devices to ehci) +- Resolves: bz#1454641 + (Windows 10 BSOD when using rhel6.4.0/rhel6.5.0/rhel6.6.0) + +* Tue Jun 06 2017 Miroslav Rezanina - rhev-2.9.0-8.el7 +- kvm-input-don-t-queue-delay-if-paused.patch [bz#1444326] +- kvm-block-gluster-glfs_lseek-workaround.patch [bz#1451191] +- kvm-mirror-Drop-permissions-on-s-target-on-completion.patch [bz#1456456] +- kvm-stream-fix-crash-in-stream_start-when-block_job_crea.patch [bz#1456424] +- kvm-qemu-iotests-Test-streaming-with-missing-job-ID.patch [bz#1456424] +- kvm-monitor-Use-numa_get_node_for_cpu-on-info-numa.patch [bz#1274567] +- kvm-virtio_net-Bypass-backends-for-MTU-feature-negotiati.patch [bz#1452756] +- kvm-vhost-user-pass-message-as-a-pointer-to-process_mess.patch [bz#1447592] +- kvm-virtio-serial-bus-Unset-hotplug-handler-when-unreali.patch [bz#1458782] +- kvm-gluster-add-support-for-PREALLOC_MODE_FALLOC.patch [bz#1450759] +- kvm-numa-Allow-setting-NUMA-distance-for-different-NUMA-.patch [bz#1395339] +- kvm-tests-acpi-extend-cphp-and-memhp-testcase-with-numa-.patch [bz#1395339] +- kvm-copy-SLIT-test-reference-blobs-into-tests-directory.patch [bz#1395339] +- Resolves: bz#1274567 + (HMP doesn't reflect the correct numa topology after hot plugging vCPU) +- Resolves: bz#1395339 + ([Intel 7.4 FEAT] Enable configuration of NUMA distance in QEMU) +- Resolves: bz#1444326 + (Keyboard inputs are buffered when qemu in stop status) +- Resolves: bz#1447592 + (vhost-user/reply-ack: Wait for ack even if no request sent (one-time requests)) +- Resolves: bz#1450759 + (Creating fallocated image using qemu-img using gfapi fails) +- Resolves: bz#1451191 + (qemu-img: block/gluster.c:1307: find_allocation: Assertion `offs >= start' failed.) +- Resolves: bz#1452756 + (Enable VIRTIO_NET_F_MTU feature in QEMU) +- Resolves: bz#1456424 + (qemu crash when starting image streaming job fails) +- Resolves: bz#1456456 + (qemu crashes on job completion during drain) +- Resolves: bz#1458782 + (QEMU crashes after hot-unplugging virtio-serial device) + +* Tue May 30 2017 Miroslav Rezanina - rhev-2.9.0-7.el7 +- kvm-e1000e-Fix-ICR-Other-causes-clear-logic.patch [bz#1449490] +- kvm-pc-fwcfg-unbreak-migration-from-qemu-2.5-and-qemu-2..patch [bz#1441394] +- kvm-disable-linuxboot_dma.bin-option-rom-for-7.3-machine.patch [bz#1441394] +- kvm-Revert-hw-pci-disable-pci-bridge-s-shpc-by-default.patch [bz#1434706] +- kvm-qemu-img-wait-for-convert-coroutines-to-complete.patch [bz#1451849] +- kvm-target-ppc-Show-POWER9-in-cpu-help.patch +- Resolves: bz#1434706 + ([pci-bridge] Hotplug devices to pci-bridge failed) +- Resolves: bz#1441394 + (fw_cfg.dma_enabled value incorrect in pc-i440fx-7.3.0 compat_props) +- Resolves: bz#1449490 + ([q35] guest hang after do migration with virtio-scsi-pci.) +- Resolves: bz#1451849 + (qemu-img convert crashes on error) +- Resolves: bz#1449969 + ([Pegas1.0] POWER9* cpu model is not listed in /usr/libexec/qemu-kvm -cpu ?) + + +* Tue May 23 2017 Miroslav Rezanina - rhev-2.9.0-6.el7 +- kvm-aarch64-Enable-usb-xhci.patch [bz#1446570] +- kvm-scsi-Disable-deprecated-implicit-SCSI-HBA-creation-m.patch [bz#971799] +- kvm-block-vhdx-Make-vhdx_create-always-set-errp.patch [bz#1447551] +- kvm-block-Add-errp-to-b-lk-drv-_truncate.patch [bz#1447551] +- kvm-blockdev-use-drained_begin-end-for-qmp_block_resize.patch [bz#1447551] +- kvm-spapr-Don-t-accidentally-advertise-HTM-support-on-PO.patch [bz#1449007] +- kvm-target-ppc-Allow-workarounds-for-POWER9-DD1.patch [bz#1443289] +- kvm-xhci-relax-link-check.patch [bz#1444003] +- kvm-curl-strengthen-assertion-in-curl_clean_state.patch [bz#1437393] +- kvm-curl-never-invoke-callbacks-with-s-mutex-held.patch [bz#1437393] +- kvm-curl-avoid-recursive-locking-of-BDRVCURLState-mutex.patch [bz#1437393] +- kvm-curl-split-curl_find_state-curl_init_state.patch [bz#1437393] +- kvm-curl-convert-CURLAIOCB-to-byte-values.patch [bz#1437393] +- kvm-curl-convert-readv-to-coroutines.patch [bz#1437393] +- kvm-curl-do-not-do-aio_poll-when-waiting-for-a-free-CURL.patch [bz#1437393] +- kvm-usb-hub-clear-PORT_STAT_SUSPEND-on-wakeup.patch [bz#1447581] +- kvm-migration-setup-bi-directional-I-O-channel-for-exec-.patch [bz#1430620] +- kvm-block-Reuse-bs-as-backing-hd-for-drive-backup-sync-n.patch [bz#1452066] +- kvm-migration-Fix-non-multiple-of-page-size-migration.patch [bz#1449037] +- kvm-postcopy-Require-RAMBlocks-that-are-whole-pages.patch [bz#1449037] +- kvm-hw-virtio-fix-vhost-user-fails-to-startup-when-MQ.patch [bz#1447592] +- kvm-iommu-Don-t-crash-if-machine-is-not-PC_MACHINE.patch [bz#1451483] +- kvm-migration-Call-blk_resume_after_migration-for-postco.patch [bz#1452148] +- kvm-migration-Unify-block-node-activation-error-handling.patch [bz#1452148] +- kvm-disable-pulseaudio-and-alsa.patch [bz#1452605] +- kvm-block-An-empty-filename-counts-as-no-filename.patch [bz#1452702] +- kvm-block-Do-not-unref-bs-file-on-error-in-BD-s-open.patch [bz#1452752] +- Resolves: bz#1430620 + (TLS encryption migration via exec failed with "TLS handshake failed: The TLS connection was non-properly terminated") +- Resolves: bz#1437393 + (snapshot created base on the image in https server will hang during booting) +- Resolves: bz#1443289 + ([Pegas1.0 04/03 nightly build + 4.10.0-7 kernel] qemu+guest fail to apply POWER9 DD1 workarounds) +- Resolves: bz#1444003 + (USB 3.0 flash drive not accessible on Windows guest) +- Resolves: bz#1446570 + (enable qemu-xhci USB3 controller device model for the aarch64 target) +- Resolves: bz#1447551 + (qemu hang when do block_resize guest disk during crystal running) +- Resolves: bz#1447581 + ([RHEV7.4] [usb-hub] input devices under usb hub don't work on win2016 with xhci) +- Resolves: bz#1447592 + (vhost-user/reply-ack: Wait for ack even if no request sent (one-time requests)) +- Resolves: bz#1449007 + (Pegas 1.0: Booting pegas guest on pegas host (POWER9 DD1) panics with signal 4 at userspace entry) +- Resolves: bz#1449037 + (Dst qemu quit when migrate guest with hugepage and total memory is not a multiple of pagesize) +- Resolves: bz#1451483 + (QEMU crashes with "-machine none -device intel-iommu") +- Resolves: bz#1452066 + (Fix backing image referencing in drive-backup sync=none) +- Resolves: bz#1452148 + (Op blockers don't work after postcopy migration) +- Resolves: bz#1452605 + (disable pulseaudio and alsa support) +- Resolves: bz#1452702 + (qemu-img aborts on empty filenames) +- Resolves: bz#1452752 + (Some block drivers incorrectly close their associated file) +- Resolves: bz#971799 + (qemu should not crash when if=scsi although it's unsupportable device) + +* Tue May 16 2017 Miroslav Rezanina - rhev-2.9.0-5.el7 +- kvm-blockdev-ignore-aio-native-for-empty-drives.patch [bz#1402645] +- kvm-dump-Acquire-BQL-around-vm_start-in-dump-thread.patch [bz#1445174] +- kvm-Downstream-Don-t-disable-SMT-on-POWER9-hosts.patch [bz#1450724] +- kvm-aio-add-missing-aio_notify-to-aio_enable_external.patch [bz#1446498] +- kvm-Update-configuration-for-qemu-2.9.patch [bz#1400962] +- Resolves: bz#1400962 + (Verify configuration coverage for rebased qemu-kvm-rhev) +- Resolves: bz#1402645 + (Required cache.direct=on when set aio=native) +- Resolves: bz#1445174 + ([RHEV7.4] [guest memory dump]dump-guest-memory QMP command with "detach" param makes qemu-kvm process aborted) +- Resolves: bz#1446498 + (Guest freeze after live snapshot with data-plane) +- Resolves: bz#1450724 + ([Pegas 1.0] qemu package scripts should not disable host multi-threading for POWER9) + +* Fri May 12 2017 Miroslav Rezanina - rhev-2.9.0-4.el7 +- kvm-Reenable-Educational-device.patch [bz#1414694] +- kvm-usb-xhci-Fix-PCI-capability-order.patch [bz#1447874] +- kvm-block-vxhs.c-Add-support-for-a-new-block-device-type.patch [bz#1265869] +- kvm-block-vxhs.c-Add-qemu-iotests-for-new-block-device-t.patch [bz#1265869] +- kvm-qemu-iotests-exclude-vxhs-from-image-creation-via-pr.patch [bz#1265869] +- kvm-block-vxhs-modularize-VXHS-via-g_module.patch [bz#1265869] +- kvm-Remove-the-dependencies-to-seavgabios-bin-and-ipxe-r.patch [bz#1449939] +- Resolves: bz#1265869 + (RFE: Veritas HyperScale VxHS block device support (qemu-kvm-rhev)) +- Resolves: bz#1414694 + (Reenable edu device for kvm-unit-tests support) +- Resolves: bz#1447874 + (Migration failed from rhel7.2.z->rhel7.4 with "-M rhel7.0.0" and "-device nec-usb-xhci") +- Resolves: bz#1449939 + (Remove dependency on seavgabios-bin and ipxe-roms-qemu for qemu-kvm-rhev on s390x) + +* Fri May 05 2017 Miroslav Rezanina - rhev-2.9.0-3.el7 +- kvm-x86-machine-compat-2.9-stragglers.patch [bz#1435756] +- kvm-block-add-bdrv_set_read_only-helper-function.patch [bz#1189998] +- kvm-block-do-not-set-BDS-read_only-if-copy_on_read-enabl.patch [bz#1189998] +- kvm-block-honor-BDRV_O_ALLOW_RDWR-when-clearing-bs-read_.patch [bz#1189998] +- kvm-block-code-movement.patch [bz#1189998] +- kvm-block-introduce-bdrv_can_set_read_only.patch [bz#1189998] +- kvm-block-use-bdrv_can_set_read_only-during-reopen.patch [bz#1189998] +- kvm-block-rbd-update-variable-names-to-more-apt-names.patch [bz#1189998] +- kvm-block-rbd-Add-support-for-reopen.patch [bz#1189998] +- kvm-replication-Make-disable-replication-compile-again.patch [bz#1422846] +- kvm-Disable-replication-feature.patch [bz#1422846] +- Resolves: bz#1189998 + (Active commit does not support on rbd based disk) +- Resolves: bz#1422846 + (Disable replication feature) +- Resolves: bz#1435756 + (Backport device/machtype compat settings from v2.8.0..v2.9.0 final) + +* Fri Apr 28 2017 Miroslav Rezanina - rhev-2.9.0-2.el7 +- kvm-Disable-unimplemented-device.patch [bz#1443029] +- kvm-Disable-serial-isa-for-ppc64.patch [bz#1443029] +- kvm-Disable-rs6000-mc-device.patch [bz#1443029] +- kvm-ppc64le-Remove-isabus-bridge-device.patch [bz#1443029] +- kvm-hmp-gpa2hva-and-gpa2hpa-hostaddr-command.patch [bz#1432295] +- kvm-memory-add-section-range-info-for-IOMMU-notifier.patch [bz#1335808] +- kvm-memory-provide-IOMMU_NOTIFIER_FOREACH-macro.patch [bz#1335808] +- kvm-memory-provide-iommu_replay_all.patch [bz#1335808] +- kvm-memory-introduce-memory_region_notify_one.patch [bz#1335808] +- kvm-memory-add-MemoryRegionIOMMUOps.replay-callback.patch [bz#1335808] +- kvm-intel_iommu-use-the-correct-memory-region-for-device.patch [bz#1335808] +- kvm-intel_iommu-provide-its-own-replay-callback.patch [bz#1335808] +- kvm-intel_iommu-allow-dynamic-switch-of-IOMMU-region.patch [bz#1335808] +- kvm-intel_iommu-enable-remote-IOTLB.patch [bz#1335808] +- kvm-virtio-rng-stop-virtqueue-while-the-CPU-is-stopped.patch [bz#1435521] +- kvm-target-ppc-kvm-make-use-of-KVM_CREATE_SPAPR_TCE_64.patch [bz#1440619] +- kvm-spapr-Add-ibm-processor-radix-AP-encodings-to-the-de.patch [bz#1368786] +- kvm-target-ppc-support-KVM_CAP_PPC_MMU_RADIX-KVM_CAP_PPC.patch [bz#1368786] +- kvm-target-ppc-Add-new-H-CALL-shells-for-in-memory-table.patch [bz#1368786] +- kvm-target-ppc-Implement-H_REGISTER_PROCESS_TABLE-H_CALL.patch [bz#1368786] +- kvm-spapr-move-spapr_populate_pa_features.patch [bz#1368786] +- kvm-spapr-Enable-ISA-3.0-MMU-mode-selection-via-CAS.patch [bz#1368786] +- kvm-spapr-Workaround-for-broken-radix-guests.patch [bz#1368786] +- Resolves: bz#1335808 + ([RFE] [vIOMMU] Add Support for VFIO devices with vIOMMU present) +- Resolves: bz#1368786 + ([Pegas1.0 FEAT] POWER9 guest - qemu - base enablement) +- Resolves: bz#1432295 + (Add gpa2hpa command to qemu hmp) +- Resolves: bz#1435521 + (Migration failed with postcopy enabled from rhel7.3.z host to rhel7.4 host "error while loading state for instance 0x0 of device 'pci) +- Resolves: bz#1440619 + (Reboot guest will induce error message - KVM: Failed to create TCE table for liobn 0x80000001) +- Resolves: bz#1443029 + (Disable new devices in qemu 2.9) + +* Fri Apr 21 2017 Miroslav Rezanina - rhev-2.9.0-1.el7 +- Rebase to QEMU 2.9.0 + +* Wed Mar 08 2017 Miroslav Rezanina - rhev-2.8.0-6.el7 +- kvm-virtio-Report-real-progress-in-VQ-aio-poll-handler.patch [bz#1425700] +- kvm-intel-hda-fix-rhel6-compat-property.patch [bz#1425765] +- Resolves: bz#1425700 + (virtio-scsi data plane takes 100% host CPU with polling) +- Resolves: bz#1425765 + (The guest failed to start with ich6 sound when machine type is rhel6.*.0) + +* Mon Feb 20 2017 Miroslav Rezanina - rhev-2.8.0-5.el7 +- kvm-Disable-qemu-register-device.patch [bz#1392328] +- kvm-Disable-vfio-pci-igd-lpc-bridge-device.patch [bz#1392328] +- kvm-Disable-new-virtio-crypto-devices.patch [bz#1392328] +- kvm-Disable-amd-iommu-devices.patch [bz#1392328] +- kvm-Disable-loader-device.patch [bz#1392328] +- kvm-Disable-or-irq-device.patch [bz#1392328] +- kvm-Hide-new-floppy-device.patch [bz#1392328] +- kvm-migcompat-e1000e-Work-around-7.3-msi-intr_state-fiel.patch [bz#1420216] +- kvm-migcompat-rtl8139-Work-around-version-bump.patch [bz#1420195] +- kvm-sync-linux-headers.patch [bz#1391942] +- kvm-kvmclock-reduce-kvmclock-difference-on-migration.patch [bz#1391942] +- kvm-ahci-advertise-HOST_CAP_64.patch [bz#1411105] +- kvm-Disable-devices-for-for-AArch64-QEMU.patch [bz#1422349] +- kvm-hw-arm-virt-Disable-virtio-net-pci-option-ROM-file-l.patch [bz#1337510] +- kvm-vfio-Use-error_setg-when-reporting-max-assigned-devi.patch [bz#1369795] +- kvm-cirrus-fix-patterncopy-checks.patch [bz#1420494] +- kvm-Revert-cirrus-allow-zero-source-pitch-in-pattern-fil.patch [bz#1420494] +- kvm-cirrus-add-blit_is_unsafe-call-to-cirrus_bitblt_cput.patch [bz#1420494] +- kvm-Package-man-page-of-kvm_stat-tool.patch [bz#1417840] +- kvm-Update-configuration-for-2.8.0-release.patch [bz#1400962] +- Resolves: bz#1337510 + (Don't try to use a romfile for virtio-net-pci on aarch64) +- Resolves: bz#1369795 + (QMP should prompt more specific information when hotplug more than 32 vfs to guest) +- Resolves: bz#1391942 + (kvmclock: advance clock by time window between vm_stop and pre_save (backport patch)) +- Resolves: bz#1392328 + (Disable new devices in QEMU 2.8 (x86_64)) +- Resolves: bz#1400962 + (Verify configuration coverage for rebased qemu-kvm-rhev) +- Resolves: bz#1411105 + (Windows Server 2008-32 crashes on startup with q35 if cdrom attached) +- Resolves: bz#1417840 + (Include kvm_stat man page in qemu-kvm-tools package) +- Resolves: bz#1420195 + (Migration from RHEL7.4 -> RHEL7.3.z failed with rtl8139 nic card) +- Resolves: bz#1420216 + (Migration from RHEL7.3.z -> RHEL4 failed with e1000e nic card) +- Resolves: bz#1420494 + (EMBARGOED CVE-2017-2620 qemu-kvm-rhev: Qemu: display: cirrus: potential arbitrary code execution via cirrus_bitblt_cputovideo [rhel-7.4]) +- Resolves: bz#1422349 + (Disable new devices in QEMU 2.8 (aarch64)) + +* Fri Feb 10 2017 Miroslav Rezanina - rhev-2.8.0-4.el7 +- kvm-fix-abort-in-acpi_setup-since-2.8-with-rhel6-machine.patch [bz#1410826] +- kvm-spapr-clock-should-count-only-if-vm-is-running.patch [bz#1264258] +- kvm-display-cirrus-ignore-source-pitch-value-as-needed-i.patch [bz#1418236] +- kvm-cirrus-handle-negative-pitch-in-cirrus_invalidate_re.patch [bz#1418236] +- kvm-cirrus-allow-zero-source-pitch-in-pattern-fill-rops.patch [bz#1418236] +- kvm-cirrus-fix-blit-address-mask-handling.patch [bz#1418236] +- kvm-cirrus-fix-oob-access-issue-CVE-2017-2615.patch [bz#1418236] +- kvm-QMP-Fix-forward-port-of-__com.redhat_drive_add.patch [bz#1418575] +- kvm-QMP-Fix-forward-port-of-__com.redhat_drive_del.patch [bz#1418575] +- kvm-Drop-macro-RFQDN_REDHAT.patch [bz#1418575] +- kvm-HMP-Clean-up-botched-conflict-resolution-in-user-man.patch [bz#1418575] +- kvm-HMP-Fix-user-manual-typo-of-__com.redhat_qxl_screend.patch [bz#1419899] +- kvm-HMP-Fix-documentation-of-__com.redhat.drive_add.patch [bz#1419899] +- kvm-aio-add-flag-to-skip-fds-to-aio_dispatch.patch [bz#1404303] +- kvm-aio-add-AioPollFn-and-io_poll-interface.patch [bz#1404303] +- kvm-aio-add-polling-mode-to-AioContext.patch [bz#1404303] +- kvm-virtio-poll-virtqueues-for-new-buffers.patch [bz#1404303] +- kvm-linux-aio-poll-ring-for-completions.patch [bz#1404303] +- kvm-iothread-add-polling-parameters.patch [bz#1404303] +- kvm-virtio-blk-suppress-virtqueue-kick-during-processing.patch [bz#1404303] +- kvm-virtio-scsi-suppress-virtqueue-kick-during-processin.patch [bz#1404303] +- kvm-aio-add-.io_poll_begin-end-callbacks.patch [bz#1404303] +- kvm-virtio-disable-virtqueue-notifications-during-pollin.patch [bz#1404303] +- kvm-aio-self-tune-polling-time.patch [bz#1404303] +- kvm-iothread-add-poll-grow-and-poll-shrink-parameters.patch [bz#1404303] +- kvm-virtio-disable-notifications-again-after-poll-succee.patch [bz#1404303] +- kvm-aio-posix-honor-is_external-in-AioContext-polling.patch [bz#1404303] +- kvm-iothread-enable-AioContext-polling-by-default.patch [bz#1404303] +- kvm-Disable-usbredir-and-libcacard-for-unsupported-archi.patch [bz#1418166] +- Resolves: bz#1264258 + (Guest's time stops with option clock=vm when guest is paused) +- Resolves: bz#1404303 + (RFE: virtio-blk/scsi polling mode (QEMU)) +- Resolves: bz#1410826 + (rhel6 machine types assert; acpi-build.c:2985: acpi_setup: Assertion `build_state->table_mr != ((void *)0)' failed) +- Resolves: bz#1418166 + (Remove dependencies required by spice on ppc64le) +- Resolves: bz#1418236 + (CVE-2017-2615 qemu-kvm-rhev: Qemu: display: cirrus: oob access while doing bitblt copy backward mode [rhel-7.4]) +- Resolves: bz#1418575 + (Forward port of downstream-only QMP commands is incorrect) +- Resolves: bz#1419899 + (Documentation inaccurate for __com.redhat_qxl_screendump and __com.redhat_drive_add) + +* Fri Feb 03 2017 Miroslav Rezanina - rhev-2.8.0-3.el7 +- kvm-hw-arm-virt-remove-aarch64-rhel-machine-type.patch [bz#1390964] +- kvm-hw-arm-virt-create-virt-rhel7.3.0-machine-type.patch [bz#1390964] +- kvm-hw-arm-virt-create-virt-rhel7.4.0-machine-type.patch [bz#1390964] +- kvm-tools-kvm_stat-Introduce-pid-monitoring.patch [bz#1397697] +- kvm-tools-kvm_stat-Add-comments.patch [bz#1397697] +- kvm-x86-Split-out-options-for-the-head-rhel7-machine-typ.patch [bz#1390737] +- kvm-x86-Create-PC_RHEL7_3_COMPAT-definition.patch [bz#1390737] +- kvm-x86-Define-pc-i440fx-rhel7.4.0.patch [bz#1390737] +- kvm-x86-Define-pc-q35-rhel7.4.0.patch [bz#1390737] +- kvm-x86-Remove-downstream-opteron-rdtscp-override.patch [bz#1390737] +- kvm-pci-mark-ROMs-read-only.patch [bz#1404673] +- kvm-vhost-skip-ROM-sections.patch [bz#1404673] +- kvm-Enable-seccomp-for-ppc64-ppc64le-architecture.patch [bz#1385537] +- kvm-Update-qemu-kvm-package-Summary-and-Description.patch [bz#1378538] +- Resolves: bz#1378538 + (QEMU: update package summary and description) +- Resolves: bz#1385537 + ([V4.1 FEAT] Enable seccomp support in QEMU) +- Resolves: bz#1390737 + (RHEL-7.4 new qemu-kvm-rhev machine type (x86)) +- Resolves: bz#1390964 + (RHEL-7.4 new QEMU machine type (AArch64)) +- Resolves: bz#1397697 + (Backport remaining kvm_stat patches from the kernel to QEMU) +- Resolves: bz#1404673 + ([ppc64le]qemu-kvm-rhev-2.8 upstream package, reset vm when do migration, HMP in src host promp "tcmalloc: large alloc 1073872896 bytes...") + +* Mon Jan 16 2017 Miroslav Rezanina - rhev-2.8.0-2.el7 +- kvm-Revert-kvm_stat-Remove.patch [bz#1389238] +- kvm-Include-kvm_stat-in-qemu-kvm.spec.patch [bz#1389238] +- kvm-tools-kvm_stat-Powerpc-related-fixes.patch [bz#1389238] +- kvm-compat-define-HW_COMPAT_RHEL7_3.patch [bz#1390734] +- kvm-spapr-define-pseries-rhel7.4.0-machine-type.patch [bz#1390734] +- kvm-config-Remove-EHCI-from-ppc64-builds.patch [bz#1410674] +- kvm-Fix-unuseds-Fedora-build.patch [bz#1410758] +- Resolves: bz#1389238 + (Re-enable kvm_stat script) +- Resolves: bz#1390734 + (ppc64: pseries-rhel7.4.0 machine type) +- Resolves: bz#1410674 + (qemu: Remove unnecessary EHCI implementation for Power) +- Resolves: bz#1410758 + (Make 7.4 qemu-kvm-rhev build on fedora25) + +* Tue Jan 10 2017 Miroslav Rezanina - rhev-2.8.0-1.el7 +- Rebase to QEMU 2.8.0 [bz#1387600] +- Resolves: bz#1387600 + (Rebase qemu-kvm-rhev to 2.8.0) + +* Tue Sep 27 2016 Miroslav Rezanina - rhev-2.6.0-28.el7 +- kvm-ARM-ACPI-fix-the-AML-ID-format-for-CPU-devices.patch [bz#1373733] +- Resolves: bz#1373733 + (failed to run a guest VM with >= 12 vcpu under ACPI mode) + +* Fri Sep 23 2016 Miroslav Rezanina - rhev-2.6.0-27.el7 +- kvm-char-fix-waiting-for-TLS-and-telnet-connection.patch [bz#1300773] +- kvm-target-i386-introduce-kvm_put_one_msr.patch [bz#1377920] +- kvm-apic-set-APIC-base-as-part-of-kvm_apic_put.patch [bz#1377920] +- Resolves: bz#1300773 + (RFE: add support for native TLS encryption on chardev TCP transports) +- Resolves: bz#1377920 + (Guest fails reboot and causes kernel-panic) + +* Tue Sep 20 2016 Miroslav Rezanina - rhev-2.6.0-26.el7 +- kvm-target-i386-Add-more-Intel-AVX-512-instructions-supp.patch [bz#1372455] +- kvm-iothread-Stop-threads-before-main-quits.patch [bz#1343021] +- kvm-virtio-pci-error-out-when-both-legacy-and-modern-mod.patch [bz#1370005] +- kvm-virtio-bus-Plug-devices-after-features-are-negotiate.patch [bz#1370005] +- kvm-virtio-pci-reduce-modern_mem_bar-size.patch [bz#1365613] +- kvm-virtio-vga-adapt-to-page-per-vq-off.patch [bz#1365613] +- kvm-virtio-gpu-pci-tag-as-not-hotpluggable.patch [bz#1368032] +- kvm-scsi-disk-Cleaning-up-around-tray-open-state.patch [bz#1374251] +- kvm-virtio-scsi-Don-t-abort-when-media-is-ejected.patch [bz#1374251] +- kvm-io-remove-mistaken-call-to-object_ref-on-QTask.patch [bz#1375677] +- kvm-block-Invalidate-all-children.patch [bz#1355927] +- kvm-block-Drop-superfluous-invalidating-bs-file-from-dri.patch [bz#1355927] +- kvm-block-Inactivate-all-children.patch [bz#1355927] +- kvm-vfio-pci-Fix-regression-in-MSI-routing-configuration.patch [bz#1373802] +- kvm-x86-lapic-Load-LAPIC-state-at-post_load.patch [bz#1363998] +- kvm-blockdev-ignore-cache-options-for-empty-CDROM-drives.patch [bz#1342999] +- kvm-block-reintroduce-bdrv_flush_all.patch [bz#1338638] +- kvm-qemu-use-bdrv_flush_all-for-vm_stop-et-al.patch [bz#1338638] +- Resolves: bz#1338638 + (Migration fails after ejecting the cdrom in the guest) +- Resolves: bz#1342999 + ('cache=x' cannot work with empty cdrom) +- Resolves: bz#1343021 + (Core dump when quit from HMP after migration finished) +- Resolves: bz#1355927 + (qemu SIGABRT when doing inactive blockcommit with external system checkpoint snapshot) +- Resolves: bz#1363998 + (Live migration via a compressed file causes the guest desktop to freeze) +- Resolves: bz#1365613 + ([PCI] The default MMIO range reserved by firmware for PCI bridges is not enough to hotplug virtio-1 devices) +- Resolves: bz#1368032 + (kernel crash after hot remove virtio-gpu device) +- Resolves: bz#1370005 + (Fail to get network device info(eth0) in guest with virtio-net-pci/vhostforce) +- Resolves: bz#1372455 + ([Intel 7.3 Bug] SKL-SP Guest cpu doesn't support avx512 instruction sets(avx512bw, avx512dq and avx512vl)(qemu-kvm-rhev)) +- Resolves: bz#1373802 + (Network can't recover when trigger EEH one time) +- Resolves: bz#1374251 + (qemu-kvm-rhev core dumped when enabling virtio-scsi "data plane" and executing "eject") +- Resolves: bz#1375677 + (Crash when performing VNC websockets handshake) + +* Tue Sep 13 2016 Miroslav Rezanina - rhev-2.6.0-25.el7 +- kvm-virtio-recalculate-vq-inuse-after-migration.patch [bz#1372763] +- kvm-virtio-decrement-vq-inuse-in-virtqueue_discard.patch [bz#1372763] +- kvm-virtio-balloon-discard-virtqueue-element-on-reset.patch [bz#1370703] +- kvm-virtio-zero-vq-inuse-in-virtio_reset.patch [bz#1370703 bz#1374623] +- Resolves: bz#1370703 + ([Balloon] Whql Job "Commom scenario stress with IO" failed on 2008-32/64) +- Resolves: bz#1372763 + (RHSA-2016-1756 breaks migration of instances) +- Resolves: bz#1374623 + (RHSA-2016-1756 breaks migration of instances) + +* Fri Sep 09 2016 Miroslav Rezanina - rhev-2.6.0-24.el7 +- kvm-Fix-configure-test-for-PBKDF2-in-nettle.patch [bz#1301019] +- kvm-redhat-switch-from-gcrypt-to-nettle-for-crypto.patch [bz#1301019] +- kvm-crypto-assert-that-qcrypto_hash_digest_len-is-in-ran.patch [bz#1301019] +- kvm-crypto-fix-handling-of-iv-generator-hash-defaults.patch [bz#1301019] +- kvm-crypto-ensure-XTS-is-only-used-with-ciphers-with-16-.patch [bz#1301019] +- kvm-vhost-user-test-Use-libqos-instead-of-pxe-virtio.rom.patch [bz#1371211] +- kvm-vl-Delay-initialization-of-memory-backends.patch [bz#1371211] +- kvm-spapr-implement-H_CHANGE_LOGICAL_LAN_MAC-h_call.patch [bz#1371419] +- Resolves: bz#1301019 + (RFE: add support for LUKS disk encryption format driver w/ RBD, iSCSI, and qcow2) +- Resolves: bz#1371211 + (Qemu 2.6 won't boot guest with 2 meg hugepages) +- Resolves: bz#1371419 + ([ppc64le] Can't modify mac address for spapr-vlan device in rhel6.8 guest) + +* Tue Sep 06 2016 Miroslav Rezanina - rhev-2.6.0-23.el7 +- kvm-vhost-user-disconnect-on-HUP.patch [bz#1355902] +- kvm-vhost-don-t-assume-opaque-is-a-fd-use-backend-cleanu.patch [bz#1355902] +- kvm-vhost-make-vhost_log_put-idempotent.patch [bz#1355902] +- kvm-vhost-assert-the-log-was-cleaned-up.patch [bz#1355902] +- kvm-vhost-fix-cleanup-on-not-fully-initialized-device.patch [bz#1355902] +- kvm-vhost-make-vhost_dev_cleanup-idempotent.patch [bz#1355902] +- kvm-vhost-net-always-call-vhost_dev_cleanup-on-failure.patch [bz#1355902] +- kvm-vhost-fix-calling-vhost_dev_cleanup-after-vhost_dev_.patch [bz#1355902] +- kvm-vhost-do-not-assert-on-vhost_ops-failure.patch [bz#1355902] +- kvm-vhost-add-missing-VHOST_OPS_DEBUG.patch [bz#1355902] +- kvm-vhost-use-error_report-instead-of-fprintf-stderr.patch [bz#1355902] +- kvm-qemu-char-fix-qemu_chr_fe_set_msgfds-crash-when-disc.patch [bz#1355902] +- kvm-vhost-user-call-set_msgfds-unconditionally.patch [bz#1355902] +- kvm-vhost-user-check-qemu_chr_fe_set_msgfds-return-value.patch [bz#1355902] +- kvm-vhost-user-check-vhost_user_-read-write-return-value.patch [bz#1355902] +- kvm-vhost-user-keep-vhost_net-after-a-disconnection.patch [bz#1355902] +- kvm-vhost-user-add-get_vhost_net-assertions.patch [bz#1355902] +- kvm-Revert-vhost-net-do-not-crash-if-backend-is-not-pres.patch [bz#1355902] +- kvm-vhost-net-vhost_migration_done-is-vhost-user-specifi.patch [bz#1355902] +- kvm-vhost-add-assert-to-check-runtime-behaviour.patch [bz#1355902] +- kvm-char-add-chr_wait_connected-callback.patch [bz#1355902] +- kvm-char-add-and-use-tcp_chr_wait_connected.patch [bz#1355902] +- kvm-vhost-user-wait-until-backend-init-is-completed.patch [bz#1355902] +- kvm-vhost-user-add-error-report-in-vhost_user_write.patch [bz#1355902] +- kvm-vhost-add-vhost_net_set_backend.patch [bz#1355902] +- kvm-vhost-do-not-update-last-avail-idx-on-get_vring_base.patch [bz#1355902] +- kvm-vhost-check-for-vhost_ops-before-using.patch [bz#1355902] +- kvm-vhost-user-Introduce-a-new-protocol-feature-REPLY_AC.patch [bz#1355902] +- kvm-linux-aio-Handle-io_submit-failure-gracefully.patch [bz#1285928] +- kvm-Revert-acpi-pc-add-fw_cfg-device-node-to-dsdt.patch [bz#1368153] +- Resolves: bz#1285928 + (linux-aio aborts on io_submit() failure) +- Resolves: bz#1355902 + (vhost-user reconnect misc fixes and improvements) +- Resolves: bz#1368153 + (Please hide fw_cfg device in windows guest in order to make svvp test pass) + +* Mon Aug 22 2016 Miroslav Rezanina - rhev-2.6.0-22.el7 +- kvm-target-i386-kvm-Report-kvm_pv_unhalt-as-unsupported-.patch [bz#1363679] +- kvm-ioapic-keep-RO-bits-for-IOAPIC-entry.patch [bz#1358653] +- kvm-ioapic-clear-remote-irr-bit-for-edge-triggered-inter.patch [bz#1358653] +- kvm-x86-iommu-introduce-parent-class.patch [bz#1358653] +- kvm-intel_iommu-rename-VTD_PCI_DEVFN_MAX-to-x86-iommu.patch [bz#1358653] +- kvm-x86-iommu-provide-x86_iommu_get_default.patch [bz#1358653] +- kvm-x86-iommu-introduce-intremap-property.patch [bz#1358653] +- kvm-acpi-enable-INTR-for-DMAR-report-structure.patch [bz#1358653] +- kvm-intel_iommu-allow-queued-invalidation-for-IR.patch [bz#1358653] +- kvm-intel_iommu-set-IR-bit-for-ECAP-register.patch [bz#1358653] +- kvm-acpi-add-DMAR-scope-definition-for-root-IOAPIC.patch [bz#1358653] +- kvm-intel_iommu-define-interrupt-remap-table-addr-regist.patch [bz#1358653] +- kvm-intel_iommu-handle-interrupt-remap-enable.patch [bz#1358653] +- kvm-intel_iommu-define-several-structs-for-IOMMU-IR.patch [bz#1358653] +- kvm-intel_iommu-add-IR-translation-faults-defines.patch [bz#1358653] +- kvm-intel_iommu-Add-support-for-PCI-MSI-remap.patch [bz#1358653] +- kvm-intel_iommu-get-rid-of-0-initializers.patch [bz#1358653] +- kvm-q35-ioapic-add-support-for-emulated-IOAPIC-IR.patch [bz#1358653] +- kvm-ioapic-introduce-ioapic_entry_parse-helper.patch [bz#1358653] +- kvm-intel_iommu-add-support-for-split-irqchip.patch [bz#1358653] +- kvm-x86-iommu-introduce-IEC-notifiers.patch [bz#1358653] +- kvm-ioapic-register-IOMMU-IEC-notifier-for-ioapic.patch [bz#1358653] +- kvm-intel_iommu-Add-support-for-Extended-Interrupt-Mode.patch [bz#1358653] +- kvm-intel_iommu-add-SID-validation-for-IR.patch [bz#1358653] +- kvm-irqchip-simplify-kvm_irqchip_add_msi_route.patch [bz#1358653] +- kvm-irqchip-i386-add-hook-for-add-remove-virq.patch [bz#1358653] +- kvm-irqchip-x86-add-msi-route-notify-fn.patch [bz#1358653] +- kvm-irqchip-do-explicit-commit-when-update-irq.patch [bz#1358653] +- kvm-intel_iommu-support-all-masks-in-interrupt-entry-cac.patch [bz#1358653] +- kvm-all-add-trace-events-for-kvm-irqchip-ops.patch [bz#1358653] +- kvm-intel_iommu-disallow-kernel-irqchip-on-with-IR.patch [bz#1358653] +- kvm-intel_iommu-avoid-unnamed-fields.patch [bz#1358653] +- kvm-irqchip-only-commit-route-when-irqchip-is-used.patch [bz#1358653] +- kvm-x86-ioapic-ignore-level-irq-during-processing.patch [bz#1358653] +- kvm-x86-ioapic-add-support-for-explicit-EOI.patch [bz#1358653] +- kvm-memory-Fix-IOMMU-replay-base-address.patch [bz#1364035] +- kvm-Add-luks-to-block-driver-whitelist.patch [bz#1301019] +- Resolves: bz#1301019 + (RFE: add support for LUKS disk encryption format driver w/ RBD, iSCSI, and qcow2) +- Resolves: bz#1358653 + ([RFE] Interrupt remapping support for Intel vIOMMUs) +- Resolves: bz#1363679 + (RHEL guest hangs with kernel-irqchip=off and smp>1) +- Resolves: bz#1364035 + ([ppc64le][VFIO]Qemu complains:vfio_dma_map(0x10033d3a980, 0x1f34f0000, 0x10000, 0x3fff9a6d0000) = -6 (No such device or address)) + +* Tue Aug 16 2016 Miroslav Rezanina - rhev-2.6.0-21.el7 +- kvm-fix-qemu-exit-on-memory-hotplug-when-allocation-fail.patch [bz#1351409] +- kvm-spapr-remove-extra-type-variable.patch [bz#1363812] +- kvm-ppc-Introduce-a-function-to-look-up-CPU-alias-string.patch [bz#1363812] +- kvm-hw-ppc-spapr-Look-up-CPU-alias-names-instead-of-hard.patch [bz#1363812] +- kvm-ppc-kvm-Do-not-mess-up-the-generic-CPU-family-regist.patch [bz#1363812] +- kvm-ppc-kvm-Register-also-a-generic-spapr-CPU-core-famil.patch [bz#1363812] +- kvm-ppc64-fix-compressed-dump-with-pseries-kernel.patch [bz#1240497] +- kvm-monitor-fix-crash-when-leaving-qemu-with-spice-audio.patch [bz#1355704] +- kvm-audio-clean-up-before-monitor-clean-up.patch [bz#1355704] +- kvm-vnc-don-t-crash-getting-server-info-if-lsock-is-NULL.patch [bz#1359655] +- kvm-vnc-fix-crash-when-vnc_server_info_get-has-an-error.patch [bz#1359655] +- kvm-vnc-ensure-connection-sharing-limits-is-always-confi.patch [bz#1359655] +- kvm-vnc-make-sure-we-finish-disconnect.patch [bz#1352799] +- kvm-virtio-net-allow-increasing-rx-queue-size.patch [bz#1358962] +- kvm-input-add-trace-events-for-full-queues.patch [bz#1366471] +- kvm-virtio-set-low-features-early-on-load.patch [bz#1365747] +- kvm-Revert-virtio-net-unbreak-self-announcement-and-gues.patch [bz#1365747] +- Resolves: bz#1240497 + (qemu-kvm-rhev: dump-guest-memory creates invalid header with format kdump-{zlib,lzo,snappy} on ppc64) +- Resolves: bz#1351409 + (When hotplug memory, guest will shutdown as Insufficient free host memory pages available to allocate) +- Resolves: bz#1352799 + (Client information from hmp doesn't vanish after client disconnect when using vnc display) +- Resolves: bz#1355704 + (spice: core dump when 'quit') +- Resolves: bz#1358962 + (Increase the queue size to the max allowed, 1024.) +- Resolves: bz#1359655 + (Qemu crashes when connecting to a guest started with "-vnc none" by virt-viewer) +- Resolves: bz#1363812 + (qemu-kvm-rhev: -cpu POWER8 no longer works) +- Resolves: bz#1365747 + (Migrate guest(win10) after hot plug/unplug memory balloon device [Missing section footer for 0000:00:07.0/virtio-net]) +- Resolves: bz#1366471 + (QEMU prints "usb-kbd: warning: key event queue full" when pressing keys during SLOF boot) + +* Wed Aug 10 2016 Miroslav Rezanina - rhev-2.6.0-20.el7 +- kvm-block-gluster-rename-server-volname-image-host-volum.patch [bz#1247933] +- kvm-block-gluster-code-cleanup.patch [bz#1247933] +- kvm-block-gluster-deprecate-rdma-support.patch [bz#1247933] +- kvm-block-gluster-using-new-qapi-schema.patch [bz#1247933] +- kvm-block-gluster-add-support-for-multiple-gluster-serve.patch [bz#1247933] +- kvm-block-gluster-fix-doc-in-the-qapi-schema-and-member-.patch [bz#1247933] +- kvm-throttle-Don-t-allow-burst-limits-to-be-lower-than-t.patch [bz#1355665] +- kvm-throttle-Test-burst-limits-lower-than-the-normal-lim.patch [bz#1355665] +- kvm-spapr-Error-out-when-CPU-hotplug-is-attempted-on-old.patch [bz#1362019] +- kvm-spapr-Correctly-set-query_hotpluggable_cpus-hook-bas.patch [bz#1362019] +- Resolves: bz#1247933 + (RFE: qemu-kvm-rhev: support multiple volume hosts for gluster volumes) +- Resolves: bz#1355665 + (Suggest to limit the burst value to be not less than the throttle value) +- Resolves: bz#1362019 + (Crashes when using query-hotpluggable-cpus with pseries-rhel7.2.0 machine type) + +* Fri Aug 05 2016 Miroslav Rezanina - rhev-2.6.0-19.el7 +- kvm-hw-pcie-root-port-Fix-PCIe-root-port-initialization.patch [bz#1323976] +- kvm-hw-pxb-declare-pxb-devices-as-not-hot-pluggable.patch [bz#1323976] +- kvm-hw-acpi-fix-a-DSDT-table-issue-when-a-pxb-is-present.patch [bz#1323976] +- kvm-acpi-refactor-pxb-crs-computation.patch [bz#1323976] +- kvm-hw-apci-handle-64-bit-MMIO-regions-correctly.patch [bz#1323976] +- kvm-target-i386-Move-TCG-initialization-check-to-tcg_x86.patch [bz#1087672] +- kvm-target-i386-Move-TCG-initialization-to-realize-time.patch [bz#1087672] +- kvm-target-i386-Call-cpu_exec_init-on-realize.patch [bz#1087672] +- kvm-tests-acpi-report-names-of-expected-files-in-verbose.patch [bz#1087672] +- kvm-acpi-add-aml_debug.patch [bz#1087672] +- kvm-acpi-add-aml_refof.patch [bz#1087672] +- kvm-pc-acpi-remove-AML-for-empty-not-used-GPE-handlers.patch [bz#1087672] +- kvm-pc-acpi-consolidate-CPU-hotplug-AML.patch [bz#1087672] +- kvm-pc-acpi-consolidate-GPE._E02-with-the-rest-of-CPU-ho.patch [bz#1087672] +- kvm-pc-acpi-cpu-hotplug-make-AML-CPU_foo-defines-local-t.patch [bz#1087672] +- kvm-pc-acpi-mark-current-CPU-hotplug-functions-as-legacy.patch [bz#1087672] +- kvm-pc-acpi-consolidate-legacy-CPU-hotplug-in-one-file.patch [bz#1087672] +- kvm-pc-acpi-simplify-build_legacy_cpu_hotplug_aml-signat.patch [bz#1087672] +- kvm-pc-acpi-cpuhp-legacy-switch-ProcessorID-to-possible_.patch [bz#1087672] +- kvm-acpi-extend-ACPI-interface-to-provide-send_event-hoo.patch [bz#1087672] +- kvm-pc-use-AcpiDeviceIfClass.send_event-to-issue-GPE-eve.patch [bz#1087672] +- kvm-target-i386-Remove-xlevel-hv-spinlocks-option-fixups.patch [bz#1087672] +- kvm-target-i386-Move-features-logic-that-requires-CPUSta.patch [bz#1087672] +- kvm-target-i386-Remove-assert-kvm_enabled-from-host_x86_.patch [bz#1087672] +- kvm-target-i386-Move-xcc-kvm_required-check-to-realize-t.patch [bz#1087672] +- kvm-target-i386-Use-cpu_generic_init-in-cpu_x86_init.patch [bz#1087672] +- kvm-target-i386-Consolidate-calls-of-object_property_par.patch [bz#1087672] +- kvm-docs-update-ACPI-CPU-hotplug-spec-with-new-protocol.patch [bz#1087672] +- kvm-pc-piix4-ich9-add-cpu-hotplug-legacy-property.patch [bz#1087672] +- kvm-acpi-cpuhp-add-CPU-devices-AML-with-_STA-method.patch [bz#1087672] +- kvm-pc-acpi-introduce-AcpiDeviceIfClass.madt_cpu-hook.patch [bz#1087672] +- kvm-acpi-cpuhp-implement-hot-add-parts-of-CPU-hotplug-in.patch [bz#1087672] +- kvm-acpi-cpuhp-implement-hot-remove-parts-of-CPU-hotplug.patch [bz#1087672] +- kvm-acpi-cpuhp-add-cpu._OST-handling.patch [bz#1087672] +- kvm-pc-use-new-CPU-hotplug-interface-since-2.7-machine-t.patch [bz#1087672] +- kvm-pc-acpi-drop-intermediate-PCMachineState.node_cpu.patch [bz#1087672] +- kvm-qmp-fix-spapr-example-of-query-hotpluggable-cpus.patch [bz#1087672] +- kvm-qdev-Don-t-stop-applying-globals-on-first-error.patch [bz#1087672] +- kvm-qdev-Eliminate-qemu_add_globals-function.patch [bz#1087672] +- kvm-qdev-Use-GList-for-global-properties.patch [bz#1087672] +- kvm-qdev-GlobalProperty.errp-field.patch [bz#1087672] +- kvm-vl-Simplify-global-property-registration.patch [bz#1087672] +- kvm-machine-add-properties-to-compat_props-incrementaly.patch [bz#1087672] +- kvm-machine-Add-machine_register_compat_props-function.patch [bz#1087672] +- kvm-vl-Set-errp-to-error_abort-on-machine-compat_props.patch [bz#1087672] +- kvm-target-sparc-Use-sparc_cpu_parse_features-directly.patch [bz#1087672] +- kvm-target-i386-Avoid-using-locals-outside-their-scope.patch [bz#1087672] +- kvm-cpu-Use-CPUClass-parse_features-as-convertor-to-glob.patch [bz#1087672] +- kvm-arm-virt-Parse-cpu_model-only-once.patch [bz#1087672] +- kvm-cpu-make-cpu-qom.h-only-include-able-from-cpu.h.patch [bz#1087672] +- kvm-target-i386-make-cpu-qom.h-not-target-specific.patch [bz#1087672] +- kvm-target-Don-t-redefine-cpu_exec.patch [bz#1087672] +- kvm-pc-Parse-CPU-features-only-once.patch [bz#1087672] +- kvm-target-i386-Use-uint32_t-for-X86CPU.apic_id.patch [bz#1087672] +- kvm-pc-Add-x86_topo_ids_from_apicid.patch [bz#1087672] +- kvm-pc-Extract-CPU-lookup-into-a-separate-function.patch [bz#1087672] +- kvm-pc-cpu-Consolidate-apic-id-validity-checks-in-pc_cpu.patch [bz#1087672] +- kvm-target-i386-Replace-custom-apic-id-setter-getter-wit.patch [bz#1087672] +- kvm-target-i386-Add-socket-core-thread-properties-to-X86.patch [bz#1087672] +- kvm-target-i386-cpu-Do-not-ignore-error-and-fix-apic-par.patch [bz#1087672] +- kvm-target-i386-Fix-apic-object-leak-when-CPU-is-deleted.patch [bz#1087672] +- kvm-pc-Set-APIC-ID-based-on-socket-core-thread-ids-if-it.patch [bz#1087672] +- kvm-pc-Delay-setting-number-of-boot-CPUs-to-machine_done.patch [bz#1087672] +- kvm-pc-Register-created-initial-and-hotpluged-CPUs-in-on.patch [bz#1087672] +- kvm-pc-Forbid-BSP-removal.patch [bz#1087672] +- kvm-pc-Enforce-adding-CPUs-contiguously-and-removing-the.patch [bz#1087672] +- kvm-pc-cpu-Allow-device_add-to-be-used-with-x86-cpu.patch [bz#1087672] +- kvm-pc-Implement-query-hotpluggable-cpus-callback.patch [bz#1087672] +- kvm-apic-move-MAX_APICS-check-to-apic-class.patch [bz#1087672] +- kvm-apic-Drop-APICCommonState.idx-and-use-APIC-ID-as-ind.patch [bz#1087672] +- kvm-apic-kvm-apic-Fix-crash-due-to-access-to-freed-memor.patch [bz#1087672] +- kvm-apic-Add-unrealize-callbacks.patch [bz#1087672] +- kvm-apic-Use-apic_id-as-apic-s-migration-instance_id.patch [bz#1087672] +- kvm-target-i386-Add-x86_cpu_unrealizefn.patch [bz#1087672] +- kvm-pc-Make-device_del-CPU-work-for-x86-CPUs.patch [bz#1087672] +- kvm-exec-Reduce-CONFIG_USER_ONLY-ifdeffenery.patch [bz#1087672] +- kvm-exec-Don-t-use-cpu_index-to-detect-if-cpu_exec_init-.patch [bz#1087672] +- kvm-exec-Set-cpu_index-only-if-it-s-not-been-explictly-s.patch [bz#1087672] +- kvm-qdev-Fix-object-reference-leak-in-case-device.realiz.patch [bz#1087672] +- kvm-pc-Init-CPUState-cpu_index-with-index-in-possible_cp.patch [bz#1087672] +- kvm-Revert-pc-Enforce-adding-CPUs-contiguously-and-remov.patch [bz#1087672] +- kvm-qdev-ignore-GlobalProperty.errp-for-hotplugged-devic.patch [bz#1087672] +- kvm-vl-exit-if-a-bad-property-value-is-passed-to-global.patch [bz#1087672] +- kvm-apic-fix-broken-migration-for-kvm-apic.patch [bz#1087672] +- kvm-RHEL-only-hw-char-pl011-fix-SBSA-reset.patch [bz#1266048] +- kvm-migration-regain-control-of-images-when-migration-fa.patch [bz#1361539] +- kvm-migration-Promote-improved-autoconverge-commands-out.patch [bz#1358141] +- kvm-spapr-Ensure-CPU-cores-are-added-contiguously-and-re.patch [bz#1361443] +- kvm-spapr-disintricate-core-id-from-DT-semantics.patch [bz#1361443] +- kvm-spapr-init-CPUState-cpu_index-with-index-relative-to.patch [bz#1361443] +- kvm-Revert-spapr-Ensure-CPU-cores-are-added-contiguously.patch [bz#1361443] +- kvm-spapr-Prevent-boot-CPU-core-removal.patch [bz#1361443] +- kvm-virtio-vga-propagate-on-gpu-realized-error.patch [bz#1360664] +- kvm-hw-virtio-pci-fix-virtio-behaviour.patch [bz#1360664] +- kvm-q35-disable-s3-s4-by-default.patch [bz#1357202] +- kvm-pcie-fix-link-active-status-bit-migration.patch [bz#1352860] +- kvm-pc-rhel-7.2-pcie-fix-link-active-status-bit-migratio.patch [bz#1352860] +- kvm-add-e1000e-ipxe-rom-symlink.patch [bz#1343092] +- kvm-e1000e-add-boot-rom.patch [bz#1343092] +- Resolves: bz#1087672 + ([Fujitsu 7.2 FEAT]: qemu vcpu hot-remove support) +- Resolves: bz#1266048 + (login prompt does not work inside KVM guest when keys are pressed while the kernel is booting) +- Resolves: bz#1323976 + (PCI: Add 64-bit MMIO support to PXB devices) +- Resolves: bz#1343092 + (RFE: Integrate e1000e implementation in downstream QEMU) +- Resolves: bz#1352860 + (Migration is failed from host RHEL7.2.z to host RHEL7.3 with "-M pc-i440fx-rhel7.0.0 -device nec-usb-xhci") +- Resolves: bz#1357202 + ([Q35] S3 should be disabled by default for the pc-q35-rhel7.3.0 machine type) +- Resolves: bz#1358141 + (Removal of the "x-" prefix for dynamic cpu throttling) +- Resolves: bz#1360664 + ([virtio] Update default virtio-1 behavior for virtio devices) +- Resolves: bz#1361443 + (ppc64le: Introduce stable cpu_index for cpu hotplugging) +- Resolves: bz#1361539 + (block/io.c:1342: bdrv_co_do_pwritev: Assertion `!(bs->open_flags & 0x0800)' failed on failed migrate) + +* Tue Aug 02 2016 Miroslav Rezanina - rhev-2.6.0-18.el7 +- kvm-pci-fix-unaligned-access-in-pci_xxx_quad.patch [bz#1343092] +- kvm-msix-make-msix_clr_pending-visible-for-clients.patch [bz#1343092] +- kvm-pci-Introduce-define-for-PM-capability-version-1.1.patch [bz#1343092] +- kvm-pcie-Add-support-for-PCIe-CAP-v1.patch [bz#1343092] +- kvm-pcie-Introduce-function-for-DSN-capability-creation.patch [bz#1343092] +- kvm-vmxnet3-Use-generic-function-for-DSN-capability-defi.patch [bz#1343092] +- kvm-net-Introduce-Toeplitz-hash-calculator.patch [bz#1343092] +- kvm-net-Add-macros-for-MAC-address-tracing.patch [bz#1343092] +- kvm-vmxnet3-Use-common-MAC-address-tracing-macros.patch [bz#1343092] +- kvm-net_pkt-Name-vmxnet3-packet-abstractions-more-generi.patch [bz#1343092] +- kvm-rtl8139-Move-more-TCP-definitions-to-common-header.patch [bz#1343092] +- kvm-net_pkt-Extend-packet-abstraction-as-required-by-e10.patch [bz#1343092] +- kvm-vmxnet3-Use-pci_dma_-API-instead-of-cpu_physical_mem.patch [bz#1343092] +- kvm-e1000_regs-Add-definitions-for-Intel-82574-specific-.patch [bz#1343092] +- kvm-e1000-Move-out-code-that-will-be-reused-in-e1000e.patch [bz#1343092] +- kvm-net-Introduce-e1000e-device-emulation.patch [bz#1343092] +- kvm-e1000e-Fix-build-with-gcc-4.6.3-and-ust-tracing.patch [bz#1343092] +- kvm-pci-fix-pci_requester_id.patch [bz#1350196] +- kvm-hw-pci-delay-bus_master_enable_region-initialization.patch [bz#1350196] +- kvm-q35-allow-dynamic-sysbus.patch [bz#1350196] +- kvm-q35-rhel-allow-dynamic-sysbus.patch [bz#1350196] +- kvm-hw-iommu-enable-iommu-with-device.patch [bz#1350196] +- kvm-machine-remove-iommu-property.patch [bz#1350196] +- kvm-rhel-Revert-unwanted-inconsequential-changes-to-ivsh.patch [bz#1333318] +- kvm-rhel-Disable-ivshmem-plain-migration-ivshmem-doorbel.patch [bz#1333318] +- kvm-nvdimm-fix-memory-leak-in-error-code-path.patch [bz#1361205] +- kvm-i8257-Set-no-user-flag.patch [bz#1337457] +- kvm-bitops-Add-MAKE_64BIT_MASK-macro.patch [bz#1339196] +- kvm-target-i386-Provide-TCG_PHYS_ADDR_BITS.patch [bz#1339196] +- kvm-target-i386-Allow-physical-address-bits-to-be-set.patch [bz#1339196] +- kvm-target-i386-Mask-mtrr-mask-based-on-CPU-physical-add.patch [bz#1339196] +- kvm-target-i386-Fill-high-bits-of-mtrr-mask.patch [bz#1339196] +- kvm-target-i386-Set-physical-address-bits-based-on-host.patch [bz#1339196] +- kvm-target-i386-Enable-host-phys-bits-on-RHEL.patch [bz#1339196] +- kvm-pc-Fix-rhel6.3.0-compat_props-setting.patch [bz#1362264] +- Resolves: bz#1333318 + (ivshmem-plain support in RHEL 7.3) +- Resolves: bz#1337457 + (enable i8257 device) +- Resolves: bz#1339196 + (qemu-kvm (on target host) killed by SIGABRT when migrating a guest from AMD host to Intel host.) +- Resolves: bz#1343092 + (RFE: Integrate e1000e implementation in downstream QEMU) +- Resolves: bz#1350196 + (Enable IOMMU device with -device intel-iommu) +- Resolves: bz#1361205 + (nvdimm: fix memory leak in error code path) +- Resolves: bz#1362264 + (rhel6.3.0 machine-type using wrong compat_props list) + +* Fri Jul 29 2016 Miroslav Rezanina - rhev-2.6.0-17.el7 +- kvm-Disable-mptsas1068-device.patch [bz#1333282] +- kvm-Disable-sd-card.patch [bz#1333282] +- kvm-Disable-rocker-device.patch [bz#1333282] +- kvm-Disable-new-ipmi-devices.patch [bz#1333282] +- kvm-Disable-hyperv-testdev.patch [bz#1333282] +- kvm-Disable-allwiner_ahci-device.patch [bz#1333282] +- kvm-Disable-igd-passthrough-i440FX.patch [bz#1333282] +- kvm-Disable-vfio-platform-device.patch [bz#1333282] +- kvm-tap-vhost-busy-polling-support.patch [bz#1345715 bz#1353791] +- kvm-vl-change-runstate-only-if-new-state-is-different-fr.patch [bz#1355982] +- kvm-virtio-error-out-if-guest-exceeds-virtqueue-size.patch [bz#1359733] +- kvm-migration-set-state-to-post-migrate-on-failure.patch [bz#1355683] +- kvm-block-drop-support-for-using-qcow-2-encryption-with-.patch [bz#1336659] +- kvm-json-streamer-Don-t-leak-tokens-on-incomplete-parse.patch [bz#1360612] +- kvm-json-streamer-fix-double-free-on-exiting-during-a-pa.patch [bz#1360612] +- kvm-Add-dump-guest-memory.py-to-all-archs.patch [bz#1360225] +- Resolves: bz#1333282 + (Disable new devices in QEMU 2.6) +- Resolves: bz#1336659 + (Core dump when re-launch guest with encrypted block device) +- Resolves: bz#1345715 + (Busy polling support for vhost net in qemu) +- Resolves: bz#1353791 + (Busy polling support for vhost) +- Resolves: bz#1355683 + (qemu core dump when do postcopy migration again after canceling a migration in postcopy phase) +- Resolves: bz#1355982 + (qemu will abort after type two"system_reset" after the guest poweroff) +- Resolves: bz#1359733 + (CVE-2016-5403 qemu-kvm-rhev: Qemu: virtio: unbounded memory allocation on host via guest leading to DoS [rhel-7.3]) +- Resolves: bz#1360225 + (Can't extract guest memory dump from qemu core) +- Resolves: bz#1360612 + (Memory leak on incomplete JSON parse) + +* Tue Jul 26 2016 Miroslav Rezanina - rhev-2.6.0-16.el7 +- kvm-exec-Remove-cpu-from-cpus-list-during-cpu_exec_exit.patch [bz#1172917] +- kvm-exec-Do-vmstate-unregistration-from-cpu_exec_exit.patch [bz#1172917] +- kvm-cpu-Reclaim-vCPU-objects.patch [bz#1172917] +- kvm-cpu-Add-a-sync-version-of-cpu_remove.patch [bz#1172917] +- kvm-qdev-hotplug-Introduce-HotplugHandler.pre_plug-callb.patch [bz#1172917] +- kvm-cpu-Abstract-CPU-core-type.patch [bz#1172917] +- kvm-xics-xics_kvm-Handle-CPU-unplug-correctly.patch [bz#1172917] +- kvm-spapr_drc-Prevent-detach-racing-against-attach-for-C.patch [bz#1172917] +- kvm-qom-API-to-get-instance_size-of-a-type.patch [bz#1172917] +- kvm-spapr-Abstract-CPU-core-device-and-type-specific-cor.patch [bz#1172917] +- kvm-spapr-Move-spapr_cpu_init-to-spapr_cpu_core.c.patch [bz#1172917] +- kvm-spapr-convert-boot-CPUs-into-CPU-core-devices.patch [bz#1172917] +- kvm-spapr-CPU-hotplug-support.patch [bz#1172917] +- kvm-spapr-CPU-hot-unplug-support.patch [bz#1172917] +- kvm-QMP-Add-query-hotpluggable-cpus.patch [bz#1172917] +- kvm-hmp-Add-info-hotpluggable-cpus-HMP-command.patch [bz#1172917] +- kvm-spapr-implement-query-hotpluggable-cpus-callback.patch [bz#1172917] +- kvm-qapi-Report-support-for-device-cpu-hotplug-in-query-.patch [bz#1172917] +- kvm-qapi-keep-names-in-CpuInstanceProperties-in-sync-wit.patch [bz#1172917] +- kvm-spapr-fix-write-past-end-of-array-error-in-cpu-core-.patch [bz#1172917] +- kvm-spapr-Restore-support-for-older-PowerPC-CPU-cores.patch [bz#1172917] +- kvm-spapr-Restore-support-for-970MP-and-POWER8NVL-CPU-co.patch [bz#1172917] +- kvm-spapr-drop-reference-on-child-object-during-core-rea.patch [bz#1172917] +- kvm-spapr-do-proper-error-propagation-in-spapr_cpu_core_.patch [bz#1172917] +- kvm-spapr-drop-duplicate-variable-in-spapr_core_release.patch [bz#1172917] +- kvm-spapr-Ensure-thread0-of-CPU-core-is-always-realized-.patch [bz#1172917] +- kvm-spapr-fix-core-unplug-crash.patch [bz#1172917] +- kvm-usbredir-add-streams-property.patch [bz#1353180] +- kvm-usbredir-turn-off-streams-for-rhel7.2-older.patch [bz#1353180] +- kvm-net-fix-qemu_announce_self-not-emitting-packets.patch [bz#1343433] +- kvm-Fix-crash-bug-in-rebase-of__com.redhat_drive_add.patch [bz#1352865] +- kvm-ppc-Yet-another-fix-for-the-huge-page-support-detect.patch [bz#1347498] +- kvm-ppc-Huge-page-detection-mechanism-fixes-Episode-III.patch [bz#1347498] +- kvm-hw-ppc-spapr-Make-sure-to-close-the-htab_fd-when-mig.patch [bz#1354341] +- Resolves: bz#1172917 + (add support for CPU hotplugging (qemu-kvm-rhev)) +- Resolves: bz#1343433 + (migration: announce_self fix) +- Resolves: bz#1347498 + ([ppc64le] Guest can't boot up with hugepage memdev) +- Resolves: bz#1352865 + (Boot guest with two virtio-scsi-pci devices and spice, QEMU core dump after executing '(qemu)__com.redhat_drive_add') +- Resolves: bz#1353180 + (7.3->7.2 migration: qemu-kvm: usbredirparser: error unserialize caps mismatch) +- Resolves: bz#1354341 + (guest hang after cancel migration then migrate again) + +* Fri Jul 22 2016 Miroslav Rezanina - rhev-2.6.0-15.el7 +- kvm-spapr_pci-Use-correct-DMA-LIOBN-when-composing-the-d.patch [bz#1213667] +- kvm-spapr_iommu-Finish-renaming-vfio_accel-to-need_vfio.patch [bz#1213667] +- kvm-spapr_iommu-Move-table-allocation-to-helpers.patch [bz#1213667] +- kvm-vmstate-Define-VARRAY-with-VMS_ALLOC.patch [bz#1213667] +- kvm-spapr_iommu-Introduce-enabled-state-for-TCE-table.patch [bz#1213667] +- kvm-spapr_iommu-Migrate-full-state.patch [bz#1213667] +- kvm-spapr_iommu-Add-root-memory-region.patch [bz#1213667] +- kvm-spapr_pci-Reset-DMA-config-on-PHB-reset.patch [bz#1213667] +- kvm-spapr_pci-Add-and-export-DMA-resetting-helper.patch [bz#1213667] +- kvm-memory-Add-reporting-of-supported-page-sizes.patch [bz#1213667] +- kvm-spapr-ensure-device-trees-are-always-associated-with.patch [bz#1213667] +- kvm-spapr_iommu-Realloc-guest-visible-TCE-table-when-sta.patch [bz#1213667] +- kvm-vfio-spapr-Add-DMA-memory-preregistering-SPAPR-IOMMU.patch [bz#1213667] +- kvm-vfio-Add-host-side-DMA-window-capabilities.patch [bz#1213667] +- kvm-vfio-spapr-Create-DMA-window-dynamically-SPAPR-IOMMU.patch [bz#1213667] +- kvm-spapr_pci-spapr_pci_vfio-Support-Dynamic-DMA-Windows.patch [bz#1213667] +- kvm-qemu-sockets-use-qapi_free_SocketAddress-in-cleanup.patch [bz#1354090] +- kvm-tap-use-an-exit-notifier-to-call-down_script.patch [bz#1354090] +- kvm-slirp-use-exit-notifier-for-slirp_smb_cleanup.patch [bz#1354090] +- kvm-net-do-not-use-atexit-for-cleanup.patch [bz#1354090] +- kvm-virtio-mmio-format-transport-base-address-in-BusClas.patch [bz#1356815] +- kvm-vfio-pci-Hide-ARI-capability.patch [bz#1356376] +- kvm-qxl-factor-out-qxl_get_check_slot_offset.patch [bz#1235732] +- kvm-qxl-store-memory-region-and-offset-instead-of-pointe.patch [bz#1235732] +- kvm-qxl-fix-surface-migration.patch [bz#1235732] +- kvm-qxl-fix-qxl_set_dirty-call-in-qxl_dirty_one_surface.patch [bz#1235732] +- kvm-Add-install-dependency-required-for-usb-streams.patch [bz#1354443] +- Resolves: bz#1213667 + (Dynamic DMA Windows for VFIO on Power (qemu component)) +- Resolves: bz#1235732 + (spice-gtk shows outdated screen state after migration [qemu-kvm-rhev]) +- Resolves: bz#1354090 + (Boot guest with vhostuser server mode, QEMU prompt 'Segmentation fault' after executing '(qemu)system_powerdown') +- Resolves: bz#1354443 + (/usr/libexec/qemu-kvm: undefined symbol: libusb_free_ss_endpoint_companion_descriptor) +- Resolves: bz#1356376 + ([Q35] Nic which passthrough from host didn't be found in guest when enable multifunction) +- Resolves: bz#1356815 + (AArch64: backport virtio-mmio dev pathname fix) + +* Tue Jul 19 2016 Miroslav Rezanina - rhev-2.6.0-14.el7 +- kvm-add-vgabios-virtio.bin-symlink.patch [bz#1347402] +- kvm-usb-enable-streams-support.patch [bz#1033733] +- kvm-hw-arm-virt-kill-7.2-machine-type.patch [bz#1356814] +- kvm-blockdev-Fix-regression-with-the-default-naming-of-t.patch [bz#1353801] +- kvm-qemu-iotests-Test-naming-of-throttling-groups.patch [bz#1353801] +- kvm-target-i386-Show-host-and-VM-TSC-frequencies-on-mism.patch [bz#1351442] +- Resolves: bz#1033733 + (RFE: add support for USB-3 bulk streams - qemu-kvm) +- Resolves: bz#1347402 + (vgabios-virtio.bin should be symlinked in qemu-kvm-rhev) +- Resolves: bz#1351442 + ("TSC frequency mismatch" warning message after migration) +- Resolves: bz#1353801 + (The default io throttling group name is null, which makes all throttled disks with a default group name in the same group) +- Resolves: bz#1356814 + (AArch64: remove non-released 7.2 machine type) + +* Tue Jul 12 2016 Miroslav Rezanina - rhev-2.6.0-13.el7 +- kvm-block-gluster-add-support-for-selecting-debug-loggin.patch [bz#1320714] +- kvm-Revert-static-checker-e1000-82540em-got-aliased-to-e.patch [bz#1353070] +- kvm-Revert-e1000-use-alias-for-default-model.patch [bz#1353070] +- kvm-7.x-compat-e1000-82540em.patch [bz#1353070] +- kvm-target-i386-add-Skylake-Client-cpu-model.patch [bz#1327589] +- kvm-scsi-generic-Merge-block-max-xfer-len-in-INQUIRY-res.patch [bz#1353816] +- kvm-raw-posix-Fetch-max-sectors-for-host-block-device.patch [bz#1353816] +- kvm-scsi-Advertise-limits-by-blocksize-not-512.patch [bz#1353816] +- kvm-mirror-clarify-mirror_do_read-return-code.patch [bz#1336705] +- kvm-mirror-limit-niov-to-IOV_MAX-elements-again.patch [bz#1336705] +- kvm-iotests-add-small-granularity-mirror-test.patch [bz#1336705] +- Resolves: bz#1320714 + ([RFE] Allow the libgfapi logging level to be controlled.) +- Resolves: bz#1327589 + (Add Skylake CPU model) +- Resolves: bz#1336705 + (Drive mirror with option granularity fail) +- Resolves: bz#1353070 + (Migration is failed from host RHEL7.2.z to host RHEL7.3 with "-M rhel6.6.0 -device e1000-82540em") +- Resolves: bz#1353816 + (expose host BLKSECTGET limit in scsi-block (qemu-kvm-rhev)) + +* Fri Jul 08 2016 Miroslav Rezanina - rhev-2.6.0-12.el7 +- kvm-Fix-crash-with-__com.redhat_drive_del.patch [bz#1341531] +- kvm-hw-arm-virt-fix-limit-of-64-bit-ACPI-ECAM-PCI-MMIO-r.patch [bz#1349337] +- kvm-Increase-locked-memory-limit-for-all-users-not-just-.patch [bz#1350735] +- kvm-target-i386-Remove-SSE4a-from-qemu64-CPU-model.patch [bz#1318386 bz#1321139 bz#1321139] +- kvm-target-i386-Remove-ABM-from-qemu64-CPU-model.patch [bz#1318386 bz#1321139 bz#1321139] +- kvm-pc-Recover-PC_RHEL7_1_COMPAT-from-RHEL-7.2-code.patch [bz#1318386 bz#1318386 bz#1321139] +- kvm-pc-Include-missing-PC_COMPAT_2_3-entries-in-PC_RHEL7.patch [bz#1318386 bz#1318386 bz#1321139] +- kvm-vhost-user-disable-chardev-handlers-on-close.patch [bz#1347077] +- kvm-char-clean-up-remaining-chardevs-when-leaving.patch [bz#1347077] +- kvm-socket-add-listen-feature.patch [bz#1347077] +- kvm-socket-unlink-unix-socket-on-remove.patch [bz#1347077] +- kvm-char-do-not-use-atexit-cleanup-handler.patch [bz#1347077] +- kvm-vfio-add-pcie-extended-capability-support.patch [bz#1346688] +- kvm-vfio-pci-Hide-SR-IOV-capability.patch [bz#1346688] +- kvm-memory-Add-MemoryRegionIOMMUOps.notify_started-stopp.patch [bz#1346920] +- kvm-intel_iommu-Throw-hw_error-on-notify_started.patch [bz#1346920] +- Resolves: bz#1318386 + (pc-rhel7.2.0 machine type definition needs some fixes) +- Resolves: bz#1321139 + (qemu-kvm-rhev prints warnings in the default CPU+machine-type configuration.) +- Resolves: bz#1341531 + (qemu gets SIGSEGV when hot-plug a scsi hostdev device with duplicate target address) +- Resolves: bz#1346688 + ([Q35] vfio read-only SR-IOV capability confuses OVMF) +- Resolves: bz#1346920 + (vIOMMU: prevent unsupported configurations with vfio) +- Resolves: bz#1347077 + (vhost-user: A socket file is not deleted after VM's port is detached.) +- Resolves: bz#1349337 + (hw/arm/virt: fix limit of 64-bit ACPI/ECAM PCI MMIO range) +- Resolves: bz#1350735 + (memory locking limit for regular users is too low to launch guests through libvirt) + +* Fri Jul 01 2016 Miroslav Rezanina - rhev-2.6.0-11.el7 +- kvm-Postcopy-Avoid-0-length-discards.patch [bz#1347256] +- kvm-Migration-Split-out-ram-part-of-qmp_query_migrate.patch [bz#1347256] +- kvm-Postcopy-Add-stats-on-page-requests.patch [bz#1347256] +- kvm-test-Postcopy.patch [bz#1347256] +- kvm-tests-fix-libqtest-socket-timeouts.patch [bz#1347256] +- kvm-Postcopy-Check-for-support-when-setting-the-capabili.patch [bz#1347256] +- kvm-rbd-change-error_setg-to-error_setg_errno.patch [bz#1329641] +- kvm-ppc-Disable-huge-page-support-if-it-is-not-available.patch [bz#1347498] +- kvm-acpi-do-not-use-TARGET_PAGE_SIZE.patch [bz#1270345] +- kvm-acpi-convert-linker-from-GArray-to-BIOSLinker-struct.patch [bz#1270345] +- kvm-acpi-simplify-bios_linker-API-by-removing-redundant-.patch [bz#1270345] +- kvm-acpi-cleanup-bios_linker_loader_cleanup.patch [bz#1270345] +- kvm-tpm-apci-cleanup-TCPA-table-initialization.patch [bz#1270345] +- kvm-acpi-make-bios_linker_loader_add_pointer-API-offset-.patch [bz#1270345] +- kvm-acpi-make-bios_linker_loader_add_checksum-API-offset.patch [bz#1270345] +- kvm-pc-dimm-get-memory-region-from-get_memory_region.patch [bz#1270345] +- kvm-pc-dimm-introduce-realize-callback.patch [bz#1270345] +- kvm-pc-dimm-introduce-get_vmstate_memory_region-callback.patch [bz#1270345] +- kvm-nvdimm-support-nvdimm-label.patch [bz#1270345] +- kvm-acpi-add-aml_object_type.patch [bz#1270345] +- kvm-acpi-add-aml_call5.patch [bz#1270345] +- kvm-nvdimm-acpi-set-HDLE-properly.patch [bz#1270345] +- kvm-nvdimm-acpi-save-arg3-of-_DSM-method.patch [bz#1270345] +- kvm-nvdimm-acpi-check-UUID.patch [bz#1270345] +- kvm-nvdimm-acpi-abstract-the-operations-for-root-nvdimm-.patch [bz#1270345] +- kvm-nvdimm-acpi-check-revision.patch [bz#1270345] +- kvm-nvdimm-acpi-support-Get-Namespace-Label-Size-functio.patch [bz#1270345] +- kvm-nvdimm-acpi-support-Get-Namespace-Label-Data-functio.patch [bz#1270345] +- kvm-nvdimm-acpi-support-Set-Namespace-Label-Data-functio.patch [bz#1270345] +- kvm-docs-add-NVDIMM-ACPI-documentation.patch [bz#1270345] +- kvm-Fix-qemu-kvm-does-not-quit-when-booting-guest-w-241-.patch [bz#1126666] +- kvm-Adjust-locked-memory-limits-to-allow-unprivileged-VM.patch [bz#1350735] +- kvm-dma-helpers-dma_blk_io-cancel-support.patch [bz#1346237] +- Resolves: bz#1126666 + (qemu-kvm does not quit when booting guest w/ 161 vcpus and "-no-kvm") +- Resolves: bz#1270345 + ([Intel 7.3 FEAT] Virtualization support for NVDIMM - qemu support) +- Resolves: bz#1329641 + ([RFE]Ceph/RBD block driver for qemu-kvm : change error_setg() to error_setg_errno()) +- Resolves: bz#1346237 + (win 10.x86_64 guest coredump when execute avocado test case: win_virtio_update.install_driver) +- Resolves: bz#1347256 + (Backport 2.7 postcopy fix, test and stats) +- Resolves: bz#1347498 + ([ppc64le] Guest can't boot up with hugepage memdev) +- Resolves: bz#1350735 + (memory locking limit for regular users is too low to launch guests through libvirt) + +* Tue Jun 28 2016 Miroslav Rezanina - rhev-2.6.0-10.el7 +- kvm-block-clarify-error-message-for-qmp-eject.patch [bz#961589] +- kvm-blockdev-clean-up-error-handling-in-do_open_tray.patch [bz#961589] +- kvm-blockdev-clarify-error-on-attempt-to-open-locked-tra.patch [bz#961589] +- kvm-blockdev-backup-Use-bdrv_lookup_bs-on-target.patch [bz#1336310 bz#1339498] +- kvm-blockdev-backup-Don-t-move-target-AioContext-if-it-s.patch [bz#1336310 bz#1339498] +- kvm-virtio-blk-Remove-op-blocker-for-dataplane.patch [bz#1336310 bz#1339498] +- kvm-virtio-scsi-Remove-op-blocker-for-dataplane.patch [bz#1336310 bz#1339498] +- kvm-spec-add-a-sample-kvm.conf-to-enable-Nested-Virtuali.patch [bz#1290150] +- Resolves: bz#1290150 + (Include example kvm.conf with nested options commented out) +- Resolves: bz#1336310 + (virtio-scsi data-plane does not support block management QMP commands) +- Resolves: bz#1339498 + (Core dump when do 'block-job-complete' after 'drive-mirror') +- Resolves: bz#961589 + (rhel7 guest sometimes didnt unlock the cdrom when qemu-kvm trying to eject) + +* Thu Jun 23 2016 Miroslav Rezanina - rhev-2.6.0-9.el7 +- kvm-7.2-machine-type-compatibility.patch [bz#1344269] +- kvm-vhost-user-add-ability-to-know-vhost-user-backend-di.patch [bz#1322087] +- kvm-tests-vhost-user-bridge-add-client-mode.patch [bz#1322087] +- kvm-tests-vhost-user-bridge-workaround-stale-vring-base.patch [bz#1322087] +- kvm-qemu-char-add-qemu_chr_disconnect-to-close-a-fd-acce.patch [bz#1322087] +- kvm-vhost-user-disconnect-on-start-failure.patch [bz#1322087] +- kvm-vhost-net-do-not-crash-if-backend-is-not-present.patch [bz#1322087] +- kvm-vhost-net-save-restore-vhost-user-acked-features.patch [bz#1322087] +- kvm-vhost-net-save-restore-vring-enable-state.patch [bz#1322087] +- kvm-tests-append-i386-tests.patch [bz#1322087] +- kvm-test-start-vhost-user-reconnect-test.patch [bz#1322087] +- kvm-block-Prevent-sleeping-jobs-from-resuming-if-they-ha.patch [bz#1265179] +- kvm-blockjob-move-iostatus-reset-out-of-block_job_enter.patch [bz#1265179] +- kvm-blockjob-rename-block_job_is_paused.patch [bz#1265179] +- kvm-blockjob-add-pause-points.patch [bz#1265179] +- kvm-blockjob-add-block_job_get_aio_context.patch [bz#1265179] +- kvm-block-use-safe-iteration-over-AioContext-notifiers.patch [bz#1265179] +- kvm-blockjob-add-AioContext-attached-callback.patch [bz#1265179] +- kvm-mirror-follow-AioContext-change-gracefully.patch [bz#1265179] +- kvm-backup-follow-AioContext-change-gracefully.patch [bz#1265179] +- kvm-block-Fix-snapshot-on-with-aio-native.patch [bz#1336649] +- kvm-block-iscsi-avoid-potential-overflow-of-acb-task-cdb.patch [bz#1340930] +- kvm-block-fixed-BdrvTrackedRequest-filling-in-bdrv_co_di.patch [bz#1348763] +- kvm-block-fix-race-in-bdrv_co_discard-with-drive-mirror.patch [bz#1348763] +- kvm-block-process-before_write_notifiers-in-bdrv_co_disc.patch [bz#1348763] +- Resolves: bz#1265179 + (With dataplane, when migrate to remote NBD disk after drive-mirror, qemu core dump ( both src host and des host)) +- Resolves: bz#1322087 + (No recovery after vhost-user process restart) +- Resolves: bz#1336649 + ([RHEL.7.3] Guest will not boot up when specify aio=native and snapshot=on together) +- Resolves: bz#1340930 + (CVE-2016-5126 qemu-kvm-rhev: Qemu: block: iscsi: buffer overflow in iscsi_aio_ioctl [rhel-7.3]) +- Resolves: bz#1344269 + (Migration: Fixup machine types and HW_COMPAT (stage 2a)) +- Resolves: bz#1348763 + (Fix dirty marking with block discard requests) + +* Tue Jun 21 2016 Miroslav Rezanina - rhev-2.6.0-8.el7 +- kvm-Disable-Windows-enlightnements.patch [bz#1336517] +- kvm-ppc-spapr-Refactor-h_client_architecture_support-CPU.patch [bz#1341492] +- kvm-ppc-Split-pcr_mask-settings-into-supported-bits-and-.patch [bz#1341492] +- kvm-ppc-Provide-function-to-get-CPU-class-of-the-host-CP.patch [bz#1341492] +- kvm-ppc-Improve-PCR-bit-selection-in-ppc_set_compat.patch [bz#1341492] +- kvm-ppc-Add-PowerISA-2.07-compatibility-mode.patch [bz#1341492] +- kvm-machine-types-fix-pc_machine_-_options-chain.patch [bz#1344320] +- kvm-Fix-rhel6-rom-file.patch [bz#1344320] +- kvm-fix-vga-type-for-older-machines.patch [bz#1344320] +- kvm-Revert-aio_notify-force-main-loop-wakeup-with-SIGIO-.patch [bz#1188656] +- kvm-Make-avx2-configure-test-work-with-O2.patch [bz#1323294] +- kvm-avx2-configure-Use-primitives-in-test.patch [bz#1323294] +- kvm-vfio-Fix-broken-EEH.patch [bz#1346627] +- Resolves: bz#1188656 + (lost block IO completion notification (for virtio-scsi disk) hangs main loop) +- Resolves: bz#1323294 + (AVX-2 migration optimisation) +- Resolves: bz#1336517 + (Disable hv-vpindex, hv-runtime, hv-reset, hv-synic & hv-stimer enlightenment for Windows) +- Resolves: bz#1341492 + (QEMU on POWER does not support the PowerISA 2.07 compatibility mode) +- Resolves: bz#1344320 + (migration: fix pc_i440fx_*_options chaining) +- Resolves: bz#1346627 + (qemu discards EEH ioctl results) + +* Thu Jun 16 2016 Miroslav Rezanina - rhev-2.6.0-7.el7 +- kvm-pc-allow-raising-low-memory-via-max-ram-below-4g-opt.patch [bz#1176144] +- kvm-vga-add-sr_vbe-register-set.patch [bz#1331415 bz#1346976] +- Resolves: bz#1176144 + ([Nokia RHEL 7.3 Feature]: 32-bit operating systems get very little memory space with new Qemu's) +- Resolves: bz#1331415 + (CVE-2016-3710 qemu-kvm-rhev: qemu: incorrect banked access bounds checking in vga module [rhel-7.3]) +- Resolves: bz#1346976 + (Regression from CVE-2016-3712: windows installer fails to start) +- Resolves: bz#1339467 + (User can not create windows 7 virtual machine in rhevm3.6.5.) + +* Wed Jun 15 2016 Miroslav Rezanina - rhev-2.6.0-6.el7 +- kvm-throttle-refuse-iops-size-without-iops-total-read-wr.patch [bz#1342330] +- kvm-scsi-mark-TYPE_SCSI_DISK_BASE-as-abstract.patch [bz#1338043] +- kvm-scsi-disk-add-missing-break.patch [bz#1338043] +- kvm-Disable-spapr-rng.patch [bz#1343891] +- kvm-spec-Update-rules-before-triggering-for-kvm-device.patch [bz#1338755] +- kvm-spec-Do-not-package-ivshmem-server-and-ivshmem-clien.patch [bz#1320476] +- Resolves: bz#1320476 + (Failed to upgrade qemu-kvm-tools-rhev from 2.3.0 to 2.5.0) +- Resolves: bz#1338043 + (scsi-block fix - receive the right SCSI status on reads and writes) +- Resolves: bz#1338755 + (qemu-kvm-rhev doesn't reload udev rules before triggering for kvm device) +- Resolves: bz#1342330 + (There is no error prompt when set the io throttling parameters iops_size without iops) +- Resolves: bz#1343891 + (Disable spapr-rng device in downstream qemu 2.6) + +* Mon Jun 06 2016 Miroslav Rezanina - rhev-2.6.0-5.el7 +- kvm-spapr-update-RHEL-7.2-machine-type.patch [bz#1316303] +- kvm-migration-fix-HW_COMPAT_RHEL7_2.patch [bz#1316303] +- kvm-target-i386-add-a-generic-x86-nmi-handler.patch [bz#1335720] +- kvm-nmi-remove-x86-specific-nmi-handling.patch [bz#1335720] +- kvm-cpus-call-the-core-nmi-injection-function.patch [bz#1335720] +- kvm-spec-link-sgabios.bin-only-for-x86_64.patch [bz#1337917] +- kvm-Add-PCIe-bridge-devices-for-AArch64.patch [bz#1326420] +- kvm-Remove-unsupported-VFIO-devices-from-QEMU.patch [bz#1326420] +- kvm-hw-net-spapr_llan-Delay-flushing-of-the-RX-queue-whi.patch [bz#1210221] +- kvm-hw-net-spapr_llan-Provide-counter-with-dropped-rx-fr.patch [bz#1210221] +- kvm-iscsi-pass-SCSI-status-back-for-SG_IO.patch [bz#1338043] +- kvm-dma-helpers-change-BlockBackend-to-opaque-value-in-D.patch [bz#1338043] +- kvm-scsi-disk-introduce-a-common-base-class.patch [bz#1338043] +- kvm-scsi-disk-introduce-dma_readv-and-dma_writev.patch [bz#1338043] +- kvm-scsi-disk-add-need_fua_emulation-to-SCSIDiskClass.patch [bz#1338043] +- kvm-scsi-disk-introduce-scsi_disk_req_check_error.patch [bz#1338043] +- kvm-scsi-block-always-use-SG_IO.patch [bz#1338043] +- kvm-tools-kvm_stat-Powerpc-related-fixes.patch [bz#1337033] +- kvm-pc-New-default-pc-i440fx-rhel7.3.0-machine-type.patch [bz#1305121] +- kvm-7.3-mismerge-fix-Fix-ich9-intel-hda-compatibility.patch [bz#1342015] +- kvm-PC-migration-compat-Section-footers-global-state.patch [bz#1342015] +- kvm-fw_cfg-for-7.2-compatibility.patch [bz#1342015] +- kvm-pc-Create-new-pc-q35-rhel7.3.0-machine-type.patch [bz#1342015] +- kvm-q35-Remove-7.0-7.1-7.2-machine-types.patch [bz#1342015] +- Resolves: bz#1210221 + (Netperf UDP_STREAM Lost most of the packets on spapr-vlan device) +- Resolves: bz#1305121 + (rhel7.3.0 machine-types) +- Resolves: bz#1316303 + (Live migration of VMs from RHEL 7.2 <--> 7.3 with pseries-rhel7.2.0 machine type (qemu 2.6)) +- Resolves: bz#1326420 + (AArch64: clean and add devices to fully support aarch64 vm) +- Resolves: bz#1335720 + (watchdog action 'inject-nmi' takes no effect) +- Resolves: bz#1337033 + (kvm_stat AttributeError: 'ArchPPC' object has no attribute 'exit_reasons') +- Resolves: bz#1337917 + (qemu-kvm-rhev: Only ship /usr/share/qemu-kvm/sgabios.bin on x86) +- Resolves: bz#1338043 + (scsi-block fix - receive the right SCSI status on reads and writes) +- Resolves: bz#1342015 + (Migration: Fixup machine types and HW_COMPAT (stage 1b)) + +* Wed May 25 2016 Miroslav Rezanina - rhev-2.6.0-4.el7 +- kvm-pc-Use-right-HW_COMPAT_-macros-at-PC_RHEL7-compat-ma.patch [bz#1318386] +- kvm-compat-Add-missing-any_layout-in-HW_COMPAT_RHEL7_1.patch [bz#1318386] +- kvm-RHEL-Disable-unsupported-PowerPC-CPU-models.patch [bz#1317977] +- kvm-spec-Use-correct-upstream-QEMU-version.patch [bz#1335705] +- Resolves: bz#1317977 + (qemu-kvm-rhev supports a lot of CPU models) +- Resolves: bz#1318386 + (pc-rhel7.2.0 machine type definition needs some fixes) +- Resolves: bz#1335705 + ('QEMU 2.5.94 monitor' is used for qemu-kvm-rhev-2.6.0-1.el7.x86_64) + +* Mon May 23 2016 Miroslav Rezanina - rhev-2.6.0-3.el7 +- kvm-qmp-Report-drive_add-error-to-monitor.patch [bz#1337100] +- kvm-spec-Remove-dependency-to-ipxe-roms-qemu-for-aarch64.patch [bz#1337496] +- Resolves: bz#1337100 + (redhat_drive_add should report error to qmp if it fails to initialize) +- Resolves: bz#1337496 + (qemu-kvm-rhev should not depend on ipxe-roms-qemu on aarch64) + +* Tue May 17 2016 Miroslav Rezanina - rhev-2.6.0-2.el7 +- kvm-Fix-SLOF-dependency.patch [bz#1336296] +- Resolves: bz#1336296 + (failed dependencies on SLOF) + +* Thu May 12 2016 Miroslav Rezanina - rhev-2.6.0-1.el7 +- Rebase to QEMU 2.6.0 [bz#1289417] +- Resolves: bz#1289417 + (Rebase to QEMU 2.6) + +* Wed Oct 14 2015 Miroslav Rezanina - rhev-2.3.0-31.el7 +- kvm-Migration-Generate-the-completed-event-only-when-we-.patch [bz#1271145] +- Resolves: bz#1271145 + (Guest OS paused after migration.) + +* Mon Oct 12 2015 Jeff E. Nelson - rhev-2.3.0-30.el7 +- kvm-memhp-extend-address-auto-assignment-to-support-gaps.patch [bz#1267533] +- kvm-pc-memhp-force-gaps-between-DIMM-s-GPA.patch [bz#1267533] +- kvm-memory-allow-destroying-a-non-empty-MemoryRegion.patch [bz#1264347] +- kvm-hw-do-not-pass-NULL-to-memory_region_init-from-insta.patch [bz#1264347] +- kvm-tests-Fix-how-qom-test-is-run.patch [bz#1264347] +- kvm-libqtest-Clean-up-unused-QTestState-member-sigact_ol.patch [bz#1264347] +- kvm-libqtest-New-hmp-friends.patch [bz#1264347] +- kvm-device-introspect-test-New-covering-device-introspec.patch [bz#1264347] +- kvm-qmp-Fix-device-list-properties-not-to-crash-for-abst.patch [bz#1264347] +- kvm-qdev-Protect-device-list-properties-against-broken-d.patch [bz#1264347] +- kvm-Revert-qdev-Use-qdev_get_device_class-for-device-typ.patch [bz#1264347] +- Resolves: bz#1264347 + (QMP device-list-properties crashes for CPU devices) +- Resolves: bz#1267533 + (qemu quit when rebooting guest which hotplug memory >=13 times) + +* Thu Oct 08 2015 Miroslav Rezanina - rhev-2.3.0-29.el7 +- kvm-vfio-Remove-unneeded-union-from-VFIOContainer.patch [bz#1259556] +- kvm-vfio-Generalize-vfio_listener_region_add-failure-pat.patch [bz#1259556] +- kvm-vfio-Check-guest-IOVA-ranges-against-host-IOMMU-capa.patch [bz#1259556] +- kvm-vfio-Record-host-IOMMU-s-available-IO-page-sizes.patch [bz#1259556] +- kvm-memory-Allow-replay-of-IOMMU-mapping-notifications.patch [bz#1259556] +- kvm-vfio-Allow-hotplug-of-containers-onto-existing-guest.patch [bz#1259556] +- kvm-spapr_pci-Allow-PCI-host-bridge-DMA-window-to-be-con.patch [bz#1259556] +- kvm-spapr_iommu-Rename-vfio_accel-parameter.patch [bz#1259556] +- kvm-spapr_iommu-Provide-a-function-to-switch-a-TCE-table.patch [bz#1259556] +- kvm-spapr_pci-Allow-VFIO-devices-to-work-on-the-normal-P.patch [bz#1259556] +- Resolves: bz#1259556 + (Allow VFIO devices on the same guest PHB as emulated devices) + +* Mon Oct 05 2015 Miroslav Rezanina - rhev-2.3.0-28.el7 +- kvm-rhel-Revert-unwanted-cannot_instantiate_with_device_.patch [bz#1224542] +- kvm-Disable-additional-e1000-models.patch [bz#1224542 bz#1265161] +- kvm-Remove-intel-iommu-device.patch [bz#1224542] +- kvm-virtio-net-unbreak-self-announcement-and-guest-offlo.patch [bz#1262232] +- kvm-block-mirror-fix-full-sync-mode-when-target-does-not.patch [bz#1136382] +- Resolves: bz#1136382 + (block: Mirroring to raw block device doesn't zero out unused blocks) +- Resolves: bz#1224542 + (unsupported devices need to be disabled in qemu-kvm-rhev after rebasing to 2.3.0) +- Resolves: bz#1262232 + (self announcement and ctrl offloads does not work after migration) +- Resolves: bz#1265161 + (Support various e1000 variants) + +* Wed Sep 30 2015 Miroslav Rezanina - rhev-2.3.0-27.el7 +- kvm-sdl2-Fix-RGB555.patch [bz#1247479] +- kvm-spice-surface-switch-fast-path-requires-same-format-.patch [bz#1247479] +- kvm-virtio-blk-only-clear-VIRTIO_F_ANY_LAYOUT-for-legacy.patch [bz#1207687] +- kvm-vhost-enable-vhost-without-without-MSI-X.patch [bz#1207687] +- kvm-vhost-user-Send-VHOST_RESET_OWNER-on-vhost-stop.patch [bz#1207687] +- kvm-virtio-avoid-leading-underscores-for-helpers.patch [bz#1207687] +- kvm-vhost-user-use-VHOST_USER_XXX-macro-for-switch-state.patch [bz#1207687] +- kvm-vhost-user-add-protocol-feature-negotiation.patch [bz#1207687] +- kvm-vhost-rename-VHOST_RESET_OWNER-to-VHOST_RESET_DEVICE.patch [bz#1207687] +- kvm-vhost-user-add-VHOST_USER_GET_QUEUE_NUM-message.patch [bz#1207687] +- kvm-vhost-introduce-vhost_backend_get_vq_index-method.patch [bz#1207687] +- kvm-vhost-user-add-multiple-queue-support.patch [bz#1207687] +- kvm-vhost-user-add-a-new-message-to-disable-enable-a-spe.patch [bz#1207687] +- Resolves: bz#1207687 + ([6wind 7.2 FEAT]: vhost-user does not support multique) +- Resolves: bz#1247479 + (display mess when boot a win2012-r2-64 guest with -vga std) + +* Thu Sep 24 2015 Miroslav Rezanina - rhev-2.3.0-26.el7 +- kvm-qcow2-Make-size_to_clusters-return-uint64_t.patch [bz#1260365] +- kvm-iotests-Add-test-for-checking-large-image-files.patch [bz#1260365] +- Resolves: bz#1260365 + (Guest image created coredump after installation.) + +* Wed Sep 23 2015 Miroslav Rezanina - rhev-2.3.0-25.el7 +- kvm-block-backend-Expose-bdrv_write_zeroes.patch [bz#1256541] +- kvm-qemu-img-convert-Rewrite-copying-logic.patch [bz#1256541] +- kvm-main-loop-fix-qemu_notify_event-for-aio_notify-optim.patch [bz#1256541] +- kvm-error-New-error_fatal.patch [bz#1232308] +- kvm-Fix-bad-error-handling-after-memory_region_init_ram.patch [bz#1232308] +- kvm-loader-Fix-memory_region_init_resizeable_ram-error-h.patch [bz#1232308] +- kvm-memory-Fix-bad-error-handling-in-memory_region_init_.patch [bz#1232308] +- kvm-spapr_pci-encode-class-code-including-Prog-IF-regist.patch [bz#1264845] +- kvm-scripts-dump-guest-memory.py-fix-after-RAMBlock-chan.patch [bz#1234802] +- kvm-spec-Require-proper-version-of-SLOF.patch [bz#1263795] +- Resolves: bz#1232308 + ([abrt] qemu-system-x86: qemu_ram_alloc(): qemu-system-x86_64 killed by SIGABRT) +- Resolves: bz#1234802 + ([RHEL7.2] dump-guest-memory failed because of Python Exception There is no member named length.) +- Resolves: bz#1256541 + (qemu-img hangs forever in aio_poll when used to convert some images) +- Resolves: bz#1263795 + (vfio device can't be hot unplugged on powerpc guest) +- Resolves: bz#1264845 + ([regression] Guest usb mouse/keyboard could not be used on qemu-kvm-rhev-2.3.0-24.el7.ppc64le) + +* Fri Sep 18 2015 Miroslav Rezanina - rhev-2.3.0-24.el7 +- kvm-spapr-Don-t-use-QOM-syntax-for-DR-connectors.patch [bz#1262143] +- kvm-virtio-mmio-ioeventfd-support.patch [bz#1185480] +- kvm-scsi-fix-buffer-overflow-in-scsi_req_parse_cdb-CVE-2.patch [bz#1244334] +- kvm-spapr-Populate-ibm-associativity-lookup-arrays-corre.patch [bz#1262670] +- kvm-ppc-spapr-Fix-buffer-overflow-in-spapr_populate_drco.patch [bz#1262670] +- kvm-spapr_pci-Introduce-a-liobn-number-generating-macros.patch [bz#1263795] +- kvm-spapr_iommu-Make-spapr_tce_find_by_liobn-public.patch [bz#1263795] +- kvm-spapr_pci-Rework-device-tree-rendering.patch [bz#1263795] +- kvm-spapr_pci-enumerate-and-add-PCI-device-tree.patch [bz#1263795] +- kvm-spapr_pci-populate-ibm-loc-code.patch [bz#1263795] +- kvm-tests-remove-irrelevant-assertions-from-test-aio.patch [bz#1211689] +- kvm-aio-posix-move-pollfds-to-thread-local-storage.patch [bz#1211689] +- kvm-aio-Introduce-type-in-aio_set_fd_handler-and-aio_set.patch [bz#1211689] +- kvm-aio-Save-type-to-AioHandler.patch [bz#1211689] +- kvm-aio-posix-Introduce-aio_poll_clients.patch [bz#1211689] +- kvm-block-Mark-fd-handlers-as-protocol.patch [bz#1211689] +- kvm-nbd-Mark-fd-handlers-client-type-as-nbd-server.patch [bz#1211689] +- kvm-aio-Mark-ctx-notifier-s-client-type-as-context.patch [bz#1211689] +- kvm-dataplane-Mark-host-notifiers-client-type-as-datapla.patch [bz#1211689] +- kvm-block-Introduce-bdrv_aio_poll.patch [bz#1211689] +- kvm-block-Replace-nested-aio_poll-with-bdrv_aio_poll.patch [bz#1211689] +- kvm-block-Only-poll-block-layer-fds-in-bdrv_aio_poll.patch [bz#1211689] +- Resolves: bz#1185480 + (backport ioeventfd support for virtio-mmio) +- Resolves: bz#1211689 + (atomic live snapshots are not atomic with dataplane-backed devices) +- Resolves: bz#1244334 + (qemu-kvm-rhev: Qemu: scsi stack buffer overflow [rhel-7.2]) +- Resolves: bz#1262143 + (VM startup is very slow with large amounts of hotpluggable memory) +- Resolves: bz#1262670 + ([PowerKVM]SIGSEGV when boot up guest with -numa node and set up the cpus in one node to the boundary) +- Resolves: bz#1263795 + (vfio device can't be hot unplugged on powerpc guest) + +* Tue Sep 15 2015 Miroslav Rezanina - rhev-2.3.0-23.el7 +- kvm-scsi-disk-Fix-assertion-failure-on-WRITE-SAME.patch [bz#1247042] +- kvm-mirror-Speed-up-bitmap-initial-scanning.patch [bz#1259229] +- kvm-qemu-iotests-Disable-099-requires-blkverify.patch [bz#1257059] +- kvm-spapr-Reduce-advertised-max-LUNs-for-spapr_vscsi.patch [bz#1260464] +- kvm-vnc-Don-t-assert-if-opening-unix-socket-fails.patch [bz#1261263] +- kvm-qcow2-Handle-EAGAIN-returned-from-update_refcount.patch [bz#1254927] +- kvm-pc-memhotplug-fix-incorrectly-set-reserved-memory-en.patch [bz#1261846] +- kvm-pc-memhotplug-keep-reserved-memory-end-broken-on-rhe.patch [bz#1261846] +- Resolves: bz#1247042 + (qemu quit when using sg_write_same command inside RHEL7.2 guest) +- Resolves: bz#1254927 + (qemu-img shows Input/output error when compressing guest image) +- Resolves: bz#1257059 + (qemu-iotests 099 failed for vmdk) +- Resolves: bz#1259229 + (drive-mirror blocks QEMU due to lseek64() on raw image files) +- Resolves: bz#1260464 + (The spapr vscsi disks for lun id '9-31' and channel id '4-7' could not be recognized inside a power pc guest) +- Resolves: bz#1261263 + (qemu crash while start a guest with invalid vnc socket path) +- Resolves: bz#1261846 + (qemu-kvm-rhev: 64-bit PCI bars may overlap hotplugged memory and vice verse) + +* Thu Sep 03 2015 Miroslav Rezanina - rhev-2.3.0-22.el7 +- kvm-mirror-Fix-coroutine-reentrance.patch [bz#1251487] +- kvm-RHEL-Set-vcpus-hard-limit-to-240-for-Power.patch [bz#1257781] +- kvm-provide-vhost-module-config-file-with-max_mem_region.patch [bz#1255349] +- Resolves: bz#1251487 + (qemu core dump when do drive mirror) +- Resolves: bz#1255349 + (vhost: default value of 'max_mem_regions' should be set larger(>=260) than 64) +- Resolves: bz#1257781 + (The prompt is confusing when boot a guest with larger vcpu number than host physical cpu) + +* Fri Aug 28 2015 Miroslav Rezanina - rhev-2.3.0-21.el7 +- kvm-vnc-fix-memory-corruption-CVE-2015-5225.patch [bz#1255898] +- Resolves: bz#1255898 + (CVE-2015-5225 qemu-kvm-rhev: Qemu: ui: vnc: heap memory corruption in vnc_refresh_server_surface [rhel-7.2]) + +* Thu Aug 27 2015 Yash Mankad - rhev-2.3.0-20.el7 +- kvm-pseries-define-coldplugged-devices-as-configured.patch [bz#1243721] +- kvm-spice-fix-spice_chr_add_watch-pre-condition.patch [bz#1128992] +- Resolves: bz#1128992 + (Spiceport character device is not reliable caused domain shutoff) +- Resolves: bz#1243721 + (After hotunpug virtio device, the device still exist in pci info) + +* Mon Aug 24 2015 Miroslav Rezanina - rhev-2.3.0-19.el7 +- kvm-ppc-add-helpful-message-when-KVM-fails-to-start-VCPU.patch [bz#1215618] +- kvm-pci-allow-0-address-for-PCI-IO-MEM-regions.patch [bz#1241886] +- kvm-RHEL-Suppress-scary-but-unimportant-errors-for-KVM-V.patch [bz#1237034] +- Resolves: bz#1215618 + (Unhelpful error message on Power when SMT is enabled) +- Resolves: bz#1237034 + (Error prompt while booting with vfio-pci device) +- Resolves: bz#1241886 + (hot plugged pci devices won't appear unless reboot) + +* Fri Aug 14 2015 Miroslav Rezanina - rhev-2.3.0-18.el7 +- kvm-vhost-correctly-pass-error-to-caller-in-vhost_dev_en.patch [bz#1248312] +- kvm-Revert-virtio-net-enable-virtio-1.0.patch [bz#1248312] +- kvm-virtio-net-unbreak-any-layout.patch [bz#1248312] +- kvm-virtio-hide-legacy-features-from-modern-guests.patch [bz#1248312] +- kvm-virtio-serial-fix-ANY_LAYOUT.patch [bz#1248312] +- kvm-virtio-9p-fix-any_layout.patch [bz#1248312] +- kvm-virtio-set-any_layout-in-virtio-core.patch [bz#1248312] +- kvm-virtio-pci-fix-memory-MR-cleanup-for-modern.patch [bz#1248312] +- kvm-virtio-get_features-can-fail.patch [bz#1248312] +- kvm-virtio-blk-fail-get_features-when-both-scsi-and-1.0-.patch [bz#1248312] +- kvm-virtio-minor-cleanup.patch [bz#1248312] +- kvm-memory-do-not-add-a-reference-to-the-owner-of-aliase.patch [bz#1248312] +- kvm-virtio-net-remove-virtio-queues-if-the-guest-doesn-t.patch [bz#1248312] +- kvm-virtio-fix-1.0-virtqueue-migration.patch [bz#1248312] +- kvm-Downstream-only-Start-kvm-setup-service-before-libvi.patch [bz#1251962] +- kvm-qcow2-Flush-pending-discards-before-allocating-clust.patch [bz#1226297] +- Resolves: bz#1226297 + (qcow2 crash during discard operation) +- Resolves: bz#1248312 + ("fdisk -l"can not output anything and the process status is D+ after migrating RHEL7.2 guest with virtio-1 virtio-scsi disk) +- Resolves: bz#1251962 + (kvm-setup.service should include Before=libvirtd.service) + +* Wed Aug 12 2015 Miroslav Rezanina - rhev-2.3.0-17.el7 +- kvm-migration-avoid-divide-by-zero-in-xbzrle-cache-miss-.patch [bz#580006] +- kvm-migration-move-ram-stuff-to-migration-ram.patch [bz#580006] +- kvm-migration-move-savevm.c-inside-migration.patch [bz#580006] +- kvm-migration-Add-myself-to-the-copyright-list-of-both-f.patch [bz#580006] +- kvm-migration-reduce-include-files.patch [bz#580006] +- kvm-migration-Remove-duplicated-assignment-of-SETUP-stat.patch [bz#580006] +- kvm-migration-create-savevm_state.patch [bz#580006] +- kvm-migration-Use-normal-VMStateDescriptions-for-Subsect.patch [bz#580006] +- kvm-Add-qemu_get_counted_string-to-read-a-string-prefixe.patch [bz#580006] +- kvm-runstate-Add-runstate-store.patch [bz#580006] +- kvm-runstate-migration-allows-more-transitions-now.patch [bz#580006] +- kvm-migration-create-new-section-to-store-global-state.patch [bz#580006] +- kvm-global_state-Make-section-optional.patch [bz#580006] +- kvm-vmstate-Create-optional-sections.patch [bz#580006] +- kvm-migration-Add-configuration-section.patch [bz#580006] +- kvm-migration-ensure-we-start-in-NONE-state.patch [bz#580006] +- kvm-migration-Use-always-helper-to-set-state.patch [bz#580006] +- kvm-migration-No-need-to-call-trace_migrate_set_state.patch [bz#580006] +- kvm-migration-create-migration-event.patch [bz#580006] +- kvm-migration-Make-events-a-capability.patch [bz#580006] +- kvm-migration-Add-migration-events-on-target-side.patch [bz#580006] +- kvm-migration-Only-change-state-after-migration-has-fini.patch [bz#580006] +- kvm-migration-Trace-event-and-migration-event-are-differ.patch [bz#580006] +- kvm-migration-Write-documetation-for-events-capabilites.patch [bz#580006] +- kvm-migration-Register-global-state-section-before-loadv.patch [bz#580006] +- kvm-migration-We-also-want-to-store-the-global-state-for.patch [bz#580006] +- kvm-block-mirror-limit-qiov-to-IOV_MAX-elements.patch [bz#1238585] +- kvm-i6300esb-fix-timer-overflow.patch [bz#1247893] +- Resolves: bz#1238585 + (drive-mirror has spurious failures with low 'granularity' values) +- Resolves: bz#1247893 + (qemu's i6300esb watchdog does not fire on time with large heartbeat like 2046) +- Resolves: bz#580006 + (QMP: A QMP event notification when migration finish.) + +* Fri Aug 07 2015 Miroslav Rezanina - rhev-2.3.0-16.el7 +- kvm-virtio-scsi-use-virtqueue_map_sg-when-loading-reques.patch [bz#1160169] +- kvm-scsi-disk-fix-cmd.mode-field-typo.patch [bz#1160169] +- kvm-target-i386-emulate-CPUID-level-of-real-hardware.patch [bz#1223317] +- kvm-target-i386-fix-IvyBridge-xlevel-in-PC_COMPAT_2_3.patch [bz#1223317] +- Resolves: bz#1160169 + (Segfault occurred at Dst VM while completed migration upon ENOSPC) +- Resolves: bz#1223317 + (BSod occurs When installing latest Windows Enterprise Insider 10 and windows server 2016 Preview) + +* Wed Aug 05 2015 Miroslav Rezanina - rhev-2.3.0-15.el7 +- kvm-usb-ccid-add-missing-wakeup-calls.patch [bz#1211970] +- kvm-vfio-pci-Fix-bootindex.patch [bz#1245127] +- kvm-acpi-fix-pvpanic-device-is-not-shown-in-ui.patch [bz#1238141] +- kvm-redhat-add-kvm-unit-tests-tarball-to-environment.patch [bz#1225980] +- kvm-spec-Build-tscdeadline_latency.flat-from-kvm-unit-te.patch [bz#1225980] +- Resolves: bz#1211970 + (smart card emulation doesn't work with USB3 (nec-xhci) controller) +- Resolves: bz#1225980 + (Package tscdeadline_latency.flat with qemu-kvm-rhev) +- Resolves: bz#1238141 + ([virtio-win][pvpanic]win10-32 guest can not detect pvpanic device in device manager) +- Resolves: bz#1245127 + (bootindex doesn't work for vfio-pci) + +* Fri Jul 31 2015 Miroslav Rezanina - rhev-2.3.0-14.el7 +- kvm-rtl8139-avoid-nested-ifs-in-IP-header-parsing-CVE-20.patch [bz#1248768] +- kvm-rtl8139-drop-tautologous-if-ip-.-statement-CVE-2015-.patch [bz#1248768] +- kvm-rtl8139-skip-offload-on-short-Ethernet-IP-header-CVE.patch [bz#1248768] +- kvm-rtl8139-check-IP-Header-Length-field-CVE-2015-5165.patch [bz#1248768] +- kvm-rtl8139-check-IP-Total-Length-field-CVE-2015-5165.patch [bz#1248768] +- kvm-rtl8139-skip-offload-on-short-TCP-header-CVE-2015-51.patch [bz#1248768] +- kvm-rtl8139-check-TCP-Data-Offset-field-CVE-2015-5165.patch [bz#1248768] +- Resolves: bz#1248768 + (EMBARGOED CVE-2015-5165 qemu-kvm-rhev: Qemu: rtl8139 uninitialized heap memory information leakage to guest [rhel-7.2]) + +* Fri Jul 24 2015 Miroslav Rezanina - rhev-2.3.0-13.el7 +- kvm-block-Add-bdrv_get_block_status_above.patch [bz#1242316] +- kvm-qmp-Add-optional-bool-unmap-to-drive-mirror.patch [bz#1242316] +- kvm-mirror-Do-zero-write-on-target-if-sectors-not-alloca.patch [bz#1242316] +- kvm-block-Fix-dirty-bitmap-in-bdrv_co_discard.patch [bz#1242316] +- kvm-block-Remove-bdrv_reset_dirty.patch [bz#1242316] +- kvm-iotests-add-QMP-event-waiting-queue.patch [bz#1242316] +- kvm-qemu-iotests-Make-block-job-methods-common.patch [bz#1242316] +- kvm-qemu-iotests-Add-test-case-for-mirror-with-unmap.patch [bz#1242316] +- kvm-iotests-Use-event_wait-in-wait_ready.patch [bz#1242316] +- kvm-rdma-fix-memory-leak.patch [bz#1210715] +- kvm-Only-try-and-read-a-VMDescription-if-it-should-be-th.patch [bz#1210715] +- kvm-qemu_ram_foreach_block-pass-up-error-value-and-down-.patch [bz#1210715] +- kvm-rdma-Fix-qemu-crash-when-IPv6-address-is-used-for-mi.patch [bz#1210715] +- kvm-Rename-RDMA-structures-to-make-destination-clear.patch [bz#1210715] +- kvm-Remove-unneeded-memset.patch [bz#1210715] +- kvm-rdma-typos.patch [bz#1210715] +- kvm-Store-block-name-in-local-blocks-structure.patch [bz#1210715] +- kvm-Translate-offsets-to-destination-address-space.patch [bz#1210715] +- kvm-Rework-ram_control_load_hook-to-hook-during-block-lo.patch [bz#1210715] +- kvm-Allow-rdma_delete_block-to-work-without-the-hash.patch [bz#1210715] +- kvm-Rework-ram-block-hash.patch [bz#1210715] +- kvm-Sort-destination-RAMBlocks-to-be-the-same-as-the-sou.patch [bz#1210715] +- kvm-Sanity-check-RDMA-remote-data.patch [bz#1210715] +- kvm-Fail-more-cleanly-in-mismatched-RAM-cases.patch [bz#1210715] +- kvm-migration-Use-cmpxchg-correctly.patch [bz#1210715] +- kvm-RDMA-Fix-error-exits-for-2.4.patch [bz#1210715] +- kvm-block-mirror-Sleep-periodically-during-bitmap-scanni.patch [bz#1233826] +- kvm-block-curl-Don-t-lose-original-error-when-a-connecti.patch [bz#1235813] +- kvm-vfio-pci-Add-pba_offset-PCI-quirk-for-Chelsio-T5-dev.patch [bz#1244348] +- kvm-hostmem-Fix-qemu_opt_get_bool-crash-in-host_memory_b.patch [bz#1237220] +- kvm-pc-pc-dimm-Extract-hotplug-related-fields-in-PCMachi.patch [bz#1211117] +- kvm-pc-pc-dimm-Factor-out-reusable-parts-in-pc_dimm_plug.patch [bz#1211117] +- kvm-pc-Abort-if-HotplugHandlerClass-plug-fails.patch [bz#1211117] +- kvm-numa-pc-dimm-Store-pc-dimm-memory-information-in-num.patch [bz#1211117] +- kvm-numa-Store-boot-memory-address-range-in-node_info.patch [bz#1211117] +- kvm-numa-API-to-lookup-NUMA-node-by-address.patch [bz#1211117] +- kvm-docs-add-sPAPR-hotplug-dynamic-reconfiguration-docum.patch [bz#1211117] +- kvm-machine-add-default_ram_size-to-machine-class.patch [bz#1211117] +- kvm-spapr-override-default-ram-size-to-512MB.patch [bz#1211117] +- kvm-spapr_pci-Make-find_phb-find_dev-public.patch [bz#1211117] +- kvm-spapr-Merge-sPAPREnvironment-into-sPAPRMachineState.patch [bz#1211117] +- kvm-spapr-Remove-obsolete-ram_limit-field-from-sPAPRMach.patch [bz#1211117] +- kvm-spapr-Remove-obsolete-entry_point-field-from-sPAPRMa.patch [bz#1211117] +- kvm-spapr-Add-sPAPRMachineClass.patch [bz#1211117] +- kvm-spapr-ensure-we-have-at-least-one-XICS-server.patch [bz#1211117] +- kvm-spapr-Consider-max_cpus-during-xics-initialization.patch [bz#1211117] +- kvm-spapr-Support-ibm-lrdr-capacity-device-tree-property.patch [bz#1211117] +- kvm-cpus-Add-a-macro-to-walk-CPUs-in-reverse.patch [bz#1211117] +- kvm-spapr-Reorganize-CPU-dt-generation-code.patch [bz#1211117] +- kvm-spapr-Consolidate-cpu-init-code-into-a-routine.patch [bz#1211117] +- kvm-ppc-Update-cpu_model-in-MachineState.patch [bz#1211117] +- kvm-xics_kvm-Don-t-enable-KVM_CAP_IRQ_XICS-if-already-en.patch [bz#1211117] +- kvm-spapr-Initialize-hotplug-memory-address-space.patch [bz#1211117] +- kvm-spapr-Add-LMB-DR-connectors.patch [bz#1211117] +- kvm-spapr-Support-ibm-dynamic-reconfiguration-memory.patch [bz#1211117] +- kvm-spapr-Make-hash-table-size-a-factor-of-maxram_size.patch [bz#1211117] +- kvm-spapr-Memory-hotplug-support.patch [bz#1211117] +- kvm-spapr-Don-t-allow-memory-hotplug-to-memory-less-node.patch [bz#1211117] +- Resolves: bz#1210715 + (migration/rdma: 7.1->7.2: RDMA ERROR: ram blocks mismatch #3!) +- Resolves: bz#1211117 + (add support for memory hotplug on Power) +- Resolves: bz#1233826 + (issueing drive-mirror command causes monitor unresponsive) +- Resolves: bz#1235813 + (block/curl: Fix generic "Input/output error" on failure) +- Resolves: bz#1237220 + (Fail to create NUMA guest with ) +- Resolves: bz#1242316 + (Add "unmap" support for drive-mirror) +- Resolves: bz#1244348 + (Quirk for Chelsio T5 MSI-X PBA) + +* Fri Jul 17 2015 Miroslav Rezanina - rhev-2.3.0-12.el7 +- kvm-ide-Check-array-bounds-before-writing-to-io_buffer-C.patch [bz#1243692] +- kvm-ide-atapi-Fix-START-STOP-UNIT-command-completion.patch [bz#1243692] +- kvm-ide-Clear-DRQ-after-handling-all-expected-accesses.patch [bz#1243692] +- Resolves: bz#1243692 + () + +* Fri Jul 17 2015 Miroslav Rezanina - rhev-2.3.0-11.el7 +- kvm-hw-acpi-acpi_pm1_cnt_init-take-disable_s3-and-disabl.patch [bz#1204696] +- kvm-hw-acpi-move-etc-system-states-fw_cfg-file-from-PIIX.patch [bz#1204696] +- kvm-hw-acpi-piix4_pm_init-take-fw_cfg-object-no-more.patch [bz#1204696] +- kvm-i386-pc-pc_basic_device_init-delegate-FDC-creation-r.patch [bz#1227282] +- kvm-i386-pc-drive-if-floppy-should-imply-a-board-default.patch [bz#1227282] +- kvm-i386-pc_q35-don-t-insist-on-board-FDC-if-there-s-no-.patch [bz#1227282] +- kvm-i386-drop-FDC-in-pc-q35-rhel7.2.0-if-neither-it-nor-.patch [bz#1227282] +- kvm-hw-i386-pc-factor-out-pc_cmos_init_floppy.patch [bz#1227282] +- kvm-hw-i386-pc-reflect-any-FDC-ioport-0x3f0-in-the-CMOS.patch [bz#1227282] +- kvm-hw-i386-pc-don-t-carry-FDC-from-pc_basic_device_init.patch [bz#1227282] +- kvm-Fix-reported-machine-type.patch [bz#1241331] +- kvm-i386-acpi-build-more-traditional-_UID-and-_HID-for-P.patch [bz#1242479] +- kvm-i386-acpi-build-fix-PXB-workarounds-for-unsupported-.patch [bz#1242479] +- kvm-hw-core-rebase-sysbus_get_fw_dev_path-to-g_strdup_pr.patch [bz#1242479] +- kvm-migration-introduce-VMSTATE_BUFFER_UNSAFE_INFO_TEST.patch [bz#1242479] +- kvm-hw-pci-bridge-expose-_test-parameter-in-SHPC_VMSTATE.patch [bz#1242479] +- kvm-hw-pci-bridge-add-macro-for-chassis_nr-property.patch [bz#1242479] +- kvm-hw-pci-bridge-add-macro-for-msi-property.patch [bz#1242479] +- kvm-hw-pci-introduce-shpc_present-helper-function.patch [bz#1242479] +- kvm-hw-pci-bridge-introduce-shpc-property.patch [bz#1242479] +- kvm-hw-pci-bridge-disable-SHPC-in-PXB.patch [bz#1242479] +- kvm-hw-core-explicit-OFW-unit-address-callback-for-SysBu.patch [bz#1242479] +- kvm-hw-pci-bridge-format-special-OFW-unit-address-for-PX.patch [bz#1242479] +- Resolves: bz#1204696 + (Expose PM system states in fw_cfg file on Q35) +- Resolves: bz#1227282 + (tighten conditions for board-implied FDC in pc-q35-rhel7.2.0+) +- Resolves: bz#1241331 + (Machine type reported by guest is different with that in RHEL.7.1 GA version) +- Resolves: bz#1242479 + (backport QEMU changes needed for supporting multiple PCI root buses with OVMF) + +* Tue Jul 14 2015 Miroslav Rezanina - rhev-2.3.0-10.el7 +- kvm-Disable-Educational-device.patch [bz#1194151] +- kvm-Disable-sdhci-device.patch [bz#1194151] +- kvm-Mark-onboard-devices-as-cannot_instantiate_with_devi.patch [bz#1194151] +- kvm-target-arm-Add-GIC-phandle-to-VirtBoardInfo.patch [bz#1231929] +- kvm-arm_gicv2m-Add-GICv2m-widget-to-support-MSIs.patch [bz#1231929] +- kvm-target-arm-Extend-the-gic-node-properties.patch [bz#1231929] +- kvm-target-arm-Add-the-GICv2m-to-the-virt-board.patch [bz#1231929] +- kvm-introduce-kvm_arch_msi_data_to_gsi.patch [bz#1231929] +- kvm-arm_gicv2m-set-kvm_gsi_direct_mapping-and-kvm_msi_vi.patch [bz#1231929] +- kvm-hw-arm-virt-acpi-build-Fix-table-revision-and-some-c.patch [bz#1231929] +- kvm-hw-arm-virt-acpi-build-Add-GICv2m-description-in-ACP.patch [bz#1231929] +- Resolves: bz#1194151 + (Rebase to qemu 2.3) +- Resolves: bz#1231929 + (AArch64: backport MSI support (gicv2m)) + +* Thu Jul 09 2015 Miroslav Rezanina - rhev-2.3.0-9.el7 +- kvm-acpi-add-a-missing-backslash-to-the-_SB-scope.patch [bz#1103313] +- kvm-range-remove-useless-inclusions.patch [bz#1103313] +- kvm-acpi-Simplify-printing-to-dynamic-string.patch [bz#1103313] +- kvm-acpi-add-aml_add-term.patch [bz#1103313] +- kvm-acpi-add-aml_lless-term.patch [bz#1103313] +- kvm-acpi-add-aml_index-term.patch [bz#1103313] +- kvm-acpi-add-aml_shiftleft-term.patch [bz#1103313] +- kvm-acpi-add-aml_shiftright-term.patch [bz#1103313] +- kvm-acpi-add-aml_increment-term.patch [bz#1103313] +- kvm-acpi-add-aml_while-term.patch [bz#1103313] +- kvm-acpi-add-implementation-of-aml_while-term.patch [bz#1103313] +- kvm-hw-pci-made-pci_bus_is_root-a-PCIBusClass-method.patch [bz#1103313] +- kvm-hw-pci-made-pci_bus_num-a-PCIBusClass-method.patch [bz#1103313] +- kvm-hw-i386-query-only-for-q35-pc-when-looking-for-pci-h.patch [bz#1103313] +- kvm-hw-pci-extend-PCI-config-access-to-support-devices-b.patch [bz#1103313] +- kvm-hw-acpi-add-support-for-i440fx-snooping-root-busses.patch [bz#1103313] +- kvm-hw-apci-add-_PRT-method-for-extra-PCI-root-busses.patch [bz#1103313] +- kvm-hw-acpi-add-_CRS-method-for-extra-root-busses.patch [bz#1103313] +- kvm-hw-acpi-remove-from-root-bus-0-the-crs-resources-use.patch [bz#1103313] +- kvm-hw-pci-removed-rootbus-nr-is-0-assumption-from-qmp_p.patch [bz#1103313] +- kvm-hw-pci-introduce-PCI-Expander-Bridge-PXB.patch [bz#1103313] +- kvm-hw-pci-inform-bios-if-the-system-has-extra-pci-root-.patch [bz#1103313] +- kvm-hw-pxb-add-map_irq-func.patch [bz#1103313] +- kvm-hw-pci-add-support-for-NUMA-nodes.patch [bz#1103313] +- kvm-hw-pxb-add-numa_node-parameter.patch [bz#1103313] +- kvm-apci-fix-PXB-behaviour-if-used-with-unsupported-BIOS.patch [bz#1103313] +- kvm-docs-Add-PXB-documentation.patch [bz#1103313] +- kvm-sPAPR-Don-t-enable-EEH-on-emulated-PCI-devices.patch [bz#1213681] +- kvm-sPAPR-Reenable-EEH-functionality-on-reboot.patch [bz#1213681] +- kvm-sPAPR-Clear-stale-MSIx-table-during-EEH-reset.patch [bz#1213681] +- kvm-configure-Add-support-for-tcmalloc.patch [bz#1213882] +- Resolves: bz#1103313 + (RFE: configure guest NUMA node locality for guest PCI devices) +- Resolves: bz#1213681 + (PAPR PCI-e EEH (Enhanced Error Handling) for KVM/Power guests with VFIO devices (qemu)) +- Resolves: bz#1213882 + (enable using tcmalloc for memory allocation in qemu-kvm-rhev) + +* Wed Jul 08 2015 Miroslav Rezanina - rhev-2.3.0-8.el7 +- kvm-block-Fix-NULL-deference-for-unaligned-write-if-qiov.patch [bz#1207034] +- kvm-qemu-iotests-Test-unaligned-sub-block-zero-write.patch [bz#1207034] +- kvm-spapr_drc-initial-implementation-of-sPAPRDRConnector.patch [bz#1172478] +- kvm-spapr_rtas-add-get-set-power-level-RTAS-interfaces.patch [bz#1172478] +- kvm-spapr_rtas-add-set-indicator-RTAS-interface.patch [bz#1172478] +- kvm-spapr_rtas-add-get-sensor-state-RTAS-interface.patch [bz#1172478] +- kvm-spapr-add-rtas_st_buffer_direct-helper.patch [bz#1172478] +- kvm-spapr_rtas-add-ibm-configure-connector-RTAS-interfac.patch [bz#1172478] +- kvm-spapr_events-re-use-EPOW-event-infrastructure-for-ho.patch [bz#1172478] +- kvm-spapr_events-event-scan-RTAS-interface.patch [bz#1172478] +- kvm-spapr_drc-add-spapr_drc_populate_dt.patch [bz#1172478] +- kvm-spapr_pci-add-dynamic-reconfiguration-option-for-spa.patch [bz#1172478] +- kvm-spapr_pci-create-DRConnectors-for-each-PCI-slot-duri.patch [bz#1172478] +- kvm-pci-make-pci_bar-useable-outside-pci.c.patch [bz#1172478] +- kvm-spapr_pci-enable-basic-hotplug-operations.patch [bz#1172478] +- kvm-spapr_pci-emit-hotplug-add-remove-events-during-hotp.patch [bz#1172478] +- kvm-Print-error-when-failing-to-load-PCI-config-data.patch [bz#1209793] +- kvm-Fix-ich9-intel-hda-compatibility.patch [bz#1209793] +- kvm-pseries-Enable-in-kernel-H_LOGICAL_CI_-LOAD-STORE-im.patch [bz#1217277] +- kvm-Split-serial-isa-into-its-own-config-option.patch [bz#1191845] +- kvm-rhel-Disable-info-irq-and-info-pic-for-Power.patch [bz#1191845] +- kvm-RHEL-Disable-remaining-unsupported-devices-for-ppc.patch [bz#1191845] +- kvm-linux-headers-sync-vhost.h.patch [bz#1225715] +- kvm-virtio-introduce-virtio_legacy_is_cross_endian.patch [bz#1225715] +- kvm-vhost-set-vring-endianness-for-legacy-virtio.patch [bz#1225715] +- kvm-tap-add-VNET_LE-VNET_BE-operations.patch [bz#1225715] +- kvm-tap-fix-non-linux-build.patch [bz#1225715] +- kvm-vhost-net-tell-tap-backend-about-the-vnet-endianness.patch [bz#1225715] +- kvm-vhost_net-re-enable-when-cross-endian.patch [bz#1225715] +- kvm-linux-headers-update.patch [bz#1227343] +- kvm-virtio-input-add-linux-input.h.patch [bz#1227343] +- kvm-virtio-input-core-code-base-class-device.patch [bz#1227343] +- kvm-virtio-input-emulated-devices-device.patch [bz#1227343] +- kvm-virtio-net-Move-DEFINE_VIRTIO_NET_FEATURES-to-virtio.patch [bz#1227343] +- kvm-virtio-scsi-Move-DEFINE_VIRTIO_SCSI_FEATURES-to-virt.patch [bz#1227343] +- kvm-memory-Define-API-for-MemoryRegionOps-to-take-attrs-.patch [bz#1227343] +- kvm-memory-Replace-io_mem_read-write-with-memory_region_.patch [bz#1227343] +- kvm-Make-CPU-iotlb-a-structure-rather-than-a-plain-hwadd.patch [bz#1227343] +- kvm-Add-MemTxAttrs-to-the-IOTLB.patch [bz#1227343] +- kvm-exec.c-Convert-subpage-memory-ops-to-_with_attrs.patch [bz#1227343] +- kvm-exec.c-Make-address_space_rw-take-transaction-attrib.patch [bz#1227343] +- kvm-exec.c-Add-new-address_space_ld-st-functions.patch [bz#1227343] +- kvm-Switch-non-CPU-callers-from-ld-st-_phys-to-address_s.patch [bz#1227343] +- kvm-s390-virtio-sort-into-categories.patch [bz#1227343] +- kvm-s390-virtio-use-common-features.patch [bz#1227343] +- kvm-virtio-move-host_features.patch [bz#1227343] +- kvm-virtio-ccw-Don-t-advertise-VIRTIO_F_BAD_FEATURE.patch [bz#1227343] +- kvm-virtio-move-VIRTIO_F_NOTIFY_ON_EMPTY-into-core.patch [bz#1227343] +- kvm-qdev-add-64bit-properties.patch [bz#1227343] +- kvm-virtio-make-features-64bit-wide.patch [bz#1227343] +- kvm-virtio-input-const_le16-and-const_le32-not-build-tim.patch [bz#1227343] +- kvm-virtio-input-make-virtio-devices-follow-usual-naming.patch [bz#1227343] +- kvm-virtio-64bit-features-fixups.patch [bz#1227343] +- kvm-virtio-endianness-checks-for-virtio-1.0-devices.patch [bz#1227343] +- kvm-virtio-allow-virtio-1-queue-layout.patch [bz#1227343] +- kvm-virtio-disallow-late-feature-changes-for-virtio-1.patch [bz#1227343] +- kvm-virtio-allow-to-fail-setting-status.patch [bz#1227343] +- kvm-virtio-net-no-writeable-mac-for-virtio-1.patch [bz#1227343] +- kvm-virtio-net-support-longer-header.patch [bz#1227343] +- kvm-virtio-net-enable-virtio-1.0.patch [bz#1227343] +- kvm-vhost_net-add-version_1-feature.patch [bz#1227343] +- kvm-vhost-64-bit-features.patch [bz#1227343] +- kvm-linux-headers-add-virtio_pci.patch [bz#1227343] +- kvm-virtio-pci-initial-virtio-1.0-support.patch [bz#1227343] +- kvm-virtio-generation-counter-support.patch [bz#1227343] +- kvm-virtio-add-modern-config-accessors.patch [bz#1227343] +- kvm-virtio-pci-switch-to-modern-accessors-for-1.0.patch [bz#1227343] +- kvm-virtio-pci-add-flags-to-enable-disable-legacy-modern.patch [bz#1227343] +- kvm-virtio-pci-make-QEMU_VIRTIO_PCI_QUEUE_MEM_MULT-small.patch [bz#1227343] +- kvm-virtio-pci-change-document-virtio-pci-bar-layout.patch [bz#1227343] +- kvm-virtio-pci-make-modern-bar-64bit-prefetchable.patch [bz#1227343] +- kvm-virtio-pci-correctly-set-host-notifiers-for-modern-b.patch [bz#1227343] +- kvm-virtio_balloon-header-update.patch [bz#1227343] +- kvm-virtio-balloon-switch-to-virtio_add_feature.patch [bz#1227343] +- kvm-virtio-pci-add-struct-VirtIOPCIRegion-for-virtio-1-r.patch [bz#1227343] +- kvm-virtio-pci-add-virtio_pci_modern_regions_init.patch [bz#1227343] +- kvm-virtio-pci-add-virtio_pci_modern_region_map.patch [bz#1227343] +- kvm-virtio-pci-move-virtio_pci_add_mem_cap-call-to-virti.patch [bz#1227343] +- kvm-virtio-pci-move-cap-type-to-VirtIOPCIRegion.patch [bz#1227343] +- kvm-virtio-pci-drop-identical-virtio_pci_cap.patch [bz#1227343] +- kvm-virtio-pci-fill-VirtIOPCIRegions-early.patch [bz#1227343] +- kvm-pci-add-PCI_CLASS_INPUT_.patch [bz#1227343] +- kvm-virtio-input-core-code-base-class-pci.patch [bz#1227343] +- kvm-virtio-input-emulated-devices-pci.patch [bz#1227343] +- kvm-virtio-net-move-qdev-properties-into-virtio-net.c.patch [bz#1227343] +- kvm-virtio-net.h-Remove-unsed-DEFINE_VIRTIO_NET_PROPERTI.patch [bz#1227343] +- kvm-virtio-scsi-move-qdev-properties-into-virtio-scsi.c.patch [bz#1227343] +- kvm-virtio-rng-move-qdev-properties-into-virtio-rng.c.patch [bz#1227343] +- kvm-virtio-serial-bus-move-qdev-properties-into-virtio-s.patch [bz#1227343] +- kvm-virtio-9p-device-move-qdev-properties-into-virtio-9p.patch [bz#1227343] +- kvm-vhost-scsi-move-qdev-properties-into-vhost-scsi.c.patch [bz#1227343] +- kvm-virito-pci-fix-OVERRUN-problem.patch [bz#1227343] +- kvm-virtio-input-move-properties-use-virtio_instance_ini.patch [bz#1227343] +- kvm-virtio-input-evdev-passthrough.patch [bz#1227343] +- kvm-Add-MAINTAINERS-entry-for-virtio-input.patch [bz#1227343] +- kvm-virtio-input-add-input-routing-support.patch [bz#1227343] +- kvm-dataplane-fix-cross-endian-issues.patch [bz#1227343] +- kvm-aarch64-allow-enable-seccomp.patch [bz#1174861] +- kvm-aarch64-redhat-spec-enable-seccomp.patch [bz#1174861] +- kvm-rhel-Update-package-version-for-SLOF-dependency.patch [bz#1236447] +- Resolves: bz#1172478 + (add support for PCI hotplugging) +- Resolves: bz#1174861 + (use seccomp) +- Resolves: bz#1191845 + ([PowerKVM] There are some unsupported x86 devices under the output of cmds 'man qemu-kvm' and '/usr/libexec/qemu-kvm -device help') +- Resolves: bz#1207034 + (QEMU segfault when doing unaligned zero write to non-512 disk) +- Resolves: bz#1209793 + (migration: 7.1->7.2 error while loading state for instance 0x0 of device '0000:00:04.0/intel-hda') +- Resolves: bz#1217277 + (Enable KVM implementation of H_LOGICAL_CI_{LOAD,STORE}) +- Resolves: bz#1225715 + (Enable cross-endian vhost devices) +- Resolves: bz#1227343 + ([virtio-1] QEMU Virtio-1 Support) +- Resolves: bz#1236447 + (Update qemu-kvm-rhev package for new SLOF) + +* Thu Jul 02 2015 Miroslav Rezanina - rhev-2.3.0-7.el7 +- kvm-docs-update-documentation-for-memory-hot-unplug.patch [bz#1120706] +- kvm-acpi-mem-hotplug-add-acpi_memory_slot_status-to-get-.patch [bz#1120706] +- kvm-acpi-mem-hotplug-add-unplug-request-cb-for-memory-de.patch [bz#1120706] +- kvm-acpi-mem-hotplug-add-unplug-cb-for-memory-device.patch [bz#1120706] +- kvm-acpi-extend-aml_field-to-support-UpdateRule.patch [bz#1120706] +- kvm-acpi-fix-Memory-device-control-fields-register.patch [bz#1120706] +- kvm-acpi-add-hardware-implementation-for-memory-hot-unpl.patch [bz#1120706] +- kvm-qmp-event-add-event-notification-for-memory-hot-unpl.patch [bz#1120706] +- kvm-hw-acpi-aml-build-Fix-memory-leak.patch [bz#1120706] +- kvm-memory-add-memory_region_ram_resize.patch [bz#1231719] +- kvm-acpi-build-remove-dependency-from-ram_addr.h.patch [bz#1231719] +- kvm-hw-i386-Move-ACPI-header-definitions-in-an-arch-inde.patch [bz#1231719] +- kvm-hw-i386-acpi-build-move-generic-acpi-building-helper.patch [bz#1231719] +- kvm-hw-acpi-aml-build-Make-enum-values-to-be-upper-case-.patch [bz#1231719] +- kvm-hw-arm-virt-Move-common-definitions-to-virt.h.patch [bz#1231719] +- kvm-hw-arm-virt-Record-PCIe-ranges-in-MemMapEntry-array.patch [bz#1231719] +- kvm-hw-arm-virt-acpi-build-Basic-framework-for-building-.patch [bz#1231719] +- kvm-hw-acpi-aml-build-Add-aml_memory32_fixed-term.patch [bz#1231719] +- kvm-hw-acpi-aml-build-Add-aml_interrupt-term.patch [bz#1231719] +- kvm-hw-arm-virt-acpi-build-Generation-of-DSDT-table-for-.patch [bz#1231719] +- kvm-hw-arm-virt-acpi-build-Generate-FADT-table-and-updat.patch [bz#1231719] +- kvm-hw-arm-virt-acpi-build-Generate-MADT-table.patch [bz#1231719] +- kvm-hw-arm-virt-acpi-build-Generate-GTDT-table.patch [bz#1231719] +- kvm-hw-arm-virt-acpi-build-Generate-RSDT-table.patch [bz#1231719] +- kvm-hw-arm-virt-acpi-build-Generate-RSDP-table.patch [bz#1231719] +- kvm-hw-arm-virt-acpi-build-Generate-MCFG-table.patch [bz#1231719] +- kvm-hw-acpi-aml-build-Make-aml_buffer-definition-consist.patch [bz#1231719] +- kvm-hw-acpi-aml-build-Add-ToUUID-macro.patch [bz#1231719] +- kvm-hw-acpi-aml-build-Add-aml_or-term.patch [bz#1231719] +- kvm-hw-acpi-aml-build-Add-aml_lnot-term.patch [bz#1231719] +- kvm-hw-acpi-aml-build-Add-aml_else-term.patch [bz#1231719] +- kvm-hw-acpi-aml-build-Add-aml_create_dword_field-term.patch [bz#1231719] +- kvm-hw-acpi-aml-build-Add-aml_dword_io-term.patch [bz#1231719] +- kvm-hw-acpi-aml-build-Add-Unicode-macro.patch [bz#1231719] +- kvm-hw-arm-virt-acpi-build-Add-PCIe-controller-in-ACPI-D.patch [bz#1231719] +- kvm-ACPI-split-CONFIG_ACPI-into-4-pieces.patch [bz#1231719] +- kvm-hw-arm-virt-Enable-dynamic-generation-of-ACPI-v5.1-t.patch [bz#1231719] +- kvm-ACPI-Add-definitions-for-the-SPCR-table.patch [bz#1231719] +- kvm-hw-arm-virt-acpi-build-Add-SPCR-table.patch [bz#1231719] +- kvm-AArch64-Enable-ACPI.patch [bz#1231719] +- kvm-i8254-fix-out-of-bounds-memory-access-in-pit_ioport_.patch [bz#1229647] +- kvm-hw-q35-fix-floppy-controller-definition-in-ich9.patch [bz#894956] +- kvm-Migration-compat-for-pckbd.patch [bz#1215092] +- kvm-Migration-compat-for-fdc.patch [bz#1215091] +- Resolves: bz#1120706 + (Support dynamic virtual Memory deallocation - qemu-kvm) +- Resolves: bz#1215091 + (migration: 7.2->earlier; floppy compatibility) +- Resolves: bz#1215092 + (migration: 7.2->earlier: pckbd compatibility) +- Resolves: bz#1229647 + (CVE-2015-3214 qemu-kvm-rhev: qemu: i8254: out-of-bounds memory access in pit_ioport_read function [rhel-7.2]) +- Resolves: bz#1231719 + (AArch64: backport ACPI support) +- Resolves: bz#894956 + (floppy can not be recognized by Windows guest (q35)) + +* Fri Jun 26 2015 Miroslav Rezanina - rhev-2.3.0-6.el7 +- kvm-vfio-pci-Fix-error-path-sign.patch [bz#1219090] +- kvm-vfio-pci-Further-fix-BAR-size-overflow.patch [bz#1219090] +- kvm-Add-flag-for-pre-2.2-migration-compatibility.patch [bz#1215087] +- kvm-Serial-Migration-compatibility-pre-2.2-7.2.patch [bz#1215087] +- kvm-Migration-compat-for-mc146818rtc-irq_reinject_on_ack.patch [bz#1215088] +- Resolves: bz#1215087 + (migration: 7.2->earlier; serial compatibility) +- Resolves: bz#1215088 + (migration: 7.2->earlier; mc146818rtc compatibility) +- Resolves: bz#1219090 + (vfio-pci - post QEMU2.3 fixes, error sign + BAR overflow) + +* Wed Jun 24 2015 Miroslav Rezanina - rhev-2.3.0-5.el7 +- kvm-atomics-add-explicit-compiler-fence-in-__atomic-memo.patch [bz#1231335] +- kvm-pc-acpi-fix-pvpanic-for-buggy-guests.patch [bz#1221943] +- Resolves: bz#1221943 + (On_crash events didn't work when using guest's pvpanic device) +- Resolves: bz#1231335 + ([abrt] qemu-kvm: bdrv_error_action(): qemu-kvm killed by SIGABRT) + +* Mon Jun 22 2015 Miroslav Rezanina - rhev-2.3.0-4.el7 +- kvm-virtio-ccw-using-VIRTIO_NO_VECTOR-instead-of-0-for-i.patch [bz#1231610] +- kvm-virtio-ccw-sort-into-categories.patch [bz#1231610] +- kvm-virtio-ccw-change-realization-sequence.patch [bz#1231610] +- kvm-virtio-ccw-implement-device_plugged.patch [bz#1231610] +- kvm-virtio-net-fix-the-upper-bound-when-trying-to-delete.patch [bz#1231610] +- kvm-monitor-replace-the-magic-number-255-with-MAX_QUEUE_.patch [bz#1231610] +- kvm-monitor-check-return-value-of-qemu_find_net_clients_.patch [bz#1231610] +- kvm-virtio-introduce-vector-to-virtqueues-mapping.patch [bz#1231610] +- kvm-virtio-pci-speedup-MSI-X-masking-and-unmasking.patch [bz#1231610] +- kvm-pci-remove-hard-coded-bar-size-in-msix_init_exclusiv.patch [bz#1231610] +- kvm-virtio-net-adding-all-queues-in-.realize.patch [bz#1231610] +- kvm-virtio-device_plugged-can-fail.patch [bz#1231610] +- kvm-virtio-introduce-virtio_get_num_queues.patch [bz#1231610] +- kvm-virtio-ccw-introduce-ccw-specific-queue-limit.patch [bz#1231610] +- kvm-virtio-ccw-validate-the-number-of-queues-against-bus.patch [bz#1231610] +- kvm-virtio-s390-introduce-virito-s390-queue-limit.patch [bz#1231610] +- kvm-virtio-s390-introduce-virtio_s390_device_plugged.patch [bz#1231610] +- kvm-virtio-rename-VIRTIO_PCI_QUEUE_MAX-to-VIRTIO_QUEUE_M.patch [bz#1231610] +- kvm-virtio-increase-the-queue-limit-to-1024.patch [bz#1231610] +- kvm-virtio-pci-don-t-try-to-mask-or-unmask-vqs-without-n.patch [bz#1231610] +- Resolves: bz#1231610 + (Support more virtio queues) + +* Fri Jun 19 2015 Miroslav Rezanina - rhev-2.3.0-3.el7 +- kvm-vmdk-Fix-overflow-if-l1_size-is-0x20000000.patch [bz#1226809] +- kvm-Downstream-only-Add-rhel7.2.0-machine-type.patch [bz#1228574] +- kvm-spice-display-fix-segfault-in-qemu_spice_create_upda.patch [bz#1230550] +- kvm-pc-dimm-don-t-assert-if-pc-dimm-alignment-hotpluggab.patch [bz#1221425] +- kvm-Strip-brackets-from-vnc-host.patch [bz#1229073] +- kvm-qcow2-Set-MIN_L2_CACHE_SIZE-to-2.patch [bz#1226996] +- kvm-iotests-qcow2-COW-with-minimal-L2-cache-size.patch [bz#1226996] +- kvm-qcow2-Add-DEFAULT_L2_CACHE_CLUSTERS.patch [bz#1226996] +- kvm-spec-Ship-complete-QMP-documentation-files.patch [bz#1222834] +- Resolves: bz#1221425 + (qemu crash when hot-plug a memory device) +- Resolves: bz#1222834 + (We ship incomplete QMP documentation) +- Resolves: bz#1226809 + (Overflow in malloc size calculation in VMDK driver) +- Resolves: bz#1226996 + (qcow2: Fix minimum L2 cache size) +- Resolves: bz#1228574 + (Add RHEL7.2 machine type in QEMU for PPC64LE) +- Resolves: bz#1229073 + ([graphical framebuffer]Start guest failed when VNC listen on IPV6 address) +- Resolves: bz#1230550 + ([abrt] qemu-system-x86: __memcmp_sse4_1(): qemu-system-x86_64 killed by SIGSEGV) + +* Wed May 27 2015 Miroslav Rezanina - rhev-2.3.0-2.el7 +- kvm-balloon-improve-error-msg-when-adding-second-device.patch [bz#1165534] +- kvm-qmp-add-error-reason-to-the-BLOCK_IO_ERROR-event.patch [bz#1199174] +- kvm-spec-Remove-obsolete-differentiation-code.patch [bz#1122778] +- kvm-spec-Use-external-configuration-script.patch [bz#1122778] +- kvm-spec-Use-configure-options-to-prevent-default-resolu.patch [bz#1122778] +- kvm-fdc-force-the-fifo-access-to-be-in-bounds-of-the-all.patch [bz#1219272] +- Resolves: bz#1122778 + (miss "vhdx" and "iscsi" in qemu-img supported format list) +- Resolves: bz#1165534 + (balloon: improve error message when adding second device) +- Resolves: bz#1199174 + (QMP: forward port rhel-only error reason to BLOCK_IO_ERROR event) +- Resolves: bz#1219272 + (CVE-2015-3456 qemu-kvm-rhev: qemu: floppy disk controller flaw [rhel-7.2]) + +* Tue Apr 28 2015 Miroslav Rezanina - rhev-2.3.0-1.el7 +- Rebase to 2.3.0 [bz#1194151] +- kvm-misc-Add-pc-i440fx-rhel7-2-0-machine-type.patch [bz#1210050] +- kvm-misc-Add-pc-q35-rhel7-2-0-machine-type.patch [bz#1210050] +- Resolves: bz#1194151 + (Rebase to qemu 2.3) +- Resolves: bz#1210050 + (Add pc-i440fx-rhel7.2.0 machine type) + +* Thu Mar 19 2015 Miroslav Rezanina - rhev-2.2.0-8.el7 +- kvm-pc_sysfw-prevent-pflash-and-or-mis-sized-firmware-fo.patch [bz#1175099] +- kvm-build-reenable-local-builds-to-pass-enable-debug-dow.patch [] +- kvm-RPM-spec-install-dump-guest-memory.py-downstream-onl.patch [bz#1194304] +- kvm-vga-Expose-framebuffer-byteorder-as-a-QOM-property.patch [bz#1146809] +- kvm-pseries-Switch-VGA-endian-on-H_SET_MODE.patch [bz#1146809] +- kvm-Generalize-QOM-publishing-of-date-and-time-from-mc14.patch [bz#1172583] +- kvm-Add-more-VMSTATE_-_TEST-variants-for-integers.patch [bz#1171700] +- kvm-pseries-Move-sPAPR-RTC-code-into-its-own-file.patch [bz#1170132 bz#1171700 bz#1172583] +- kvm-pseries-Add-more-parameter-validation-in-RTAS-time-o.patch [bz#1170132 bz#1171700 bz#1172583] +- kvm-pseries-Add-spapr_rtc_read-helper-function.patch [bz#1170132 bz#1171700 bz#1172583] +- kvm-pseries-Make-RTAS-time-of-day-functions-respect-rtc-.patch [bz#1170132] +- kvm-pseries-Make-the-PAPR-RTC-a-qdev-device.patch [bz#1170132 bz#1171700 bz#1172583] +- kvm-pseries-Move-rtc_offset-into-RTC-device-s-state-stru.patch [bz#1171700] +- kvm-pseries-Export-RTC-time-via-QOM.patch [bz#1172583] +- kvm-pseries-Limit-PCI-host-bridge-index-value.patch [bz#1181409] +- Resolves: bz#1146809 + (Incorrect colours on virtual VGA with ppc64le guest under ppc64 host) +- Resolves: bz#1170132 + (Guest time could change with host time even specify the guest clock as "-rtc base=utc,clock=vm,...") +- Resolves: bz#1171700 + ('hwclock' in destination guest returns to base '2006-06-06' after migration) +- Resolves: bz#1172583 + ([Power KVM] Qemu monitor command don't support {"execute":"qom-get","arguments":{"path":"/machine","property":"rtc-time"}}) +- Resolves: bz#1175099 + ([migration]migration failed when configure guest with OVMF bios + machine type=rhel6.5.0) +- Resolves: bz#1181409 + (PCI pass-through device works improperly due to the PHB's index being set to a big value) +- Resolves: bz#1194304 + ([Hitachi 7.2 FEAT] Extract guest memory dump from qemu-kvm-rhev core) + +* Tue Mar 10 2015 Miroslav Rezanina - rhev-2.2.0-7.el7 +- kvm-aarch64-Add-PCI-and-VIRTIO_PCI-devices-for-AArch64.patch [bz#1200090] +- kvm-Add-specific-config-options-for-PCI-E-bridges.patch [bz#1200090] +- Resolves: bz#1200090 + (qemu-kvm-rhev (2.2.0-6) breaks ISO installation) + +* Mon Mar 02 2015 Miroslav Rezanina - rhev-2.2.0-6.el7 +- kvm-AArch64-Prune-the-devices-available-for-AArch64-gues.patch [bz#1170734] +- kvm-Give-ivshmem-its-own-config-option.patch [bz#1170734] +- kvm-aarch64-Prune-unsupported-CPU-types-for-aarch64.patch [bz#1170734] +- Resolves: bz#1170734 + (Trim qemu-kvm devices for aarch64) + +* Wed Feb 11 2015 Miroslav Rezanina - rhev-2.2.0-5.el7 +- kvm-kvm_stat-Add-aarch64-support.patch [bz#1184603] +- kvm-kvm_stat-Update-exit-reasons-to-the-latest-defintion.patch [bz#1184603] +- kvm-kvm_stat-Add-RESET-support-for-perf-event-ioctl.patch [bz#1184603] +- kvm-ignore-SIGIO-in-tests-that-use-AIO-context-aarch64-h.patch [bz#1184405] +- kvm-aio_notify-force-main-loop-wakeup-with-SIGIO-aarch64.patch [bz#1184405] +- Resolves: bz#1184405 + (lost block IO completion notification (for virtio-scsi disk) hangs main loop) +- Resolves: bz#1184603 + (enable kvm_stat support for aarch64) + +* Mon Feb 09 2015 Miroslav Rezanina - rhev-2.2.0-4.el7 +- kvm-Downstream-only-Restore-pseries-machine-alias.patch [bz#1170934] +- kvm-PPC-Fix-crash-on-spapr_tce_table_finalize.patch [bz#1170934] +- kvm-virtio_serial-Don-t-use-vser-config.max_nr_ports-int.patch [bz#1169230] +- kvm-virtio-serial-Don-t-keep-a-persistent-copy-of-config.patch [bz#1169230] +- kvm-spapr-Fix-stale-HTAB-during-live-migration-KVM.patch [bz#1168446] +- kvm-spapr-Fix-integer-overflow-during-migration-TCG.patch [bz#1168446] +- kvm-spapr-Fix-stale-HTAB-during-live-migration-TCG.patch [bz#1168446] +- Resolves: bz#1168446 + (Stale hash PTEs may be transferred during live migration of PAPR guests) +- Resolves: bz#1169230 + (QEMU core dumped when do ping-pong migration to file for LE guest) +- Resolves: bz#1170934 + (Segfault at spapr_tce_table_finalize(): QLIST_REMOVE(tcet, list)) + +* Thu Jan 22 2015 Miroslav Rezanina - rhev-2.2.0-3.el7 +- kvm-Downstream-only-arm-define-a-new-machine-type-for-RH.patch [bz#1176838] +- Resolves: bz#1176838 + (create rhelsa machine type) + +* Wed Jan 14 2015 Miroslav Rezanina - rhev-2.2.0-2.el7.next.candidate +- kvm-Update-to-qemu-kvm-rhev-2.1.2-19.el7.patch [] +- kvm-fw_cfg-remove-superfluous-blank-line.patch [bz#1169869] +- kvm-hw-arm-boot-fix-uninitialized-scalar-variable-warnin.patch [bz#1169869] +- kvm-Sort-include-qemu-typedefs.h.patch [bz#1169869] +- kvm-fw_cfg-hard-separation-between-the-MMIO-and-I-O-port.patch [bz#1169869] +- kvm-fw_cfg-move-boards-to-fw_cfg_init_io-fw_cfg_init_mem.patch [bz#1169869] +- kvm-fw_cfg_mem-max-access-size-and-region-size-are-the-s.patch [bz#1169869] +- kvm-fw_cfg_mem-flip-ctl_mem_ops-and-data_mem_ops-to-DEVI.patch [bz#1169869] +- kvm-exec-allows-8-byte-accesses-in-subpage_ops.patch [bz#1169869] +- kvm-fw_cfg_mem-introduce-the-data_width-property.patch [bz#1169869] +- kvm-fw_cfg_mem-expose-the-data_width-property-with-fw_cf.patch [bz#1169869] +- kvm-arm-add-fw_cfg-to-virt-board.patch [bz#1169869] +- kvm-hw-loader-split-out-load_image_gzipped_buffer.patch [bz#1169869] +- kvm-hw-arm-pass-pristine-kernel-image-to-guest-firmware-.patch [bz#1169869] +- kvm-hw-arm-virt-enable-passing-of-EFI-stubbed-kernel-to-.patch [bz#1169869] +- kvm-fw_cfg-fix-endianness-in-fw_cfg_data_mem_read-_write.patch [bz#1169869] +- Resolves: bz#1169869 + (add fw_cfg to mach-virt) + +* Tue Jan 13 2015 Miroslav Rezanina - rhev-2.1.2-19.el7 +- kvm-smbios-Fix-dimm-size-calculation-when-RAM-is-multipl.patch [bz#1179165] +- kvm-smbios-Don-t-report-unknown-CPU-speed-fix-SVVP-regre.patch [bz#1177127] +- Resolves: bz#1177127 + ([SVVP]smbios HCT job failed with 'Processor Max Speed cannot be Unknown' with -M pc-i440fx-rhel7.1.0) +- Resolves: bz#1179165 + ([SVVP]smbios HCT job failed with Unspecified error with -M pc-i440fx-rhel7.1.0) + +* Thu Jan 08 2015 Miroslav Rezanina - rhev-2.2.0-1.el7 +- rebase to qemu 2.2.0 + +* Thu Jan 08 2015 Miroslav Rezanina - rhev-2.1.2-18.el7 +- kvm-vl-Adjust-the-place-of-calling-mlockall-to-speedup-V.patch [bz#1173394] +- kvm-block-delete-cow-block-driver.patch [bz#1175841] +- Resolves: bz#1173394 + (numa_smaps doesn't respect bind policy with huge page) +- Resolves: bz#1175841 + (Delete cow block driver) + +* Tue Dec 16 2014 Jeff E. Nelson - rhev-2.1.2-17.el7 +- kvm-numa-Don-t-allow-memdev-on-RHEL-6-machine-types.patch [bz#1170093] +- kvm-block-allow-bdrv_unref-to-be-passed-NULL-pointers.patch [bz#1136381] +- kvm-block-vdi-use-block-layer-ops-in-vdi_create-instead-.patch [bz#1136381] +- kvm-block-use-the-standard-ret-instead-of-result.patch [bz#1136381] +- kvm-block-vpc-use-block-layer-ops-in-vpc_create-instead-.patch [bz#1136381] +- kvm-block-iotest-update-084-to-test-static-VDI-image-cre.patch [bz#1136381] +- kvm-block-remove-BLOCK_OPT_NOCOW-from-vdi_create_opts.patch [bz#1136381] +- kvm-block-remove-BLOCK_OPT_NOCOW-from-vpc_create_opts.patch [bz#1136381] +- kvm-migration-fix-parameter-validation-on-ram-load-CVE-2.patch [bz#1163079] +- kvm-qdev-monitor-fix-segmentation-fault-on-qdev_device_h.patch [bz#1169280] +- kvm-block-migration-Disable-cache-invalidate-for-incomin.patch [bz#1171552] +- kvm-acpi-Use-apic_id_limit-when-calculating-legacy-ACPI-.patch [bz#1173167] +- Resolves: bz#1136381 + (RFE: Supporting creating vdi/vpc format disk with protocols (glusterfs) for qemu-kvm-rhev-2.1.x) +- Resolves: bz#1163079 + (CVE-2014-7840 qemu-kvm-rhev: qemu: insufficient parameter validation during ram load [rhel-7.1]) +- Resolves: bz#1169280 + (Segfault while query device properties (ics, icp)) +- Resolves: bz#1170093 + (guest NUMA failed to migrate when machine is rhel6.5.0) +- Resolves: bz#1171552 + (Storage vm migration failed when running BurnInTes) +- Resolves: bz#1173167 + (Corrupted ACPI tables in some configurations using pc-i440fx-rhel7.0.0) + +* Fri Dec 05 2014 Miroslav Rezanina - rhev-2.1.2-16.el7 +- kvm-qemu-iotests-Fix-broken-test-cases.patch [bz#1169589] +- kvm-Fix-for-crash-after-migration-in-virtio-rng-on-bi-en.patch [bz#1165087] +- kvm-Downstream-only-remove-unsupported-machines-from-AAr.patch [bz#1169847] +- Resolves: bz#1165087 + (QEMU core dumped for the destination guest when do migating guest to file) +- Resolves: bz#1169589 + (test case 051 071 and 087 of qemu-iotests fail for qcow2 with qemu-kvm-rhev-2.1.2-14.el7) +- Resolves: bz#1169847 + (only support mach-virt) + +* Tue Dec 02 2014 Miroslav Rezanina - rhev-2.1.2-15.el7 +- kvm-scsi-Optimize-scsi_req_alloc.patch [bz#1141656] +- kvm-virtio-scsi-Optimize-virtio_scsi_init_req.patch [bz#1141656] +- kvm-virtio-scsi-Fix-comment-for-VirtIOSCSIReq.patch [bz#1141656] +- kvm-Downstream-only-Move-daemon-reload-to-make-sure-new-.patch [bz#1168085] +- Resolves: bz#1141656 + (Virtio-scsi: performance degradation from 1.5.3 to 2.1.0) +- Resolves: bz#1168085 + (qemu-kvm-rhev install scripts sometimes don't recognize newly installed systemd presets) + +* Thu Nov 27 2014 Miroslav Rezanina - rhev-2.1.2-14.el7 +- kvm-xhci-add-sanity-checks-to-xhci_lookup_uport.patch [bz#1161397] +- kvm-qemu-img-Allow-source-cache-mode-specification.patch [bz#1166481] +- kvm-qemu-img-Allow-cache-mode-specification-for-amend.patch [bz#1166481] +- kvm-qemu-img-fix-img_compare-flags-error-path.patch [bz#1166481] +- kvm-qemu-img-clarify-src_cache-option-documentation.patch [bz#1166481] +- kvm-qemu-img-fix-rebase-src_cache-option-documentation.patch [bz#1166481] +- Resolves: bz#1161397 + (qemu core dump when install a RHEL.7 guest(xhci) with migration) +- Resolves: bz#1166481 + (Allow qemu-img to bypass the host cache (check, compare, convert, rebase, amend)) + +* Tue Nov 25 2014 Miroslav Rezanina - rhev-2.1.2-13.el7 +- kvm-hw-pci-fixed-error-flow-in-pci_qdev_init.patch [bz#1166067] +- kvm-hw-pci-fixed-hotplug-crash-when-using-rombar-0-with-.patch [bz#1166067] +- Resolves: bz#1166067 + (qemu-kvm aborted when hot plug PCI device to guest with romfile and rombar=0) + +* Fri Nov 21 2014 Miroslav Rezanina - rhev-2.1.2-12.el7 +- kvm-migration-static-variables-will-not-be-reset-at-seco.patch [bz#1166501] +- Resolves: bz#1166501 + (Migration "expected downtime" does not refresh after reset to a new value) + +* Fri Nov 21 2014 Miroslav Rezanina - rhev-2.1.2-11.el7 +- kvm-iscsi-Refuse-to-open-as-writable-if-the-LUN-is-write.patch [bz#1160102] +- kvm-vnc-sanitize-bits_per_pixel-from-the-client.patch [bz#1157646] +- kvm-usb-host-fix-usb_host_speed_compat-tyops.patch [bz#1160504] +- kvm-block-raw-posix-Fix-disk-corruption-in-try_fiemap.patch [bz#1142331] +- kvm-block-raw-posix-use-seek_hole-ahead-of-fiemap.patch [bz#1142331] +- kvm-raw-posix-Fix-raw_co_get_block_status-after-EOF.patch [bz#1142331] +- kvm-raw-posix-raw_co_get_block_status-return-value.patch [bz#1142331] +- kvm-raw-posix-SEEK_HOLE-suffices-get-rid-of-FIEMAP.patch [bz#1142331] +- kvm-raw-posix-The-SEEK_HOLE-code-is-flawed-rewrite-it.patch [bz#1142331] +- kvm-exec-Handle-multipage-ranges-in-invalidate_and_set_d.patch [bz#1164759] +- Resolves: bz#1142331 + (qemu-img convert intermittently corrupts output images) +- Resolves: bz#1157646 + (CVE-2014-7815 qemu-kvm-rhev: qemu: vnc: insufficient bits_per_pixel from the client sanitization [rhel-7.1]) +- Resolves: bz#1160102 + (opening read-only iscsi lun as read-write should fail) +- Resolves: bz#1160504 + (guest can not show usb device after adding some usb controllers and redirdevs.) +- Resolves: bz#1164759 + (Handle multipage ranges in invalidate_and_set_dirty()) + +* Thu Nov 20 2014 Miroslav Rezanina - rhev-2.1.2-10.el7 +- kvm-pc-dimm-Don-t-check-dimm-node-when-there-is-non-NUMA.patch [bz#1150510 bz#1163735] +- kvm-vga-Start-cutting-out-non-32bpp-conversion-support.patch [bz#1146809] +- kvm-vga-Remove-remainder-of-old-conversion-cruft.patch [bz#1146809] +- kvm-vga-Separate-LE-and-BE-conversion-functions.patch [bz#1146809] +- kvm-vga-Remove-rgb_to_pixel-indirection.patch [bz#1146809] +- kvm-vga-Simplify-vga_draw_blank-a-bit.patch [bz#1146809] +- kvm-cirrus-Remove-non-32bpp-cursor-drawing.patch [bz#1146809] +- kvm-vga-Remove-some-should-be-done-in-BIOS-comments.patch [bz#1146809] +- kvm-vga-Rename-vga_template.h-to-vga-helpers.h.patch [bz#1146809] +- kvm-vga-Make-fb-endian-a-common-state-variable.patch [bz#1146809] +- kvm-vga-Add-endian-to-vmstate.patch [bz#1146809] +- kvm-vga-pci-add-qext-region-to-mmio.patch [bz#1146809] +- kvm-virtio-scsi-work-around-bug-in-old-BIOSes.patch [bz#1123812] +- kvm-Revert-Downstream-only-Add-script-to-autoload-KVM-mo.patch [bz#1158250 bz#1159706] +- kvm-Downstream-only-add-script-on-powerpc-to-configure-C.patch [bz#1158250 bz#1158251 bz#1159706] +- kvm-block-New-bdrv_nb_sectors.patch [bz#1132385] +- kvm-vmdk-Optimize-cluster-allocation.patch [bz#1132385] +- kvm-vmdk-Handle-failure-for-potentially-large-allocation.patch [bz#1132385] +- kvm-vmdk-Use-bdrv_nb_sectors-where-sectors-not-bytes-are.patch [bz#1132385] +- kvm-vmdk-fix-vmdk_parse_extents-extent_file-leaks.patch [bz#1132385] +- kvm-vmdk-fix-buf-leak-in-vmdk_parse_extents.patch [bz#1132385] +- kvm-vmdk-Fix-integer-overflow-in-offset-calculation.patch [bz#1132385] +- kvm-Revert-Build-ceph-rbd-only-for-rhev.patch [bz#1140744] +- kvm-Revert-rbd-Only-look-for-qemu-specific-copy-of-librb.patch [bz#1140744] +- kvm-Revert-rbd-link-and-load-librbd-dynamically.patch [bz#1140744] +- kvm-spec-Enable-rbd-driver-add-dependency.patch [bz#1140744] +- kvm-Use-qemu-kvm-in-documentation-instead-of-qemu-system.patch [bz#1140620] +- kvm-ide-stash-aiocb-for-flushes.patch [bz#1024599] +- kvm-ide-simplify-reset-callbacks.patch [bz#1024599] +- kvm-ide-simplify-set_inactive-callbacks.patch [bz#1024599] +- kvm-ide-simplify-async_cmd_done-callbacks.patch [bz#1024599] +- kvm-ide-simplify-start_transfer-callbacks.patch [bz#1024599] +- kvm-ide-wrap-start_dma-callback.patch [bz#1024599] +- kvm-ide-remove-wrong-setting-of-BM_STATUS_INT.patch [bz#1024599] +- kvm-ide-fold-add_status-callback-into-set_inactive.patch [bz#1024599] +- kvm-ide-move-BM_STATUS-bits-to-pci.-ch.patch [bz#1024599] +- kvm-ide-move-retry-constants-out-of-BM_STATUS_-namespace.patch [bz#1024599] +- kvm-ahci-remove-duplicate-PORT_IRQ_-constants.patch [bz#1024599] +- kvm-ide-stop-PIO-transfer-on-errors.patch [bz#1024599] +- kvm-ide-make-all-commands-go-through-cmd_done.patch [bz#1024599] +- kvm-ide-atapi-Mark-non-data-commands-as-complete.patch [bz#1024599] +- kvm-ahci-construct-PIO-Setup-FIS-for-PIO-commands.patch [bz#1024599] +- kvm-ahci-properly-shadow-the-TFD-register.patch [bz#1024599] +- kvm-ahci-Correct-PIO-D2H-FIS-responses.patch [bz#1024599] +- kvm-ahci-Update-byte-count-after-DMA-completion.patch [bz#1024599] +- kvm-ahci-Fix-byte-count-regression-for-ATAPI-PIO.patch [bz#1024599] +- kvm-ahci-Fix-SDB-FIS-Construction.patch [bz#1024599] +- kvm-vhost-user-fix-mmap-offset-calculation.patch [bz#1159710] +- Resolves: bz#1024599 + (Windows7 x86 guest with ahci backend hit BSOD when do "hibernate") +- Resolves: bz#1123812 + (Reboot guest and guest's virtio-scsi disk will be lost after forwards migration (from RHEL6.6 host to RHEL7.1 host)) +- Resolves: bz#1132385 + (qemu-img convert rate about 100k/second from qcow2/raw to vmdk format on nfs system file) +- Resolves: bz#1140620 + (Should replace "qemu-system-i386" by "/usr/libexec/qemu-kvm" in manpage of qemu-kvm for our official qemu-kvm build) +- Resolves: bz#1140744 + (Enable native support for Ceph) +- Resolves: bz#1146809 + (Incorrect colours on virtual VGA with ppc64le guest under ppc64 host) +- Resolves: bz#1150510 + (kernel ignores ACPI memory devices (PNP0C80) present at boot time) +- Resolves: bz#1158250 + (KVM modules are not autoloaded on POWER hosts) +- Resolves: bz#1158251 + (POWER KVM host starts by default with threads enabled, which prevents running guests) +- Resolves: bz#1159706 + (Need means to configure subcore mode for RHEL POWER8 hosts) +- Resolves: bz#1159710 + (vhost-user:Bad ram offset) +- Resolves: bz#1163735 + (-device pc-dimm fails to initialize on non-NUMA configs) + +* Wed Nov 19 2014 Miroslav Rezanina - rhev-2.1.2-9.el7 +- kvm-aarch64-raise-max_cpus-to-8.patch [bz#1160325] +- kvm-hw-arm-virt-add-linux-stdout-path-to-chosen-DT-node.patch [bz#1160325] +- kvm-hw-arm-virt-Provide-flash-devices-for-boot-ROMs.patch [bz#1160325] +- kvm-hw-arm-boot-load-DTB-as-a-ROM-image.patch [bz#1160325] +- kvm-hw-arm-boot-pass-an-address-limit-to-and-return-size.patch [bz#1160325] +- kvm-hw-arm-boot-load-device-tree-to-base-of-DRAM-if-no-k.patch [bz#1160325] +- kvm-hw-arm-boot-enable-DTB-support-when-booting-ELF-imag.patch [bz#1160325] +- kvm-hw-arm-virt-mark-timer-in-fdt-as-v8-compatible.patch [bz#1160325] +- kvm-hw-arm-boot-register-cpu-reset-handlers-if-using-bio.patch [bz#1160325] +- kvm-Downstream-only-Declare-ARM-kernel-support-read-only.patch [bz#1160325] +- Resolves: bz#1160325 + (arm64: support aavmf) + +* Thu Nov 13 2014 Miroslav Rezanina - rhev-2.1.2-8.el7 +- kvm-ide-Add-wwn-support-to-IDE-ATAPI-drive.patch [bz#1150820] +- kvm-exec-report-error-when-memory-hpagesize.patch [bz#1147354] +- kvm-exec-add-parameter-errp-to-gethugepagesize.patch [bz#1147354] +- kvm-block-curl-Improve-type-safety-of-s-timeout.patch [bz#1152901] +- kvm-virtio-serial-avoid-crash-when-port-has-no-name.patch [bz#1151947] +- Resolves: bz#1147354 + (Qemu core dump when boot up a guest on a non-existent hugepage path) +- Resolves: bz#1150820 + (fail to specify wwn for virtual IDE CD-ROM) +- Resolves: bz#1151947 + (virtconsole causes qemu-kvm core dump) +- Resolves: bz#1152901 + (block/curl: Fix type safety of s->timeout) + +* Thu Nov 06 2014 Miroslav Rezanina - rhev-2.1.2-7.el7 +- kvm-ac97-register-reset-via-qom.patch [bz#1141666] +- kvm-specfile-Require-glusterfs-api-3.6.patch [bz#1157329] +- kvm-smbios-Fix-assertion-on-socket-count-calculation.patch [bz#1146573] +- kvm-smbios-Encode-UUID-according-to-SMBIOS-specification.patch [bz#1152922] +- kvm-virtio-scsi-Report-error-if-num_queues-is-0-or-too-l.patch [bz#1146826] +- kvm-virtio-scsi-Fix-memory-leak-when-realize-failed.patch [bz#1146826] +- kvm-virtio-scsi-Fix-num_queue-input-validation.patch [bz#1146826] +- kvm-util-Improve-os_mem_prealloc-error-message.patch [bz#1153590] +- kvm-Downstream-only-Add-script-to-autoload-KVM-modules-o.patch [bz#1158250] +- kvm-Downstream-only-remove-uneeded-PCI-devices-for-POWER.patch [bz#1160120] +- kvm-Downstream-only-Remove-assorted-unneeded-devices-for.patch [bz#1160120] +- kvm-Downstream-only-Remove-ISA-bus-and-device-support-fo.patch [bz#1160120] +- kvm-well-defined-listing-order-for-machine-types.patch [bz#1145042] +- kvm-i386-pc-add-piix-and-q35-machtypes-to-sorting-famili.patch [bz#1145042] +- kvm-i386-pc-add-RHEL-machtypes-to-sorting-families-for-M.patch [bz#1145042] +- Resolves: bz#1141666 + (Qemu crashed if reboot guest after hot remove AC97 sound device) +- Resolves: bz#1145042 + (The output of "/usr/libexec/qemu-kvm -M ?" should be ordered.) +- Resolves: bz#1146573 + (qemu core dump when boot guest with smp(num) - rhev-2.1.2-6.el7 +- kvm-ivshmem-use-error_report.patch [bz#1104063] +- kvm-ivshmem-RHEL-only-remove-unsupported-code.patch [bz#1104063] +- kvm-ivshmem-RHEL-only-explicitly-remove-dead-code.patch [bz#1104063] +- kvm-Revert-rhel-Drop-ivshmem-device.patch [bz#1104063] +- kvm-serial-reset-state-at-startup.patch [bz#1135844] +- kvm-spice-call-qemu_spice_set_passwd-during-init.patch [bz#1140975] +- kvm-input-fix-send-key-monitor-command-release-event-ord.patch [bz#1145028 bz#1146801] +- kvm-virtio-scsi-sense-in-virtio_scsi_command_complete.patch [bz#1152830] +- Resolves: bz#1104063 + ([RHEL7.1 Feat] Enable qemu-kvm Inter VM Shared Memory (IVSHM) feature) +- Resolves: bz#1135844 + ([virtio-win]communication ports were marked with a yellow exclamation after hotplug pci-serial,pci-serial-2x,pci-serial-4x) +- Resolves: bz#1140975 + (fail to login spice session with password + expire time) +- Resolves: bz#1145028 + (send-key does not crash windows guest even when it should) +- Resolves: bz#1146801 + (sendkey: releasing order of combined keys was wrongly converse) +- Resolves: bz#1152830 + (Fix sense buffer in virtio-scsi LUN passthrough) + +* Fri Oct 24 2014 Miroslav Rezanina - rhev-2.1.2-5.el7 +- kvm-blockdev-Orphaned-drive-search.patch [bz#946993] +- kvm-blockdev-Allow-overriding-if_max_dev-property.patch [bz#946993] +- kvm-pc-vl-Add-units-per-default-bus-property.patch [bz#946993] +- kvm-ide-Update-ide_drive_get-to-be-HBA-agnostic.patch [bz#946993] +- kvm-qtest-bios-tables-Correct-Q35-command-line.patch [bz#946993] +- kvm-q35-ahci-Pick-up-cdrom-and-hda-options.patch [bz#946993] +- kvm-trace-events-drop-orphan-virtio_blk_data_plane_compl.patch [bz#1144325] +- kvm-trace-events-drop-orphan-usb_mtp_data_out.patch [bz#1144325] +- kvm-trace-events-drop-orphan-iscsi-trace-events.patch [bz#1144325] +- kvm-cleanup-trace-events.pl-Tighten-search-for-trace-eve.patch [bz#1144325] +- kvm-trace-events-Drop-unused-megasas-trace-event.patch [bz#1144325] +- kvm-trace-events-Drop-orphaned-monitor-trace-event.patch [bz#1144325] +- kvm-trace-events-Fix-comments-pointing-to-source-files.patch [bz#1144325] +- kvm-simpletrace-add-simpletrace.py-no-header-option.patch [bz#1155015] +- kvm-trace-extract-stap_escape-function-for-reuse.patch [bz#1155015] +- kvm-trace-add-tracetool-simpletrace_stap-format.patch [bz#1155015] +- kvm-trace-install-simpletrace-SystemTap-tapset.patch [bz#1155015] +- kvm-trace-install-trace-events-file.patch [bz#1155015] +- kvm-trace-add-SystemTap-init-scripts-for-simpletrace-bri.patch [bz#1155015] +- kvm-simpletrace-install-simpletrace.py.patch [bz#1155015] +- kvm-trace-add-systemtap-initscript-README-file-to-RPM.patch [bz#1155015] +- Resolves: bz#1144325 + (Can not probe "qemu.kvm.virtio_blk_data_plane_complete_request") +- Resolves: bz#1155015 + ([Fujitsu 7.1 FEAT]:QEMU: capturing trace data all the time using ftrace-based tracing) +- Resolves: bz#946993 + (Q35 does not honor -drive if=ide,... and its sugared forms -cdrom, -hda, ...) + +* Mon Oct 20 2014 Miroslav Rezanina - rhev-2.1.2-4.el7 +- kvm-seccomp-add-semctl-to-the-syscall-whitelist.patch [bz#1126704] +- kvm-dataplane-fix-virtio_blk_data_plane_create-op-blocke.patch [bz#1140001] +- kvm-block-fix-overlapping-multiwrite-requests.patch [bz#1123908] +- kvm-qemu-iotests-add-multiwrite-test-cases.patch [bz#1123908] +- Resolves: bz#1123908 + (block.c: multiwrite_merge() truncates overlapping requests) +- Resolves: bz#1126704 + (BUG: When use '-sandbox on'+'vnc'+'hda' and quit, qemu-kvm hang) +- Resolves: bz#1140001 + (data-plane hotplug should be refused to start if device is already in use (drive-mirror job)) + +* Fri Oct 10 2014 Miroslav Rezanina - rhev-2.1.2-3.el7 +- kvm-Disable-tests-for-removed-features.patch [bz#1108040] +- kvm-Disable-arm-board-types-using-lsi53c895a.patch [bz#1108040] +- kvm-libqtest-launch-QEMU-with-QEMU_AUDIO_DRV-none.patch [bz#1108040] +- kvm-Whitelist-blkdebug-driver.patch [bz#1108040] +- kvm-Turn-make-check-on.patch [bz#1108040] +- Resolves: bz#1108040 + (Enable make check for qemu-kvm-rhev 2.0 and newer) + +* Fri Oct 10 2014 Miroslav Rezanina - rhev-2.1.2-2.el7 +- kvm-RPM-spec-Add-enable-numa-to-configure-command-line.patch [bz#1076990] +- kvm-block.curl-adding-timeout-option.patch [bz#1132569] +- kvm-curl-Allow-a-cookie-or-cookies-to-be-sent-with-http-.patch [bz#1132569] +- kvm-curl-Don-t-deref-NULL-pointer-in-call-to-aio_poll.patch [bz#1132569] +- kvm-curl-Add-timeout-and-cookie-options-and-misc.-fix-RH.patch [bz#1132569] +- kvm-Introduce-cpu_clean_all_dirty.patch [bz#1143054] +- kvm-kvmclock-Ensure-proper-env-tsc-value-for-kvmclock_cu.patch [bz#1143054] +- kvm-kvmclock-Ensure-time-in-migration-never-goes-backwar.patch [bz#1143054] +- kvm-IDE-Fill-the-IDENTIFY-request-consistently.patch [bz#852348] +- kvm-ide-Add-resize-callback-to-ide-core.patch [bz#852348] +- kvm-virtio-balloon-fix-integer-overflow-in-memory-stats-.patch [bz#1140997] +- kvm-block-extend-BLOCK_IO_ERROR-event-with-nospace-indic.patch [bz#1117445] +- kvm-block-extend-BLOCK_IO_ERROR-with-reason-string.patch [bz#1117445] +- Resolves: bz#1076990 + (Enable complex memory requirements for virtual machines) +- Resolves: bz#1117445 + (QMP: extend block events with error information) +- Resolves: bz#1132569 + (RFE: Enable curl driver in qemu-kvm-rhev: https only) +- Resolves: bz#1140997 + (guest is stuck when setting balloon memory with large guest-stats-polling-interval) +- Resolves: bz#1143054 + (kvmclock: Ensure time in migration never goes backward (backport)) +- Resolves: bz#852348 + (fail to block_resize local data disk with IDE/AHCI disk_interface) + +* Fri Sep 26 2014 Miroslav Rezanina - rhev-2.1.2-1.el7 +- Rebase to qemu 2.1.2 [bz#1121609] +- Resolves: bz#1121609 + Rebase qemu-kvm-rhev to qemu 2.1.2 + +* Wed Sep 24 2014 Miroslav Rezanina - rhev-2.1.0-5.el7 +- kvm-target-i386-Reject-invalid-CPU-feature-names-on-the-.patch [bz#1055532] +- kvm-target-ppc-virtex-ml507-machine-type-should-depend-o.patch [bz#1113998] +- kvm-RHEL-only-Disable-tests-that-don-t-work-with-RHEL-bu.patch [bz#1113998] +- kvm-RHEL-onlyy-Disable-unused-ppc-machine-types.patch [bz#1113998] +- kvm-RHEL-only-Remove-unneeded-devices-from-ppc64-qemu-kv.patch [] +- kvm-RHEL-only-Replace-upstream-pseries-machine-types-wit.patch [] +- kvm-scsi-bus-prepare-scsi_req_new-for-introduction-of-pa.patch [bz#1123349] +- kvm-scsi-bus-introduce-parse_cdb-in-SCSIDeviceClass-and-.patch [bz#1123349] +- kvm-scsi-block-extract-scsi_block_is_passthrough.patch [bz#1123349] +- kvm-scsi-block-scsi-generic-implement-parse_cdb.patch [bz#1123349] +- kvm-virtio-scsi-implement-parse_cdb.patch [bz#1123349] +- kvm-exec-file_ram_alloc-print-error-when-prealloc-fails.patch [bz#1135893] +- kvm-pc-increase-maximal-VCPU-count-to-240.patch [bz#1144089] +- kvm-ssh-Enable-ssh-driver-in-qemu-kvm-rhev-RHBZ-1138359.patch [bz#1138359] +- Resolves: bz#1055532 + (QEMU should abort when invalid CPU flag name is used) +- Resolves: bz#1113998 + (RHEL Power/KVM (qemu-kvm-rhev)) +- Resolves: bz#1123349 + ([FJ7.0 Bug] SCSI command issued from KVM guest doesn't reach target device) +- Resolves: bz#1135893 + (qemu-kvm should report an error message when host's freehugepage memory < domain's memory) +- Resolves: bz#1138359 + (RFE: Enable ssh driver in qemu-kvm-rhev) +- Resolves: bz#1144089 + ([HP 7.1 FEAT] Increase qemu-kvm-rhev's VCPU limit to 240) + +* Wed Sep 17 2014 Miroslav Rezanina - rhev-2.1.0-4.el7 +- kvm-virtio-rng-add-some-trace-events.patch [bz#1129259] +- kvm-block-vhdx-add-error-check.patch [bz#1126976] +- kvm-block-VHDX-endian-fixes.patch [bz#1126976] +- kvm-qdev-monitor-include-QOM-properties-in-device-FOO-he.patch [bz#1133736] +- kvm-block-acquire-AioContext-in-qmp_block_resize.patch [bz#1136752] +- kvm-virtio-blk-allow-block_resize-with-dataplane.patch [bz#1136752] +- kvm-block-acquire-AioContext-in-do_drive_del.patch [bz#1136752] +- kvm-virtio-blk-allow-drive_del-with-dataplane.patch [bz#1136752] +- kvm-rhel-Add-rhel7.1.0-machine-types.patch [bz#1093023] +- kvm-vmstate_xhci_event-bug-compat-for-rhel7.0.0-machine-.patch [bz#1136512] +- kvm-pflash_cfi01-fixup-stale-DPRINTF-calls.patch [bz#1139706] +- kvm-pflash_cfi01-write-flash-contents-to-bdrv-on-incomin.patch [bz#1139706] +- kvm-ide-Fix-segfault-when-flushing-a-device-that-doesn-t.patch [bz#1140145] +- kvm-xhci-PCIe-endpoint-migration-compatibility-fix.patch [bz#1138579] +- kvm-rh-machine-types-xhci-PCIe-endpoint-migration-compat.patch [bz#1138579] +- Resolves: bz#1093023 + (provide RHEL-specific machine types in QEMU) +- Resolves: bz#1126976 + (VHDX image format does not work on PPC64 (Endian issues)) +- Resolves: bz#1129259 + (Add traces to virtio-rng device) +- Resolves: bz#1133736 + (qemu should provide iothread and x-data-plane properties for /usr/libexec/qemu-kvm -device virtio-blk-pci,?) +- Resolves: bz#1136512 + (rhel7.0.0 machtype compat after CVE-2014-5263 vmstate_xhci_event: fix unterminated field list) +- Resolves: bz#1136752 + (virtio-blk dataplane support for block_resize and hot unplug) +- Resolves: bz#1138579 + (Migration failed with nec-usb-xhci from RHEL7. 0 to RHEL7.1) +- Resolves: bz#1139706 + (pflash (UEFI varstore) migration shortcut for libvirt [RHEV]) +- Resolves: bz#1140145 + (qemu-kvm crashed when doing iofuzz testing) + +* Thu Aug 28 2014 Miroslav Rezanina - rhev-2.1.0-3.el7 +- kvm-Fix-pkgversion-value.patch [bz#1064742] +- kvm-virtio-serial-create-a-linked-list-of-all-active-dev.patch [bz#1003432] +- kvm-virtio-serial-search-for-duplicate-port-names-before.patch [bz#1003432] +- kvm-pc-RHEL-6-CPUID-compat-code-for-Broadwell-CPU-model.patch [bz#1111351] +- kvm-rpm-spec-build-qemu-kvm-with-lzo-and-snappy-enabled.patch [bz#1126933] +- Resolves: bz#1003432 + (qemu-kvm should not allow different virtio serial port use the same name) +- Resolves: bz#1064742 + (QMP: "query-version" doesn't include the -rhev prefix from the qemu-kvm-rhev package) +- Resolves: bz#1111351 + (RHEL-6.6 migration compatibility: CPU models) +- Resolves: bz#1126933 + ([FEAT RHEV7.1]: qemu: Support compression for dump-guest-memory command) + +* Mon Aug 18 2014 Miroslav Rezanina <> - rhev-2.1.0-2.el7 +- kvm-exit-when-no-kvm-and-vcpu-count-160.patch [bz#1076326 bz#1118665] +- kvm-Revert-Use-legacy-SMBIOS-for-rhel-machine-types.patch [bz#1118665] +- kvm-rhel-Use-SMBIOS-legacy-mode-for-machine-types-7.0.patch [bz#1118665] +- kvm-rhel-Suppress-hotplug-memory-address-space-for-machi.patch [bz#1118665] +- kvm-rhel-Fix-ACPI-table-size-for-machine-types-7.0.patch [bz#1118665] +- kvm-rhel-Fix-missing-pc-q35-rhel7.0.0-compatibility-prop.patch [bz#1118665] +- kvm-rhel-virtio-scsi-pci.any_layout-off-for-machine-type.patch [bz#1118665] +- kvm-rhel-PIIX4_PM.memory-hotplug-support-off-for-machine.patch [bz#1118665] +- kvm-rhel-apic.version-0x11-for-machine-types-7.0.patch [bz#1118665] +- kvm-rhel-nec-usb-xhci.superspeed-ports-first-off-for-mac.patch [bz#1118665] +- kvm-rhel-pci-serial.prog_if-0-for-machine-types-7.0.patch [bz#1118665] +- kvm-rhel-virtio-net-pci.guest_announce-off-for-machine-t.patch [bz#1118665] +- kvm-rhel-ICH9-LPC.memory-hotplug-support-off-for-machine.patch [bz#1118665] +- kvm-rhel-.power_controller_present-off-for-machine-types.patch [bz#1118665] +- kvm-rhel-virtio-net-pci.ctrl_guest_offloads-off-for-mach.patch [bz#1118665] +- kvm-pc-q35-rhel7.0.0-Disable-x2apic-default.patch [bz#1118665] +- Resolves: bz#1076326 + (qemu-kvm does not quit when booting guest w/ 161 vcpus and "-no-kvm") +- Resolves: bz#1118665 + (Migration: rhel7.0->rhev7.1) + +* Sat Aug 02 2014 Miroslav Rezanina - rhev-2.1.0-1.el7 +- Rebase to 2.1.0 [bz#1121609] +- Resolves: bz#1121609 + (Rebase qemu-kvm-rhev to qemu 2.1) + +* Wed Jul 09 2014 Miroslav Rezanina - rhev-2.0.0-3.el7 +- kvm-Remove-CONFIG_NE2000_ISA-from-all-config-files.patch [] +- kvm-Fix-conditional-rpmbuild.patch [] +- kvm-RHEL7-RHEV7.1-2.0-migration-compatibility.patch [bz#1085950] +- kvm-remove-superfluous-.hot_add_cpu-and-.max_cpus-initia.patch [bz#1085950] +- kvm-set-model-in-PC_RHEL6_5_COMPAT-for-qemu32-VCPU-RHEV-.patch [bz#1085950] +- kvm-Undo-Enable-x2apic-by-default-for-compatibility.patch [bz#1085950] +- kvm-qemu_loadvm_state-shadow-SeaBIOS-for-VM-incoming-fro.patch [bz#1103579] +- Resolves: bz#1085950 + (Migration/virtio-net: 7.0->vp-2.0-rc2: Mix of migration issues) +- Resolves: bz#1103579 + (fail to reboot guest after migration from RHEL6.5 host to RHEL7.0 host) + +* Fri May 30 2014 Miroslav Rezanina - rhev-2.0.0-2.el7 +- kvm-pc-add-hot_add_cpu-callback-to-all-machine-types.patch [bz#1093411] +- Resolves: bz#1093411 + (Hot unplug CPU not working for RHEL7 host) + +* Fri Apr 18 2014 Miroslav Rezanina - 2.0.0-1.el7ev +- Rebase to qemu 2.0.0 + +* Wed Apr 02 2014 Miroslav Rezanina - 1.5.3-60.el7 +- kvm-qcow2-fix-dangling-refcount-table-entry.patch [bz#1081793] +- kvm-qcow2-link-all-L2-meta-updates-in-preallocate.patch [bz#1081393] +- Resolves: bz#1081393 + (qemu-img will prompt that 'leaked clusters were found' while creating images with '-o preallocation=metadata,cluster_size<=1024') +- Resolves: bz#1081793 + (qemu-img core dumped when creating a qcow2 image base on block device(iscsi or libiscsi)) + +* Wed Mar 26 2014 Miroslav Rezanina - 1.5.3-59.el7 +- kvm-qemu-iotests-add-.-check-cloop-support.patch [bz#1066691] +- kvm-qemu-iotests-add-cloop-input-validation-tests.patch [bz#1066691] +- kvm-block-cloop-validate-block_size-header-field-CVE-201.patch [bz#1079455] +- kvm-block-cloop-prevent-offsets_size-integer-overflow-CV.patch [bz#1079320] +- kvm-block-cloop-refuse-images-with-huge-offsets-arrays-C.patch [bz#1079455] +- kvm-block-cloop-refuse-images-with-bogus-offsets-CVE-201.patch [bz#1079455] +- kvm-size-off-by-one.patch [bz#1066691] +- kvm-qemu-iotests-Support-for-bochs-format.patch [bz#1066691] +- kvm-bochs-Unify-header-structs-and-make-them-QEMU_PACKED.patch [bz#1066691] +- kvm-bochs-Use-unsigned-variables-for-offsets-and-sizes-C.patch [bz#1079339] +- kvm-bochs-Check-catalog_size-header-field-CVE-2014-0143.patch [bz#1079320] +- kvm-bochs-Check-extent_size-header-field-CVE-2014-0142.patch [bz#1079315] +- kvm-bochs-Fix-bitmap-offset-calculation.patch [bz#1066691] +- kvm-vpc-vhd-add-bounds-check-for-max_table_entries-and-b.patch [bz#1079455] +- kvm-vpc-Validate-block-size-CVE-2014-0142.patch [bz#1079315] +- kvm-vdi-add-bounds-checks-for-blocks_in_image-and-disk_s.patch [bz#1079455] +- kvm-vhdx-Bounds-checking-for-block_size-and-logical_sect.patch [bz#1079346] +- kvm-curl-check-data-size-before-memcpy-to-local-buffer.-.patch [bz#1079455] +- kvm-qcow2-Check-header_length-CVE-2014-0144.patch [bz#1079455] +- kvm-qcow2-Check-backing_file_offset-CVE-2014-0144.patch [bz#1079455] +- kvm-qcow2-Check-refcount-table-size-CVE-2014-0144.patch [bz#1079455] +- kvm-qcow2-Validate-refcount-table-offset.patch [bz#1066691] +- kvm-qcow2-Validate-snapshot-table-offset-size-CVE-2014-0.patch [bz#1079455] +- kvm-qcow2-Validate-active-L1-table-offset-and-size-CVE-2.patch [bz#1079455] +- kvm-qcow2-Fix-backing-file-name-length-check.patch [bz#1066691] +- kvm-qcow2-Don-t-rely-on-free_cluster_index-in-alloc_refc.patch [bz#1079339] +- kvm-qcow2-Avoid-integer-overflow-in-get_refcount-CVE-201.patch [bz#1079320] +- kvm-qcow2-Check-new-refcount-table-size-on-growth.patch [bz#1066691] +- kvm-qcow2-Fix-types-in-qcow2_alloc_clusters-and-alloc_cl.patch [bz#1066691] +- kvm-qcow2-Protect-against-some-integer-overflows-in-bdrv.patch [bz#1066691] +- kvm-qcow2-Fix-new-L1-table-size-check-CVE-2014-0143.patch [bz#1079320] +- kvm-dmg-coding-style-and-indentation-cleanup.patch [bz#1066691] +- kvm-dmg-prevent-out-of-bounds-array-access-on-terminator.patch [bz#1066691] +- kvm-dmg-drop-broken-bdrv_pread-loop.patch [bz#1066691] +- kvm-dmg-use-appropriate-types-when-reading-chunks.patch [bz#1066691] +- kvm-dmg-sanitize-chunk-length-and-sectorcount-CVE-2014-0.patch [bz#1079325] +- kvm-dmg-use-uint64_t-consistently-for-sectors-and-length.patch [bz#1066691] +- kvm-dmg-prevent-chunk-buffer-overflow-CVE-2014-0145.patch [bz#1079325] +- kvm-block-vdi-bounds-check-qemu-io-tests.patch [bz#1066691] +- kvm-block-Limit-request-size-CVE-2014-0143.patch [bz#1079320] +- kvm-qcow2-Fix-copy_sectors-with-VM-state.patch [bz#1066691] +- kvm-qcow2-Fix-NULL-dereference-in-qcow2_open-error-path-.patch [bz#1079333] +- kvm-qcow2-Fix-L1-allocation-size-in-qcow2_snapshot_load_.patch [bz#1079325] +- kvm-qcow2-Check-maximum-L1-size-in-qcow2_snapshot_load_t.patch [bz#1079320] +- kvm-qcow2-Limit-snapshot-table-size.patch [bz#1066691] +- kvm-parallels-Fix-catalog-size-integer-overflow-CVE-2014.patch [bz#1079320] +- kvm-parallels-Sanity-check-for-s-tracks-CVE-2014-0142.patch [bz#1079315] +- kvm-fix-machine-check-propagation.patch [bz#740107] +- Resolves: bz#1066691 + (qemu-kvm: include leftover patches from block layer security audit) +- Resolves: bz#1079315 + (CVE-2014-0142 qemu-kvm: qemu: crash by possible division by zero [rhel-7.0]) +- Resolves: bz#1079320 + (CVE-2014-0143 qemu-kvm: Qemu: block: multiple integer overflow flaws [rhel-7.0]) +- Resolves: bz#1079325 + (CVE-2014-0145 qemu-kvm: Qemu: prevent possible buffer overflows [rhel-7.0]) +- Resolves: bz#1079333 + (CVE-2014-0146 qemu-kvm: Qemu: qcow2: NULL dereference in qcow2_open() error path [rhel-7.0]) +- Resolves: bz#1079339 + (CVE-2014-0147 qemu-kvm: Qemu: block: possible crash due signed types or logic error [rhel-7.0]) +- Resolves: bz#1079346 + (CVE-2014-0148 qemu-kvm: Qemu: vhdx: bounds checking for block_size and logical_sector_size [rhel-7.0]) +- Resolves: bz#1079455 + (CVE-2014-0144 qemu-kvm: Qemu: block: missing input validation [rhel-7.0]) +- Resolves: bz#740107 + ([Hitachi 7.0 FEAT] KVM: MCA Recovery for KVM guest OS memory) + +* Wed Mar 26 2014 Miroslav Rezanina - 1.5.3-58.el7 +- kvm-pc-Use-cpu64-rhel6-CPU-model-by-default-on-rhel6-mac.patch [bz#1080170] +- kvm-target-i386-Copy-cpu64-rhel6-definition-into-qemu64.patch [bz#1078607 bz#1080170] +- Resolves: bz#1080170 + (intel 82576 VF not work in windows 2008 x86 - Code 12 [TestOnly]) +- Resolves: bz#1080170 + (Default CPU model for rhel6.* machine-types is different from RHEL-6) + +* Fri Mar 21 2014 Miroslav Rezanina - 1.5.3-57.el7 +- kvm-virtio-net-fix-guest-triggerable-buffer-overrun.patch [bz#1078308] +- Resolves: bz#1078308 + (EMBARGOED CVE-2014-0150 qemu: virtio-net: fix guest-triggerable buffer overrun [rhel-7.0]) + +* Fri Mar 21 2014 Miroslav Rezanina - 1.5.3-56.el7 +- kvm-configure-Fix-bugs-preventing-Ceph-inclusion.patch [bz#1078809] +- Resolves: bz#1078809 + (can not boot qemu-kvm-rhev with rbd image) + +* Wed Mar 19 2014 Miroslav Rezanina - 1.5.3-55.el7 +- kvm-scsi-Change-scsi-sense-buf-size-to-252.patch [bz#1058173] +- kvm-scsi-Fix-migration-of-scsi-sense-data.patch [bz#1058173] +- Resolves: bz#1058173 + (qemu-kvm core dump booting guest with scsi-generic disk attached when using built-in iscsi driver) + +* Wed Mar 19 2014 Miroslav Rezanina - 1.5.3-54.el7 +- kvm-qdev-monitor-Set-properties-after-parent-is-assigned.patch [bz#1046248] +- kvm-block-Update-image-size-in-bdrv_invalidate_cache.patch [bz#1048575] +- kvm-qcow2-Keep-option-in-qcow2_invalidate_cache.patch [bz#1048575] +- kvm-qcow2-Check-bs-drv-in-copy_sectors.patch [bz#1048575] +- kvm-block-bs-drv-may-be-NULL-in-bdrv_debug_resume.patch [bz#1048575] +- kvm-iotests-Test-corruption-during-COW-request.patch [bz#1048575] +- Resolves: bz#1046248 + (qemu-kvm crash when send "info qtree" after hot plug a device with invalid addr) +- Resolves: bz#1048575 + (Segmentation fault occurs after migrate guest(use scsi disk and add stress) to des machine) + +* Wed Mar 12 2014 Miroslav Rezanina - 1.5.3-53.el7 +- kvm-dataplane-Fix-startup-race.patch [bz#1069541] +- kvm-QMP-Relax-__com.redhat_drive_add-parameter-checking.patch [bz#1057471] +- kvm-all-exit-in-case-max-vcpus-exceeded.patch [bz#993429] +- kvm-block-gluster-code-movements-state-storage-changes.patch [bz#1031526] +- kvm-block-gluster-add-reopen-support.patch [bz#1031526] +- kvm-virtio-net-add-feature-bit-for-any-header-s-g.patch [bz#990989] +- kvm-spec-Add-README.rhel6-gpxe-source.patch [bz#1073774] +- kvm-pc-Add-RHEL6-e1000-gPXE-image.patch [bz#1073774] +- kvm-loader-rename-in_ram-has_mr.patch [bz#1064018] +- kvm-pc-avoid-duplicate-names-for-ROM-MRs.patch [bz#1064018] +- kvm-qemu-img-convert-Fix-progress-output.patch [bz#1073728] +- kvm-qemu-iotests-Test-progress-output-for-conversion.patch [bz#1073728] +- kvm-iscsi-Use-bs-sg-for-everything-else-than-disks.patch [bz#1067784] +- kvm-block-Fix-bs-request_alignment-assertion-for-bs-sg-1.patch [bz#1067784] +- kvm-qemu_file-use-fwrite-correctly.patch [bz#1005103] +- kvm-qemu_file-Fix-mismerge-of-use-fwrite-correctly.patch [bz#1005103] +- Resolves: bz#1005103 + (Migration should fail when migrate guest offline to a file which is specified to a readonly directory.) +- Resolves: bz#1031526 + (Can not commit snapshot when disk is using glusterfs:native backend) +- Resolves: bz#1057471 + (fail to do hot-plug with "discard = on" with "Invalid parameter 'discard'" error) +- Resolves: bz#1064018 + (abort from conflicting genroms) +- Resolves: bz#1067784 + (qemu-kvm: block.c:850: bdrv_open_common: Assertion `bs->request_alignment != 0' failed. Aborted (core dumped)) +- Resolves: bz#1069541 + (Segmentation fault when boot guest with dataplane=on) +- Resolves: bz#1073728 + (progress bar doesn't display when converting with -p) +- Resolves: bz#1073774 + (e1000 ROM cause migrate fail from RHEL6.5 host to RHEL7.0 host) +- Resolves: bz#990989 + (backport inline header virtio-net optimization) +- Resolves: bz#993429 + (kvm: test maximum number of vcpus supported (rhel7)) + +* Wed Mar 05 2014 Miroslav Rezanina - 1.5.3-52.el7 +- kvm-target-i386-Move-hyperv_-static-globals-to-X86CPU.patch [bz#1004773] +- kvm-Fix-uninitialized-cpuid_data.patch [bz#1057173] +- kvm-fix-coexistence-of-KVM-and-Hyper-V-leaves.patch [bz#1004773] +- kvm-make-availability-of-Hyper-V-enlightenments-depe.patch [bz#1004773] +- kvm-make-hyperv-hypercall-and-guest-os-id-MSRs-migra.patch [bz#1004773] +- kvm-make-hyperv-vapic-assist-page-migratable.patch [bz#1004773] +- kvm-target-i386-Convert-hv_relaxed-to-static-property.patch [bz#1057173] +- kvm-target-i386-Convert-hv_vapic-to-static-property.patch [bz#1057173] +- kvm-target-i386-Convert-hv_spinlocks-to-static-property.patch [bz#1057173] +- kvm-target-i386-Convert-check-and-enforce-to-static-prop.patch [bz#1004773] +- kvm-target-i386-Cleanup-foo-feature-handling.patch [bz#1057173] +- kvm-add-support-for-hyper-v-timers.patch [bz#1057173] +- Resolves: bz#1004773 + (Hyper-V guest OS id and hypercall MSRs not migrated) +- Resolves: bz#1057173 + (KVM Hyper-V Enlightenment - New feature - hv-time (QEMU)) + +* Wed Mar 05 2014 Miroslav Rezanina - 1.5.3-51.el7 +- kvm-qmp-access-the-local-QemuOptsLists-for-drive-option.patch [bz#1026184] +- kvm-qxl-add-sanity-check.patch [bz#751937] +- kvm-Fix-two-XBZRLE-corruption-issues.patch [bz#1063417] +- kvm-qdev-monitor-set-DeviceState-opts-before-calling-rea.patch [bz#1037956] +- kvm-vfio-blacklist-loading-of-unstable-roms.patch [bz#1037956] +- kvm-block-Set-block-filename-sizes-to-PATH_MAX-instead-o.patch [bz#1072339] +- Resolves: bz#1026184 + (QMP: querying -drive option returns a NULL parameter list) +- Resolves: bz#1037956 + (bnx2x: boot one guest to do vfio-pci with all PFs assigned in same group meet QEMU segmentation fault (Broadcom BCM57810 card)) +- Resolves: bz#1063417 + (google stressapptest vs Migration) +- Resolves: bz#1072339 + (RHEV: Cannot start VMs that have more than 23 snapshots.) +- Resolves: bz#751937 + (qxl triggers assert during iofuzz test) + +* Wed Feb 26 2014 Miroslav Rezanina - 1.5.3-50.el7 +- kvm-mempath-prefault-fix-off-by-one-error.patch [bz#1069039] +- kvm-qemu-option-has_help_option-and-is_valid_option_list.patch [bz#1065873] +- kvm-qemu-img-create-Support-multiple-o-options.patch [bz#1065873] +- kvm-qemu-img-convert-Support-multiple-o-options.patch [bz#1065873] +- kvm-qemu-img-amend-Support-multiple-o-options.patch [bz#1065873] +- kvm-qemu-img-Allow-o-help-with-incomplete-argument-list.patch [bz#1065873] +- kvm-qemu-iotests-Check-qemu-img-command-line-parsing.patch [bz#1065873] +- Resolves: bz#1065873 + (qemu-img silently ignores options with multiple -o parameters) +- Resolves: bz#1069039 + (-mem-prealloc option behaviour is opposite to expected) + +* Wed Feb 19 2014 Miroslav Rezanina - 1.5.3-49.el7 +- kvm-xhci-add-support-for-suspend-resume.patch [bz#1012365] +- kvm-qcow2-remove-n_start-and-n_end-of-qcow2_alloc_cluste.patch [bz#1049176] +- kvm-qcow2-fix-offset-overflow-in-qcow2_alloc_clusters_at.patch [bz#1049176] +- kvm-qcow2-check-for-NULL-l2meta.patch [bz#1055848] +- kvm-qemu-iotests-add-test-for-qcow2-preallocation-with-d.patch [bz#1055848] +- Resolves: bz#1012365 + (xhci usb storage lost in guest after wakeup from S3) +- Resolves: bz#1049176 + (qemu-img core dump when using "-o preallocation=metadata,cluster_size=2048k" to create image of libiscsi lun) +- Resolves: bz#1055848 + (qemu-img core dumped when cluster size is larger than the default value with opreallocation=metadata specified) + +* Mon Feb 17 2014 Miroslav Rezanina - 1.5.3-48.el7 +- kvm-spec-disable-qom-cast-debug.patch [bz#1063942] +- kvm-fix-guest-physical-bits-to-match-host-to-go-beyond-1.patch [bz#989677] +- kvm-monitor-Cleanup-mon-outbuf-on-write-error.patch [bz#1065225] +- Resolves: bz#1063942 + (configure qemu-kvm with --disable-qom-cast-debug) +- Resolves: bz#1065225 + (QMP socket breaks on unexpected close) +- Resolves: bz#989677 + ([HP 7.0 FEAT]: Increase KVM guest supported memory to 4TiB) + +* Wed Feb 12 2014 Miroslav Rezanina - 1.5.3-47.el7 +- kvm-seccomp-add-mkdir-and-fchmod-to-the-whitelist.patch [bz#1026314] +- kvm-seccomp-add-some-basic-shared-memory-syscalls-to-the.patch [bz#1026314] +- kvm-scsi-Support-TEST-UNIT-READY-in-the-dummy-LUN0.patch [bz#1004143] +- kvm-usb-add-vendor-request-defines.patch [bz#1039530] +- kvm-usb-move-usb_-hi-lo-helpers-to-header-file.patch [bz#1039530] +- kvm-usb-add-support-for-microsoft-os-descriptors.patch [bz#1039530] +- kvm-usb-add-microsoft-os-descriptors-compat-property.patch [bz#1039530] +- kvm-usb-hid-add-microsoft-os-descriptor-support.patch [bz#1039530] +- kvm-configure-add-option-to-disable-fstack-protect.patch [bz#1044182] +- kvm-exec-always-use-MADV_DONTFORK.patch [bz#1004197] +- kvm-pc-Save-size-of-RAM-below-4GB.patch [bz#1048080] +- kvm-acpi-Fix-PCI-hole-handling-on-build_srat.patch [bz#1048080] +- kvm-Add-check-for-cache-size-smaller-than-page-size.patch [bz#1017096] +- kvm-XBZRLE-cache-size-should-not-be-larger-than-guest-me.patch [bz#1047448] +- kvm-Don-t-abort-on-out-of-memory-when-creating-page-cach.patch [bz#1047448] +- kvm-Don-t-abort-on-memory-allocation-error.patch [bz#1047448] +- kvm-Set-xbzrle-buffers-to-NULL-after-freeing-them-to-avo.patch [bz#1038540] +- kvm-migration-fix-free-XBZRLE-decoded_buf-wrong.patch [bz#1038540] +- kvm-block-resize-backing-file-image-during-offline-commi.patch [bz#1047254] +- kvm-block-resize-backing-image-during-active-layer-commi.patch [bz#1047254] +- kvm-block-update-block-commit-documentation-regarding-im.patch [bz#1047254] +- kvm-block-Fix-bdrv_commit-return-value.patch [bz#1047254] +- kvm-block-remove-QED-.bdrv_make_empty-implementation.patch [bz#1047254] +- kvm-block-remove-qcow2-.bdrv_make_empty-implementation.patch [bz#1047254] +- kvm-qemu-progress-Drop-unused-include.patch [bz#997878] +- kvm-qemu-progress-Fix-progress-printing-on-SIGUSR1.patch [bz#997878] +- kvm-Documentation-qemu-img-Mention-SIGUSR1-progress-repo.patch [bz#997878] +- Resolves: bz#1004143 + ("test unit ready failed" on LUN 0 delays boot when a virtio-scsi target does not have any disk on LUN 0) +- Resolves: bz#1004197 + (Cannot hot-plug nic in windows VM when the vmem is larger) +- Resolves: bz#1017096 + (Fail to migrate while the size of migrate-compcache less then 4096) +- Resolves: bz#1026314 + (qemu-kvm hang when use '-sandbox on'+'vnc'+'hda') +- Resolves: bz#1038540 + (qemu-kvm aborted while cancel migration then restart it (with page delta compression)) +- Resolves: bz#1039530 + (add support for microsoft os descriptors) +- Resolves: bz#1044182 + (Relax qemu-kvm stack protection to -fstack-protector-strong) +- Resolves: bz#1047254 + (qemu-img failed to commit image) +- Resolves: bz#1047448 + (qemu-kvm core dump in src host when do migration with "migrate_set_capability xbzrle on and migrate_set_cache_size 10000G") +- Resolves: bz#1048080 + (Qemu-kvm NUMA emulation failed) +- Resolves: bz#997878 + (Kill -SIGUSR1 `pidof qemu-img convert` can not get progress of qemu-img) + +* Wed Feb 12 2014 Miroslav Rezanina - 1.5.3-46.el7 +- kvm-block-fix-backing-file-segfault.patch [bz#748906] +- kvm-block-Move-initialisation-of-BlockLimits-to-bdrv_ref.patch [bz#748906] +- kvm-raw-Fix-BlockLimits-passthrough.patch [bz#748906] +- kvm-block-Inherit-opt_transfer_length.patch [bz#748906] +- kvm-block-Update-BlockLimits-when-they-might-have-change.patch [bz#748906] +- kvm-qemu_memalign-Allow-small-alignments.patch [bz#748906] +- kvm-block-Detect-unaligned-length-in-bdrv_qiov_is_aligne.patch [bz#748906] +- kvm-block-Don-t-use-guest-sector-size-for-qemu_blockalig.patch [bz#748906] +- kvm-block-rename-buffer_alignment-to-guest_block_size.patch [bz#748906] +- kvm-raw-Probe-required-direct-I-O-alignment.patch [bz#748906] +- kvm-block-Introduce-bdrv_aligned_preadv.patch [bz#748906] +- kvm-block-Introduce-bdrv_co_do_preadv.patch [bz#748906] +- kvm-block-Introduce-bdrv_aligned_pwritev.patch [bz#748906] +- kvm-block-write-Handle-COR-dependency-after-I-O-throttli.patch [bz#748906] +- kvm-block-Introduce-bdrv_co_do_pwritev.patch [bz#748906] +- kvm-block-Switch-BdrvTrackedRequest-to-byte-granularity.patch [bz#748906] +- kvm-block-Allow-waiting-for-overlapping-requests-between.patch [bz#748906] +- kvm-block-use-DIV_ROUND_UP-in-bdrv_co_do_readv.patch [bz#748906] +- kvm-block-Make-zero-after-EOF-work-with-larger-alignment.patch [bz#748906] +- kvm-block-Generalise-and-optimise-COR-serialisation.patch [bz#748906] +- kvm-block-Make-overlap-range-for-serialisation-dynamic.patch [bz#748906] +- kvm-block-Fix-32-bit-truncation-in-mark_request_serialis.patch [bz#748906] +- kvm-block-Allow-wait_serialising_requests-at-any-point.patch [bz#748906] +- kvm-block-Align-requests-in-bdrv_co_do_pwritev.patch [bz#748906] +- kvm-lock-Fix-memory-leaks-in-bdrv_co_do_pwritev.patch [bz#748906] +- kvm-block-Assert-serialisation-assumptions-in-pwritev.patch [bz#748906] +- kvm-block-Change-coroutine-wrapper-to-byte-granularity.patch [bz#748906] +- kvm-block-Make-bdrv_pread-a-bdrv_prwv_co-wrapper.patch [bz#748906] +- kvm-block-Make-bdrv_pwrite-a-bdrv_prwv_co-wrapper.patch [bz#748906] +- kvm-iscsi-Set-bs-request_alignment.patch [bz#748906] +- kvm-blkdebug-Make-required-alignment-configurable.patch [bz#748906] +- kvm-blkdebug-Don-t-leak-bs-file-on-failure.patch [bz#748906] +- kvm-qemu-io-New-command-sleep.patch [bz#748906] +- kvm-qemu-iotests-Filter-out-qemu-io-prompt.patch [bz#748906] +- kvm-qemu-iotests-Test-pwritev-RMW-logic.patch [bz#748906] +- kvm-block-bdrv_aligned_pwritev-Assert-overlap-range.patch [bz#748906] +- kvm-block-Don-t-call-ROUND_UP-with-negative-values.patch [bz#748906] +- Resolves: bz#748906 + (qemu fails on disk with 4k sectors and cache=off) + +* Wed Feb 05 2014 Miroslav Rezanina - 1.5.3-45.el7 +- kvm-vfio-pci-Fail-initfn-on-DMA-mapping-errors.patch [bz#1044815] +- kvm-vfio-Destroy-memory-regions.patch [bz#1052030] +- kvm-docs-qcow2-compat-1.1-is-now-the-default.patch [bz#1048092] +- kvm-hda-codec-disable-streams-on-reset.patch [bz#947812] +- kvm-QEMUBH-make-AioContext-s-bh-re-entrant.patch [bz#1009297] +- kvm-qxl-replace-pipe-signaling-with-bottom-half.patch [bz#1009297] +- Resolves: bz#1009297 + (RHEL7.0 guest gui can not be used in dest host after migration) +- Resolves: bz#1044815 + (vfio initfn succeeds even if IOMMU mappings fail) +- Resolves: bz#1048092 + (manpage of qemu-img contains error statement about compat option) +- Resolves: bz#1052030 + (src qemu-kvm core dump after hotplug/unhotplug GPU device and do local migration) +- Resolves: bz#947812 + (There's a shot voice after 'system_reset' during playing music inside rhel6 guest w/ intel-hda device) + +* Wed Jan 29 2014 Miroslav Rezanina - 1.5.3-44.el7 +- kvm-Partially-revert-rhel-Drop-cfi.pflash01-and-isa-ide-.patch [bz#1032346] +- kvm-Revert-pc-Disable-the-use-flash-device-for-BIOS-unle.patch [bz#1032346] +- kvm-memory-Replace-open-coded-memory_region_is_romd.patch [bz#1032346] +- kvm-memory-Rename-readable-flag-to-romd_mode.patch [bz#1032346] +- kvm-isapc-Fix-non-KVM-qemu-boot-read-write-memory-for-is.patch [bz#1032346] +- kvm-add-kvm_readonly_mem_enabled.patch [bz#1032346] +- kvm-support-using-KVM_MEM_READONLY-flag-for-regions.patch [bz#1032346] +- kvm-pc_sysfw-allow-flash-pflash-memory-to-be-used-with-K.patch [bz#1032346] +- kvm-fix-double-free-the-memslot-in-kvm_set_phys_mem.patch [bz#1032346] +- kvm-sysfw-remove-read-only-pc_sysfw_flash_vs_rom_bug_com.patch [bz#1032346] +- kvm-pc_sysfw-remove-the-rom_only-property.patch [bz#1032346] +- kvm-pc_sysfw-do-not-make-it-a-device-anymore.patch [bz#1032346] +- kvm-hw-i386-pc_sysfw-support-two-flash-drives.patch [bz#1032346] +- kvm-i440fx-test-qtest_start-should-be-paired-with-qtest_.patch [bz#1032346] +- kvm-i440fx-test-give-each-GTest-case-its-own-qtest.patch [bz#1032346] +- kvm-i440fx-test-generate-temporary-firmware-blob.patch [bz#1032346] +- kvm-i440fx-test-verify-firmware-under-4G-and-1M-both-bio.patch [bz#1032346] +- kvm-piix-fix-32bit-pci-hole.patch [bz#1032346] +- kvm-qapi-Add-backing-to-BlockStats.patch [bz#1041564] +- kvm-pc-Disable-RDTSCP-unconditionally-on-rhel6.-machine-.patch [bz#918907] +- kvm-pc-Disable-RDTSCP-on-AMD-CPU-models.patch [bz#1056428 bz#874400] +- kvm-block-add-.bdrv_reopen_prepare-stub-for-iscsi.patch [bz#1030301] +- Resolves: bz#1030301 + (qemu-img can not merge live snapshot to backing file(r/w backing file via libiscsi)) +- Resolves: bz#1032346 + (basic OVMF support (non-volatile UEFI variables in flash, and fixup for ACPI tables)) +- Resolves: bz#1041564 + ([NFR] qemu: Returning the watermark for all the images opened for writing) +- Resolves: bz#1056428 + ("rdtscp" flag defined on Opteron_G5 model and cann't be exposed to guest) +- Resolves: bz#874400 + ("rdtscp" flag defined on Opteron_G5 model and cann't be exposed to guest) +- Resolves: bz#918907 + (provide backwards-compatible RHEL specific machine types in QEMU - CPU features) + +* Mon Jan 27 2014 Miroslav Rezanina - 1.5.3-43.el7 +- kvm-piix-gigabyte-alignment-for-ram.patch [bz#1026548] +- kvm-pc_piix-document-gigabyte_align.patch [bz#1026548] +- kvm-q35-gigabyle-alignment-for-ram.patch [bz#1026548] +- kvm-virtio-bus-remove-vdev-field.patch [bz#983344] +- kvm-virtio-pci-remove-vdev-field.patch [bz#983344] +- kvm-virtio-bus-cleanup-plug-unplug-interface.patch [bz#983344] +- kvm-virtio-blk-switch-exit-callback-to-VirtioDeviceClass.patch [bz#983344] +- kvm-virtio-serial-switch-exit-callback-to-VirtioDeviceCl.patch [bz#983344] +- kvm-virtio-net-switch-exit-callback-to-VirtioDeviceClass.patch [bz#983344] +- kvm-virtio-scsi-switch-exit-callback-to-VirtioDeviceClas.patch [bz#983344] +- kvm-virtio-balloon-switch-exit-callback-to-VirtioDeviceC.patch [bz#983344] +- kvm-virtio-rng-switch-exit-callback-to-VirtioDeviceClass.patch [bz#983344] +- kvm-virtio-pci-add-device_unplugged-callback.patch [bz#983344] +- kvm-block-use-correct-filename-for-error-report.patch [bz#1051438] +- Resolves: bz#1026548 + (i386: pc: align gpa<->hpa on 1GB boundary) +- Resolves: bz#1051438 + (Error message contains garbled characters when unable to open image due to bad permissions (permission denied).) +- Resolves: bz#983344 + (QEMU core dump and host will reboot when do hot-unplug a virtio-blk disk which use the switch behind switch) + +* Fri Jan 24 2014 Daniel Mach - 10:1.5.3-42 +- Mass rebuild 2014-01-24 + +* Wed Jan 22 2014 Miroslav Rezanina - 1.5.3-41.el7 +- kvm-help-add-id-suboption-to-iscsi.patch [bz#1019221] +- kvm-scsi-disk-add-UNMAP-limits-to-block-limits-VPD-page.patch [bz#1037503] +- kvm-qdev-Fix-32-bit-compilation-in-print_size.patch [bz#1034876] +- kvm-qdev-Use-clz-in-print_size.patch [bz#1034876] +- Resolves: bz#1019221 + (Iscsi miss id sub-option in help output) +- Resolves: bz#1034876 + (export acpi tables to guests) +- Resolves: bz#1037503 + (fix thin provisioning support for block device backends) + +* Wed Jan 22 2014 Miroslav Rezanina - 1.5.3-40.el7 +- kvm-avoid-a-bogus-COMPLETED-CANCELLED-transition.patch [bz#1053699] +- kvm-introduce-MIG_STATE_CANCELLING-state.patch [bz#1053699] +- kvm-vvfat-use-bdrv_new-to-allocate-BlockDriverState.patch [bz#1041301] +- kvm-block-implement-reference-count-for-BlockDriverState.patch [bz#1041301] +- kvm-block-make-bdrv_delete-static.patch [bz#1041301] +- kvm-migration-omit-drive-ref-as-we-have-bdrv_ref-now.patch [bz#1041301] +- kvm-xen_disk-simplify-blk_disconnect-with-refcnt.patch [bz#1041301] +- kvm-nbd-use-BlockDriverState-refcnt.patch [bz#1041301] +- kvm-block-use-BDS-ref-for-block-jobs.patch [bz#1041301] +- kvm-block-Make-BlockJobTypes-const.patch [bz#1041301] +- kvm-blockjob-rename-BlockJobType-to-BlockJobDriver.patch [bz#1041301] +- kvm-qapi-Introduce-enum-BlockJobType.patch [bz#1041301] +- kvm-qapi-make-use-of-new-BlockJobType.patch [bz#1041301] +- kvm-mirror-Don-t-close-target.patch [bz#1041301] +- kvm-mirror-Move-base-to-MirrorBlockJob.patch [bz#1041301] +- kvm-block-Add-commit_active_start.patch [bz#1041301] +- kvm-commit-Support-commit-active-layer.patch [bz#1041301] +- kvm-qemu-iotests-prefill-some-data-to-test-image.patch [bz#1041301] +- kvm-qemu-iotests-Update-test-cases-for-commit-active.patch [bz#1041301] +- kvm-commit-Remove-unused-check.patch [bz#1041301] +- kvm-blockdev-use-bdrv_getlength-in-qmp_drive_mirror.patch [bz#921890] +- kvm-qemu-iotests-make-assert_no_active_block_jobs-common.patch [bz#921890] +- kvm-block-drive-mirror-Check-for-NULL-backing_hd.patch [bz#921890] +- kvm-qemu-iotests-Extend-041-for-unbacked-mirroring.patch [bz#921890] +- kvm-qapi-schema-Update-description-for-NewImageMode.patch [bz#921890] +- kvm-block-drive-mirror-Reuse-backing-HD-for-sync-none.patch [bz#921890] +- kvm-qemu-iotests-Fix-test-041.patch [bz#921890] +- kvm-scsi-bus-fix-transfer-length-and-direction-for-VERIF.patch [bz#1035644] +- kvm-scsi-disk-fix-VERIFY-emulation.patch [bz#1035644] +- kvm-block-ensure-bdrv_drain_all-works-during-bdrv_delete.patch [bz#1041301] +- kvm-use-recommended-max-vcpu-count.patch [bz#998708] +- kvm-pc-Create-pc_compat_rhel-functions.patch [bz#1049706] +- kvm-pc-Enable-x2apic-by-default-on-more-recent-CPU-model.patch [bz#1049706] +- kvm-Build-all-subpackages-for-RHEV.patch [bz#1007204] +- Resolves: bz#1007204 + (qemu-img-rhev qemu-kvm-rhev-tools are not built for qemu-kvm-1.5.3-3.el7) +- Resolves: bz#1035644 + (rhel7.0host + windows guest + virtio-win + 'chkdsk' in the guest gives qemu assertion in scsi_dma_complete) +- Resolves: bz#1041301 + (live snapshot merge (commit) of the active layer) +- Resolves: bz#1049706 + (MIss CPUID_EXT_X2APIC in Westmere cpu model) +- Resolves: bz#1053699 + (Backport Cancelled race condition fixes) +- Resolves: bz#921890 + (Core dump when block mirror with "sync" is "none" and mode is "absolute-paths") +- Resolves: bz#998708 + (qemu-kvm: maximum vcpu should be recommended maximum) + +* Tue Jan 21 2014 Miroslav Rezanina - 1.5.3-39.el7 +- kvm-Revert-qdev-monitor-Fix-crash-when-device_add-is-cal.patch [bz#669524] +- kvm-Revert-qdev-Do-not-let-the-user-try-to-device_add-wh.patch [bz#669524] +- kvm-qdev-monitor-Clean-up-qdev_device_add-variable-namin.patch [bz#669524] +- kvm-qdev-monitor-Fix-crash-when-device_add-is-called.2.patch.patch [bz#669524] +- kvm-qdev-monitor-Avoid-qdev-as-variable-name.patch [bz#669524] +- kvm-qdev-monitor-Inline-qdev_init-for-device_add.patch [bz#669524] +- kvm-qdev-Do-not-let-the-user-try-to-device_add-when-it.2.patch.patch [bz#669524] +- kvm-qdev-monitor-Avoid-device_add-crashing-on-non-device.patch [bz#669524] +- kvm-qdev-monitor-Improve-error-message-for-device-nonexi.patch [bz#669524] +- kvm-exec-change-well-known-physical-sections-to-macros.patch [bz#1003535] +- kvm-exec-separate-sections-and-nodes-per-address-space.patch [bz#1003535] +- Resolves: bz#1003535 + (qemu-kvm core dump when boot vm with more than 32 virtio disks/nics) +- Resolves: bz#669524 + (Confusing error message from -device ) + +* Fri Jan 17 2014 Miroslav Rezanina - 1.5.3-38.el7 +- kvm-intel-hda-fix-position-buffer.patch [bz#947785] +- kvm-The-calculation-of-bytes_xfer-in-qemu_put_buffer-is-.patch [bz#1003467] +- kvm-migration-Fix-rate-limit.patch [bz#1003467] +- kvm-audio-honor-QEMU_AUDIO_TIMER_PERIOD-instead-of-wakin.patch [bz#1017636] +- kvm-audio-Lower-default-wakeup-rate-to-100-times-second.patch [bz#1017636] +- kvm-audio-adjust-pulse-to-100Hz-wakeup-rate.patch [bz#1017636] +- kvm-pc-Fix-rhel6.-3dnow-3dnowext-compat-bits.patch [bz#918907] +- kvm-add-firmware-to-machine-options.patch [bz#1038603] +- kvm-switch-rhel7-machine-types-to-big-bios.patch [bz#1038603] +- kvm-add-bios-256k.bin-from-seabios-bin-1.7.2.2-10.el7.no.patch [bz#1038603] +- kvm-pci-fix-pci-bridge-fw-path.patch [bz#1034518] +- kvm-hw-cannot_instantiate_with_device_add_yet-due-to-poi.patch [bz#1031098] +- kvm-qdev-Document-that-pointer-properties-kill-device_ad.patch [bz#1031098] +- kvm-Add-back-no-hpet-but-ignore-it.patch [bz#1044742] +- Resolves: bz#1003467 + (Backport migration fixes from post qemu 1.6) +- Resolves: bz#1017636 + (PATCH: fix qemu using 50% host cpu when audio is playing) +- Resolves: bz#1031098 + (Disable device smbus-eeprom) +- Resolves: bz#1034518 + (boot order wrong with q35) +- Resolves: bz#1038603 + (make seabios 256k for rhel7 machine types) +- Resolves: bz#1044742 + (Cannot create guest on remote RHEL7 host using F20 virt-manager, libvirt's qemu -no-hpet detection is broken) +- Resolves: bz#918907 + (provide backwards-compatible RHEL specific machine types in QEMU - CPU features) +- Resolves: bz#947785 + (In rhel6.4 guest sound recorder doesn't work when playing audio) + +* Wed Jan 15 2014 Miroslav Rezanina - 1.5.3-37.el7 +- kvm-bitmap-use-long-as-index.patch [bz#997559] +- kvm-memory-cpu_physical_memory_set_dirty_flags-result-is.patch [bz#997559] +- kvm-memory-cpu_physical_memory_set_dirty_range-return-vo.patch [bz#997559] +- kvm-exec-use-accessor-function-to-know-if-memory-is-dirt.patch [bz#997559] +- kvm-memory-create-function-to-set-a-single-dirty-bit.patch [bz#997559] +- kvm-exec-drop-useless-if.patch [bz#997559] +- kvm-exec-create-function-to-get-a-single-dirty-bit.patch [bz#997559] +- kvm-memory-make-cpu_physical_memory_is_dirty-return-bool.patch [bz#997559] +- kvm-memory-all-users-of-cpu_physical_memory_get_dirty-us.patch [bz#997559] +- kvm-memory-set-single-dirty-flags-when-possible.patch [bz#997559] +- kvm-memory-cpu_physical_memory_set_dirty_range-always-di.patch [bz#997559] +- kvm-memory-cpu_physical_memory_mask_dirty_range-always-c.patch [bz#997559] +- kvm-memory-use-bit-2-for-migration.patch [bz#997559] +- kvm-memory-make-sure-that-client-is-always-inside-range.patch [bz#997559] +- kvm-memory-only-resize-dirty-bitmap-when-memory-size-inc.patch [bz#997559] +- kvm-memory-cpu_physical_memory_clear_dirty_flag-result-i.patch [bz#997559] +- kvm-bitmap-Add-bitmap_zero_extend-operation.patch [bz#997559] +- kvm-memory-split-dirty-bitmap-into-three.patch [bz#997559] +- kvm-memory-unfold-cpu_physical_memory_clear_dirty_flag-i.patch [bz#997559] +- kvm-memory-unfold-cpu_physical_memory_set_dirty-in-its-o.patch [bz#997559] +- kvm-memory-unfold-cpu_physical_memory_set_dirty_flag.patch [bz#997559] +- kvm-memory-make-cpu_physical_memory_get_dirty-the-main-f.patch [bz#997559] +- kvm-memory-cpu_physical_memory_get_dirty-is-used-as-retu.patch [bz#997559] +- kvm-memory-s-mask-clear-cpu_physical_memory_mask_dirty_r.patch [bz#997559] +- kvm-memory-use-find_next_bit-to-find-dirty-bits.patch [bz#997559] +- kvm-memory-cpu_physical_memory_set_dirty_range-now-uses-.patch [bz#997559] +- kvm-memory-cpu_physical_memory_clear_dirty_range-now-use.patch [bz#997559] +- kvm-memory-s-dirty-clean-in-cpu_physical_memory_is_dirty.patch [bz#997559] +- kvm-memory-make-cpu_physical_memory_reset_dirty-take-a-l.patch [bz#997559] +- kvm-exec-Remove-unused-global-variable-phys_ram_fd.patch [bz#997559] +- kvm-memory-cpu_physical_memory_set_dirty_tracking-should.patch [bz#997559] +- kvm-memory-move-private-types-to-exec.c.patch [bz#997559] +- kvm-memory-split-cpu_physical_memory_-functions-to-its-o.patch [bz#997559] +- kvm-memory-unfold-memory_region_test_and_clear.patch [bz#997559] +- kvm-use-directly-cpu_physical_memory_-api-for-tracki.patch [bz#997559] +- kvm-refactor-start-address-calculation.patch [bz#997559] +- kvm-memory-move-bitmap-synchronization-to-its-own-functi.patch [bz#997559] +- kvm-memory-syncronize-kvm-bitmap-using-bitmaps-operation.patch [bz#997559] +- kvm-ram-split-function-that-synchronizes-a-range.patch [bz#997559] +- kvm-migration-synchronize-memory-bitmap-64bits-at-a-time.patch [bz#997559] +- Resolves: bz#997559 + (Improve live migration bitmap handling) + +* Tue Jan 14 2014 Miroslav Rezanina - 1.5.3-36.el7 +- kvm-Add-support-statement-to-help-output.patch [bz#972773] +- kvm-__com.redhat_qxl_screendump-add-docs.patch [bz#903910] +- kvm-vl-Round-memory-sizes-below-2MiB-up-to-2MiB.patch [bz#999836] +- kvm-seccomp-exit-if-seccomp_init-fails.patch [bz#1044845] +- kvm-redhat-qemu-kvm.spec-require-python-for-build.patch [bz#1034876] +- kvm-redhat-qemu-kvm.spec-require-iasl.patch [bz#1034876] +- kvm-configure-make-iasl-option-actually-work.patch [bz#1034876] +- kvm-redhat-qemu-kvm.spec-add-cpp-as-build-dependency.patch [bz#1034876] +- kvm-acpi-build-disable-with-no-acpi.patch [bz#1045386] +- kvm-ehci-implement-port-wakeup.patch [bz#1039513] +- kvm-qdev-monitor-Fix-crash-when-device_add-is-called-wit.patch [bz#1026712 bz#1046007] +- kvm-block-vhdx-improve-error-message-and-.bdrv_check-imp.patch [bz#1035001] +- kvm-docs-updated-qemu-img-man-page-and-qemu-doc-to-refle.patch [bz#1017650] +- kvm-enable-pvticketlocks-by-default.patch [bz#1052340] +- kvm-fix-boot-strict-regressed-in-commit-6ef4716.patch [bz#997817] +- kvm-vl-make-boot_strict-variable-static-not-used-outside.patch [bz#997817] +- Resolves: bz#1017650 + (need to update qemu-img man pages on "VHDX" format) +- Resolves: bz#1026712 + (Qemu core dumpd when boot guest with driver name as "virtio-pci") +- Resolves: bz#1034876 + (export acpi tables to guests) +- Resolves: bz#1035001 + (VHDX: journal log should not be replayed by default, but rather via qemu-img check -r all) +- Resolves: bz#1039513 + (backport remote wakeup for ehci) +- Resolves: bz#1044845 + (QEMU seccomp sandbox - exit if seccomp_init() fails) +- Resolves: bz#1045386 + (qemu-kvm: hw/i386/acpi-build.c:135: acpi_get_pm_info: Assertion `obj' failed.) +- Resolves: bz#1046007 + (qemu-kvm aborted when hot plug PCI device to guest with romfile and rombar=0) +- Resolves: bz#1052340 + (pvticketlocks: default on) +- Resolves: bz#903910 + (RHEL7 does not have equivalent functionality for __com.redhat_qxl_screendump) +- Resolves: bz#972773 + (RHEL7: Clarify support statement in KVM help) +- Resolves: bz#997817 + (-boot order and -boot once regressed since RHEL-6) +- Resolves: bz#999836 + (-m 1 crashes) + +* Thu Jan 09 2014 Miroslav Rezanina - 1.5.3-35.el7 +- kvm-option-Add-assigned-flag-to-QEMUOptionParameter.patch [bz#1033490] +- kvm-qcow2-refcount-Snapshot-update-for-zero-clusters.patch [bz#1033490] +- kvm-qemu-iotests-Snapshotting-zero-clusters.patch [bz#1033490] +- kvm-block-Image-file-option-amendment.patch [bz#1033490] +- kvm-qcow2-cache-Empty-cache.patch [bz#1033490] +- kvm-qcow2-cluster-Expand-zero-clusters.patch [bz#1033490] +- kvm-qcow2-Save-refcount-order-in-BDRVQcowState.patch [bz#1033490] +- kvm-qcow2-Implement-bdrv_amend_options.patch [bz#1033490] +- kvm-qcow2-Correct-bitmap-size-in-zero-expansion.patch [bz#1033490] +- kvm-qcow2-Free-only-newly-allocated-clusters-on-error.patch [bz#1033490] +- kvm-qcow2-Add-missing-space-in-error-message.patch [bz#1033490] +- kvm-qemu-iotest-qcow2-image-option-amendment.patch [bz#1033490] +- kvm-qemu-iotests-New-test-case-in-061.patch [bz#1033490] +- kvm-qemu-iotests-Preallocated-zero-clusters-in-061.patch [bz#1033490] +- Resolves: bz#1033490 + (Cannot upgrade/downgrade qcow2 images) + +* Wed Jan 08 2014 Miroslav Rezanina - 1.5.3-34.el7 +- kvm-block-stream-Don-t-stream-unbacked-devices.patch [bz#965636] +- kvm-qemu-io-Let-open-pass-options-to-block-driver.patch [bz#1004347] +- kvm-qcow2.py-Subcommand-for-changing-header-fields.patch [bz#1004347] +- kvm-qemu-iotests-Remaining-error-propagation-adjustments.patch [bz#1004347] +- kvm-qemu-iotests-Add-test-for-inactive-L2-overlap.patch [bz#1004347] +- kvm-qemu-iotests-Adjust-test-result-039.patch [bz#1004347] +- kvm-virtio-net-don-t-update-mac_table-in-error-state.patch [bz#1048671] +- kvm-qcow2-Zero-initialise-first-cluster-for-new-images.patch [bz#1032904] +- Resolves: bz#1004347 + (Backport qcow2 corruption prevention patches) +- Resolves: bz#1032904 + (qemu-img can not create libiscsi qcow2_v3 image) +- Resolves: bz#1048671 + (virtio-net: mac_table change isn't recovered in error state) +- Resolves: bz#965636 + (streaming with no backing file should not do anything) + +* Wed Jan 08 2014 Miroslav Rezanina - 1.5.3-33.el7 +- kvm-block-qemu-iotests-for-vhdx-read-sample-dynamic-imag.patch [bz#879234] +- kvm-block-qemu-iotests-add-quotes-to-TEST_IMG-usage-io-p.patch [bz#879234] +- kvm-block-qemu-iotests-fix-_make_test_img-to-work-with-s.patch [bz#879234] +- kvm-block-qemu-iotests-add-quotes-to-TEST_IMG.base-usage.patch [bz#879234] +- kvm-block-qemu-iotests-add-quotes-to-TEST_IMG-usage-in-0.patch [bz#879234] +- kvm-block-qemu-iotests-removes-duplicate-double-quotes-i.patch [bz#879234] +- kvm-block-vhdx-minor-comments-and-typo-correction.patch [bz#879234] +- kvm-block-vhdx-add-header-update-capability.patch [bz#879234] +- kvm-block-vhdx-code-movement-VHDXMetadataEntries-and-BDR.patch [bz#879234] +- kvm-block-vhdx-log-support-struct-and-defines.patch [bz#879234] +- kvm-block-vhdx-break-endian-translation-functions-out.patch [bz#879234] +- kvm-block-vhdx-update-log-guid-in-header-and-first-write.patch [bz#879234] +- kvm-block-vhdx-code-movement-move-vhdx_close-above-vhdx_.patch [bz#879234] +- kvm-block-vhdx-log-parsing-replay-and-flush-support.patch [bz#879234] +- kvm-block-vhdx-add-region-overlap-detection-for-image-fi.patch [bz#879234] +- kvm-block-vhdx-add-log-write-support.patch [bz#879234] +- kvm-block-vhdx-write-support.patch [bz#879234] +- kvm-block-vhdx-remove-BAT-file-offset-bit-shifting.patch [bz#879234] +- kvm-block-vhdx-move-more-endian-translations-to-vhdx-end.patch [bz#879234] +- kvm-block-vhdx-break-out-code-operations-to-functions.patch [bz#879234] +- kvm-block-vhdx-fix-comment-typos-in-header-fix-incorrect.patch [bz#879234] +- kvm-block-vhdx-add-.bdrv_create-support.patch [bz#879234] +- kvm-block-vhdx-update-_make_test_img-to-filter-out-vhdx-.patch [bz#879234] +- kvm-block-qemu-iotests-for-vhdx-add-write-test-support.patch [bz#879234] +- kvm-block-vhdx-qemu-iotest-log-replay-of-data-sector.patch [bz#879234] +- Resolves: bz#879234 + ([RFE] qemu-img: Add/improve support for VHDX format) + +* Mon Jan 06 2014 Michal Novotny - 1.5.3-32.el7 +- kvm-block-change-default-of-.has_zero_init-to-0.patch.patch [bz#1007815] +- kvm-iscsi-factor-out-sector-conversions.patch.patch [bz#1007815] +- kvm-iscsi-add-logical-block-provisioning-information-to-.patch.patch [bz#1007815] +- kvm-iscsi-add-.bdrv_get_block_status.patch.patch.patch [bz#1007815] +- kvm-iscsi-split-discard-requests-in-multiple-parts.patch.patch.patch [bz#1007815] +- kvm-block-make-BdrvRequestFlags-public.patch.patch.patch [bz#1007815] +- kvm-block-add-flags-to-bdrv_-_write_zeroes.patch.patch.patch [bz#1007815] +- kvm-block-introduce-BDRV_REQ_MAY_UNMAP-request-flag.patch.patch.patch [bz#1007815] +- kvm-block-add-logical-block-provisioning-info-to-BlockDr.patch.patch.patch [bz#1007815] +- kvm-block-add-wrappers-for-logical-block-provisioning-in.patch.patch.patch [bz#1007815] +- kvm-block-iscsi-add-.bdrv_get_info.patch.patch [bz#1007815] +- kvm-block-add-BlockLimits-structure-to-BlockDriverState.patch.patch.patch [bz#1007815] +- kvm-block-raw-copy-BlockLimits-on-raw_open.patch.patch.patch [bz#1007815] +- kvm-block-honour-BlockLimits-in-bdrv_co_do_write_zeroes.patch.patch.patch [bz#1007815] +- kvm-block-honour-BlockLimits-in-bdrv_co_discard.patch.patch.patch [bz#1007815] +- kvm-iscsi-set-limits-in-BlockDriverState.patch.patch.patch [bz#1007815] +- kvm-iscsi-simplify-iscsi_co_discard.patch.patch.patch [bz#1007815] +- kvm-iscsi-add-bdrv_co_write_zeroes.patch.patch.patch [bz#1007815] +- kvm-block-introduce-bdrv_make_zero.patch.patch.patch [bz#1007815] +- kvm-block-get_block_status-fix-BDRV_BLOCK_ZERO-for-unall.patch.patch.patch [bz#1007815] +- kvm-qemu-img-add-support-for-fully-allocated-images.patch.patch.patch [bz#1007815] +- kvm-qemu-img-conditionally-zero-out-target-on-convert.patch.patch.patch [bz#1007815] +- kvm-block-generalize-BlockLimits-handling-to-cover-bdrv_.patch.patch.patch [bz#1007815] +- kvm-block-add-flags-to-BlockRequest.patch.patch.patch [bz#1007815] +- kvm-block-add-flags-argument-to-bdrv_co_write_zeroes-tra.patch.patch.patch [bz#1007815] +- kvm-block-add-bdrv_aio_write_zeroes.patch.patch.patch [bz#1007815] +- kvm-block-handle-ENOTSUP-from-discard-in-generic-code.patch.patch.patch [bz#1007815] +- kvm-block-make-bdrv_co_do_write_zeroes-stricter-in-produ.patch.patch.patch [bz#1007815] +- kvm-vpc-vhdx-add-get_info.patch.patch.patch [bz#1007815] +- kvm-block-drivers-add-discard-write_zeroes-properties-to.patch.patch.patch [bz#1007815] +- kvm-block-drivers-expose-requirement-for-write-same-alig.patch.patch.patch [bz#1007815] +- kvm-block-iscsi-remove-.bdrv_has_zero_init.patch.patch.patch [bz#1007815] +- kvm-block-iscsi-updated-copyright.patch.patch.patch [bz#1007815] +- kvm-block-iscsi-check-WRITE-SAME-support-differently-dep.patch.patch.patch [bz#1007815] +- kvm-scsi-disk-catch-write-protection-errors-in-UNMAP.patch.patch.patch [bz#1007815] +- kvm-scsi-disk-reject-ANCHOR-1-for-UNMAP-and-WRITE-SAME-c.patch.patch.patch [bz#1007815] +- kvm-scsi-disk-correctly-implement-WRITE-SAME.patch.patch.patch [bz#1007815] +- kvm-scsi-disk-fix-WRITE-SAME-with-large-non-zero-payload.patch.patch.patch [bz#1007815] +- kvm-raw-posix-implement-write_zeroes-with-MAY_UNMAP-for-.patch.patch.patch.patch [bz#1007815] +- kvm-raw-posix-implement-write_zeroes-with-MAY_UNMAP-for-.patch.patch.patch.patch.patch [bz#1007815] +- kvm-raw-posix-add-support-for-write_zeroes-on-XFS-and-bl.patch.patch [bz#1007815] +- kvm-qemu-iotests-033-is-fast.patch.patch [bz#1007815] +- kvm-qemu-img-add-support-for-skipping-zeroes-in-input-du.patch.patch [bz#1007815] +- kvm-qemu-img-fix-usage-instruction-for-qemu-img-convert.patch.patch [bz#1007815] +- kvm-block-iscsi-set-bdi-cluster_size.patch.patch [bz#1007815] +- kvm-block-add-opt_transfer_length-to-BlockLimits.patch.patch [bz#1039557] +- kvm-block-iscsi-set-bs-bl.opt_transfer_length.patch.patch [bz#1039557] +- kvm-qemu-img-dynamically-adjust-iobuffer-size-during-con.patch.patch [bz#1039557] +- kvm-qemu-img-round-down-request-length-to-an-aligned-sec.patch.patch [bz#1039557] +- kvm-qemu-img-decrease-progress-update-interval-on-conver.patch.patch [bz#1039557] +- Resolves: bz#1007815 + (fix WRITE SAME support) +- Resolves: bz#1039557 + (optimize qemu-img for thin provisioned images) + +* Fri Dec 27 2013 Daniel Mach - 10:1.5.3-31 +- Mass rebuild 2013-12-27 + +* Wed Dec 18 2013 Michal Novotny - 1.5.3-30.el7 +- kvm-Revert-HMP-Disable-drive_add-for-Red-Hat-Enterprise-2.patch.patch [bz#889051] +- Resolves: bz#889051 + (Commands "__com.redhat_drive_add/del" don' t exist in RHEL7.0) + +* Wed Dec 18 2013 Michal Novotny - 1.5.3-29.el7 +- kvm-QMP-Forward-port-__com.redhat_drive_del-from-RHEL-6.patch [bz#889051] +- kvm-QMP-Forward-port-__com.redhat_drive_add-from-RHEL-6.patch [bz#889051] +- kvm-HMP-Forward-port-__com.redhat_drive_add-from-RHEL-6.patch [bz#889051] +- kvm-QMP-Document-throttling-parameters-of-__com.redhat_d.patch [bz#889051] +- kvm-HMP-Disable-drive_add-for-Red-Hat-Enterprise-Linux.patch [bz#889051] +- Resolves: bz#889051 + (Commands "__com.redhat_drive_add/del" don' t exist in RHEL7.0) + +* Wed Dec 18 2013 Michal Novotny - 1.5.3-28.el7 +- kvm-virtio_pci-fix-level-interrupts-with-irqfd.patch [bz#1035132] +- Resolves: bz#1035132 + (fail to boot and call trace with x-data-plane=on specified for rhel6.5 guest) + +* Wed Dec 18 2013 Michal Novotny - 1.5.3-27.el7 +- Change systemd service location [bz#1025217] +- kvm-vmdk-Allow-read-only-open-of-VMDK-version-3.patch [bz#1007710 bz#1029852] +- Resolves: bz#1007710 + ([RFE] Enable qemu-img to support VMDK version 3) +- Resolves: bz#1025217 + (systemd can't control ksm.service and ksmtuned.service) +- Resolves: bz#1029852 + (qemu-img fails to convert vmdk image with "qemu-img: Could not open 'image.vmdk'") + +* Wed Dec 18 2013 Michal Novotny - 1.5.3-26.el7 +- Add BuildRequires to libRDMAcm-devel for RDMA support [bz#1011720] +- kvm-add-a-header-file-for-atomic-operations.patch [bz#1011720] +- kvm-savevm-Fix-potential-memory-leak.patch [bz#1011720] +- kvm-migration-Fail-migration-on-bdrv_flush_all-error.patch [bz#1011720] +- kvm-rdma-add-documentation.patch [bz#1011720] +- kvm-rdma-introduce-qemu_update_position.patch [bz#1011720] +- kvm-rdma-export-yield_until_fd_readable.patch [bz#1011720] +- kvm-rdma-export-throughput-w-MigrationStats-QMP.patch [bz#1011720] +- kvm-rdma-introduce-qemu_file_mode_is_not_valid.patch [bz#1011720] +- kvm-rdma-introduce-qemu_ram_foreach_block.patch [bz#1011720] +- kvm-rdma-new-QEMUFileOps-hooks.patch [bz#1011720] +- kvm-rdma-introduce-capability-x-rdma-pin-all.patch [bz#1011720] +- kvm-rdma-update-documentation-to-reflect-new-unpin-suppo.patch [bz#1011720]- kvm-rdma-bugfix-ram_control_save_page.patch [bz#1011720] +- kvm-rdma-introduce-ram_handle_compressed.patch [bz#1011720] +- kvm-rdma-core-logic.patch [bz#1011720] +- kvm-rdma-send-pc.ram.patch [bz#1011720] +- kvm-rdma-allow-state-transitions-between-other-states-be.patch [bz#1011720] +- kvm-rdma-introduce-MIG_STATE_NONE-and-change-MIG_STATE_S.patch [bz#1011720] +- kvm-rdma-account-for-the-time-spent-in-MIG_STATE_SETUP-t.patch [bz#1011720] +- kvm-rdma-bugfix-make-IPv6-support-work.patch [bz#1011720] +- kvm-rdma-forgot-to-turn-off-the-debugging-flag.patch [bz#1011720] +- kvm-rdma-correct-newlines-in-error-statements.patch [bz#1011720] +- kvm-rdma-don-t-use-negative-index-to-array.patch [bz#1011720] +- kvm-rdma-qemu_rdma_post_send_control-uses-wrongly-RDMA_W.patch [bz#1011720] +- kvm-rdma-use-DRMA_WRID_READY.patch [bz#1011720] +- kvm-rdma-memory-leak-RDMAContext-host.patch [bz#1011720] +- kvm-rdma-use-resp.len-after-validation-in-qemu_rdma_regi.patch [bz#1011720] +- kvm-rdma-validate-RDMAControlHeader-len.patch [bz#1011720] +- kvm-rdma-check-if-RDMAControlHeader-len-match-transferre.patch [bz#1011720] +- kvm-rdma-proper-getaddrinfo-handling.patch [bz#1011720] +- kvm-rdma-IPv6-over-Ethernet-RoCE-is-broken-in-linux-work.patch [bz#1011720] +- kvm-rdma-remaining-documentation-fixes.patch [bz#1011720] +- kvm-rdma-silly-ipv6-bugfix.patch [bz#1011720] +- kvm-savevm-fix-wrong-initialization-by-ram_control_load_.patch [bz#1011720] +- kvm-arch_init-right-return-for-ram_save_iterate.patch [bz#1011720] +- kvm-rdma-clean-up-of-qemu_rdma_cleanup.patch [bz#1011720] +- kvm-rdma-constify-ram_chunk_-index-start-end.patch [bz#1011720] +- kvm-migration-Fix-debug-print-type.patch [bz#1011720] +- kvm-arch_init-make-is_zero_page-accept-size.patch [bz#1011720] +- kvm-migration-ram_handle_compressed.patch [bz#1011720] +- kvm-migration-fix-spice-migration.patch [bz#1011720] +- kvm-pci-assign-cap-number-of-devices-that-can-be-assigne.patch [bz#678368] +- kvm-vfio-cap-number-of-devices-that-can-be-assigned.patch [bz#678368] +- kvm-Revert-usb-tablet-Don-t-claim-wakeup-capability-for-.patch [bz#1039513] +- kvm-mempath-prefault-pages-manually-v4.patch [bz#1026554] +- Resolves: bz#1011720 + ([HP 7.0 Feat]: Backport RDMA based live guest migration changes from upstream to RHEL7.0 KVM) +- Resolves: bz#1026554 + (qemu: mempath: prefault pages manually) +- Resolves: bz#1039513 + (backport remote wakeup for ehci) +- Resolves: bz#678368 + (RFE: Support more than 8 assigned devices) + +* Wed Dec 18 2013 Michal Novotny - 1.5.3-25.el7 +- kvm-Change-package-description.patch [bz#1017696] +- kvm-seccomp-add-kill-to-the-syscall-whitelist.patch [bz#1026314] +- kvm-json-parser-fix-handling-of-large-whole-number-value.patch [bz#997915] +- kvm-qapi-add-QMP-input-test-for-large-integers.patch [bz#997915] +- kvm-qapi-fix-visitor-serialization-tests-for-numbers-dou.patch [bz#997915] +- kvm-qapi-add-native-list-coverage-for-visitor-serializat.patch [bz#997915] +- kvm-qapi-add-native-list-coverage-for-QMP-output-visitor.patch [bz#997915] +- kvm-qapi-add-native-list-coverage-for-QMP-input-visitor-.patch [bz#997915] +- kvm-qapi-lack-of-two-commas-in-dict.patch [bz#997915] +- kvm-tests-QAPI-schema-parser-tests.patch [bz#997915] +- kvm-tests-Use-qapi-schema-test.json-as-schema-parser-tes.patch [bz#997915] +- kvm-qapi.py-Restructure-lexer-and-parser.patch [bz#997915] +- kvm-qapi.py-Decent-syntax-error-reporting.patch [bz#997915] +- kvm-qapi.py-Reject-invalid-characters-in-schema-file.patch [bz#997915] +- kvm-qapi.py-Fix-schema-parser-to-check-syntax-systematic.patch [bz#997915] +- kvm-qapi.py-Fix-diagnosing-non-objects-at-a-schema-s-top.patch [bz#997915] +- kvm-qapi.py-Rename-expr_eval-to-expr-in-parse_schema.patch [bz#997915] +- kvm-qapi.py-Permit-comments-starting-anywhere-on-the-lin.patch [bz#997915] +- kvm-scripts-qapi.py-Avoid-syntax-not-supported-by-Python.patch [bz#997915] +- kvm-tests-Fix-schema-parser-test-for-in-tree-build.patch [bz#997915] +- Resolves: bz#1017696 + ([branding] remove references to dynamic translation and user-mode emulation) +- Resolves: bz#1026314 + (qemu-kvm hang when use '-sandbox on'+'vnc'+'hda') +- Resolves: bz#997915 + (Backport new QAPI parser proactively to help developers and avoid silly conflicts) + +* Tue Dec 17 2013 Michal Novotny - 1.5.3-24.el7 +- kvm-range-add-Range-structure.patch [bz#1034876] +- kvm-range-add-Range-to-typedefs.patch [bz#1034876] +- kvm-range-add-min-max-operations-on-ranges.patch [bz#1034876] +- kvm-qdev-Add-SIZE-type-to-qdev-properties.patch [bz#1034876] +- kvm-qapi-make-visit_type_size-fallback-to-type_int.patch [bz#1034876] +- kvm-pc-move-IO_APIC_DEFAULT_ADDRESS-to-include-hw-i386-i.patch [bz#1034876] +- kvm-pci-add-helper-to-retrieve-the-64-bit-range.patch [bz#1034876] +- kvm-pci-fix-up-w64-size-calculation-helper.patch [bz#1034876] +- kvm-refer-to-FWCfgState-explicitly.patch [bz#1034876] +- kvm-fw_cfg-move-typedef-to-qemu-typedefs.h.patch [bz#1034876] +- kvm-arch_init-align-MR-size-to-target-page-size.patch [bz#1034876] +- kvm-loader-store-FW-CFG-ROM-files-in-RAM.patch [bz#1034876] +- kvm-pci-store-PCI-hole-ranges-in-guestinfo-structure.patch [bz#1034876] +- kvm-pc-pass-PCI-hole-ranges-to-Guests.patch [bz#1034876] +- kvm-pc-replace-i440fx_common_init-with-i440fx_init.patch [bz#1034876] +- kvm-pc-don-t-access-fw-cfg-if-NULL.patch [bz#1034876] +- kvm-pc-add-I440FX-QOM-cast-macro.patch [bz#1034876] +- kvm-pc-limit-64-bit-hole-to-2G-by-default.patch [bz#1034876] +- kvm-q35-make-pci-window-address-size-match-guest-cfg.patch [bz#1034876] +- kvm-q35-use-64-bit-window-programmed-by-guest.patch [bz#1034876] +- kvm-piix-use-64-bit-window-programmed-by-guest.patch [bz#1034876] +- kvm-pc-fix-regression-for-64-bit-PCI-memory.patch [bz#1034876] +- kvm-cleanup-object.h-include-error.h-directly.patch [bz#1034876] +- kvm-qom-cleanup-struct-Error-references.patch [bz#1034876] +- kvm-qom-add-pointer-to-int-property-helpers.patch [bz#1034876] +- kvm-fw_cfg-interface-to-trigger-callback-on-read.patch [bz#1034876] +- kvm-loader-support-for-unmapped-ROM-blobs.patch [bz#1034876] +- kvm-pcie_host-expose-UNMAPPED-macro.patch [bz#1034876] +- kvm-pcie_host-expose-address-format.patch [bz#1034876] +- kvm-q35-use-macro-for-MCFG-property-name.patch [bz#1034876] +- kvm-q35-expose-mmcfg-size-as-a-property.patch [bz#1034876] +- kvm-i386-add-ACPI-table-files-from-seabios.patch [bz#1034876] +- kvm-acpi-add-rules-to-compile-ASL-source.patch [bz#1034876] +- kvm-acpi-pre-compiled-ASL-files.patch [bz#1034876] +- kvm-acpi-ssdt-pcihp-updat-generated-file.patch [bz#1034876] +- kvm-loader-use-file-path-size-from-fw_cfg.h.patch [bz#1034876] +- kvm-i386-add-bios-linker-loader.patch [bz#1034876] +- kvm-loader-allow-adding-ROMs-in-done-callbacks.patch [bz#1034876] +- kvm-i386-define-pc-guest-info.patch [bz#1034876] +- kvm-acpi-piix-add-macros-for-acpi-property-names.patch [bz#1034876] +- kvm-piix-APIs-for-pc-guest-info.patch [bz#1034876] +- kvm-ich9-APIs-for-pc-guest-info.patch [bz#1034876] +- kvm-pvpanic-add-API-to-access-io-port.patch [bz#1034876] +- kvm-hpet-add-API-to-find-it.patch [bz#1034876] +- kvm-hpet-fix-build-with-CONFIG_HPET-off.patch [bz#1034876] +- kvm-acpi-add-interface-to-access-user-installed-tables.patch [bz#1034876] +- kvm-pc-use-new-api-to-add-builtin-tables.patch [bz#1034876] +- kvm-i386-ACPI-table-generation-code-from-seabios.patch [bz#1034876] +- kvm-ssdt-fix-PBLK-length.patch [bz#1034876] +- kvm-ssdt-proc-update-generated-file.patch [bz#1034876] +- kvm-pc-disable-pci-info.patch [bz#1034876] +- kvm-acpi-build-fix-build-on-glib-2.22.patch [bz#1034876] +- kvm-acpi-build-fix-build-on-glib-2.14.patch [bz#1034876] +- kvm-acpi-build-fix-support-for-glib-2.22.patch [bz#1034876] +- kvm-acpi-build-Fix-compiler-warning-missing-gnu_printf-f.patch [bz#1034876] +- kvm-exec-Fix-prototype-of-phys_mem_set_alloc-and-related.patch [bz#1034876] +- Resolves: bz#1034876 + (export acpi tables to guests) + +* Tue Dec 17 2013 Michal Novotny - 1.5.3-23.el7 +- kvm-qdev-monitor-Unref-device-when-device_add-fails.patch [bz#1003773] +- kvm-qdev-Drop-misleading-qdev_free-function.patch [bz#1003773] +- kvm-blockdev-fix-drive_init-opts-and-bs_opts-leaks.patch [bz#1003773] +- kvm-libqtest-rename-qmp-to-qmp_discard_response.patch [bz#1003773] +- kvm-libqtest-add-qmp-fmt-.-QDict-function.patch [bz#1003773] +- kvm-blockdev-test-add-test-case-for-drive_add-duplicate-.patch [bz#1003773] +- kvm-qdev-monitor-test-add-device_add-leak-test-cases.patch [bz#1003773] +- kvm-qtest-Use-display-none-by-default.patch [bz#1003773] +- Resolves: bz#1003773 + (When virtio-blk-pci device with dataplane is failed to be added, the drive cannot be released.) + +* Tue Dec 17 2013 Michal Novotny - 1.5.3-22.el7 +- Fix ksmtuned with set_process_name=1 [bz#1027420] +- Fix committed memory when no qemu-kvm running [bz#1027418] +- kvm-virtio-net-fix-the-memory-leak-in-rxfilter_notify.patch [bz#1033810] +- kvm-qom-Fix-memory-leak-in-object_property_set_link.patch [bz#1033810] +- kvm-fix-intel-hda-live-migration.patch [bz#1036537] +- kvm-vfio-pci-Release-all-MSI-X-vectors-when-disabled.patch [bz#1029743] +- kvm-Query-KVM-for-available-memory-slots.patch [bz#921490] +- kvm-block-Dont-ignore-previously-set-bdrv_flags.patch [bz#1039501] +- kvm-cleanup-trace-events.pl-New.patch [bz#997832] +- kvm-slavio_misc-Fix-slavio_led_mem_readw-_writew-tracepo.patch [bz#997832] +- kvm-milkymist-minimac2-Fix-minimac2_read-_write-tracepoi.patch [bz#997832] +- kvm-trace-events-Drop-unused-events.patch [bz#997832] +- kvm-trace-events-Fix-up-source-file-comments.patch [bz#997832] +- kvm-trace-events-Clean-up-with-scripts-cleanup-trace-eve.patch [bz#997832] +- kvm-trace-events-Clean-up-after-removal-of-old-usb-host-.patch [bz#997832] +- kvm-net-Update-netdev-peer-on-link-change.patch [bz#1027571] +- Resolves: bz#1027418 + (ksmtuned committed_memory() still returns "", not 0, when no qemu running) +- Resolves: bz#1027420 + (ksmtuned can’t handle libvirt WITH set_process_name=1) +- Resolves: bz#1027571 + ([virtio-win]win8.1 guest network can not resume automatically after do "set_link tap1 on") +- Resolves: bz#1029743 + (qemu-kvm core dump after hot plug/unplug 82576 PF about 100 times) +- Resolves: bz#1033810 + (memory leak in using object_get_canonical_path()) +- Resolves: bz#1036537 + (Cross version migration from RHEL6.5 host to RHEL7.0 host with sound device failed.) +- Resolves: bz#1039501 + ([provisioning] discard=on broken) +- Resolves: bz#921490 + (qemu-kvm core dumped after hot plugging more than 11 VF through vfio-pci) +- Resolves: bz#997832 + (Backport trace fixes proactively to avoid confusion and silly conflicts) + +* Tue Dec 03 2013 Miroslav Rezanina - 1.5.3-21.el7 +- kvm-scsi-Allocate-SCSITargetReq-r-buf-dynamically-CVE-20.patch [bz#1007334] +- Resolves: bz#1007334 + (CVE-2013-4344 qemu-kvm: qemu: buffer overflow in scsi_target_emulate_report_luns [rhel-7.0]) + +* Thu Nov 28 2013 Miroslav Rezanina - 1.5.3-20.el7 +- kvm-pc-drop-virtio-balloon-pci-event_idx-compat-property.patch [bz#1029539] +- kvm-virtio-net-only-delete-bh-that-existed.patch [bz#922463] +- kvm-virtio-net-broken-RX-filtering-logic-fixed.patch [bz#1029370] +- kvm-block-Avoid-unecessary-drv-bdrv_getlength-calls.patch [bz#1025138] +- kvm-block-Round-up-total_sectors.patch [bz#1025138] +- kvm-doc-fix-hardcoded-helper-path.patch [bz#1016952] +- kvm-introduce-RFQDN_REDHAT-RHEL-6-7-fwd.patch [bz#971933] +- kvm-error-reason-in-BLOCK_IO_ERROR-BLOCK_JOB_ERROR-event.patch [bz#971938] +- kvm-improve-debuggability-of-BLOCK_IO_ERROR-BLOCK_JOB_ER.patch [bz#895041] +- kvm-vfio-pci-Fix-multifunction-on.patch [bz#1029275] +- kvm-qcow2-Change-default-for-new-images-to-compat-1.1.patch [bz#1026739] +- kvm-qcow2-change-default-for-new-images-to-compat-1.1-pa.patch [bz#1026739] +- kvm-rng-egd-offset-the-point-when-repeatedly-read-from-t.patch [bz#1032862] +- kvm-Fix-rhel-rhev-conflict-for-qemu-kvm-common.patch [bz#1033463] +- Resolves: bz#1016952 + (qemu-kvm man page guide wrong path for qemu-bridge-helper) +- Resolves: bz#1025138 + (Read/Randread/Randrw performance regression) +- Resolves: bz#1026739 + (qcow2: Switch to compat=1.1 default for new images) +- Resolves: bz#1029275 + (Guest only find one 82576 VF(function 0) while use multifunction) +- Resolves: bz#1029370 + ([whql][netkvm][wlk] Virtio-net device handles RX multicast filtering improperly) +- Resolves: bz#1029539 + (Machine type rhel6.1.0 and balloon device cause migration fail from RHEL6.5 host to RHEL7.0 host) +- Resolves: bz#1032862 + (virtio-rng-egd: repeatedly read same random data-block w/o considering the buffer offset) +- Resolves: bz#1033463 + (can not upgrade qemu-kvm-common to qemu-kvm-common-rhev due to conflicts) +- Resolves: bz#895041 + (QMP: forward port I/O error debug messages) +- Resolves: bz#922463 + (qemu-kvm core dump when virtio-net multi queue guest hot-unpluging vNIC) +- Resolves: bz#971933 + (QMP: add RHEL's vendor extension prefix) +- Resolves: bz#971938 + (QMP: Add error reason to BLOCK_IO_ERROR event) + +* Mon Nov 11 2013 Miroslav Rezanina - 1.5.3-19.el7 +- kvm-qapi-qapi-visit.py-fix-list-handling-for-union-types.patch [bz#848203] +- kvm-qapi-qapi-visit.py-native-list-support.patch [bz#848203] +- kvm-qapi-enable-generation-of-native-list-code.patch [bz#848203] +- kvm-net-add-support-of-mac-programming-over-macvtap-in-Q.patch [bz#848203] +- Resolves: bz#848203 + (MAC Programming for virtio over macvtap - qemu-kvm support) + +* Fri Nov 08 2013 Michal Novotny - 1.5.3-18.el7 +- Removing leaked patch kvm-e1000-rtl8139-update-HMP-NIC-when-every-bit-is-writt.patch + +* Thu Nov 07 2013 Miroslav Rezanina - 1.5.3-17.el7 +- kvm-pci-assign-Add-MSI-affinity-support.patch [bz#1025877] +- kvm-Fix-potential-resource-leak-missing-fclose.patch [bz#1025877] +- kvm-pci-assign-remove-the-duplicate-function-name-in-deb.patch [bz#1025877] +- kvm-Remove-s390-ccw-img-loader.patch [bz#1017682] +- kvm-Fix-vscclient-installation.patch [bz#1017681] +- kvm-Change-qemu-bridge-helper-permissions-to-4755.patch [bz#1017689] +- kvm-net-update-nic-info-during-device-reset.patch [bz#922589] +- kvm-net-e1000-update-network-information-when-macaddr-is.patch [bz#922589] +- kvm-net-rtl8139-update-network-information-when-macaddr-.patch [bz#922589] +- kvm-virtio-net-fix-up-HMP-NIC-info-string-on-reset.patch [bz#1026689] +- kvm-vfio-pci-VGA-quirk-update.patch [bz#1025477] +- kvm-vfio-pci-Add-support-for-MSI-affinity.patch [bz#1025477] +- kvm-vfio-pci-Test-device-reset-capabilities.patch [bz#1026550] +- kvm-vfio-pci-Lazy-PCI-option-ROM-loading.patch [bz#1026550] +- kvm-vfio-pci-Cleanup-error_reports.patch [bz#1026550] +- kvm-vfio-pci-Add-dummy-PCI-ROM-write-accessor.patch [bz#1026550] +- kvm-vfio-pci-Fix-endian-issues-in-vfio_pci_size_rom.patch [bz#1026550] +- kvm-linux-headers-Update-to-include-vfio-pci-hot-reset-s.patch [bz#1025472] +- kvm-vfio-pci-Implement-PCI-hot-reset.patch [bz#1025472] +- kvm-linux-headers-Update-for-KVM-VFIO-device.patch [bz#1025474] +- kvm-vfio-pci-Make-use-of-new-KVM-VFIO-device.patch [bz#1025474] +- kvm-vmdk-Fix-vmdk_parse_extents.patch [bz#995866] +- kvm-vmdk-fix-VMFS-extent-parsing.patch [bz#995866] +- kvm-e1000-rtl8139-update-HMP-NIC-when-every-bit-is-writt.patch [bz#922589] +- kvm-don-t-disable-ctrl_mac_addr-feature-for-6.5-machine-.patch [bz#1005039] +- Resolves: bz#1005039 + (add compat property to disable ctrl_mac_addr feature) +- Resolves: bz#1017681 + (rpmdiff test "Multilib regressions": vscclient is a libtool script on s390/s390x/ppc/ppc64) +- Resolves: bz#1017682 + (/usr/share/qemu-kvm/s390-ccw.img need not be distributed) +- Resolves: bz#1017689 + (/usr/libexec/qemu-bridge-helper permissions should be 4755) +- Resolves: bz#1025472 + (Nvidia GPU device assignment - qemu-kvm - bus reset support) +- Resolves: bz#1025474 + (Nvidia GPU device assignment - qemu-kvm - NoSnoop support) +- Resolves: bz#1025477 + (VFIO MSI affinity) +- Resolves: bz#1025877 + (pci-assign lacks MSI affinity support) +- Resolves: bz#1026550 + (QEMU VFIO update ROM loading code) +- Resolves: bz#1026689 + (virtio-net: macaddr is reset but network info of monitor isn't updated) +- Resolves: bz#922589 + (e1000/rtl8139: qemu mac address can not be changed via set the hardware address in guest) +- Resolves: bz#995866 + (fix vmdk support to ESX images) + +* Thu Nov 07 2013 Miroslav Rezanina - 1.5.3-16.el7 +- kvm-block-drop-bs_snapshots-global-variable.patch [bz#1026524] +- kvm-block-move-snapshot-code-in-block.c-to-block-snapsho.patch [bz#1026524] +- kvm-block-fix-vvfat-error-path-for-enable_write_target.patch [bz#1026524] +- kvm-block-Bugfix-format-and-snapshot-used-in-drive-optio.patch [bz#1026524] +- kvm-iscsi-use-bdrv_new-instead-of-stack-structure.patch [bz#1026524] +- kvm-qcow2-Add-corrupt-bit.patch [bz#1004347] +- kvm-qcow2-Metadata-overlap-checks.patch [bz#1004347] +- kvm-qcow2-Employ-metadata-overlap-checks.patch [bz#1004347] +- kvm-qcow2-refcount-Move-OFLAG_COPIED-checks.patch [bz#1004347] +- kvm-qcow2-refcount-Repair-OFLAG_COPIED-errors.patch [bz#1004347] +- kvm-qcow2-refcount-Repair-shared-refcount-blocks.patch [bz#1004347] +- kvm-qcow2_check-Mark-image-consistent.patch [bz#1004347] +- kvm-qemu-iotests-Overlapping-cluster-allocations.patch [bz#1004347] +- kvm-w32-Fix-access-to-host-devices-regression.patch [bz#1026524] +- kvm-add-qemu-img-convert-n-option-skip-target-volume-cre.patch [bz#1026524] +- kvm-bdrv-Use-Error-for-opening-images.patch [bz#1026524] +- kvm-bdrv-Use-Error-for-creating-images.patch [bz#1026524] +- kvm-block-Error-parameter-for-open-functions.patch [bz#1026524] +- kvm-block-Error-parameter-for-create-functions.patch [bz#1026524] +- kvm-qemu-img-create-Emit-filename-on-error.patch [bz#1026524] +- kvm-qcow2-Use-Error-parameter.patch [bz#1026524] +- kvm-qemu-iotests-Adjustments-due-to-error-propagation.patch [bz#1026524] +- kvm-block-raw-Employ-error-parameter.patch [bz#1026524] +- kvm-block-raw-win32-Employ-error-parameter.patch [bz#1026524] +- kvm-blkdebug-Employ-error-parameter.patch [bz#1026524] +- kvm-blkverify-Employ-error-parameter.patch [bz#1026524] +- kvm-block-raw-posix-Employ-error-parameter.patch [bz#1026524] +- kvm-block-raw-win32-Always-use-errno-in-hdev_open.patch [bz#1026524] +- kvm-qmp-Documentation-for-BLOCK_IMAGE_CORRUPTED.patch [bz#1004347] +- kvm-qcow2-Correct-snapshots-size-for-overlap-check.patch [bz#1004347] +- kvm-qcow2-CHECK_OFLAG_COPIED-is-obsolete.patch [bz#1004347] +- kvm-qcow2-Correct-endianness-in-overlap-check.patch [bz#1004347] +- kvm-qcow2-Switch-L1-table-in-a-single-sequence.patch [bz#1004347] +- kvm-qcow2-Use-pread-for-inactive-L1-in-overlap-check.patch [bz#1004347] +- kvm-qcow2-Remove-wrong-metadata-overlap-check.patch [bz#1004347] +- kvm-qcow2-Use-negated-overflow-check-mask.patch [bz#1004347] +- kvm-qcow2-Make-overlap-check-mask-variable.patch [bz#1004347] +- kvm-qcow2-Add-overlap-check-options.patch [bz#1004347] +- kvm-qcow2-Array-assigning-options-to-OL-check-bits.patch [bz#1004347] +- kvm-qcow2-Add-more-overlap-check-bitmask-macros.patch [bz#1004347] +- kvm-qcow2-Evaluate-overlap-check-options.patch [bz#1004347] +- kvm-qapi-types.py-Split-off-generate_struct_fields.patch [bz#978402] +- kvm-qapi-types.py-Fix-enum-struct-sizes-on-i686.patch [bz#978402] +- kvm-qapi-types-visit.py-Pass-whole-expr-dict-for-structs.patch [bz#978402] +- kvm-qapi-types-visit.py-Inheritance-for-structs.patch [bz#978402] +- kvm-blockdev-Introduce-DriveInfo.enable_auto_del.patch [bz#978402] +- kvm-Implement-qdict_flatten.patch [bz#978402] +- kvm-blockdev-blockdev-add-QMP-command.patch [bz#978402] +- kvm-blockdev-Separate-ID-generation-from-DriveInfo-creat.patch [bz#978402] +- kvm-blockdev-Pass-QDict-to-blockdev_init.patch [bz#978402] +- kvm-blockdev-Move-parsing-of-media-option-to-drive_init.patch [bz#978402] +- kvm-blockdev-Move-parsing-of-if-option-to-drive_init.patch [bz#978402] +- kvm-blockdev-Moving-parsing-of-geometry-options-to-drive.patch [bz#978402] +- kvm-blockdev-Move-parsing-of-boot-option-to-drive_init.patch [bz#978402] +- kvm-blockdev-Move-bus-unit-index-processing-to-drive_ini.patch [bz#978402] +- kvm-blockdev-Move-virtio-blk-device-creation-to-drive_in.patch [bz#978402] +- kvm-blockdev-Remove-IF_-check-for-read-only-blockdev_ini.patch [bz#978402] +- kvm-qemu-iotests-Check-autodel-behaviour-for-device_del.patch [bz#978402] +- kvm-blockdev-Remove-media-parameter-from-blockdev_init.patch [bz#978402] +- kvm-blockdev-Don-t-disable-COR-automatically-with-blockd.patch [bz#978402] +- kvm-blockdev-blockdev_init-error-conversion.patch [bz#978402] +- kvm-sd-Avoid-access-to-NULL-BlockDriverState.patch [bz#978402] +- kvm-blockdev-fix-cdrom-read_only-flag.patch [bz#978402] +- kvm-block-fix-backing-file-overriding.patch [bz#978402] +- kvm-block-Disable-BDRV_O_COPY_ON_READ-for-the-backing-fi.patch [bz#978402] +- kvm-block-Don-t-copy-backing-file-name-on-error.patch [bz#978402] +- kvm-qemu-iotests-Try-creating-huge-qcow2-image.patch [bz#980771] +- kvm-block-move-qmp-and-info-dump-related-code-to-block-q.patch [bz#980771] +- kvm-block-dump-snapshot-and-image-info-to-specified-outp.patch [bz#980771] +- kvm-block-add-snapshot-info-query-function-bdrv_query_sn.patch [bz#980771] +- kvm-block-add-image-info-query-function-bdrv_query_image.patch [bz#980771] +- kvm-qmp-add-ImageInfo-in-BlockDeviceInfo-used-by-query-b.patch [bz#980771] +- kvm-vmdk-Implement-.bdrv_has_zero_init.patch [bz#980771] +- kvm-qemu-iotests-Add-basic-ability-to-use-binary-sample-.patch [bz#980771] +- kvm-qemu-iotests-Quote-TEST_IMG-and-TEST_DIR-usage.patch [bz#980771] +- kvm-qemu-iotests-fix-test-case-059.patch [bz#980771] +- kvm-qapi-Add-ImageInfoSpecific-type.patch [bz#980771] +- kvm-block-Add-bdrv_get_specific_info.patch [bz#980771] +- kvm-block-qapi-Human-readable-ImageInfoSpecific-dump.patch [bz#980771] +- kvm-qcow2-Add-support-for-ImageInfoSpecific.patch [bz#980771] +- kvm-qemu-iotests-Discard-specific-info-in-_img_info.patch [bz#980771] +- kvm-qemu-iotests-Additional-info-from-qemu-img-info.patch [bz#980771] +- kvm-vmdk-convert-error-code-to-use-errp.patch [bz#980771] +- kvm-vmdk-refuse-enabling-zeroed-grain-with-flat-images.patch [bz#980771] +- kvm-qapi-Add-optional-field-compressed-to-ImageInfo.patch [bz#980771] +- kvm-vmdk-Only-read-cid-from-image-file-when-opening.patch [bz#980771] +- kvm-vmdk-Implment-bdrv_get_specific_info.patch [bz#980771] +- Resolves: bz#1004347 + (Backport qcow2 corruption prevention patches) +- Resolves: bz#1026524 + (Backport block layer error parameter patches) +- Resolves: bz#978402 + ([RFE] Add discard support to qemu-kvm layer) +- Resolves: bz#980771 + ([RFE] qemu-img should be able to tell the compat version of a qcow2 image) + +* Thu Nov 07 2013 Miroslav Rezanina - 1.5.3-15.el7 +- kvm-cow-make-reads-go-at-a-decent-speed.patch [bz#989646] +- kvm-cow-make-writes-go-at-a-less-indecent-speed.patch [bz#989646] +- kvm-cow-do-not-call-bdrv_co_is_allocated.patch [bz#989646] +- kvm-block-keep-bs-total_sectors-up-to-date-even-for-grow.patch [bz#989646] +- kvm-block-make-bdrv_co_is_allocated-static.patch [bz#989646] +- kvm-block-do-not-use-total_sectors-in-bdrv_co_is_allocat.patch [bz#989646] +- kvm-block-remove-bdrv_is_allocated_above-bdrv_co_is_allo.patch [bz#989646] +- kvm-block-expect-errors-from-bdrv_co_is_allocated.patch [bz#989646] +- kvm-block-Fix-compiler-warning-Werror-uninitialized.patch [bz#989646] +- kvm-qemu-img-always-probe-the-input-image-for-allocated-.patch [bz#989646] +- kvm-block-make-bdrv_has_zero_init-return-false-for-copy-.patch [bz#989646] +- kvm-block-introduce-bdrv_get_block_status-API.patch [bz#989646] +- kvm-block-define-get_block_status-return-value.patch [bz#989646] +- kvm-block-return-get_block_status-data-and-flags-for-for.patch [bz#989646] +- kvm-block-use-bdrv_has_zero_init-to-return-BDRV_BLOCK_ZE.patch [bz#989646] +- kvm-block-return-BDRV_BLOCK_ZERO-past-end-of-backing-fil.patch [bz#989646] +- kvm-qemu-img-add-a-map-subcommand.patch [bz#989646] +- kvm-docs-qapi-document-qemu-img-map.patch [bz#989646] +- kvm-raw-posix-return-get_block_status-data-and-flags.patch [bz#989646] +- kvm-raw-posix-report-unwritten-extents-as-zero.patch [bz#989646] +- kvm-block-add-default-get_block_status-implementation-fo.patch [bz#989646] +- kvm-block-look-for-zero-blocks-in-bs-file.patch [bz#989646] +- kvm-qemu-img-fix-invalid-JSON.patch [bz#989646] +- kvm-block-get_block_status-set-pnum-0-on-error.patch [bz#989646] +- kvm-block-get_block_status-avoid-segfault-if-there-is-no.patch [bz#989646] +- kvm-block-get_block_status-avoid-redundant-callouts-on-r.patch [bz#989646] +- kvm-qcow2-Restore-total_sectors-value-in-save_vmstate.patch [bz#1025740] +- kvm-qcow2-Unset-zero_beyond_eof-in-save_vmstate.patch [bz#1025740] +- kvm-qemu-iotests-Test-for-loading-VM-state-from-qcow2.patch [bz#1025740] +- kvm-apic-rename-apic-specific-bitopts.patch [bz#1001216] +- kvm-hw-import-bitmap-operations-in-qdev-core-header.patch [bz#1001216] +- kvm-qemu-help-Sort-devices-by-logical-functionality.patch [bz#1001216] +- kvm-devices-Associate-devices-to-their-logical-category.patch [bz#1001216] +- kvm-Mostly-revert-qemu-help-Sort-devices-by-logical-func.patch [bz#1001216] +- kvm-qdev-monitor-Group-device_add-help-and-info-qdm-by-c.patch [bz#1001216] +- kvm-qdev-Replace-no_user-by-cannot_instantiate_with_devi.patch [bz#1001216] +- kvm-sysbus-Set-cannot_instantiate_with_device_add_yet.patch [bz#1001216] +- kvm-cpu-Document-why-cannot_instantiate_with_device_add_.patch [bz#1001216] +- kvm-apic-Document-why-cannot_instantiate_with_device_add.patch [bz#1001216] +- kvm-pci-host-Consistently-set-cannot_instantiate_with_de.patch [bz#1001216] +- kvm-ich9-Document-why-cannot_instantiate_with_device_add.patch [bz#1001216] +- kvm-piix3-piix4-Clean-up-use-of-cannot_instantiate_with_.patch [bz#1001216] +- kvm-vt82c686-Clean-up-use-of-cannot_instantiate_with_dev.patch [bz#1001216] +- kvm-isa-Clean-up-use-of-cannot_instantiate_with_device_a.patch [bz#1001216] +- kvm-qdev-Do-not-let-the-user-try-to-device_add-when-it-c.patch [bz#1001216] +- kvm-rhel-Revert-unwanted-cannot_instantiate_with_device_.patch [bz#1001216] +- kvm-rhel-Revert-downstream-changes-to-unused-default-con.patch [bz#1001076] +- kvm-rhel-Drop-cfi.pflash01-and-isa-ide-device.patch [bz#1001076] +- kvm-rhel-Drop-isa-vga-device.patch [bz#1001088] +- kvm-rhel-Make-isa-cirrus-vga-device-unavailable.patch [bz#1001088] +- kvm-rhel-Make-ccid-card-emulated-device-unavailable.patch [bz#1001123] +- kvm-x86-fix-migration-from-pre-version-12.patch [bz#1005695] +- kvm-x86-cpuid-reconstruct-leaf-0Dh-data.patch [bz#1005695] +- kvm-kvmvapic-Catch-invalid-ROM-size.patch [bz#920021] +- kvm-kvmvapic-Enter-inactive-state-on-hardware-reset.patch [bz#920021] +- kvm-kvmvapic-Clear-also-physical-ROM-address-when-enteri.patch [bz#920021] +- kvm-block-optionally-disable-live-block-jobs.patch [bz#987582] +- kvm-rpm-spec-template-disable-live-block-ops-for-rhel-en.patch [bz#987582] +- kvm-migration-disable-live-block-migration-b-i-for-rhel-.patch [bz#1022392] +- kvm-Build-ceph-rbd-only-for-rhev.patch [bz#987583] +- kvm-spec-Disable-host-cdrom-RHEL-only.patch [bz#760885] +- kvm-rhel-Make-pci-serial-2x-and-pci-serial-4x-device-una.patch [bz#1001180] +- kvm-usb-host-libusb-Fix-reset-handling.patch [bz#980415] +- kvm-usb-host-libusb-Configuration-0-may-be-a-valid-confi.patch [bz#980383] +- kvm-usb-host-libusb-Detach-kernel-drivers-earlier.patch [bz#980383] +- kvm-monitor-Remove-pci_add-command-for-Red-Hat-Enterpris.patch [bz#1010858] +- kvm-monitor-Remove-pci_del-command-for-Red-Hat-Enterpris.patch [bz#1010858] +- kvm-monitor-Remove-usb_add-del-commands-for-Red-Hat-Ente.patch [bz#1010858] +- kvm-monitor-Remove-host_net_add-remove-for-Red-Hat-Enter.patch [bz#1010858] +- kvm-fw_cfg-add-API-to-find-FW-cfg-object.patch [bz#990601] +- kvm-pvpanic-use-FWCfgState-explicitly.patch [bz#990601] +- kvm-pvpanic-initialization-cleanup.patch [bz#990601] +- kvm-pvpanic-fix-fwcfg-for-big-endian-hosts.patch [bz#990601] +- kvm-hw-misc-make-pvpanic-known-to-user.patch [bz#990601] +- kvm-gdbstub-do-not-restart-crashed-guest.patch [bz#990601] +- kvm-gdbstub-fix-for-commit-87f25c12bfeaaa0c41fb857713bbc.patch [bz#990601] +- kvm-vl-allow-cont-from-panicked-state.patch [bz#990601] +- kvm-hw-misc-don-t-create-pvpanic-device-by-default.patch [bz#990601] +- kvm-block-vhdx-add-migration-blocker.patch [bz#1007176] +- kvm-qemu-kvm.spec-add-vhdx-to-the-read-only-block-driver.patch [bz#1007176] +- kvm-qemu-kvm.spec-Add-VPC-VHD-driver-to-the-block-read-o.patch [bz#1007176] +- Resolves: bz#1001076 + (Disable or remove other block devices we won't support) +- Resolves: bz#1001088 + (Disable or remove display devices we won't support) +- Resolves: bz#1001123 + (Disable or remove device ccid-card-emulated) +- Resolves: bz#1001180 + (Disable or remove devices pci-serial-2x, pci-serial-4x) +- Resolves: bz#1001216 + (Fix no_user or provide another way make devices unavailable with -device / device_add) +- Resolves: bz#1005695 + (QEMU should hide CPUID.0Dh values that it does not support) +- Resolves: bz#1007176 + (Add VPC and VHDX file formats as supported in qemu-kvm (read-only)) +- Resolves: bz#1010858 + (Disable unused human monitor commands) +- Resolves: bz#1022392 + (Disable live-storage-migration in qemu-kvm (migrate -b/-i)) +- Resolves: bz#1025740 + (Saving VM state on qcow2 images results in VM state corruption) +- Resolves: bz#760885 + (Disable host cdrom passthrough) +- Resolves: bz#920021 + (qemu-kvm segment fault when reboot guest after hot unplug device with option ROM) +- Resolves: bz#980383 + (The usb3.0 stick can't be returned back to host after shutdown guest with usb3.0 pass-through) +- Resolves: bz#980415 + (libusbx: error [_open_sysfs_attr] open /sys/bus/usb/devices/4-1/bConfigurationValue failed ret=-1 errno=2) +- Resolves: bz#987582 + (Initial Virtualization Differentiation for RHEL7 (Live snapshots)) +- Resolves: bz#987583 + (Initial Virtualization Differentiation for RHEL7 (Ceph enablement)) +- Resolves: bz#989646 + (Support backup vendors in qemu to access qcow disk readonly) +- Resolves: bz#990601 + (pvpanic device triggers guest bugs when present by default) + +* Wed Nov 06 2013 Miroslav Rezanina - 1.5.3-14.el7 +- kvm-target-i386-remove-tabs-from-target-i386-cpu.h.patch [bz#928867] +- kvm-migrate-vPMU-state.patch [bz#928867] +- kvm-blockdev-do-not-default-cache.no-flush-to-true.patch [bz#1009993] +- kvm-virtio-blk-do-not-relay-a-previous-driver-s-WCE-conf.patch [bz#1009993] +- kvm-rng-random-use-error_setg_file_open.patch [bz#907743] +- kvm-block-mirror_complete-use-error_setg_file_open.patch [bz#907743] +- kvm-blockdev-use-error_setg_file_open.patch [bz#907743] +- kvm-cpus-use-error_setg_file_open.patch [bz#907743] +- kvm-dump-qmp_dump_guest_memory-use-error_setg_file_open.patch [bz#907743] +- kvm-savevm-qmp_xen_save_devices_state-use-error_setg_fil.patch [bz#907743] +- kvm-block-bdrv_reopen_prepare-don-t-use-QERR_OPEN_FILE_F.patch [bz#907743] +- kvm-qerror-drop-QERR_OPEN_FILE_FAILED-macro.patch [bz#907743] +- kvm-rhel-Drop-ivshmem-device.patch [bz#787463] +- kvm-usb-remove-old-usb-host-code.patch [bz#1001144] +- kvm-Add-rhel6-pxe-roms-files.patch [bz#997702] +- kvm-Add-rhel6-pxe-rom-to-redhat-rpm.patch [bz#997702] +- kvm-Fix-migration-from-rhel6.5-to-rhel7-with-ipxe.patch [bz#997702] +- kvm-pc-Don-t-prematurely-explode-QEMUMachineInitArgs.patch [bz#994490] +- kvm-pc-Don-t-explode-QEMUMachineInitArgs-into-local-vari.patch [bz#994490] +- kvm-smbios-Normalize-smbios_entry_add-s-error-handling-t.patch [bz#994490] +- kvm-smbios-Convert-to-QemuOpts.patch [bz#994490] +- kvm-smbios-Improve-diagnostics-for-conflicting-entries.patch [bz#994490] +- kvm-smbios-Make-multiple-smbios-type-accumulate-sanely.patch [bz#994490] +- kvm-smbios-Factor-out-smbios_maybe_add_str.patch [bz#994490] +- kvm-hw-Pass-QEMUMachine-to-its-init-method.patch [bz#994490] +- kvm-smbios-Set-system-manufacturer-product-version-by-de.patch [bz#994490] +- kvm-smbios-Decouple-system-product-from-QEMUMachine.patch [bz#994490] +- kvm-rhel-SMBIOS-type-1-branding.patch [bz#994490] +- kvm-Add-disable-rhev-features-option-to-configure.patch [] +- Resolves: bz#1001144 + (Disable or remove device usb-host-linux) +- Resolves: bz#1009993 + (RHEL7 guests do not issue fdatasyncs on virtio-blk) +- Resolves: bz#787463 + (disable ivshmem (was: [Hitachi 7.0 FEAT] Support ivshmem (Inter-VM Shared Memory))) +- Resolves: bz#907743 + (qemu-ga: empty reason string for OpenFileFailed error) +- Resolves: bz#928867 + (Virtual PMU support during live migration - qemu-kvm) +- Resolves: bz#994490 + (Set per-machine-type SMBIOS strings) +- Resolves: bz#997702 + (Migration from RHEL6.5 host to RHEL7.0 host is failed with virtio-net device) + +* Tue Nov 05 2013 Miroslav Rezanina - 1.5.3-13.el7 +- kvm-seabios-paravirt-allow-more-than-1TB-in-x86-guest.patch [bz#989677] +- kvm-scsi-prefer-UUID-to-VM-name-for-the-initiator-name.patch [bz#1006468] +- kvm-Fix-incorrect-rhel_rhev_conflicts-macro-usage.patch [bz#1017693] +- Resolves: bz#1006468 + (libiscsi initiator name should use vm UUID) +- Resolves: bz#1017693 + (incorrect use of rhel_rhev_conflicts) +- Resolves: bz#989677 + ([HP 7.0 FEAT]: Increase KVM guest supported memory to 4TiB) + +* Mon Nov 04 2013 Michal Novotny - 1.5.3-12.el7 +- kvm-vl-Clean-up-parsing-of-boot-option-argument.patch [bz#997817] +- kvm-qemu-option-check_params-is-now-unused-drop-it.patch [bz#997817] +- kvm-vl-Fix-boot-order-and-once-regressions-and-related-b.patch [bz#997817] +- kvm-vl-Rename-boot_devices-to-boot_order-for-consistency.patch [bz#997817] +- kvm-pc-Make-no-fd-bootchk-stick-across-boot-order-change.patch [bz#997817] +- kvm-doc-Drop-ref-to-Bochs-from-no-fd-bootchk-documentati.patch [bz#997817] +- kvm-libqtest-Plug-fd-and-memory-leaks-in-qtest_quit.patch [bz#997817] +- kvm-libqtest-New-qtest_end-to-go-with-qtest_start.patch [bz#997817] +- kvm-qtest-Don-t-reset-on-qtest-chardev-connect.patch [bz#997817] +- kvm-boot-order-test-New-covering-just-PC-for-now.patch [bz#997817] +- kvm-qemu-ga-execute-fsfreeze-freeze-in-reverse-order-of-.patch [bz#1019352] +- kvm-rbd-link-and-load-librbd-dynamically.patch [bz#989608] +- kvm-rbd-Only-look-for-qemu-specific-copy-of-librbd.so.1.patch [bz#989608] +- kvm-spec-Whitelist-rbd-block-driver.patch [bz#989608] +- Resolves: bz#1019352 + (qemu-guest-agent: "guest-fsfreeze-freeze" deadlocks if the guest have mounted disk images) +- Resolves: bz#989608 + ([7.0 FEAT] qemu runtime support for librbd backend (ceph)) +- Resolves: bz#997817 + (-boot order and -boot once regressed since RHEL-6) + +* Thu Oct 31 2013 Miroslav Rezanina - 1.5.3-11.el7 +- kvm-chardev-fix-pty_chr_timer.patch [bz#994414] +- kvm-qemu-socket-zero-initialize-SocketAddress.patch [bz#922010] +- kvm-qemu-socket-drop-pointless-allocation.patch [bz#922010] +- kvm-qemu-socket-catch-monitor_get_fd-failures.patch [bz#922010] +- kvm-qemu-char-check-optional-fields-using-has_.patch [bz#922010] +- kvm-error-add-error_setg_file_open-helper.patch [bz#922010] +- kvm-qemu-char-use-more-specific-error_setg_-variants.patch [bz#922010] +- kvm-qemu-char-print-notification-to-stderr.patch [bz#922010] +- kvm-qemu-char-fix-documentation-for-telnet-wait-socket-f.patch [bz#922010] +- kvm-qemu-char-don-t-leak-opts-on-error.patch [bz#922010] +- kvm-qemu-char-use-ChardevBackendKind-in-CharDriver.patch [bz#922010] +- kvm-qemu-char-minor-mux-chardev-fixes.patch [bz#922010] +- kvm-qemu-char-add-chardev-mux-support.patch [bz#922010] +- kvm-qemu-char-report-udp-backend-errors.patch [bz#922010] +- kvm-qemu-socket-don-t-leak-opts-on-error.patch [bz#922010] +- kvm-chardev-handle-qmp_chardev_add-KIND_MUX-failure.patch [bz#922010] +- kvm-acpi-piix4-Enable-qemu-kvm-compatibility-mode.patch [bz#1019474] +- kvm-target-i386-support-loading-of-cpu-xsave-subsection.patch [bz#1004743] +- Resolves: bz#1004743 + (XSAVE migration format not compatible between RHEL6 and RHEL7) +- Resolves: bz#1019474 + (RHEL-7 can't load piix4_pm migration section from RHEL-6.5) +- Resolves: bz#922010 + (RFE: support hotplugging chardev & serial ports) +- Resolves: bz#994414 + (hot-unplug chardev with pty backend caused qemu Segmentation fault) + +* Thu Oct 17 2013 Miroslav Rezanina - 1.5.3-10.el7 +- kvm-xhci-fix-endpoint-interval-calculation.patch [bz#1001604] +- kvm-xhci-emulate-intr-endpoint-intervals-correctly.patch [bz#1001604] +- kvm-xhci-reset-port-when-disabling-slot.patch [bz#1001604] +- kvm-Revert-usb-hub-report-status-changes-only-once.patch [bz#1001604] +- kvm-target-i386-Set-model-6-on-qemu64-qemu32-CPU-models.patch [bz#1004290] +- kvm-pc-rhel6-doesn-t-have-APIC-on-pentium-CPU-models.patch [bz#918907] +- kvm-pc-RHEL-6-had-x2apic-set-on-Opteron_G-123.patch [bz#918907] +- kvm-pc-RHEL-6-don-t-have-RDTSCP.patch [bz#918907] +- kvm-scsi-Fix-scsi_bus_legacy_add_drive-scsi-generic-with.patch [bz#1009285] +- kvm-seccomp-fine-tuning-whitelist-by-adding-times.patch [bz#1004175] +- kvm-block-add-bdrv_write_zeroes.patch [bz#921465] +- kvm-block-raw-add-bdrv_co_write_zeroes.patch [bz#921465] +- kvm-rdma-export-qemu_fflush.patch [bz#921465] +- kvm-block-migration-efficiently-encode-zero-blocks.patch [bz#921465] +- kvm-Fix-real-mode-guest-migration.patch [bz#921465] +- kvm-Fix-real-mode-guest-segments-dpl-value-in-savevm.patch [bz#921465] +- kvm-migration-add-autoconvergence-documentation.patch [bz#921465] +- kvm-migration-send-total-time-in-QMP-at-completed-stage.patch [bz#921465] +- kvm-migration-don-t-use-uninitialized-variables.patch [bz#921465] +- kvm-pc-drop-external-DSDT-loading.patch [bz#921465] +- kvm-hda-codec-refactor-common-definitions-into-a-header-.patch [bz#954195] +- kvm-hda-codec-make-mixemu-selectable-at-runtime.patch [bz#954195] +- kvm-audio-remove-CONFIG_MIXEMU-configure-option.patch [bz#954195] +- kvm-pc_piix-disable-mixer-for-6.4.0-machine-types-and-be.patch [bz#954195] +- kvm-spec-mixemu-config-option-is-no-longer-supported-and.patch [bz#954195] +- Resolves: bz#1001604 + (usb hub doesn't work properly (win7 sees downstream port #1 only).) +- Resolves: bz#1004175 + ('-sandbox on' option cause qemu-kvm process hang) +- Resolves: bz#1004290 + (Use model 6 for qemu64 and intel cpus) +- Resolves: bz#1009285 + (-device usb-storage,serial=... crashes with SCSI generic drive) +- Resolves: bz#918907 + (provide backwards-compatible RHEL specific machine types in QEMU - CPU features) +- Resolves: bz#921465 + (Migration can not finished even the "remaining ram" is already 0 kb) +- Resolves: bz#954195 + (RHEL machines <=6.4 should not use mixemu) + +* Thu Oct 10 2013 Miroslav Rezanina - 1.5.3-9.el7 +- kvm-qxl-fix-local-renderer.patch [bz#1005036] +- kvm-spec-include-userspace-iSCSI-initiator-in-block-driv.patch [bz#923843] +- kvm-linux-headers-update-to-kernel-3.10.0-26.el7.patch [bz#1008987] +- kvm-target-i386-add-feature-kvm_pv_unhalt.patch [bz#1008987] +- kvm-warn-if-num-cpus-is-greater-than-num-recommended.patch [bz#1010881] +- kvm-char-move-backends-io-watch-tag-to-CharDriverState.patch [bz#1007222] +- kvm-char-use-common-function-to-disable-callbacks-on-cha.patch [bz#1007222] +- kvm-char-remove-watch-callback-on-chardev-detach-from-fr.patch [bz#1007222] +- kvm-block-don-t-lose-data-from-last-incomplete-sector.patch [bz#1017049] +- kvm-vmdk-fix-cluster-size-check-for-flat-extents.patch [bz#1017049] +- kvm-qemu-iotests-add-monolithicFlat-creation-test-to-059.patch [bz#1017049] +- Resolves: bz#1005036 + (When using “-vga qxl” together with “-display vnc=:5” or “-display sdl” qemu displays pixel garbage) +- Resolves: bz#1007222 + (QEMU core dumped when do hot-unplug virtio serial port during transfer file between host to guest with virtio serial through TCP socket) +- Resolves: bz#1008987 + (pvticketlocks: add kvm feature kvm_pv_unhalt) +- Resolves: bz#1010881 + (backport vcpu soft limit warning) +- Resolves: bz#1017049 + (qemu-img refuses to open the vmdk format image its created) +- Resolves: bz#923843 + (include userspace iSCSI initiator in block driver whitelist) + +* Wed Oct 09 2013 Miroslav Rezanina - qemu-kvm-1.5.3-8.el7 +- kvm-vmdk-Make-VMDK3Header-and-VmdkGrainMarker-QEMU_PACKE.patch [bz#995866] +- kvm-vmdk-use-unsigned-values-for-on-disk-header-fields.patch [bz#995866] +- kvm-qemu-iotests-add-poke_file-utility-function.patch [bz#995866] +- kvm-qemu-iotests-add-empty-test-case-for-vmdk.patch [bz#995866] +- kvm-vmdk-check-granularity-field-in-opening.patch [bz#995866] +- kvm-vmdk-check-l2-table-size-when-opening.patch [bz#995866] +- kvm-vmdk-check-l1-size-before-opening-image.patch [bz#995866] +- kvm-vmdk-use-heap-allocation-for-whole_grain.patch [bz#995866] +- kvm-vmdk-rename-num_gtes_per_gte-to-num_gtes_per_gt.patch [bz#995866] +- kvm-vmdk-Move-l1_size-check-into-vmdk_add_extent.patch [bz#995866] +- kvm-vmdk-fix-L1-and-L2-table-size-in-vmdk3-open.patch [bz#995866] +- kvm-vmdk-support-vmfsSparse-files.patch [bz#995866] +- kvm-vmdk-support-vmfs-files.patch [bz#995866] +- Resolves: bz#995866 + (fix vmdk support to ESX images) + +* Thu Sep 26 2013 Miroslav Rezanina - qemu-kvm-1.5.3-7.el7 +- kvm-spice-fix-display-initialization.patch [bz#974887] +- kvm-Remove-i82550-network-card-emulation.patch [bz#921983] +- kvm-Remove-usb-wacom-tablet.patch [bz#903914] +- kvm-Disable-usb-uas.patch [bz#903914] +- kvm-Disable-vhost-scsi.patch [bz#994642] +- kvm-Remove-no-hpet-option.patch [bz#947441] +- kvm-Disable-isa-parallel.patch [bz#1002286] +- kvm-xhci-implement-warm-port-reset.patch [bz#949514] +- kvm-usb-add-serial-bus-property.patch [bz#953304] +- kvm-rhel6-compat-usb-serial-numbers.patch [bz#953304] +- kvm-vmdk-fix-comment-for-vmdk_co_write_zeroes.patch [bz#995866] +- kvm-gluster-Add-image-resize-support.patch [bz#1007226] +- kvm-block-Introduce-bs-zero_beyond_eof.patch [bz#1007226] +- kvm-block-Produce-zeros-when-protocols-reading-beyond-en.patch [bz#1007226] +- kvm-gluster-Abort-on-AIO-completion-failure.patch [bz#1007226] +- kvm-Preparation-for-usb-bt-dongle-conditional-build.patch [bz#1001131] +- kvm-Remove-dev-bluetooth.c-dependency-from-vl.c.patch [bz#1001131] +- kvm-exec-Fix-Xen-RAM-allocation-with-unusual-options.patch [bz#1009328] +- kvm-exec-Clean-up-fall-back-when-mem-path-allocation-fai.patch [bz#1009328] +- kvm-exec-Reduce-ifdeffery-around-mem-path.patch [bz#1009328] +- kvm-exec-Simplify-the-guest-physical-memory-allocation-h.patch [bz#1009328] +- kvm-exec-Drop-incorrect-dead-S390-code-in-qemu_ram_remap.patch [bz#1009328] +- kvm-exec-Clean-up-unnecessary-S390-ifdeffery.patch [bz#1009328] +- kvm-exec-Don-t-abort-when-we-can-t-allocate-guest-memory.patch [bz#1009328] +- kvm-pc_sysfw-Fix-ISA-BIOS-init-for-ridiculously-big-flas.patch [bz#1009328] +- kvm-virtio-scsi-Make-type-virtio-scsi-common-abstract.patch [bz#903918] +- kvm-qga-move-logfiles-to-new-directory-for-easier-SELinu.patch [bz#1009491] +- kvm-target-i386-add-cpu64-rhel6-CPU-model.patch [bz#918907] +- kvm-fix-steal-time-MSR-vmsd-callback-to-proper-opaque-ty.patch [bz#903889] +- Resolves: bz#1001131 + (Disable or remove device usb-bt-dongle) +- Resolves: bz#1002286 + (Disable or remove device isa-parallel) +- Resolves: bz#1007226 + (Introduce bs->zero_beyond_eof) +- Resolves: bz#1009328 + ([RFE] Nicer error report when qemu-kvm can't allocate guest RAM) +- Resolves: bz#1009491 + (move qga logfiles to new /var/log/qemu-ga/ directory [RHEL-7]) +- Resolves: bz#903889 + (The value of steal time in "top" command always is "0.0% st" after guest migration) +- Resolves: bz#903914 + (Disable or remove usb related devices that we will not support) +- Resolves: bz#903918 + (Disable or remove emulated SCSI devices we will not support) +- Resolves: bz#918907 + (provide backwards-compatible RHEL specific machine types in QEMU - CPU features) +- Resolves: bz#921983 + (Disable or remove emulated network devices that we will not support) +- Resolves: bz#947441 + (HPET device must be disabled) +- Resolves: bz#949514 + (fail to passthrough the USB3.0 stick to windows guest with xHCI controller under pc-i440fx-1.4) +- Resolves: bz#953304 + (Serial number of some USB devices must be fixed for older RHEL machine types) +- Resolves: bz#974887 + (the screen of guest fail to display correctly when use spice + qxl driver) +- Resolves: bz#994642 + (should disable vhost-scsi) +- Resolves: bz#995866 + (fix vmdk support to ESX images) + +* Mon Sep 23 2013 Paolo Bonzini - qemu-kvm-1.5.3-6.el7 +- re-enable spice +- Related: #979953 + +* Mon Sep 23 2013 Paolo Bonzini - qemu-kvm-1.5.3-5.el7 +- temporarily disable spice until libiscsi rebase is complete +- Related: #979953 + +* Thu Sep 19 2013 Michal Novotny - qemu-kvm-1.5.3-4.el7 +- kvm-block-package-preparation-code-in-qmp_transaction.patch [bz#1005818] +- kvm-block-move-input-parsing-code-in-qmp_transaction.patch [bz#1005818] +- kvm-block-package-committing-code-in-qmp_transaction.patch [bz#1005818] +- kvm-block-package-rollback-code-in-qmp_transaction.patch [bz#1005818] +- kvm-block-make-all-steps-in-qmp_transaction-as-callback.patch [bz#1005818] +- kvm-blockdev-drop-redundant-proto_drv-check.patch [bz#1005818] +- kvm-block-Don-t-parse-protocol-from-file.filename.patch [bz#1005818] +- kvm-Revert-block-Disable-driver-specific-options-for-1.5.patch [bz#1005818] +- kvm-qcow2-Add-refcount-update-reason-to-all-callers.patch [bz#1005818] +- kvm-qcow2-Options-to-enable-discard-for-freed-clusters.patch [bz#1005818] +- kvm-qcow2-Batch-discards.patch [bz#1005818] +- kvm-block-Always-enable-discard-on-the-protocol-level.patch [bz#1005818] +- kvm-qapi.py-Avoid-code-duplication.patch [bz#1005818] +- kvm-qapi.py-Allow-top-level-type-reference-for-command-d.patch [bz#1005818] +- kvm-qapi-schema-Use-BlockdevSnapshot-type-for-blockdev-s.patch [bz#1005818] +- kvm-qapi-types.py-Implement-base-for-unions.patch [bz#1005818] +- kvm-qapi-visit.py-Split-off-generate_visit_struct_fields.patch [bz#1005818] +- kvm-qapi-visit.py-Implement-base-for-unions.patch [bz#1005818] +- kvm-docs-Document-QAPI-union-types.patch [bz#1005818] +- kvm-qapi-Add-visitor-for-implicit-structs.patch [bz#1005818] +- kvm-qapi-Flat-unions-with-arbitrary-discriminator.patch [bz#1005818] +- kvm-qapi-Add-consume-argument-to-qmp_input_get_object.patch [bz#1005818] +- kvm-qapi.py-Maintain-a-list-of-union-types.patch [bz#1005818] +- kvm-qapi-qapi-types.py-native-list-support.patch [bz#1005818] +- kvm-qapi-Anonymous-unions.patch [bz#1005818] +- kvm-block-Allow-driver-option-on-the-top-level.patch [bz#1005818] +- kvm-QemuOpts-Add-qemu_opt_unset.patch [bz#1005818] +- kvm-blockdev-Rename-I-O-throttling-options-for-QMP.patch [bz#1005818] +- kvm-qemu-iotests-Update-051-reference-output.patch [bz#1005818] +- kvm-blockdev-Rename-readonly-option-to-read-only.patch [bz#1005818] +- kvm-blockdev-Split-up-cache-option.patch [bz#1005818] +- kvm-qcow2-Use-dashes-instead-of-underscores-in-options.patch [bz#1005818] +- kvm-qemu-iotests-filter-QEMU-version-in-monitor-banner.patch [bz#1006959] +- kvm-tests-set-MALLOC_PERTURB_-to-expose-memory-bugs.patch [bz#1006959] +- kvm-qemu-iotests-Whitespace-cleanup.patch [bz#1006959] +- kvm-qemu-iotests-Fixed-test-case-026.patch [bz#1006959] +- kvm-qemu-iotests-Fix-test-038.patch [bz#1006959] +- kvm-qemu-iotests-Remove-lsi53c895a-tests-from-051.patch [bz#1006959] +- Resolves: bz#1005818 + (qcow2: Backport discard command line options) +- Resolves: bz#1006959 + (qemu-iotests false positives) + +* Thu Aug 29 2013 Miroslav Rezanina - qemu-kvm-1.5.3-3.el7 +- Fix rhel/rhev split + +* Thu Aug 29 2013 Miroslav Rezanina - qemu-kvm-1.5.3-2.el7 +- kvm-osdep-add-qemu_get_local_state_pathname.patch [bz#964304] +- kvm-qga-determine-default-state-dir-and-pidfile-dynamica.patch [bz#964304] +- kvm-configure-don-t-save-any-fixed-local_statedir-for-wi.patch [bz#964304] +- kvm-qga-create-state-directory-on-win32.patch [bz#964304] +- kvm-qga-save-state-directory-in-ga_install_service-RHEL-.patch [bz#964304] +- kvm-Makefile-create-.-var-run-when-installing-the-POSIX-.patch [bz#964304] +- kvm-qemu-option-Fix-qemu_opts_find-for-null-id-arguments.patch [bz#980782] +- kvm-qemu-option-Fix-qemu_opts_set_defaults-for-corner-ca.patch [bz#980782] +- kvm-vl-New-qemu_get_machine_opts.patch [bz#980782] +- kvm-Fix-machine-options-accel-kernel_irqchip-kvm_shadow_.patch [bz#980782] +- kvm-microblaze-Fix-latent-bug-with-default-DTB-lookup.patch [bz#980782] +- kvm-Simplify-machine-option-queries-with-qemu_get_machin.patch [bz#980782] +- kvm-pci-add-VMSTATE_MSIX.patch [bz#838170] +- kvm-xhci-add-XHCISlot-addressed.patch [bz#838170] +- kvm-xhci-add-xhci_alloc_epctx.patch [bz#838170] +- kvm-xhci-add-xhci_init_epctx.patch [bz#838170] +- kvm-xhci-add-live-migration-support.patch [bz#838170] +- kvm-pc-set-level-xlevel-correctly-on-486-qemu32-CPU-mode.patch [bz#918907] +- kvm-pc-Remove-incorrect-rhel6.x-compat-model-value-for-C.patch [bz#918907] +- kvm-pc-rhel6.x-has-x2apic-present-on-Conroe-Penryn-Nehal.patch [bz#918907] +- kvm-pc-set-compat-CPUID-0x80000001-.EDX-bits-on-Westmere.patch [bz#918907] +- kvm-pc-Remove-PCLMULQDQ-from-Westmere-on-rhel6.x-machine.patch [bz#918907] +- kvm-pc-SandyBridge-rhel6.x-compat-fixes.patch [bz#918907] +- kvm-pc-Haswell-doesn-t-have-rdtscp-on-rhel6.x.patch [bz#918907] +- kvm-i386-fix-LAPIC-TSC-deadline-timer-save-restore.patch [bz#972433] +- kvm-all.c-max_cpus-should-not-exceed-KVM-vcpu-limit.patch [bz#996258] +- kvm-add-timestamp-to-error_report.patch [bz#906937] +- kvm-Convert-stderr-message-calling-error_get_pretty-to-e.patch [bz#906937] +- Resolves: bz#838170 + (Add live migration support for USB [xhci, usb-uas]) +- Resolves: bz#906937 + ([Hitachi 7.0 FEAT][QEMU]Add a time stamp to error message (*)) +- Resolves: bz#918907 + (provide backwards-compatible RHEL specific machine types in QEMU - CPU features) +- Resolves: bz#964304 + (Windows guest agent service failed to be started) +- Resolves: bz#972433 + ("INFO: rcu_sched detected stalls" after RHEL7 kvm vm migrated) +- Resolves: bz#980782 + (kernel_irqchip defaults to off instead of on without -machine) +- Resolves: bz#996258 + (boot guest with maxcpu=255 successfully but actually max number of vcpu is 160) + +* Wed Aug 28 2013 Miroslav Rezanina - 10:1.5.3-1 +- Rebase to qemu 1.5.3 + +* Tue Aug 20 2013 Miroslav Rezanina - 10:1.5.2-4 +- qemu: guest agent creates files with insecure permissions in deamon mode [rhel-7.0] (rhbz 974444) +- update qemu-ga config & init script in RHEL7 wrt. fsfreeze hook (rhbz 969942) +- RHEL7 does not have equivalent functionality for __com.redhat_qxl_screendump (rhbz 903910) +- SEP flag behavior for CPU models of RHEL6 machine types should be compatible (rhbz 960216) +- crash command can not read the dump-guest-memory file when paging=false [RHEL-7] (rhbz 981582) +- RHEL 7 qemu-kvm fails to build on F19 host due to libusb deprecated API (rhbz 996469) +- Live migration support in virtio-blk-data-plane (rhbz 995030) +- qemu-img resize can execute successfully even input invalid syntax (rhbz 992935) + +* Fri Aug 09 2013 Miroslav Rezanina - 10:1.5.2-3 +- query mem info from monitor would cause qemu-kvm hang [RHEL-7] (rhbz #970047) +- Throttle-down guest to help with live migration convergence (backport to RHEL7.0) (rhbz #985958) +- disable (for now) EFI-enabled roms (rhbz #962563) +- qemu-kvm "vPMU passthrough" mode breaks migration, shouldn't be enabled by default (rhbz #853101) +- Remove pending watches after virtserialport unplug (rhbz #992900) +- Containment of error when an SR-IOV device encounters an error... (rhbz #984604) + +* Wed Jul 31 2013 Miroslav Rezanina - 10:1.5.2-2 +- SPEC file prepared for RHEL/RHEV split (rhbz #987165) +- RHEL guest( sata disk ) can not boot up (rhbz #981723) +- Kill the "use flash device for BIOS unless KVM" misfeature (rhbz #963280) +- Provide RHEL-6 machine types (rhbz #983991) +- Change s3/s4 default to "disable". (rhbz #980840) +- Support Virtual Memory Disk Format in qemu (rhbz #836675) +- Glusterfs backend for QEMU (rhbz #805139) + +* Tue Jul 02 2013 Miroslav Rezanina - 10:1.5.2-1 +- Rebase to 1.5.2 + +* Tue Jul 02 2013 Miroslav Rezanina - 10:1.5.1-2 +- Fix package package version info (bz #952996) +- pc: Replace upstream machine types by RHEL-7 types (bz #977864) +- target-i386: Update model values on Conroe/Penryn/Nehalem CPU model (bz #861210) +- target-i386: Set level=4 on Conroe/Penryn/Nehalem (bz #861210) + +* Fri Jun 28 2013 Miroslav Rezanina - 10:1.5.1-1 +- Rebase to 1.5.1 +- Change epoch to 10 to obsolete RHEL-6 qemu-kvm-rhev package (bz #818626) + +* Fri May 24 2013 Miroslav Rezanina - 3:1.5.0-2 +- Enable werror (bz #948290) +- Enable nbd driver (bz #875871) +- Fix udev rules file location (bz #958860) +- Remove +x bit from systemd unit files (bz #965000) +- Drop unneeded kvm.modules on x86 (bz #963642) +- Fix build flags +- Enable libusb + +* Thu May 23 2013 Miroslav Rezanina - 3:1.5.0-1 +- Rebase to 1.5.0 + +* Tue Apr 23 2013 Miroslav Rezanina - 3:1.4.0-4 +- Enable build of libcacard subpackage for non-x86_64 archs (bz #873174) +- Enable build of qemu-img subpackage for non-x86_64 archs (bz #873174) +- Enable build of qemu-guest-agent subpackage for non-x86_64 archs (bz #873174) + +* Tue Apr 23 2013 Miroslav Rezanina - 3:1.4.0-3 +- Enable/disable features supported by rhel7 +- Use qemu-kvm instead of qemu in filenames and pathes + +* Fri Apr 19 2013 Daniel Mach - 3:1.4.0-2.1 +- Rebuild for cyrus-sasl + +* Fri Apr 05 2013 Miroslav Rezanina - 3:1.4.0-2 +- Synchronization with Fedora 19 package version 2:1.4.0-8 + +* Wed Apr 03 2013 Daniel Mach - 3:1.4.0-1.1 +- Rebuild for libseccomp + +* Thu Mar 07 2013 Miroslav Rezanina - 3:1.4.0-1 +- Rebase to 1.4.0 + +* Mon Feb 25 2013 Michal Novotny - 3:1.3.0-8 +- Missing package qemu-system-x86 in hardware certification kvm testing (bz#912433) +- Resolves: bz#912433 + (Missing package qemu-system-x86 in hardware certification kvm testing) + +* Fri Feb 22 2013 Alon Levy - 3:1.3.0-6 +- Bump epoch back to 3 since there has already been a 3 package release: + 3:1.2.0-20.el7 https://brewweb.devel.redhat.com/buildinfo?buildID=244866 +- Mark explicit libcacard dependency on new enough qemu-img to avoid conflict + since /usr/bin/vscclient was moved from qemu-img to libcacard subpackage. + +* Wed Feb 13 2013 Michal Novotny - 2:1.3.0-5 +- Fix patch contents for usb-redir (bz#895491) +- Resolves: bz#895491 + (PATCH: 0110-usb-redir-Add-flow-control-support.patch has been mangled on rebase !!) + +* Wed Feb 06 2013 Alon Levy - 2:1.3.0-4 +- Add patch from f19 package for libcacard missing error_set symbol. +- Resolves: bz#891552 + +* Mon Jan 07 2013 Michal Novotny - 2:1.3.0-3 +- Remove dependency on bogus qemu-kvm-kvm package [bz#870343] +- Resolves: bz#870343 + (qemu-kvm-1.2.0-16.el7 cant be installed) + +* Tue Dec 18 2012 Michal Novotny - 2:1.3.0-2 +- Rename qemu to qemu-kvm +- Move qemu-kvm to libexecdir + +* Fri Dec 07 2012 Cole Robinson - 2:1.3.0-1 +- Switch base tarball from qemu-kvm to qemu +- qemu 1.3 release +- Option to use linux VFIO driver to assign PCI devices +- Many USB3 improvements +- New paravirtualized hardware random number generator device. +- Support for Glusterfs volumes with "gluster://" -drive URI +- Block job commands for live block commit and storage migration + +* Wed Nov 28 2012 Alon Levy - 2:1.2.0-25 +* Merge libcacard into qemu, since they both use the same sources now. + +* Thu Nov 22 2012 Paolo Bonzini - 2:1.2.0-24 +- Move vscclient to qemu-common, qemu-nbd to qemu-img + +* Tue Nov 20 2012 Alon Levy - 2:1.2.0-23 +- Rewrite fix for bz #725965 based on fix for bz #867366 +- Resolve bz #867366 + +* Fri Nov 16 2012 Paolo Bonzini - 2:1.2.0-23 +- Backport --with separate_kvm support from EPEL branch + +* Fri Nov 16 2012 Paolo Bonzini - 2:1.2.0-22 +- Fix previous commit + +* Fri Nov 16 2012 Paolo Bonzini - 2:1.2.0-21 +- Backport commit 38f419f (configure: Fix CONFIG_QEMU_HELPERDIR generation, + 2012-10-17) + +* Thu Nov 15 2012 Paolo Bonzini - 2:1.2.0-20 +- Install qemu-bridge-helper as suid root +- Distribute a sample /etc/qemu/bridge.conf file + +* Thu Nov 1 2012 Hans de Goede - 2:1.2.0-19 +- Sync spice patches with upstream, minor bugfixes and set the qxl pci + device revision to 4 by default, so that guests know they can use + the new features + +* Tue Oct 30 2012 Cole Robinson - 2:1.2.0-18 +- Fix loading arm initrd if kernel is very large (bz #862766) +- Don't use reserved word 'function' in systemtap files (bz #870972) +- Drop assertion that was triggering when pausing guests w/ qxl (bz + #870972) + +* Sun Oct 28 2012 Cole Robinson - 2:1.2.0-17 +- Pull patches queued for qemu 1.2.1 + +* Fri Oct 19 2012 Paolo Bonzini - 2:1.2.0-16 +- add s390x KVM support +- distribute pre-built firmware or device trees for Alpha, Microblaze, S390 +- add missing system targets +- add missing linux-user targets +- fix previous commit + +* Thu Oct 18 2012 Dan Horák - 2:1.2.0-15 +- fix build on non-kvm arches like s390(x) + +* Wed Oct 17 2012 Paolo Bonzini - 2:1.2.0-14 +- Change SLOF Requires for the new version number + +* Thu Oct 11 2012 Paolo Bonzini - 2:1.2.0-13 +- Add ppc support to kvm.modules (original patch by David Gibson) +- Replace x86only build with kvmonly build: add separate defines and + conditionals for all packages, so that they can be chosen and + renamed in kvmonly builds and so that qemu has the appropriate requires +- Automatically pick libfdt dependancy +- Add knob to disable spice+seccomp + +* Fri Sep 28 2012 Paolo Bonzini - 2:1.2.0-12 +- Call udevadm on post, fixing bug 860658 + +* Fri Sep 28 2012 Hans de Goede - 2:1.2.0-11 +- Rebuild against latest spice-server and spice-protocol +- Fix non-seamless migration failing with vms with usb-redir devices, + to allow boxes to load such vms from disk + +* Tue Sep 25 2012 Hans de Goede - 2:1.2.0-10 +- Sync Spice patchsets with upstream (rhbz#860238) +- Fix building with usbredir >= 0.5.2 + +* Thu Sep 20 2012 Hans de Goede - 2:1.2.0-9 +- Sync USB and Spice patchsets with upstream + +* Sun Sep 16 2012 Richard W.M. Jones - 2:1.2.0-8 +- Use 'global' instead of 'define', and underscore in definition name, + n-v-r, and 'dist' tag of SLOF, all to fix RHBZ#855252. + +* Fri Sep 14 2012 Paolo Bonzini - 2:1.2.0-4 +- add versioned dependency from qemu-system-ppc to SLOF (BZ#855252) + +* Wed Sep 12 2012 Richard W.M. Jones - 2:1.2.0-3 +- Fix RHBZ#853408 which causes libguestfs failure. + +* Sat Sep 8 2012 Hans de Goede - 2:1.2.0-2 +- Fix crash on (seamless) migration +- Sync usbredir live migration patches with upstream + +* Fri Sep 7 2012 Hans de Goede - 2:1.2.0-1 +- New upstream release 1.2.0 final +- Add support for Spice seamless migration +- Add support for Spice dynamic monitors +- Add support for usb-redir live migration + +* Tue Sep 04 2012 Adam Jackson 1.2.0-0.5.rc1 +- Flip Requires: ceph >= foo to Conflicts: ceph < foo, so we pull in only the + libraries which we need and not the rest of ceph which we don't. + +* Tue Aug 28 2012 Cole Robinson 1.2.0-0.4.rc1 +- Update to 1.2.0-rc1 + +* Mon Aug 20 2012 Richard W.M. Jones - 1.2-0.3.20120806git3e430569 +- Backport Bonzini's vhost-net fix (RHBZ#848400). + +* Tue Aug 14 2012 Cole Robinson - 1.2-0.2.20120806git3e430569 +- Bump release number, previous build forgot but the dist bump helped us out + +* Tue Aug 14 2012 Cole Robinson - 1.2-0.1.20120806git3e430569 +- Revive qemu-system-{ppc*, sparc*} (bz 844502) +- Enable KVM support for all targets (bz 844503) + +* Mon Aug 06 2012 Cole Robinson - 1.2-0.1.20120806git3e430569.fc18 +- Update to git snapshot + +* Sun Jul 29 2012 Cole Robinson - 1.1.1-1 +- Upstream stable release 1.1.1 +- Fix systemtap tapsets (bz 831763) +- Fix VNC audio tunnelling (bz 840653) +- Don't renable ksm on update (bz 815156) +- Bump usbredir dep (bz 812097) +- Fix RPM install error on non-virt machines (bz 660629) +- Obsolete openbios to fix upgrade dependency issues (bz 694802) + +* Sat Jul 21 2012 Fedora Release Engineering - 2:1.1.0-9 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Tue Jul 10 2012 Richard W.M. Jones - 2:1.1.0-8 +- Re-diff previous patch so that it applies and actually apply it + +* Tue Jul 10 2012 Richard W.M. Jones - 2:1.1.0-7 +- Add patch to fix default machine options. This fixes libvirt + detection of qemu. +- Back out patch 1 which conflicts. + +* Fri Jul 6 2012 Hans de Goede - 2:1.1.0-5 +- Fix qemu crashing (on an assert) whenever USB-2.0 isoc transfers are used + +* Thu Jul 5 2012 Richard W.M. Jones - 2:1.1.0-4 +- Disable tests since they hang intermittently. +- Add kvmvapic.bin (replaces vapic.bin). +- Add cpus-x86_64.conf. qemu now creates /etc/qemu/target-x86_64.conf + as an empty file. +- Add qemu-icon.bmp. +- Add qemu-bridge-helper. +- Build and include virtfs-proxy-helper + man page (thanks Hans de Goede). + +* Wed Jul 4 2012 Hans de Goede - 2:1.1.0-1 +- New upstream release 1.1.0 +- Drop about a 100 spice + USB patches, which are all upstream + +* Mon Apr 23 2012 Paolo Bonzini - 2:1.0-17 +- Fix install failure due to set -e (rhbz #815272) + +* Mon Apr 23 2012 Paolo Bonzini - 2:1.0-16 +- Fix kvm.modules to exit successfully on non-KVM capable systems (rhbz #814932) + +* Thu Apr 19 2012 Hans de Goede - 2:1.0-15 +- Add a couple of backported QXL/Spice bugfixes +- Add spice volume control patches + +* Fri Apr 6 2012 Paolo Bonzini - 2:1.0-12 +- Add back PPC and SPARC user emulators +- Update binfmt rules from upstream + +* Mon Apr 2 2012 Hans de Goede - 2:1.0-11 +- Some more USB bugfixes from upstream + +* Thu Mar 29 2012 Eduardo Habkost - 2:1.0-12 +- Fix ExclusiveArch mistake that disabled all non-x86_64 builds on Fedora + +* Wed Mar 28 2012 Eduardo Habkost - 2:1.0-11 +- Use --with variables for build-time settings + +* Wed Mar 28 2012 Daniel P. Berrange - 2:1.0-10 +- Switch to use iPXE for netboot ROMs + +* Thu Mar 22 2012 Daniel P. Berrange - 2:1.0-9 +- Remove O_NOATIME for 9p filesystems + +* Mon Mar 19 2012 Daniel P. Berrange - 2:1.0-8 +- Move udev rules to /lib/udev/rules.d (rhbz #748207) + +* Fri Mar 9 2012 Hans de Goede - 2:1.0-7 +- Add a whole bunch of USB bugfixes from upstream + +* Mon Feb 13 2012 Daniel P. Berrange - 2:1.0-6 +- Add many more missing BRs for misc QEMU features +- Enable running of test suite during build + +* Tue Feb 07 2012 Justin M. Forbes - 2:1.0-5 +- Add support for virtio-scsi + +* Sun Feb 5 2012 Richard W.M. Jones - 2:1.0-4 +- Require updated ceph for latest librbd with rbd_flush symbol. + +* Tue Jan 24 2012 Justin M. Forbes - 2:1.0-3 +- Add support for vPMU +- e1000: bounds packet size against buffer size CVE-2012-0029 + +* Fri Jan 13 2012 Justin M. Forbes - 2:1.0-2 +- Add patches for USB redirect bits +- Remove palcode-clipper, we don't build it + +* Wed Jan 11 2012 Justin M. Forbes - 2:1.0-1 +- Add patches from 1.0.1 queue + +* Fri Dec 16 2011 Justin M. Forbes - 2:1.0-1 +- Update to qemu 1.0 + +* Tue Nov 15 2011 Justin M. Forbes - 2:0.15.1-3 +- Enable spice for i686 users as well + +* Thu Nov 03 2011 Justin M. Forbes - 2:0.15.1-2 +- Fix POSTIN scriplet failure (#748281) + +* Fri Oct 21 2011 Justin M. Forbes - 2:0.15.1-1 +- Require seabios-bin >= 0.6.0-2 (#741992) +- Replace init scripts with systemd units (#741920) +- Update to 0.15.1 stable upstream + +* Fri Oct 21 2011 Paul Moore +- Enable full relro and PIE (rhbz #738812) + +* Wed Oct 12 2011 Daniel P. Berrange - 2:0.15.0-6 +- Add BR on ceph-devel to enable RBD block device + +* Wed Oct 5 2011 Daniel P. Berrange - 2:0.15.0-5 +- Create a qemu-guest-agent sub-RPM for guest installation + +* Tue Sep 13 2011 Daniel P. Berrange - 2:0.15.0-4 +- Enable DTrace tracing backend for SystemTAP (rhbz #737763) +- Enable build with curl (rhbz #737006) + +* Thu Aug 18 2011 Hans de Goede - 2:0.15.0-3 +- Add missing BuildRequires: usbredir-devel, so that the usbredir code + actually gets build + +* Thu Aug 18 2011 Richard W.M. Jones - 2:0.15.0-2 +- Add upstream qemu patch 'Allow to leave type on default in -machine' + (2645c6dcaf6ea2a51a3b6dfa407dd203004e4d11). + +* Sun Aug 14 2011 Justin M. Forbes - 2:0.15.0-1 +- Update to 0.15.0 stable release. + +* Thu Aug 04 2011 Justin M. Forbes - 2:0.15.0-0.3.201108040af4922 +- Update to 0.15.0-rc1 as we prepare for 0.15.0 release + +* Thu Aug 4 2011 Daniel P. Berrange - 2:0.15.0-0.3.2011072859fadcc +- Fix default accelerator for non-KVM builds (rhbz #724814) + +* Thu Jul 28 2011 Justin M. Forbes - 2:0.15.0-0.1.2011072859fadcc +- Update to 0.15.0-rc0 as we prepare for 0.15.0 release + +* Tue Jul 19 2011 Hans de Goede - 2:0.15.0-0.2.20110718525e3df +- Add support usb redirection over the network, see: + http://fedoraproject.org/wiki/Features/UsbNetworkRedirection +- Restore chardev flow control patches + +* Mon Jul 18 2011 Justin M. Forbes - 2:0.15.0-0.1.20110718525e3df +- Update to git snapshot as we prepare for 0.15.0 release + +* Wed Jun 22 2011 Richard W.M. Jones - 2:0.14.0-9 +- Add BR libattr-devel. This caused the -fstype option to be disabled. + https://www.redhat.com/archives/libvir-list/2011-June/thread.html#01017 + +* Mon May 2 2011 Hans de Goede - 2:0.14.0-8 +- Fix a bug in the spice flow control patches which breaks the tcp chardev + +* Tue Mar 29 2011 Justin M. Forbes - 2:0.14.0-7 +- Disable qemu-ppc and qemu-sparc packages (#679179) + +* Mon Mar 28 2011 Justin M. Forbes - 2:0.14.0-6 +- Spice fixes for flow control. + +* Tue Mar 22 2011 Dan Horák - 2:0.14.0-5 +- be more careful when removing the -g flag on s390 + +* Fri Mar 18 2011 Justin M. Forbes - 2:0.14.0-4 +- Fix thinko on adding the most recent patches. + +* Wed Mar 16 2011 Justin M. Forbes - 2:0.14.0-3 +- Fix migration issue with vhost +- Fix qxl locking issues for spice + +* Wed Mar 02 2011 Justin M. Forbes - 2:0.14.0-2 +- Re-enable sparc and cris builds + +* Thu Feb 24 2011 Justin M. Forbes - 2:0.14.0-1 +- Update to 0.14.0 release + +* Fri Feb 11 2011 Justin M. Forbes - 2:0.14.0-0.1.20110210git7aa8c46 +- Update git snapshot +- Temporarily disable qemu-cris and qemu-sparc due to build errors (to be resolved shorly) + +* Tue Feb 08 2011 Justin M. Forbes - 2:0.14.0-0.1.20110208git3593e6b +- Update to 0.14.0 rc git snapshot +- Add virtio-net to modules + +* Wed Nov 3 2010 Daniel P. Berrange - 2:0.13.0-2 +- Revert previous change +- Make qemu-common own the /etc/qemu directory +- Add /etc/qemu/target-x86_64.conf to qemu-system-x86 regardless + of host architecture. + +* Wed Nov 03 2010 Dan Horák - 2:0.13.0-2 +- Remove kvm config file on non-x86 arches (part of #639471) +- Own the /etc/qemu directory + +* Mon Oct 18 2010 Justin M. Forbes - 2:0.13.0-1 +- Update to 0.13.0 upstream release +- Fixes for vhost +- Fix mouse in certain guests (#636887) +- Fix issues with WinXP guest install (#579348) +- Resolve build issues with S390 (#639471) +- Fix Windows XP on Raw Devices (#631591) + +* Tue Oct 05 2010 jkeating - 2:0.13.0-0.7.rc1.1 +- Rebuilt for gcc bug 634757 + +* Tue Sep 21 2010 Justin M. Forbes - 2:0.13.0-0.7.rc1 +- Flip qxl pci id from unstable to stable (#634535) +- KSM Fixes from upstream (#558281) + +* Tue Sep 14 2010 Justin M. Forbes - 2:0.13.0-0.6.rc1 +- Move away from git snapshots as 0.13 is close to release +- Updates for spice 0.6 + +* Tue Aug 10 2010 Justin M. Forbes - 2:0.13.0-0.5.20100809git25fdf4a +- Fix typo in e1000 gpxe rom requires. +- Add links to newer vgabios + +* Tue Aug 10 2010 Justin M. Forbes - 2:0.13.0-0.4.20100809git25fdf4a +- Disable spice on 32bit, it is not supported and buildreqs don't exist. + +* Mon Aug 9 2010 Justin M. Forbes - 2:0.13.0-0.3.20100809git25fdf4a +- Updates from upstream towards 0.13 stable +- Fix requires on gpxe +- enable spice now that buildreqs are in the repository. +- ksmtrace has moved to a separate upstream package + +* Tue Jul 27 2010 Justin M. Forbes - 2:0.13.0-0.2.20100727gitb81fe95 +- add texinfo buildreq for manpages. + +* Tue Jul 27 2010 Justin M. Forbes - 2:0.13.0-0.1.20100727gitb81fe95 +- Update to 0.13.0 upstream snapshot +- ksm init fixes from upstream + +* Tue Jul 20 2010 Dan Horák - 2:0.12.3-8 +- Add avoid-llseek patch from upstream needed for building on s390(x) +- Don't use parallel make on s390(x) + +* Tue Jun 22 2010 Amit Shah - 2:0.12.3-7 +- Add vvfat hardening patch from upstream (#605202) + +* Fri Apr 23 2010 Justin M. Forbes - 2:0.12.3-6 +- Change requires to the noarch seabios-bin +- Add ownership of docdir to qemu-common (#572110) +- Fix "Cannot boot from non-existent NIC" error when using virt-install (#577851) + +* Thu Apr 15 2010 Justin M. Forbes - 2:0.12.3-5 +- Update virtio console patches from upstream + +* Thu Mar 11 2010 Justin M. Forbes - 2:0.12.3-4 +- Detect cdrom via ioctl (#473154) +- re add increased buffer for USB control requests (#546483) + +* Wed Mar 10 2010 Justin M. Forbes - 2:0.12.3-3 +- Migration clear the fd in error cases (#518032) + +* Tue Mar 09 2010 Justin M. Forbes - 2:0.12.3-2 +- Allow builds --with x86only +- Add libaio-devel buildreq for aio support + +* Fri Feb 26 2010 Justin M. Forbes - 2:0.12.3-1 +- Update to 0.12.3 upstream +- vhost-net migration/restart fixes +- Add F-13 machine type +- virtio-serial fixes + +* Tue Feb 09 2010 Justin M. Forbes - 2:0.12.2-6 +- Add vhost net support. + +* Thu Feb 04 2010 Justin M. Forbes - 2:0.12.2-5 +- Avoid creating too large iovecs in multiwrite merge (#559717) +- Don't try to set max_kernel_pages during ksm init on newer kernels (#558281) +- Add logfile options for ksmtuned debug. + +* Wed Jan 27 2010 Amit Shah - 2:0.12.2-4 +- Remove build dependency on iasl now that we have seabios + +* Wed Jan 27 2010 Amit Shah - 2:0.12.2-3 +- Remove source target for 0.12.1.2 + +* Wed Jan 27 2010 Amit Shah - 2:0.12.2-2 +- Add virtio-console patches from upstream for the F13 VirtioSerial feature + +* Mon Jan 25 2010 Justin M. Forbes - 2:0.12.2-1 +- Update to 0.12.2 upstream + +* Sun Jan 10 2010 Justin M. Forbes - 2:0.12.1.2-3 +- Point to seabios instead of bochs, and add a requires for seabios + +* Mon Jan 4 2010 Justin M. Forbes - 2:0.12.1.2-2 +- Remove qcow2 virtio backing file patch + +* Mon Jan 4 2010 Justin M. Forbes - 2:0.12.1.2-1 +- Update to 0.12.1.2 upstream +- Remove patches included in upstream + +* Fri Nov 20 2009 Mark McLoughlin - 2:0.11.0-12 +- Fix a use-after-free crasher in the slirp code (#539583) +- Fix overflow in the parallels image format support (#533573) + +* Wed Nov 4 2009 Mark McLoughlin - 2:0.11.0-11 +- Temporarily disable preadv/pwritev support to fix data corruption (#526549) + +* Tue Nov 3 2009 Justin M. Forbes - 2:0.11.0-10 +- Default ksm and ksmtuned services on. + +* Thu Oct 29 2009 Mark McLoughlin - 2:0.11.0-9 +- Fix dropped packets with non-virtio NICs (#531419) + +* Wed Oct 21 2009 Glauber Costa - 2:0.11.0-8 +- Properly save kvm time registers (#524229) + +* Mon Oct 19 2009 Mark McLoughlin - 2:0.11.0-7 +- Fix potential segfault from too small MSR_COUNT (#528901) + +* Fri Oct 9 2009 Mark McLoughlin - 2:0.11.0-6 +- Fix fs errors with virtio and qcow2 backing file (#524734) +- Fix ksm initscript errors on kernel missing ksm (#527653) +- Add missing Requires(post): getent, useradd, groupadd (#527087) + +* Tue Oct 6 2009 Mark McLoughlin - 2:0.11.0-5 +- Add 'retune' verb to ksmtuned init script + +* Mon Oct 5 2009 Mark McLoughlin - 2:0.11.0-4 +- Use rtl8029 PXE rom for ne2k_pci, not ne (#526777) +- Also, replace the gpxe-roms-qemu pkg requires with file-based requires + +* Thu Oct 1 2009 Justin M. Forbes - 2:0.11.0-3 +- Improve error reporting on file access (#524695) + +* Mon Sep 28 2009 Mark McLoughlin - 2:0.11.0-2 +- Fix pci hotplug to not exit if supplied an invalid NIC model (#524022) + +* Mon Sep 28 2009 Mark McLoughlin - 2:0.11.0-1 +- Update to 0.11.0 release +- Drop a couple of upstreamed patches + +* Wed Sep 23 2009 Mark McLoughlin - 2:0.10.92-5 +- Fix issue causing NIC hotplug confusion when no model is specified (#524022) + +* Wed Sep 16 2009 Mark McLoughlin - 2:0.10.92-4 +- Fix for KSM patch from Justin Forbes + +* Wed Sep 16 2009 Mark McLoughlin - 2:0.10.92-3 +- Add ksmtuned, also from Dan Kenigsberg +- Use %%_initddir macro + +* Wed Sep 16 2009 Mark McLoughlin - 2:0.10.92-2 +- Add ksm control script from Dan Kenigsberg + +* Mon Sep 7 2009 Mark McLoughlin - 2:0.10.92-1 +- Update to qemu-kvm-0.11.0-rc2 +- Drop upstreamed patches +- extboot install now fixed upstream +- Re-place TCG init fix (#516543) with the one gone upstream + +* Mon Sep 7 2009 Mark McLoughlin - 2:0.10.91-0.10.rc1 +- Fix MSI-X error handling on older kernels (#519787) + +* Fri Sep 4 2009 Mark McLoughlin - 2:0.10.91-0.9.rc1 +- Make pulseaudio the default audio backend (#519540, #495964, #496627) + +* Thu Aug 20 2009 Richard W.M. Jones - 2:0.10.91-0.8.rc1 +- Fix segfault when qemu-kvm is invoked inside a VM (#516543) + +* Tue Aug 18 2009 Mark McLoughlin - 2:0.10.91-0.7.rc1 +- Fix permissions on udev rules (#517571) + +* Mon Aug 17 2009 Lubomir Rintel - 2:0.10.91-0.6.rc1 +- Allow blacklisting of kvm modules (#517866) + +* Fri Aug 7 2009 Mark McLoughlin - 2:0.10.91-0.5.rc1 +- Fix virtio_net with -net user (#516022) + +* Tue Aug 4 2009 Mark McLoughlin - 2:0.10.91-0.4.rc1 +- Update to qemu-kvm-0.11-rc1; no changes from rc1-rc0 + +* Tue Aug 4 2009 Mark McLoughlin - 2:0.10.91-0.3.rc1.rc0 +- Fix extboot checksum (bug #514899) + +* Fri Jul 31 2009 Mark McLoughlin - 2:0.10.91-0.2.rc1.rc0 +- Add KSM support +- Require bochs-bios >= 2.3.8-0.8 for latest kvm bios updates + +* Thu Jul 30 2009 Mark McLoughlin - 2:0.10.91-0.1.rc1.rc0 +- Update to qemu-kvm-0.11.0-rc1-rc0 +- This is a pre-release of the official -rc1 +- A vista installer regression is blocking the official -rc1 release +- Drop qemu-prefer-sysfs-for-usb-host-devices.patch +- Drop qemu-fix-build-for-esd-audio.patch +- Drop qemu-slirp-Fix-guestfwd-for-incoming-data.patch +- Add patch to ensure extboot.bin is installed + +* Sun Jul 26 2009 Fedora Release Engineering - 2:0.10.50-14.kvm88 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild + +* Thu Jul 23 2009 Glauber Costa - 2:0.10.50-13.kvm88 +- Fix bug 513249, -net channel option is broken + +* Thu Jul 16 2009 Daniel P. Berrange - 2:0.10.50-12.kvm88 +- Add 'qemu' user and group accounts +- Force disable xen until it can be made to build + +* Thu Jul 16 2009 Mark McLoughlin - 2:0.10.50-11.kvm88 +- Update to kvm-88, see http://www.linux-kvm.org/page/ChangeLog +- Package mutiboot.bin +- Update for how extboot is built +- Fix sf.net source URL +- Drop qemu-fix-ppc-softmmu-kvm-disabled-build.patch +- Drop qemu-fix-pcspk-build-with-kvm-disabled.patch +- Cherry-pick fix for esound support build failure + +* Wed Jul 15 2009 Daniel Berrange - 2:0.10.50-10.kvm87 +- Add udev rules to make /dev/kvm world accessible & group=kvm (rhbz #497341) +- Create a kvm group if it doesn't exist (rhbz #346151) + +* Tue Jul 07 2009 Glauber Costa - 2:0.10.50-9.kvm87 +- use pxe roms from gpxe, instead of etherboot package. + +* Fri Jul 3 2009 Mark McLoughlin - 2:0.10.50-8.kvm87 +- Prefer sysfs over usbfs for usb passthrough (#508326) + +* Sat Jun 27 2009 Mark McLoughlin - 2:0.10.50-7.kvm87 +- Update to kvm-87 +- Drop upstreamed patches +- Cherry-pick new ppc build fix from upstream +- Work around broken linux-user build on ppc +- Fix hw/pcspk.c build with --disable-kvm +- Re-enable preadv()/pwritev() since #497429 is long since fixed +- Kill petalogix-s3adsp1800.dtb, since we don't ship the microblaze target + +* Fri Jun 5 2009 Mark McLoughlin - 2:0.10.50-6.kvm86 +- Fix 'kernel requires an x86-64 CPU' error +- BuildRequires ncurses-devel to enable '-curses' option (#504226) + +* Wed Jun 3 2009 Mark McLoughlin - 2:0.10.50-5.kvm86 +- Prevent locked cdrom eject - fixes hang at end of anaconda installs (#501412) +- Avoid harmless 'unhandled wrmsr' warnings (#499712) + +* Thu May 21 2009 Mark McLoughlin - 2:0.10.50-4.kvm86 +- Update to kvm-86 release +- ChangeLog here: http://marc.info/?l=kvm&m=124282885729710 + +* Fri May 1 2009 Mark McLoughlin - 2:0.10.50-3.kvm85 +- Really provide qemu-kvm as a metapackage for comps + +* Tue Apr 28 2009 Mark McLoughlin - 2:0.10.50-2.kvm85 +- Provide qemu-kvm as a metapackage for comps + +* Mon Apr 27 2009 Mark McLoughlin - 2:0.10.50-1.kvm85 +- Update to qemu-kvm-devel-85 +- kvm-85 is based on qemu development branch, currently version 0.10.50 +- Include new qemu-io utility in qemu-img package +- Re-instate -help string for boot=on to fix virtio booting with libvirt +- Drop upstreamed patches +- Fix missing kernel/include/asm symlink in upstream tarball +- Fix target-arm build +- Fix build on ppc +- Disable preadv()/pwritev() until bug #497429 is fixed +- Kill more .kernelrelease uselessness +- Make non-kvm qemu build verbose + +* Fri Apr 24 2009 Mark McLoughlin - 2:0.10-15 +- Fix source numbering typos caused by make-release addition + +* Thu Apr 23 2009 Mark McLoughlin - 2:0.10-14 +- Improve instructions for generating the tarball + +* Tue Apr 21 2009 Mark McLoughlin - 2:0.10-13 +- Enable pulseaudio driver to fix qemu lockup at shutdown (#495964) + +* Tue Apr 21 2009 Mark McLoughlin - 2:0.10-12 +- Another qcow2 image corruption fix (#496642) + +* Mon Apr 20 2009 Mark McLoughlin - 2:0.10-11 +- Fix qcow2 image corruption (#496642) + +* Sun Apr 19 2009 Mark McLoughlin - 2:0.10-10 +- Run sysconfig.modules from %%post on x86_64 too (#494739) + +* Sun Apr 19 2009 Mark McLoughlin - 2:0.10-9 +- Align VGA ROM to 4k boundary - fixes 'qemu-kvm -std vga' (#494376) + +* Tue Apr 14 2009 Glauber Costa - 2:0.10-8 +- Provide qemu-kvm conditional on the architecture. + +* Thu Apr 9 2009 Mark McLoughlin - 2:0.10-7 +- Add a much cleaner fix for vga segfault (#494002) + +* Sun Apr 5 2009 Glauber Costa - 2:0.10-6 +- Fixed qcow2 segfault creating disks over 2TB. #491943 + +* Fri Apr 3 2009 Mark McLoughlin - 2:0.10-5 +- Fix vga segfault under kvm-autotest (#494002) +- Kill kernelrelease hack; it's not needed +- Build with "make V=1" for more verbose logs + +* Thu Apr 02 2009 Glauber Costa - 2:0.10-4 +- Support botting gpxe roms. + +* Wed Apr 01 2009 Glauber Costa - 2:0.10-2 +- added missing patch. love for CVS. + +* Wed Apr 01 2009 Glauber Costa - 2:0.10-1 +- Include debuginfo for qemu-img +- Do not require qemu-common for qemu-img +- Explicitly own each of the firmware files +- remove firmwares for ppc and sparc. They should be provided by an external package. + Not that the packages exists for sparc in the secondary arch repo as noarch, but they + don't automatically get into main repos. Unfortunately it's the best we can do right + now. +- rollback a bit in time. Snapshot from avi's maint/2.6.30 + - this requires the sasl patches to come back. + - with-patched-kernel comes back. + +* Wed Mar 25 2009 Mark McLoughlin - 2:0.10-0.12.kvm20090323git +- BuildRequires pciutils-devel for device assignment (#492076) + +* Mon Mar 23 2009 Glauber Costa - 2:0.10-0.11.kvm20090323git +- Update to snapshot kvm20090323. +- Removed patch2 (upstream). +- use upstream's new split package. +- --with-patched-kernel flag not needed anymore +- Tell how to get the sources. + +* Wed Mar 18 2009 Glauber Costa - 2:0.10-0.10.kvm20090310git +- Added extboot to files list. + +* Wed Mar 11 2009 Glauber Costa - 2:0.10-0.9.kvm20090310git +- Fix wrong reference to bochs bios. + +* Wed Mar 11 2009 Glauber Costa - 2:0.10-0.8.kvm20090310git +- fix Obsolete/Provides pair +- Use kvm bios from bochs-bios package. +- Using RPM_OPT_FLAGS in configure +- Picked back audio-drv-list from kvm package + +* Tue Mar 10 2009 Glauber Costa - 2:0.10-0.7.kvm20090310git +- modify ppc patch + +* Tue Mar 10 2009 Glauber Costa - 2:0.10-0.6.kvm20090310git +- updated to kvm20090310git +- removed sasl patches (already in this release) + +* Tue Mar 10 2009 Glauber Costa - 2:0.10-0.5.kvm20090303git +- kvm.modules were being wrongly mentioned at %%install. +- update description for the x86 system package to include kvm support +- build kvm's own bios. It is still necessary while kvm uses a slightly different + irq routing mechanism + +* Thu Mar 05 2009 Glauber Costa - 2:0.10-0.4.kvm20090303git +- seems Epoch does not go into the tags. So start back here. + +* Thu Mar 05 2009 Glauber Costa - 2:0.10-0.1.kvm20090303git +- Use bochs-bios instead of bochs-bios-data +- It's official: upstream set on 0.10 + +* Thu Mar 5 2009 Daniel P. Berrange - 2:0.9.2-0.2.kvm20090303git +- Added BSD to license list, since many files are covered by BSD + +* Wed Mar 04 2009 Glauber Costa - 0.9.2-0.1.kvm20090303git +- missing a dot. shame on me + +* Wed Mar 04 2009 Glauber Costa - 0.92-0.1.kvm20090303git +- Set Epoch to 2 +- Set version to 0.92. It seems upstream keep changing minds here, so pick the lowest +- Provides KVM, Obsoletes KVM +- Only install qemu-kvm in ix86 and x86_64 +- Remove pkgdesc macros, as they were generating bogus output for rpm -qi. +- fix ppc and ppc64 builds + +* Tue Mar 03 2009 Glauber Costa - 0.10-0.3.kvm20090303git +- only execute post scripts for user package. +- added kvm tools. + +* Tue Mar 03 2009 Glauber Costa - 0.10-0.2.kvm20090303git +- put kvm.modules into cvs + +* Tue Mar 03 2009 Glauber Costa - 0.10-0.1.kvm20090303git +- Set Epoch to 1 +- Build KVM (basic build, no tools yet) +- Set ppc in ExcludeArch. This is temporary, just to fix one issue at a time. + ppc users (IBM ? ;-)) please wait a little bit. + +* Tue Mar 3 2009 Daniel P. Berrange - 1.0-0.5.svn6666 +- Support VNC SASL authentication protocol +- Fix dep on bochs-bios-data + +* Tue Mar 03 2009 Glauber Costa - 1.0-0.4.svn6666 +- use bios from bochs-bios package. + +* Tue Mar 03 2009 Glauber Costa - 1.0-0.3.svn6666 +- use vgabios from vgabios package. + +* Mon Mar 02 2009 Glauber Costa - 1.0-0.2.svn6666 +- use pxe roms from etherboot package. + +* Mon Mar 02 2009 Glauber Costa - 1.0-0.1.svn6666 +- Updated to tip svn (release 6666). Featuring split packages for qemu. + Unfortunately, still using binary blobs for the bioses. + +* Wed Feb 25 2009 Fedora Release Engineering - 0.9.1-13 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild + +* Sun Jan 11 2009 Debarshi Ray - 0.9.1-12 +- Updated build patch. Closes Red Hat Bugzilla bug #465041. + +* Wed Dec 31 2008 Dennis Gilmore - 0.9.1-11 +- add sparcv9 and sparc64 support + +* Fri Jul 25 2008 Bill Nottingham +- Fix qemu-img summary (#456344) + +* Wed Jun 25 2008 Daniel P. Berrange - 0.9.1-10.fc10 +- Rebuild for GNU TLS ABI change + +* Wed Jun 11 2008 Daniel P. Berrange - 0.9.1-9.fc10 +- Remove bogus wildcard from files list (rhbz #450701) + +* Sat May 17 2008 Lubomir Rintel - 0.9.1-8 +- Register binary handlers also for shared libraries + +* Mon May 5 2008 Daniel P. Berrange - 0.9.1-7.fc10 +- Fix text console PTYs to be in rawmode + +* Sun Apr 27 2008 Lubomir Kundrak - 0.9.1-6 +- Register binary handler for SuperH-4 CPU + +* Wed Mar 19 2008 Daniel P. Berrange - 0.9.1-5.fc9 +- Split qemu-img tool into sub-package for smaller footprint installs + +* Wed Feb 27 2008 Daniel P. Berrange - 0.9.1-4.fc9 +- Fix block device checks for extendable disk formats (rhbz #435139) + +* Sat Feb 23 2008 Daniel P. Berrange - 0.9.1-3.fc9 +- Fix block device extents check (rhbz #433560) + +* Mon Feb 18 2008 Fedora Release Engineering - 0.9.1-2 +- Autorebuild for GCC 4.3 + +* Tue Jan 8 2008 Daniel P. Berrange - 0.9.1-1.fc9 +- Updated to 0.9.1 release +- Fix license tag syntax +- Don't mark init script as a config file + +* Wed Sep 26 2007 Daniel P. Berrange - 0.9.0-5.fc8 +- Fix rtl8139 checksum calculation for Vista (rhbz #308201) + +* Tue Aug 28 2007 Daniel P. Berrange - 0.9.0-4.fc8 +- Fix debuginfo by passing -Wl,--build-id to linker + +* Tue Aug 28 2007 David Woodhouse 0.9.0-4 +- Update licence +- Fix CDROM emulation (#253542) + +* Tue Aug 28 2007 Daniel P. Berrange - 0.9.0-3.fc8 +- Added backport of VNC password auth, and TLS+x509 cert auth +- Switch to rtl8139 NIC by default for linkstate reporting +- Fix rtl8139 mmio region mappings with multiple NICs + +* Sun Apr 1 2007 Hans de Goede 0.9.0-2 +- Fix direct loading of a linux kernel with -kernel & -initrd (bz 234681) +- Remove spurious execute bits from manpages (bz 222573) + +* Tue Feb 6 2007 David Woodhouse 0.9.0-1 +- Update to 0.9.0 + +* Wed Jan 31 2007 David Woodhouse 0.8.2-5 +- Include licences + +* Mon Nov 13 2006 Hans de Goede 0.8.2-4 +- Backport patch to make FC6 guests work by Kevin Kofler + (bz 207843). + +* Mon Sep 11 2006 David Woodhouse 0.8.2-3 +- Rebuild + +* Thu Aug 24 2006 Matthias Saou 0.8.2-2 +- Remove the target-list iteration for x86_64 since they all build again. +- Make gcc32 vs. gcc34 conditional on %%{fedora} to share the same spec for + FC5 and FC6. + +* Wed Aug 23 2006 Matthias Saou 0.8.2-1 +- Update to 0.8.2 (#200065). +- Drop upstreamed syscall-macros patch2. +- Put correct scriplet dependencies. +- Force install mode for the init script to avoid umask problems. +- Add %%postun condrestart for changes to the init script to be applied if any. +- Update description with the latest "about" from the web page (more current). +- Update URL to qemu.org one like the Source. +- Add which build requirement. +- Don't include texi files in %%doc since we ship them in html. +- Switch to using gcc34 on devel, FC5 still has gcc32. +- Add kernheaders patch to fix linux/compiler.h inclusion. +- Add target-sparc patch to fix compiling on ppc (some int32 to float). + +* Thu Jun 8 2006 David Woodhouse 0.8.1-3 +- More header abuse in modify_ldt(), change BuildRoot: + +* Wed Jun 7 2006 David Woodhouse 0.8.1-2 +- Fix up kernel header abuse + +* Tue May 30 2006 David Woodhouse 0.8.1-1 +- Update to 0.8.1 + +* Sat Mar 18 2006 David Woodhouse 0.8.0-6 +- Update linker script for PPC + +* Sat Mar 18 2006 David Woodhouse 0.8.0-5 +- Just drop $RPM_OPT_FLAGS. They're too much of a PITA + +* Sat Mar 18 2006 David Woodhouse 0.8.0-4 +- Disable stack-protector options which gcc 3.2 doesn't like + +* Fri Mar 17 2006 David Woodhouse 0.8.0-3 +- Use -mcpu= instead of -mtune= on x86_64 too +- Disable SPARC targets on x86_64, because dyngen doesn't like fnegs + +* Fri Mar 17 2006 David Woodhouse 0.8.0-2 +- Don't use -mtune=pentium4 on i386. GCC 3.2 doesn't like it + +* Fri Mar 17 2006 David Woodhouse 0.8.0-1 +- Update to 0.8.0 +- Resort to using compat-gcc-32 +- Enable ALSA + +* Mon May 16 2005 David Woodhouse 0.7.0-2 +- Proper fix for GCC 4 putting 'blr' or 'ret' in the middle of the function, + for i386, x86_64 and PPC. + +* Sat Apr 30 2005 David Woodhouse 0.7.0-1 +- Update to 0.7.0 +- Fix dyngen for PPC functions which end in unconditional branch + +* Thu Apr 7 2005 Michael Schwendt +- rebuilt + +* Sun Feb 13 2005 David Woodhouse 0.6.1-2 +- Package cleanup + +* Sun Nov 21 2004 David Woodhouse 0.6.1-1 +- Update to 0.6.1 + +* Tue Jul 20 2004 David Woodhouse 0.6.0-2 +- Compile fix from qemu CVS, add x86_64 host support + +* Wed May 12 2004 David Woodhouse 0.6.0-1 +- Update to 0.6.0. + +* Sat May 8 2004 David Woodhouse 0.5.5-1 +- Update to 0.5.5. + +* Sun May 2 2004 David Woodhouse 0.5.4-1 +- Update to 0.5.4. + +* Thu Apr 22 2004 David Woodhouse 0.5.3-1 +- Update to 0.5.3. Add init script. + +* Thu Jul 17 2003 Jeff Johnson 0.4.3-1 +- Create.