[PATCH v2 00/15] qemu: migrate block bitmaps when migrating storage

When we are copying storage we should also preserve the block dirty bitmaps. This series implements their migration. For standard migration with shared storage we let qemu flush the bitmaps to disk and reload them on destination. There is possibility to migrate them using the migration stream too but it's not implemented in this series. v2: Patches from v1: - patches 4-7 from v1 were pushed already - patch 8 from v1 was dropped This version: - patch 1 was updated to capture the latest capabilities - patch 4 is new, replaces patch 8 from v1 - all other patches apply review feedback from Jirka - patch 14 has one additional change (thus I didn't carry Jirka's R-b): The migration of bitmaps is enabled based on QEMU_CAPS_MIGRATION_PARAM_BLOCK_BITMAP_MAPPING instead of QEMU_CAPS_INCREMENTAL_BACKUP. Peter Krempa (15): qemucapabilitiesdata: Update test data for qemu-6.0 on x86_64 qemu: capabilities: Introduce QEMU_CAPS_MIGRATION_PARAM_BLOCK_BITMAP_MAPPING qemu: Probe whether an image is 'qcow2 v2' from query-named-block-nodes qemu: migration: Create qcow2 v3 images for VIR_MIGRATE_NON_SHARED_DISK qemu: monitor: Introduce qemuMonitorBitmapRemove qemu: blockjob: Use qemuMonitorBitmapRemove for single bitmap removal qemu: migration_params: Add infrastructure for 'dirty-bitmaps' migration feature qemu: migration_cookie: Add XML handling for setting up bitmap migration qemu: migration_cookie: Add helpers for transforming the cookie into migration params qemu: domain: Store list of temporary bitmaps for migration in status XML tests: qemustatusxml2xml: Add status XML from migration with bitmaps tests: qemumigrationcookie: Add testing for block dirty bitmap migration qemu: migration: Clean up temporary bitmaps when cancelling a migration qemu: migration: Migrate block dirty bitmaps corresponding to checkpoints qemu: capabilities: Enable QEMU_CAPS_INCREMENTAL_BACKUP src/qemu/qemu_blockjob.c | 24 +- src/qemu/qemu_capabilities.c | 9 +- src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_domain.c | 87 +- src/qemu/qemu_domain.h | 15 + src/qemu/qemu_migration.c | 369 ++++++++- src/qemu/qemu_migration_cookie.c | 261 ++++++ src/qemu/qemu_migration_cookie.h | 42 + src/qemu/qemu_migration_params.c | 29 + src/qemu/qemu_migration_params.h | 5 + src/qemu/qemu_monitor.c | 13 + src/qemu/qemu_monitor.h | 8 + src/qemu/qemu_monitor_json.c | 35 + src/qemu/qemu_monitor_json.h | 6 + tests/meson.build | 2 +- tests/qemublocktest.c | 2 + tests/qemublocktestdata/bitmap/synthetic.json | 2 +- tests/qemublocktestdata/bitmap/synthetic.out | 1 + .../caps_6.0.0.x86_64.replies | 741 ++++++++++-------- .../caps_6.0.0.x86_64.xml | 23 +- .../nbd-bitmaps-xml2xml-in.xml | 52 ++ .../nbd-bitmaps-xml2xml-migparams.json | 25 + .../nbd-bitmaps-xml2xml-out.xml | 51 ++ tests/qemumigrationcookiexmltest.c | 166 +++- tests/qemumonitorjsontest.c | 2 + .../migration-out-nbd-bitmaps-in.xml | 574 ++++++++++++++ .../migration-out-nbd-bitmaps-out.xml | 1 + tests/qemustatusxml2xmltest.c | 1 + 28 files changed, 2172 insertions(+), 375 deletions(-) create mode 100644 tests/qemumigrationcookiexmldata/nbd-bitmaps-xml2xml-in.xml create mode 100644 tests/qemumigrationcookiexmldata/nbd-bitmaps-xml2xml-migparams.json create mode 100644 tests/qemumigrationcookiexmldata/nbd-bitmaps-xml2xml-out.xml create mode 100644 tests/qemustatusxml2xmldata/migration-out-nbd-bitmaps-in.xml create mode 120000 tests/qemustatusxml2xmldata/migration-out-nbd-bitmaps-out.xml -- 2.29.2

Include the 'transform' member of 'block-bitmap-mapping'. This is based on qemu commit v5.2.0-2208-gc79f01c945 Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- .../caps_6.0.0.x86_64.replies | 741 ++++++++++-------- .../caps_6.0.0.x86_64.xml | 22 +- 2 files changed, 441 insertions(+), 322 deletions(-) diff --git a/tests/qemucapabilitiesdata/caps_6.0.0.x86_64.replies b/tests/qemucapabilitiesdata/caps_6.0.0.x86_64.replies index a1e3850b59..04ebd04583 100644 --- a/tests/qemucapabilitiesdata/caps_6.0.0.x86_64.replies +++ b/tests/qemucapabilitiesdata/caps_6.0.0.x86_64.replies @@ -21,7 +21,7 @@ "minor": 2, "major": 5 }, - "package": "v5.2.0-1810-g2436651b26" + "package": "v5.2.0-2208-gc79f01c945" }, "id": "libvirt-2" } @@ -811,6 +811,10 @@ "name": "usb-hub", "parent": "usb-device" }, + { + "name": "chardev-serial", + "parent": "chardev-fd" + }, { "name": "virtio-blk-device", "parent": "virtio-device" @@ -852,8 +856,8 @@ "parent": "accel" }, { - "name": "chardev-serial", - "parent": "chardev-fd" + "name": "Cooperlake-x86_64-cpu", + "parent": "x86_64-cpu" }, { "name": "vhost-user-vsock-pci", @@ -907,10 +911,6 @@ "name": "pxb-host", "parent": "pci-host-bridge" }, - { - "name": "Cooperlake-x86_64-cpu", - "parent": "x86_64-cpu" - }, { "name": "scsi-disk", "parent": "scsi-disk-base" @@ -1143,6 +1143,10 @@ "name": "i82557c", "parent": "pci-device" }, + { + "name": "i82557b", + "parent": "pci-device" + }, { "name": "virtio-scsi-device", "parent": "virtio-scsi-common" @@ -1151,10 +1155,6 @@ "name": "pxb-pcie", "parent": "pci-device" }, - { - "name": "i82557b", - "parent": "pci-device" - }, { "name": "Haswell-IBRS-x86_64-cpu", "parent": "x86_64-cpu" @@ -1172,12 +1172,8 @@ "parent": "sys-bus-device" }, { - "name": "chardev-memory", - "parent": "chardev-ringbuf" - }, - { - "name": "pc-q35-2.8-machine", - "parent": "generic-pc-machine" + "name": "i82557a", + "parent": "pci-device" }, { "name": "vhost-user-scsi-pci-non-transitional", @@ -1188,12 +1184,16 @@ "parent": "isa-device" }, { - "name": "generic-sdhci", - "parent": "sys-bus-device" + "name": "chardev-udp", + "parent": "chardev" }, { - "name": "i82557a", - "parent": "pci-device" + "name": "pc-q35-2.8-machine", + "parent": "generic-pc-machine" + }, + { + "name": "generic-sdhci", + "parent": "sys-bus-device" }, { "name": "virtio-scsi-pci-non-transitional", @@ -1212,8 +1212,8 @@ "parent": "x86_64-cpu" }, { - "name": "chardev-udp", - "parent": "chardev" + "name": "chardev-memory", + "parent": "chardev-ringbuf" }, { "name": "EPYC-Rome-v1-x86_64-cpu", @@ -1311,14 +1311,14 @@ "name": "isabus-bridge", "parent": "sys-bus-device" }, - { - "name": "ne2k_pci", - "parent": "pci-device" - }, { "name": "IvyBridge-v2-x86_64-cpu", "parent": "x86_64-cpu" }, + { + "name": "ne2k_pci", + "parent": "pci-device" + }, { "name": "usb-bus", "parent": "bus" @@ -1328,29 +1328,25 @@ "parent": "ide-device" }, { - "name": "tcg-accel", - "parent": "accel" + "name": "qemu:memory-region", + "parent": "object" }, { - "name": "piix4-ide", - "parent": "pci-ide" + "name": "tcg-accel", + "parent": "accel" }, { "name": "virtio-balloon-pci", "parent": "virtio-balloon-pci-base" }, - { - "name": "Cascadelake-Server-v2-x86_64-cpu", - "parent": "x86_64-cpu" - }, - { - "name": "qemu:memory-region", - "parent": "object" - }, { "name": "virtio-gpu-device", "parent": "virtio-gpu-base" }, + { + "name": "Cascadelake-Server-v2-x86_64-cpu", + "parent": "x86_64-cpu" + }, { "name": "e1000", "parent": "e1000-base" @@ -1367,6 +1363,10 @@ "name": "ES1370", "parent": "pci-device" }, + { + "name": "pc-i440fx-2.6-machine", + "parent": "generic-pc-machine" + }, { "name": "i82551", "parent": "pci-device" @@ -1384,8 +1384,8 @@ "parent": "x86_64-cpu" }, { - "name": "pc-i440fx-2.6-machine", - "parent": "generic-pc-machine" + "name": "piix4-ide", + "parent": "pci-ide" }, { "name": "SandyBridge-x86_64-cpu", @@ -1403,14 +1403,14 @@ "name": "pc-q35-6.0-machine", "parent": "generic-pc-machine" }, - { - "name": "vhost-user-backend", - "parent": "object" - }, { "name": "mioe3680_pci", "parent": "pci-device" }, + { + "name": "vhost-user-backend", + "parent": "object" + }, { "name": "vmxnet3", "parent": "pci-device" @@ -1479,14 +1479,14 @@ "name": "ati-vga", "parent": "pci-device" }, - { - "name": "pentium3-x86_64-cpu", - "parent": "x86_64-cpu" - }, { "name": "kvm-i8259", "parent": "pic-common" }, + { + "name": "pentium3-x86_64-cpu", + "parent": "x86_64-cpu" + }, { "name": "isa-serial", "parent": "isa-device" @@ -1495,6 +1495,10 @@ "name": "imx.usbphy", "parent": "sys-bus-device" }, + { + "name": "x-remote-object", + "parent": "object" + }, { "name": "core2duo-v1-x86_64-cpu", "parent": "x86_64-cpu" @@ -1527,6 +1531,10 @@ "name": "i82559er", "parent": "pci-device" }, + { + "name": "remote-pcihost", + "parent": "pcie-host-bridge" + }, { "name": "virtio-serial-pci", "parent": "virtio-serial-pci-base" @@ -1679,14 +1687,14 @@ "name": "pci-ohci", "parent": "pci-device" }, - { - "name": "virtio-tablet-device", - "parent": "virtio-input-hid-device" - }, { "name": "Haswell-x86_64-cpu", "parent": "x86_64-cpu" }, + { + "name": "virtio-tablet-device", + "parent": "virtio-input-hid-device" + }, { "name": "ccid-card-passthru", "parent": "ccid-card" @@ -1844,8 +1852,8 @@ "parent": "chardev-spice" }, { - "name": "tls-creds-x509", - "parent": "tls-creds" + "name": "qio-channel-command", + "parent": "qio-channel" }, { "name": "Opteron_G3-v1-x86_64-cpu", @@ -1860,8 +1868,8 @@ "parent": "virtio-blk-pci-base" }, { - "name": "qio-channel-command", - "parent": "qio-channel" + "name": "tls-creds-x509", + "parent": "tls-creds" }, { "name": "pc-i440fx-2.4-machine", @@ -1932,21 +1940,21 @@ "parent": "virtio-iommu-device-base" }, { - "name": "chardev-braille", - "parent": "chardev" + "name": "vmcoreinfo", + "parent": "device" }, { "name": "Icelake-Client-v1-x86_64-cpu", "parent": "x86_64-cpu" }, - { - "name": "vmcoreinfo", - "parent": "device" - }, { "name": "tpci200", "parent": "pci-device" }, + { + "name": "chardev-braille", + "parent": "chardev" + }, { "name": "rocker", "parent": "pci-device" @@ -1963,10 +1971,6 @@ "name": "qio-channel-socket", "parent": "qio-channel" }, - { - "name": "chardev-socket", - "parent": "chardev" - }, { "name": "coreduo-v1-x86_64-cpu", "parent": "x86_64-cpu" @@ -1984,17 +1988,13 @@ "parent": "object" }, { - "name": "hyperv-testdev", - "parent": "isa-device" + "name": "chardev-socket", + "parent": "chardev" }, { "name": "clock", "parent": "object" }, - { - "name": "virtio-net-device", - "parent": "virtio-device" - }, { "name": "Haswell-v2-x86_64-cpu", "parent": "x86_64-cpu" @@ -2004,17 +2004,21 @@ "parent": "max-x86_64-cpu" }, { - "name": "usb-ehci", - "parent": "pci-ehci-usb" + "name": "secret", + "parent": "secret_common" }, { - "name": "pc-i440fx-2.11-machine", - "parent": "generic-pc-machine" + "name": "usb-ehci", + "parent": "pci-ehci-usb" }, { "name": "pxb", "parent": "pci-device" }, + { + "name": "pc-i440fx-2.11-machine", + "parent": "generic-pc-machine" + }, { "name": "AC97", "parent": "pci-device" @@ -2023,6 +2027,10 @@ "name": "vmgenid", "parent": "device" }, + { + "name": "Westmere-v2-x86_64-cpu", + "parent": "x86_64-cpu" + }, { "name": "virtio-pmem", "parent": "virtio-device" @@ -2031,6 +2039,10 @@ "name": "nvme-bus", "parent": "bus" }, + { + "name": "virtconsole", + "parent": "virtserialport" + }, { "name": "virtio-net-pci-non-transitional", "parent": "virtio-net-pci-base" @@ -2048,8 +2060,8 @@ "parent": "scsi-disk-base" }, { - "name": "secret", - "parent": "secret_common" + "name": "Cascadelake-Server-noTSX-x86_64-cpu", + "parent": "x86_64-cpu" }, { "name": "usb-kbd", @@ -2064,21 +2076,25 @@ "parent": "device" }, { - "name": "usb-mtp", - "parent": "usb-device" + "name": "virtio-net-device", + "parent": "virtio-device" }, { "name": "filter-replay", "parent": "netfilter" }, { - "name": "Cascadelake-Server-noTSX-x86_64-cpu", - "parent": "x86_64-cpu" + "name": "usb-mtp", + "parent": "usb-device" }, { "name": "input-linux", "parent": "object" }, + { + "name": "ccid-card-emulated", + "parent": "ccid-card" + }, { "name": "pc-q35-2.9-machine", "parent": "generic-pc-machine" @@ -2088,36 +2104,36 @@ "parent": "x86_64-cpu" }, { - "name": "pc-q35-5.1-machine", - "parent": "generic-pc-machine" + "name": "hyperv-testdev", + "parent": "isa-device" }, { "name": "s3c-sdhci", "parent": "generic-sdhci" }, { - "name": "tpm-tis", - "parent": "isa-device" + "name": "filter-mirror", + "parent": "netfilter" }, { "name": "fw_cfg_mem", "parent": "fw_cfg" }, { - "name": "Westmere-v2-x86_64-cpu", - "parent": "x86_64-cpu" + "name": "pc-i440fx-1.7-machine", + "parent": "generic-pc-machine" }, { - "name": "ccid-card-emulated", - "parent": "ccid-card" + "name": "tpm-tis", + "parent": "isa-device" }, { - "name": "virtconsole", - "parent": "virtserialport" + "name": "pc-q35-5.1-machine", + "parent": "generic-pc-machine" }, { - "name": "pc-i440fx-1.7-machine", - "parent": "generic-pc-machine" + "name": "x-pci-proxy-dev", + "parent": "pci-device" }, { "name": "virtio-mouse-device", @@ -2131,14 +2147,14 @@ "name": "ioh3420", "parent": "pcie-root-port-base" }, - { - "name": "filter-mirror", - "parent": "netfilter" - }, { "name": "Skylake-Client-v1-x86_64-cpu", "parent": "x86_64-cpu" }, + { + "name": "x-remote-machine", + "parent": "machine" + }, { "name": "throttle-group", "parent": "object" @@ -2548,36 +2564,36 @@ "parent": "pci-device" }, { - "name": "Cooperlake-v1-x86_64-cpu", - "parent": "x86_64-cpu" + "name": "virtio-balloon-device", + "parent": "virtio-device" }, { - "name": "Cascadelake-Server-v3-x86_64-cpu", + "name": "Cooperlake-v1-x86_64-cpu", "parent": "x86_64-cpu" }, { - "name": "Nehalem-IBRS-x86_64-cpu", + "name": "Cascadelake-Server-v3-x86_64-cpu", "parent": "x86_64-cpu" }, - { - "name": "virtio-balloon-device", - "parent": "virtio-device" - }, { "name": "cfi.pflash01", "parent": "sys-bus-device" }, { - "name": "Skylake-Server-x86_64-cpu", - "parent": "x86_64-cpu" + "name": "isa-parallel", + "parent": "isa-device" }, { "name": "pc-q35-2.7-machine", "parent": "generic-pc-machine" }, { - "name": "isa-parallel", - "parent": "isa-device" + "name": "Skylake-Server-x86_64-cpu", + "parent": "x86_64-cpu" + }, + { + "name": "Nehalem-IBRS-x86_64-cpu", + "parent": "x86_64-cpu" }, { "name": "pentium2-v1-x86_64-cpu", @@ -2675,14 +2691,14 @@ "name": "virtio-pmem-pci", "parent": "virtio-pmem-pci-base" }, - { - "name": "virtio-tablet-pci", - "parent": "virtio-tablet-pci-base-type" - }, { "name": "accel", "parent": "object" }, + { + "name": "virtio-tablet-pci", + "parent": "virtio-tablet-pci-base-type" + }, { "name": "dc390", "parent": "am53c974" @@ -2796,8 +2812,8 @@ "parent": "x86_64-cpu" }, { - "name": "kvm64-v1-x86_64-cpu", - "parent": "x86_64-cpu" + "name": "pcie-root-port", + "parent": "pcie-root-port-base" }, { "name": "IDE", @@ -2808,8 +2824,8 @@ "parent": "x86_64-cpu" }, { - "name": "pcie-root-port", - "parent": "pcie-root-port-base" + "name": "kvm64-v1-x86_64-cpu", + "parent": "x86_64-cpu" }, { "name": "mptsas1068", @@ -7314,6 +7330,10 @@ "name": "xsaves", "type": "bool" }, + { + "name": "vgif", + "type": "bool" + }, { "name": "mce", "type": "bool" @@ -7443,6 +7463,10 @@ "name": "tcg-cpuid", "type": "bool" }, + { + "name": "vmx-entry-load-pkrs", + "type": "bool" + }, { "name": "x-hv-max-vps", "type": "int32" @@ -7700,6 +7724,10 @@ "name": "kvm-pv-tlb-flush", "type": "bool" }, + { + "name": "vmx-rdtsc-exit", + "type": "bool" + }, { "name": "vmx-cr8-load-exit", "type": "bool" @@ -7713,7 +7741,11 @@ "type": "bool" }, { - "name": "vmx-rdtsc-exit", + "name": "svme-addr-chk", + "type": "bool" + }, + { + "name": "vmx-exit-load-pkrs", "type": "bool" }, { @@ -7855,6 +7887,14 @@ "description": "on/off", "type": "bool" }, + { + "name": "avic", + "type": "bool" + }, + { + "name": "ds", + "type": "bool" + }, { "name": "legacy-cache", "type": "bool" @@ -7873,11 +7913,11 @@ "type": "bool" }, { - "name": "ds", + "name": "osvw", "type": "bool" }, { - "name": "osvw", + "name": "pks", "type": "bool" }, { @@ -7904,6 +7944,10 @@ "name": "vmx-cr8-store-exit", "type": "bool" }, + { + "name": "vmx-ept-1gb", + "type": "bool" + }, { "name": "nrip-save", "type": "bool" @@ -7917,7 +7961,7 @@ "type": "bool" }, { - "name": "lmce", + "name": "vmx-ept-2mb", "type": "bool" }, { @@ -7937,7 +7981,7 @@ "type": "bool" }, { - "name": "sse2", + "name": "lmce", "type": "bool" }, { @@ -7965,15 +8009,15 @@ "type": "bool" }, { - "name": "vmx-ept-1gb", + "name": "avx", "type": "bool" }, { - "name": "avx", + "name": "topoext", "type": "bool" }, { - "name": "topoext", + "name": "sse2", "type": "bool" }, { @@ -8171,7 +8215,7 @@ "type": "bool" }, { - "name": "vmx-ept-2mb", + "name": "vmx-page-walk-4", "type": "bool" }, { @@ -8239,7 +8283,7 @@ "type": "bool" }, { - "name": "vmx-page-walk-4", + "name": "v-vmsave-vmload", "type": "bool" }, { @@ -8646,21 +8690,28 @@ "deprecated": false, "default-ram-id": "pc.ram" }, + { + "hotpluggable-cpus": false, + "name": "x-remote", + "numa-mem-supported": false, + "cpu-max": 1, + "deprecated": false + }, { "hotpluggable-cpus": true, - "name": "pc-i440fx-1.7", - "numa-mem-supported": true, + "name": "pc-q35-5.1", + "numa-mem-supported": false, "default-cpu-type": "qemu64-x86_64-cpu", - "cpu-max": 255, + "cpu-max": 288, "deprecated": false, "default-ram-id": "pc.ram" }, { "hotpluggable-cpus": true, - "name": "pc-q35-5.1", - "numa-mem-supported": false, + "name": "pc-i440fx-1.7", + "numa-mem-supported": true, "default-cpu-type": "qemu64-x86_64-cpu", - "cpu-max": 288, + "cpu-max": 255, "deprecated": false, "default-ram-id": "pc.ram" }, @@ -10857,134 +10908,10 @@ ], "option": "iscsi" }, - { - "parameters": [ - { - "name": "rendernode", - "type": "string" - }, - { - "name": "gl", - "type": "boolean" - }, - { - "name": "head", - "type": "number" - }, - { - "name": "display", - "type": "string" - }, - { - "name": "seamless-migration", - "type": "boolean" - }, - { - "name": "playback-compression", - "type": "boolean" - }, - { - "name": "agent-mouse", - "type": "boolean" - }, - { - "name": "streaming-video", - "type": "string" - }, - { - "name": "zlib-glz-wan-compression", - "type": "string" - }, - { - "name": "jpeg-wan-compression", - "type": "string" - }, - { - "name": "image-compression", - "type": "string" - }, - { - "name": "plaintext-channel", - "type": "string" - }, - { - "name": "tls-channel", - "type": "string" - }, - { - "name": "tls-ciphers", - "type": "string" - }, - { - "name": "x509-dh-key-file", - "type": "string" - }, - { - "name": "x509-cacert-file", - "type": "string" - }, - { - "name": "x509-cert-file", - "type": "string" - }, - { - "name": "x509-key-password", - "type": "string" - }, - { - "name": "x509-key-file", - "type": "string" - }, - { - "name": "x509-dir", - "type": "string" - }, - { - "name": "sasl", - "type": "boolean" - }, - { - "name": "disable-agent-file-xfer", - "type": "boolean" - }, - { - "name": "disable-copy-paste", - "type": "boolean" - }, - { - "name": "disable-ticketing", - "type": "boolean" - }, - { - "name": "password", - "type": "string" - }, - { - "name": "unix", - "type": "boolean" - }, - { - "name": "ipv6", - "type": "boolean" - }, - { - "name": "ipv4", - "type": "boolean" - }, - { - "name": "addr", - "type": "string" - }, - { - "name": "tls-port", - "type": "number" - }, - { - "name": "port", - "type": "number" - } + { + "parameters": [ ], - "option": "spice" + "option": "acpi" }, { "parameters": [ @@ -11205,6 +11132,135 @@ ], "option": "smbios" }, + { + "parameters": [ + { + "name": "rendernode", + "type": "string" + }, + { + "name": "gl", + "type": "boolean" + }, + { + "name": "head", + "type": "number" + }, + { + "name": "display", + "type": "string" + }, + { + "name": "seamless-migration", + "type": "boolean" + }, + { + "name": "playback-compression", + "type": "boolean" + }, + { + "name": "agent-mouse", + "type": "boolean" + }, + { + "name": "streaming-video", + "type": "string" + }, + { + "name": "zlib-glz-wan-compression", + "type": "string" + }, + { + "name": "jpeg-wan-compression", + "type": "string" + }, + { + "name": "image-compression", + "type": "string" + }, + { + "name": "plaintext-channel", + "type": "string" + }, + { + "name": "tls-channel", + "type": "string" + }, + { + "name": "tls-ciphers", + "type": "string" + }, + { + "name": "x509-dh-key-file", + "type": "string" + }, + { + "name": "x509-cacert-file", + "type": "string" + }, + { + "name": "x509-cert-file", + "type": "string" + }, + { + "name": "x509-key-password", + "type": "string" + }, + { + "name": "x509-key-file", + "type": "string" + }, + { + "name": "x509-dir", + "type": "string" + }, + { + "name": "sasl", + "type": "boolean" + }, + { + "name": "disable-agent-file-xfer", + "type": "boolean" + }, + { + "name": "disable-copy-paste", + "type": "boolean" + }, + { + "name": "disable-ticketing", + "type": "boolean" + }, + { + "name": "password", + "type": "string" + }, + { + "name": "unix", + "type": "boolean" + }, + { + "name": "ipv6", + "type": "boolean" + }, + { + "name": "ipv4", + "type": "boolean" + }, + { + "name": "addr", + "type": "string" + }, + { + "name": "tls-port", + "type": "number" + }, + { + "name": "port", + "type": "number" + } + ], + "option": "spice" + }, { "parameters": [ { @@ -11298,11 +11354,6 @@ ], "option": "vnc" }, - { - "parameters": [ - ], - "option": "acpi" - }, { "parameters": [ { @@ -19431,6 +19482,7 @@ }, { "name": "gpa", + "default": null, "type": "int" } ], @@ -25357,6 +25409,11 @@ { "name": "alias", "type": "str" + }, + { + "name": "transform", + "default": null, + "type": "568" } ], "meta-type": "object" @@ -25384,7 +25441,7 @@ "members": [ { "name": "data", - "type": "568" + "type": "569" } ], "meta-type": "object" @@ -25760,7 +25817,7 @@ "members": [ { "name": "bus", - "type": "569" + "type": "570" }, { "name": "devices", @@ -25832,7 +25889,7 @@ "members": [ { "name": "data", - "type": "570" + "type": "571" } ], "meta-type": "object" @@ -25842,7 +25899,7 @@ "members": [ { "name": "data", - "type": "571" + "type": "572" } ], "meta-type": "object" @@ -25852,7 +25909,7 @@ "members": [ { "name": "data", - "type": "572" + "type": "573" } ], "meta-type": "object" @@ -26047,7 +26104,7 @@ "members": [ { "name": "type", - "type": "573" + "type": "574" }, { "name": "hash", @@ -26126,13 +26183,13 @@ }, { "case": "luks", - "type": "575" + "type": "576" } ], "members": [ { "name": "format", - "type": "574" + "type": "575" } ], "meta-type": "object" @@ -26158,17 +26215,17 @@ "variants": [ { "case": "full", - "type": "577" + "type": "578" }, { "case": "erasure-coded", - "type": "578" + "type": "579" } ], "members": [ { "name": "type", - "type": "576" + "type": "577" } ], "meta-type": "object" @@ -26224,7 +26281,7 @@ "variants": [ { "case": "luks", - "type": "579" + "type": "580" }, { "case": "qcow", @@ -26234,7 +26291,7 @@ "members": [ { "name": "format", - "type": "574" + "type": "575" } ], "meta-type": "object" @@ -26732,7 +26789,7 @@ "members": [ { "name": "button", - "type": "580" + "type": "581" }, { "name": "down", @@ -26746,7 +26803,7 @@ "members": [ { "name": "axis", - "type": "581" + "type": "582" }, { "name": "value", @@ -26758,11 +26815,22 @@ { "name": "568", "members": [ + { + "name": "persistent", + "default": null, + "type": "bool" + } ], "meta-type": "object" }, { "name": "569", + "members": [ + ], + "meta-type": "object" + }, + { + "name": "570", "members": [ { "name": "number", @@ -26778,21 +26846,21 @@ }, { "name": "io_range", - "type": "582" + "type": "583" }, { "name": "memory_range", - "type": "582" + "type": "583" }, { "name": "prefetchable_range", - "type": "582" + "type": "583" } ], "meta-type": "object" }, { - "name": "570", + "name": "571", "members": [ { "name": "compat", @@ -26830,12 +26898,12 @@ { "name": "encrypt", "default": null, - "type": "583" + "type": "584" }, { "name": "bitmaps", "default": null, - "type": "[584]" + "type": "[585]" }, { "name": "compression-type", @@ -26845,7 +26913,7 @@ "meta-type": "object" }, { - "name": "571", + "name": "572", "members": [ { "name": "create-type", @@ -26867,7 +26935,7 @@ "meta-type": "object" }, { - "name": "572", + "name": "573", "members": [ { "name": "cipher-alg", @@ -26904,13 +26972,13 @@ }, { "name": "slots", - "type": "[585]" + "type": "[586]" } ], "meta-type": "object" }, { - "name": "573", + "name": "574", "meta-type": "enum", "values": [ "md5", @@ -26918,7 +26986,7 @@ ] }, { - "name": "574", + "name": "575", "meta-type": "enum", "values": [ "qcow", @@ -26926,7 +26994,7 @@ ] }, { - "name": "575", + "name": "576", "members": [ { "name": "key-secret", @@ -26967,7 +27035,7 @@ "meta-type": "object" }, { - "name": "576", + "name": "577", "meta-type": "enum", "values": [ "full", @@ -26975,7 +27043,7 @@ ] }, { - "name": "577", + "name": "578", "members": [ { "name": "copies", @@ -26985,7 +27053,7 @@ "meta-type": "object" }, { - "name": "578", + "name": "579", "members": [ { "name": "data-strips", @@ -26999,7 +27067,7 @@ "meta-type": "object" }, { - "name": "579", + "name": "580", "members": [ { "name": "state", @@ -27034,7 +27102,7 @@ "meta-type": "object" }, { - "name": "580", + "name": "581", "meta-type": "enum", "values": [ "left", @@ -27047,7 +27115,7 @@ ] }, { - "name": "581", + "name": "582", "meta-type": "enum", "values": [ "x", @@ -27055,7 +27123,7 @@ ] }, { - "name": "582", + "name": "583", "members": [ { "name": "base", @@ -27069,12 +27137,12 @@ "meta-type": "object" }, { - "name": "583", + "name": "584", "tag": "format", "variants": [ { "case": "luks", - "type": "572" + "type": "573" }, { "case": "aes", @@ -27090,12 +27158,12 @@ "meta-type": "object" }, { - "name": "[584]", - "element-type": "584", + "name": "[585]", + "element-type": "585", "meta-type": "array" }, { - "name": "584", + "name": "585", "members": [ { "name": "name", @@ -27107,7 +27175,7 @@ }, { "name": "flags", - "type": "[586]" + "type": "[587]" } ], "meta-type": "object" @@ -27118,12 +27186,12 @@ "meta-type": "array" }, { - "name": "[585]", - "element-type": "585", + "name": "[586]", + "element-type": "586", "meta-type": "array" }, { - "name": "585", + "name": "586", "members": [ { "name": "active", @@ -27147,12 +27215,12 @@ "meta-type": "object" }, { - "name": "[586]", - "element-type": "586", + "name": "[587]", + "element-type": "587", "meta-type": "array" }, { - "name": "586", + "name": "587", "meta-type": "enum", "values": [ "in-use", @@ -27193,6 +27261,7 @@ "name": "base", "props": { "vmx-entry-load-rtit-ctl": false, + "svme-addr-chk": false, "cmov": true, "ia64": false, "ssb-no": false, @@ -27277,6 +27346,7 @@ "vmx-unrestricted-guest": false, "vmx-cr3-store-noexit": false, "pku": false, + "pks": false, "smx": false, "cmp-legacy": true, "avx512-4fmaps": false, @@ -27392,6 +27462,7 @@ "kvmclock": true, "vmx-zero-len-inject": false, "pschange-mc-no": true, + "v-vmsave-vmload": false, "vmx-rdrand-exit": false, "lwp": false, "amd-ssbd": true, @@ -27429,6 +27500,7 @@ "vmx-movdr-exit": false, "pse": true, "avx2": true, + "avic": false, "sep": true, "virt-ssbd": true, "vmx-cr3-load-noexit": false, @@ -27445,6 +27517,7 @@ "amd-stibp": true, "vmx-preemption-timer": false, "clflushopt": true, + "vmx-entry-load-pkrs": false, "vmx-vnmi-pending": false, "monitor": false, "vmx-vintr-pending": false, @@ -27454,6 +27527,7 @@ "pcid": false, "taa-no": false, "arch-capabilities": true, + "vgif": false, "vmx-secondary-ctls": false, "vmx-xsaves": false, "clzero": true, @@ -27488,6 +27562,7 @@ "vmx-entry-load-efer": false, "model-id": "AMD Ryzen 9 3900X 12-Core Processor ", "sha-ni": true, + "vmx-exit-load-pkrs": false, "abm": true, "vmx-ept-advanced-exitinfo": false, "avx512pf": false, @@ -27519,6 +27594,7 @@ "name": "base", "props": { "vmx-entry-load-rtit-ctl": false, + "svme-addr-chk": false, "cmov": true, "ia64": false, "ssb-no": false, @@ -27603,6 +27679,7 @@ "vmx-unrestricted-guest": false, "vmx-cr3-store-noexit": false, "pku": false, + "pks": false, "smx": false, "cmp-legacy": true, "avx512-4fmaps": false, @@ -27718,6 +27795,7 @@ "kvmclock": true, "vmx-zero-len-inject": false, "pschange-mc-no": true, + "v-vmsave-vmload": false, "vmx-rdrand-exit": false, "lwp": false, "amd-ssbd": true, @@ -27755,6 +27833,7 @@ "vmx-movdr-exit": false, "pse": true, "avx2": true, + "avic": false, "sep": true, "virt-ssbd": true, "vmx-cr3-load-noexit": false, @@ -27771,6 +27850,7 @@ "amd-stibp": true, "vmx-preemption-timer": false, "clflushopt": true, + "vmx-entry-load-pkrs": false, "vmx-vnmi-pending": false, "monitor": false, "vmx-vintr-pending": false, @@ -27780,6 +27860,7 @@ "pcid": false, "taa-no": false, "arch-capabilities": true, + "vgif": false, "vmx-secondary-ctls": false, "vmx-xsaves": false, "clzero": true, @@ -27814,6 +27895,7 @@ "vmx-entry-load-efer": false, "model-id": "AMD Ryzen 9 3900X 12-Core Processor ", "sha-ni": true, + "vmx-exit-load-pkrs": false, "abm": true, "vmx-ept-advanced-exitinfo": false, "avx512pf": false, @@ -30208,6 +30290,7 @@ "name": "base", "props": { "vmx-entry-load-rtit-ctl": false, + "svme-addr-chk": false, "cmov": true, "ia64": false, "ssb-no": false, @@ -30292,6 +30375,7 @@ "vmx-unrestricted-guest": false, "vmx-cr3-store-noexit": false, "pku": true, + "pks": true, "smx": false, "cmp-legacy": false, "avx512-4fmaps": false, @@ -30407,6 +30491,7 @@ "kvmclock": false, "vmx-zero-len-inject": false, "pschange-mc-no": false, + "v-vmsave-vmload": false, "vmx-rdrand-exit": false, "lwp": false, "amd-ssbd": false, @@ -30444,6 +30529,7 @@ "vmx-movdr-exit": false, "pse": true, "avx2": false, + "avic": false, "sep": true, "virt-ssbd": false, "vmx-cr3-load-noexit": false, @@ -30460,6 +30546,7 @@ "amd-stibp": false, "vmx-preemption-timer": false, "clflushopt": true, + "vmx-entry-load-pkrs": false, "vmx-vnmi-pending": false, "monitor": true, "vmx-vintr-pending": false, @@ -30469,6 +30556,7 @@ "pcid": false, "taa-no": false, "arch-capabilities": false, + "vgif": false, "vmx-secondary-ctls": false, "vmx-xsaves": false, "clzero": false, @@ -30503,6 +30591,7 @@ "vmx-entry-load-efer": false, "model-id": "QEMU TCG CPU version 2.5+", "sha-ni": false, + "vmx-exit-load-pkrs": false, "abm": true, "vmx-ept-advanced-exitinfo": false, "avx512pf": false, @@ -30534,6 +30623,7 @@ "name": "base", "props": { "vmx-entry-load-rtit-ctl": false, + "svme-addr-chk": false, "cmov": true, "ia64": false, "ssb-no": false, @@ -30618,6 +30708,7 @@ "vmx-unrestricted-guest": false, "vmx-cr3-store-noexit": false, "pku": true, + "pks": true, "smx": false, "cmp-legacy": false, "avx512-4fmaps": false, @@ -30733,6 +30824,7 @@ "kvmclock": false, "vmx-zero-len-inject": false, "pschange-mc-no": false, + "v-vmsave-vmload": false, "vmx-rdrand-exit": false, "lwp": false, "amd-ssbd": false, @@ -30770,6 +30862,7 @@ "vmx-movdr-exit": false, "pse": true, "avx2": false, + "avic": false, "sep": true, "virt-ssbd": false, "vmx-cr3-load-noexit": false, @@ -30786,6 +30879,7 @@ "amd-stibp": false, "vmx-preemption-timer": false, "clflushopt": true, + "vmx-entry-load-pkrs": false, "vmx-vnmi-pending": false, "monitor": true, "vmx-vintr-pending": false, @@ -30795,6 +30889,7 @@ "pcid": false, "taa-no": false, "arch-capabilities": false, + "vgif": false, "vmx-secondary-ctls": false, "vmx-xsaves": false, "clzero": false, @@ -30829,6 +30924,7 @@ "vmx-entry-load-efer": false, "model-id": "QEMU TCG CPU version 2.5+", "sha-ni": false, + "vmx-exit-load-pkrs": false, "abm": true, "vmx-ept-advanced-exitinfo": false, "avx512pf": false, @@ -30972,21 +31068,28 @@ "deprecated": false, "default-ram-id": "pc.ram" }, + { + "hotpluggable-cpus": false, + "name": "x-remote", + "numa-mem-supported": false, + "cpu-max": 1, + "deprecated": false + }, { "hotpluggable-cpus": true, - "name": "pc-i440fx-1.7", - "numa-mem-supported": true, + "name": "pc-q35-5.1", + "numa-mem-supported": false, "default-cpu-type": "qemu64-x86_64-cpu", - "cpu-max": 255, + "cpu-max": 288, "deprecated": false, "default-ram-id": "pc.ram" }, { "hotpluggable-cpus": true, - "name": "pc-q35-5.1", - "numa-mem-supported": false, + "name": "pc-i440fx-1.7", + "numa-mem-supported": true, "default-cpu-type": "qemu64-x86_64-cpu", - "cpu-max": 288, + "cpu-max": 255, "deprecated": false, "default-ram-id": "pc.ram" }, diff --git a/tests/qemucapabilitiesdata/caps_6.0.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_6.0.0.x86_64.xml index 23fb5b7393..e68df54071 100644 --- a/tests/qemucapabilitiesdata/caps_6.0.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_6.0.0.x86_64.xml @@ -259,10 +259,11 @@ <version>5002050</version> <kvmVersion>0</kvmVersion> <microcodeVersion>43100242</microcodeVersion> - <package>v5.2.0-1810-g2436651b26</package> + <package>v5.2.0-2208-gc79f01c945</package> <arch>x86_64</arch> <hostCPU type='kvm' model='base' migratability='yes'> <property name='vmx-entry-load-rtit-ctl' type='boolean' value='false'/> + <property name='svme-addr-chk' type='boolean' value='false'/> <property name='cmov' type='boolean' value='true' migratable='yes'/> <property name='ia64' type='boolean' value='false'/> <property name='ssb-no' type='boolean' value='false'/> @@ -347,6 +348,7 @@ <property name='vmx-unrestricted-guest' type='boolean' value='false'/> <property name='vmx-cr3-store-noexit' type='boolean' value='false'/> <property name='pku' type='boolean' value='false'/> + <property name='pks' type='boolean' value='false'/> <property name='smx' type='boolean' value='false'/> <property name='cmp-legacy' type='boolean' value='true' migratable='yes'/> <property name='avx512-4fmaps' type='boolean' value='false'/> @@ -462,6 +464,7 @@ <property name='kvmclock' type='boolean' value='true' migratable='yes'/> <property name='vmx-zero-len-inject' type='boolean' value='false'/> <property name='pschange-mc-no' type='boolean' value='true' migratable='yes'/> + <property name='v-vmsave-vmload' type='boolean' value='false'/> <property name='vmx-rdrand-exit' type='boolean' value='false'/> <property name='lwp' type='boolean' value='false'/> <property name='amd-ssbd' type='boolean' value='true' migratable='yes'/> @@ -499,6 +502,7 @@ <property name='vmx-movdr-exit' type='boolean' value='false'/> <property name='pse' type='boolean' value='true' migratable='yes'/> <property name='avx2' type='boolean' value='true' migratable='yes'/> + <property name='avic' type='boolean' value='false'/> <property name='sep' type='boolean' value='true' migratable='yes'/> <property name='virt-ssbd' type='boolean' value='true' migratable='yes'/> <property name='vmx-cr3-load-noexit' type='boolean' value='false'/> @@ -515,6 +519,7 @@ <property name='amd-stibp' type='boolean' value='true' migratable='yes'/> <property name='vmx-preemption-timer' type='boolean' value='false'/> <property name='clflushopt' type='boolean' value='true' migratable='yes'/> + <property name='vmx-entry-load-pkrs' type='boolean' value='false'/> <property name='vmx-vnmi-pending' type='boolean' value='false'/> <property name='monitor' type='boolean' value='false'/> <property name='vmx-vintr-pending' type='boolean' value='false'/> @@ -524,6 +529,7 @@ <property name='pcid' type='boolean' value='false'/> <property name='taa-no' type='boolean' value='false'/> <property name='arch-capabilities' type='boolean' value='true' migratable='yes'/> + <property name='vgif' type='boolean' value='false'/> <property name='vmx-secondary-ctls' type='boolean' value='false'/> <property name='vmx-xsaves' type='boolean' value='false'/> <property name='clzero' type='boolean' value='true' migratable='yes'/> @@ -558,6 +564,7 @@ <property name='vmx-entry-load-efer' type='boolean' value='false'/> <property name='model-id' type='string' value='AMD Ryzen 9 3900X 12-Core Processor '/> <property name='sha-ni' type='boolean' value='true' migratable='yes'/> + <property name='vmx-exit-load-pkrs' type='boolean' value='false'/> <property name='abm' type='boolean' value='true' migratable='yes'/> <property name='vmx-ept-advanced-exitinfo' type='boolean' value='false'/> <property name='avx512pf' type='boolean' value='false'/> @@ -1436,8 +1443,9 @@ <machine type='kvm' name='pc-i440fx-2.7' hotplugCpus='yes' maxCpus='255' defaultCPU='qemu64-x86_64-cpu' numaMemSupported='yes' defaultRAMid='pc.ram'/> <machine type='kvm' name='pc-q35-2.4' hotplugCpus='yes' maxCpus='255' defaultCPU='qemu64-x86_64-cpu' numaMemSupported='yes' defaultRAMid='pc.ram'/> <machine type='kvm' name='pc-q35-2.10' hotplugCpus='yes' maxCpus='288' defaultCPU='qemu64-x86_64-cpu' numaMemSupported='yes' defaultRAMid='pc.ram'/> - <machine type='kvm' name='pc-i440fx-1.7' hotplugCpus='yes' maxCpus='255' defaultCPU='qemu64-x86_64-cpu' numaMemSupported='yes' defaultRAMid='pc.ram'/> + <machine type='kvm' name='x-remote' maxCpus='1'/> <machine type='kvm' name='pc-q35-5.1' hotplugCpus='yes' maxCpus='288' defaultCPU='qemu64-x86_64-cpu' defaultRAMid='pc.ram'/> + <machine type='kvm' name='pc-i440fx-1.7' hotplugCpus='yes' maxCpus='255' defaultCPU='qemu64-x86_64-cpu' numaMemSupported='yes' defaultRAMid='pc.ram'/> <machine type='kvm' name='pc-q35-2.9' hotplugCpus='yes' maxCpus='288' defaultCPU='qemu64-x86_64-cpu' numaMemSupported='yes' defaultRAMid='pc.ram'/> <machine type='kvm' name='pc-i440fx-2.11' hotplugCpus='yes' maxCpus='255' defaultCPU='qemu64-x86_64-cpu' numaMemSupported='yes' defaultRAMid='pc.ram'/> <machine type='kvm' name='pc-q35-3.1' hotplugCpus='yes' maxCpus='288' defaultCPU='qemu64-x86_64-cpu' numaMemSupported='yes' defaultRAMid='pc.ram'/> @@ -1471,6 +1479,7 @@ <machine type='kvm' name='pc-q35-2.11' hotplugCpus='yes' maxCpus='288' defaultCPU='qemu64-x86_64-cpu' numaMemSupported='yes' defaultRAMid='pc.ram'/> <hostCPU type='tcg' model='base' migratability='yes'> <property name='vmx-entry-load-rtit-ctl' type='boolean' value='false'/> + <property name='svme-addr-chk' type='boolean' value='false'/> <property name='cmov' type='boolean' value='true' migratable='yes'/> <property name='ia64' type='boolean' value='false'/> <property name='ssb-no' type='boolean' value='false'/> @@ -1555,6 +1564,7 @@ <property name='vmx-unrestricted-guest' type='boolean' value='false'/> <property name='vmx-cr3-store-noexit' type='boolean' value='false'/> <property name='pku' type='boolean' value='true' migratable='yes'/> + <property name='pks' type='boolean' value='true' migratable='yes'/> <property name='smx' type='boolean' value='false'/> <property name='cmp-legacy' type='boolean' value='false'/> <property name='avx512-4fmaps' type='boolean' value='false'/> @@ -1670,6 +1680,7 @@ <property name='kvmclock' type='boolean' value='false'/> <property name='vmx-zero-len-inject' type='boolean' value='false'/> <property name='pschange-mc-no' type='boolean' value='false'/> + <property name='v-vmsave-vmload' type='boolean' value='false'/> <property name='vmx-rdrand-exit' type='boolean' value='false'/> <property name='lwp' type='boolean' value='false'/> <property name='amd-ssbd' type='boolean' value='false'/> @@ -1707,6 +1718,7 @@ <property name='vmx-movdr-exit' type='boolean' value='false'/> <property name='pse' type='boolean' value='true' migratable='yes'/> <property name='avx2' type='boolean' value='false'/> + <property name='avic' type='boolean' value='false'/> <property name='sep' type='boolean' value='true' migratable='yes'/> <property name='virt-ssbd' type='boolean' value='false'/> <property name='vmx-cr3-load-noexit' type='boolean' value='false'/> @@ -1723,6 +1735,7 @@ <property name='amd-stibp' type='boolean' value='false'/> <property name='vmx-preemption-timer' type='boolean' value='false'/> <property name='clflushopt' type='boolean' value='true' migratable='yes'/> + <property name='vmx-entry-load-pkrs' type='boolean' value='false'/> <property name='vmx-vnmi-pending' type='boolean' value='false'/> <property name='monitor' type='boolean' value='true' migratable='yes'/> <property name='vmx-vintr-pending' type='boolean' value='false'/> @@ -1732,6 +1745,7 @@ <property name='pcid' type='boolean' value='false'/> <property name='taa-no' type='boolean' value='false'/> <property name='arch-capabilities' type='boolean' value='false'/> + <property name='vgif' type='boolean' value='false'/> <property name='vmx-secondary-ctls' type='boolean' value='false'/> <property name='vmx-xsaves' type='boolean' value='false'/> <property name='clzero' type='boolean' value='false'/> @@ -1766,6 +1780,7 @@ <property name='vmx-entry-load-efer' type='boolean' value='false'/> <property name='model-id' type='string' value='QEMU TCG CPU version 2.5+'/> <property name='sha-ni' type='boolean' value='false'/> + <property name='vmx-exit-load-pkrs' type='boolean' value='false'/> <property name='abm' type='boolean' value='true' migratable='yes'/> <property name='vmx-ept-advanced-exitinfo' type='boolean' value='false'/> <property name='avx512pf' type='boolean' value='false'/> @@ -3175,8 +3190,9 @@ <machine type='tcg' name='pc-i440fx-2.7' hotplugCpus='yes' maxCpus='255' defaultCPU='qemu64-x86_64-cpu' numaMemSupported='yes' defaultRAMid='pc.ram'/> <machine type='tcg' name='pc-q35-2.4' hotplugCpus='yes' maxCpus='255' defaultCPU='qemu64-x86_64-cpu' numaMemSupported='yes' defaultRAMid='pc.ram'/> <machine type='tcg' name='pc-q35-2.10' hotplugCpus='yes' maxCpus='288' defaultCPU='qemu64-x86_64-cpu' numaMemSupported='yes' defaultRAMid='pc.ram'/> - <machine type='tcg' name='pc-i440fx-1.7' hotplugCpus='yes' maxCpus='255' defaultCPU='qemu64-x86_64-cpu' numaMemSupported='yes' defaultRAMid='pc.ram'/> + <machine type='tcg' name='x-remote' maxCpus='1'/> <machine type='tcg' name='pc-q35-5.1' hotplugCpus='yes' maxCpus='288' defaultCPU='qemu64-x86_64-cpu' defaultRAMid='pc.ram'/> + <machine type='tcg' name='pc-i440fx-1.7' hotplugCpus='yes' maxCpus='255' defaultCPU='qemu64-x86_64-cpu' numaMemSupported='yes' defaultRAMid='pc.ram'/> <machine type='tcg' name='pc-q35-2.9' hotplugCpus='yes' maxCpus='288' defaultCPU='qemu64-x86_64-cpu' numaMemSupported='yes' defaultRAMid='pc.ram'/> <machine type='tcg' name='pc-i440fx-2.11' hotplugCpus='yes' maxCpus='255' defaultCPU='qemu64-x86_64-cpu' numaMemSupported='yes' defaultRAMid='pc.ram'/> <machine type='tcg' name='pc-q35-3.1' hotplugCpus='yes' maxCpus='288' defaultCPU='qemu64-x86_64-cpu' numaMemSupported='yes' defaultRAMid='pc.ram'/> -- 2.29.2

On Fri, Feb 19, 2021 at 12:58:13 +0100, Peter Krempa wrote:
Include the 'transform' member of 'block-bitmap-mapping'. This is based on qemu commit v5.2.0-2208-gc79f01c945
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- .../caps_6.0.0.x86_64.replies | 741 ++++++++++-------- .../caps_6.0.0.x86_64.xml | 22 +- 2 files changed, 441 insertions(+), 322 deletions(-)
Reviewed-by: Jiri Denemark <jdenemar@redhat.com>

The capability represents qemu's ability to setup mappings for migrating block dirty bitmaps and is based on presence of the 'transform' property of the 'block-bitmap-mapping' property of 'migrate-set-parameters' QMP command. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_capabilities.c | 3 +++ src/qemu/qemu_capabilities.h | 1 + tests/qemucapabilitiesdata/caps_6.0.0.x86_64.xml | 1 + 3 files changed, 5 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 3f8593a9e5..600952a53a 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -617,6 +617,7 @@ VIR_ENUM_IMPL(virQEMUCaps, "cpu-max", "memory-backend-file.x-use-canonical-path-for-ramblock-id", "vnc-opts", + "migration-param.block-bitmap-mapping", ); @@ -1550,6 +1551,8 @@ static struct virQEMUCapsStringFlags virQEMUCapsQMPSchemaQueries[] = { { "migrate-set-parameters/arg-type/xbzrle-cache-size", QEMU_CAPS_MIGRATION_PARAM_XBZRLE_CACHE_SIZE }, { "set-numa-node/arg-type/+hmat-lb", QEMU_CAPS_NUMA_HMAT }, { "netdev_add/arg-type/+vhost-vdpa", QEMU_CAPS_NETDEV_VHOST_VDPA }, + { "migrate-set-parameters/arg-type/block-bitmap-mapping/bitmaps/transform", + QEMU_CAPS_MIGRATION_PARAM_BLOCK_BITMAP_MAPPING }, }; typedef struct _virQEMUCapsObjectTypeProps virQEMUCapsObjectTypeProps; diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 38574eef16..a5b6c7f104 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -597,6 +597,7 @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ QEMU_CAPS_CPU_MAX, /* -cpu max */ QEMU_CAPS_X_USE_CANONICAL_PATH_FOR_RAMBLOCK_ID, /* -object memory-backend-file,x-use-canonical-path-for-ramblock-id= */ QEMU_CAPS_VNC_OPTS, /* -vnc uses QemuOpts parser instead of custom code */ + QEMU_CAPS_MIGRATION_PARAM_BLOCK_BITMAP_MAPPING, /* block-bitmap-mapping in migrate-set-parameters */ QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; diff --git a/tests/qemucapabilitiesdata/caps_6.0.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_6.0.0.x86_64.xml index e68df54071..e7e6254293 100644 --- a/tests/qemucapabilitiesdata/caps_6.0.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_6.0.0.x86_64.xml @@ -256,6 +256,7 @@ <flag name='cpu-max'/> <flag name='memory-backend-file.x-use-canonical-path-for-ramblock-id'/> <flag name='vnc-opts'/> + <flag name='migration-param.block-bitmap-mapping'/> <version>5002050</version> <kvmVersion>0</kvmVersion> <microcodeVersion>43100242</microcodeVersion> -- 2.29.2

Such images don't support stuff like dirty bitmaps. Note that the synthetic test for detecting bitmaps is used as an example to prevent adding additional test cases. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_monitor.h | 3 +++ src/qemu/qemu_monitor_json.c | 11 +++++++++++ tests/qemublocktest.c | 2 ++ tests/qemublocktestdata/bitmap/synthetic.json | 2 +- tests/qemublocktestdata/bitmap/synthetic.out | 1 + 5 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 32dc96ee82..0108703a33 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -732,6 +732,9 @@ struct _qemuBlockNamedNodeData { /* the cluster size of the image is valid only when > 0 */ unsigned long long clusterSize; + + /* image version */ + bool qcow2v2; }; GHashTable * diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index f8c78d9093..3a07306365 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2978,6 +2978,7 @@ qemuMonitorJSONBlockGetNamedNodeDataWorker(size_t pos G_GNUC_UNUSED, GHashTable *nodes = opaque; virJSONValuePtr img; virJSONValuePtr bitmaps; + virJSONValuePtr format_specific; const char *nodename; g_autoptr(qemuBlockNamedNodeData) ent = NULL; @@ -3000,6 +3001,16 @@ qemuMonitorJSONBlockGetNamedNodeDataWorker(size_t pos G_GNUC_UNUSED, if ((bitmaps = virJSONValueObjectGetArray(val, "dirty-bitmaps"))) qemuMonitorJSONBlockGetNamedNodeDataBitmaps(bitmaps, ent); + /* query qcow2 format specific props */ + if ((format_specific = virJSONValueObjectGetObject(img, "format-specific")) && + STREQ_NULLABLE(virJSONValueObjectGetString(format_specific, "type"), "qcow2")) { + virJSONValuePtr qcow2props = virJSONValueObjectGetObject(format_specific, "data"); + + if (qcow2props && + STREQ_NULLABLE(virJSONValueObjectGetString(qcow2props, "compat"), "0.10")) + ent->qcow2v2 = true; + } + if (virHashAddEntry(nodes, nodename, ent) < 0) return -1; diff --git a/tests/qemublocktest.c b/tests/qemublocktest.c index ddaf73359d..bbfcfee92d 100644 --- a/tests/qemublocktest.c +++ b/tests/qemublocktest.c @@ -599,6 +599,8 @@ testQemuDetectBitmapsWorker(GHashTable *nodedata, return; virBufferAsprintf(buf, "%s:\n", nodename); + if (data->qcow2v2) + virBufferAddLit(buf, " qcow2 v2\n"); virBufferAdjustIndent(buf, 1); for (i = 0; i < data->nbitmaps; i++) { diff --git a/tests/qemublocktestdata/bitmap/synthetic.json b/tests/qemublocktestdata/bitmap/synthetic.json index 3712c8e5fc..cd468a42a2 100644 --- a/tests/qemublocktestdata/bitmap/synthetic.json +++ b/tests/qemublocktestdata/bitmap/synthetic.json @@ -12,7 +12,7 @@ "format-specific": { "type": "qcow2", "data": { - "compat": "1.1", + "compat": "0.10", "compression-type": "zlib", "lazy-refcounts": false, "bitmaps": [ diff --git a/tests/qemublocktestdata/bitmap/synthetic.out b/tests/qemublocktestdata/bitmap/synthetic.out index cde7228e01..2d9545fc9b 100644 --- a/tests/qemublocktestdata/bitmap/synthetic.out +++ b/tests/qemublocktestdata/bitmap/synthetic.out @@ -1,4 +1,5 @@ libvirt-1-format: + qcow2 v2 current: record:1 busy:0 persist:1 inconsist:1 gran:65536 dirty:0 top-ok: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 top-inactive: record:0 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 -- 2.29.2

Use the new format when pre-creating the image for the user. Users wishing to use the legacy format can always provide their own images or use hared storage. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_migration.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index e8e35c1c7c..94b9b34ca0 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -180,6 +180,7 @@ qemuMigrationDstPrecreateDisk(virConnectPtr *conn, char *volStr = NULL; g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; const char *format = NULL; + const char *compat = NULL; unsigned int flags = 0; VIR_DEBUG("Precreate disk type=%s", virStorageTypeToString(disk->src->type)); @@ -212,8 +213,11 @@ qemuMigrationDstPrecreateDisk(virConnectPtr *conn, if (!(pool = virStoragePoolLookupByTargetPath(*conn, basePath))) goto cleanup; format = virStorageFileFormatTypeToString(disk->src->format); - if (disk->src->format == VIR_STORAGE_FILE_QCOW2) + if (disk->src->format == VIR_STORAGE_FILE_QCOW2) { flags |= VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA; + /* format qcow2v3 image */ + compat = "1.1"; + } break; case VIR_STORAGE_TYPE_VOLUME: @@ -261,6 +265,8 @@ qemuMigrationDstPrecreateDisk(virConnectPtr *conn, virBufferAddLit(&buf, "<target>\n"); virBufferAdjustIndent(&buf, 2); virBufferAsprintf(&buf, "<format type='%s'/>\n", format); + if (compat) + virBufferAsprintf(&buf, "<compat>%s</compat>\n", compat); virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, "</target>\n"); virBufferAdjustIndent(&buf, -2); -- 2.29.2

On Fri, Feb 19, 2021 at 12:58:16 +0100, Peter Krempa wrote:
Use the new format when pre-creating the image for the user. Users wishing to use the legacy format can always provide their own images or use hared storage.
s/hared/shared/
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_migration.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index e8e35c1c7c..94b9b34ca0 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -180,6 +180,7 @@ qemuMigrationDstPrecreateDisk(virConnectPtr *conn, char *volStr = NULL; g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; const char *format = NULL; + const char *compat = NULL; unsigned int flags = 0;
VIR_DEBUG("Precreate disk type=%s", virStorageTypeToString(disk->src->type)); @@ -212,8 +213,11 @@ qemuMigrationDstPrecreateDisk(virConnectPtr *conn, if (!(pool = virStoragePoolLookupByTargetPath(*conn, basePath))) goto cleanup; format = virStorageFileFormatTypeToString(disk->src->format); - if (disk->src->format == VIR_STORAGE_FILE_QCOW2) + if (disk->src->format == VIR_STORAGE_FILE_QCOW2) { flags |= VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA; + /* format qcow2v3 image */ + compat = "1.1"; + } break;
case VIR_STORAGE_TYPE_VOLUME:
And how about creating v3 only when the source offered bitmaps for migration? Although automatic creation of disk images during migration has always been magic and anyone caring about the exact image format on the destination needs to precreate the images manually. So I guess this should good enough. Reviewed-by: Jiri Denemark <jdenemar@redhat.com>

The non-transaction wrapper is useful for code paths which want to delete individual bitmaps or for cleanup after a failed job where we want to attempt to delete every bitmap individually to prevent a failure from cleaning up the rest. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_monitor.c | 13 +++++++++++++ src/qemu/qemu_monitor.h | 5 +++++ src/qemu/qemu_monitor_json.c | 24 ++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 6 ++++++ tests/qemumonitorjsontest.c | 2 ++ 5 files changed, 50 insertions(+) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 151f69acef..ed35da17e1 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -4653,6 +4653,19 @@ qemuMonitorTransactionBitmapRemove(virJSONValuePtr actions, } +int +qemuMonitorBitmapRemove(qemuMonitorPtr mon, + const char *node, + const char *name) +{ + VIR_DEBUG("node='%s', name='%s'", node, name); + + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONBitmapRemove(mon, node, name); +} + + int qemuMonitorTransactionBitmapEnable(virJSONValuePtr actions, const char *node, diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 0108703a33..d25c26343a 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1478,6 +1478,11 @@ int qemuMonitorTransactionBitmapRemove(virJSONValuePtr actions, const char *node, const char *name); + +int +qemuMonitorBitmapRemove(qemuMonitorPtr mon, + const char *node, + const char *name); int qemuMonitorTransactionBitmapEnable(virJSONValuePtr actions, const char *node, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 3a07306365..03224d4af2 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -9252,6 +9252,30 @@ qemuMonitorJSONTransactionBitmapRemove(virJSONValuePtr actions, } +int +qemuMonitorJSONBitmapRemove(qemuMonitorPtr mon, + const char *node, + const char *name) +{ + g_autoptr(virJSONValue) cmd = NULL; + g_autoptr(virJSONValue) reply = NULL; + + if (!(cmd = qemuMonitorJSONMakeCommand("block-dirty-bitmap-remove", + "s:node", node, + "s:name", name, + NULL))) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + return -1; + + if (qemuMonitorJSONCheckError(cmd, reply) < 0) + return -1; + + return 0; +} + + int qemuMonitorJSONTransactionBitmapEnable(virJSONValuePtr actions, const char *node, diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 31652d4207..3dd1eb24c7 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -659,6 +659,12 @@ int qemuMonitorJSONTransactionBitmapRemove(virJSONValuePtr actions, const char *node, const char *name); + +int +qemuMonitorJSONBitmapRemove(qemuMonitorPtr mon, + const char *node, + const char *name); + int qemuMonitorJSONTransactionBitmapEnable(virJSONValuePtr actions, const char *node, diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index 5a5976dbe4..82c74e2ef9 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -1215,6 +1215,7 @@ GEN_TEST_FUNC(qemuMonitorJSONBlockdevTrayOpen, "foodev", true) GEN_TEST_FUNC(qemuMonitorJSONBlockdevTrayClose, "foodev") GEN_TEST_FUNC(qemuMonitorJSONBlockdevMediumRemove, "foodev") GEN_TEST_FUNC(qemuMonitorJSONBlockdevMediumInsert, "foodev", "newnode") +GEN_TEST_FUNC(qemuMonitorJSONBitmapRemove, "foodev", "newnode") GEN_TEST_FUNC(qemuMonitorJSONJobDismiss, "jobname") GEN_TEST_FUNC(qemuMonitorJSONJobCancel, "jobname", false) GEN_TEST_FUNC(qemuMonitorJSONJobComplete, "jobname") @@ -3132,6 +3133,7 @@ mymain(void) DO_TEST_GEN(qemuMonitorJSONBlockdevTrayClose); DO_TEST_GEN(qemuMonitorJSONBlockdevMediumRemove); DO_TEST_GEN(qemuMonitorJSONBlockdevMediumInsert); + DO_TEST_GEN(qemuMonitorJSONBitmapRemove); DO_TEST_GEN(qemuMonitorJSONJobDismiss); DO_TEST_GEN(qemuMonitorJSONJobCancel); DO_TEST_GEN(qemuMonitorJSONJobComplete); -- 2.29.2

There's no need in the cleanup steps to invoke a transaction to delete a single bitmap. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_blockjob.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 582fe45c66..aa065b1319 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -1420,7 +1420,6 @@ qemuBlockJobProcessEventFailedActiveCommit(virQEMUDriverPtr driver, qemuDomainAsyncJob asyncJob) { qemuDomainObjPrivatePtr priv = vm->privateData; - g_autoptr(virJSONValue) actions = virJSONValueNewArray(); virDomainDiskDefPtr disk = job->disk; VIR_DEBUG("active commit job '%s' on VM '%s' failed", job->name, vm->def->name); @@ -1428,13 +1427,12 @@ qemuBlockJobProcessEventFailedActiveCommit(virQEMUDriverPtr driver, if (!disk) return; - ignore_value(qemuMonitorTransactionBitmapRemove(actions, disk->mirror->nodeformat, - "libvirt-tmp-activewrite")); - if (qemuDomainObjEnterMonitorAsync(priv->driver, vm, asyncJob) < 0) return; - qemuMonitorTransaction(priv->mon, &actions); + qemuMonitorBitmapRemove(priv->mon, + disk->mirror->nodeformat, + "libvirt-tmp-activewrite"); if (qemuDomainObjExitMonitor(priv->driver, vm) < 0) return; @@ -1502,7 +1500,6 @@ qemuBlockJobProcessEventConcludedBackup(virQEMUDriverPtr driver, unsigned long long progressTotal) { g_autoptr(qemuBlockStorageSourceAttachData) backend = NULL; - g_autoptr(virJSONValue) actions = NULL; qemuBackupNotifyBlockjobEnd(vm, job->disk, newstate, job->errmsg, progressCurrent, progressTotal, asyncJob); @@ -1511,23 +1508,16 @@ qemuBlockJobProcessEventConcludedBackup(virQEMUDriverPtr driver, !(backend = qemuBlockStorageSourceDetachPrepare(job->data.backup.store, NULL))) return; - if (job->data.backup.bitmap) { - actions = virJSONValueNewArray(); - - if (qemuMonitorTransactionBitmapRemove(actions, - job->disk->src->nodeformat, - job->data.backup.bitmap) < 0) - return; - } - if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) return; if (backend) qemuBlockStorageSourceAttachRollback(qemuDomainGetMonitor(vm), backend); - if (actions) - qemuMonitorTransaction(qemuDomainGetMonitor(vm), &actions); + if (job->data.backup.bitmap) + qemuMonitorBitmapRemove(qemuDomainGetMonitor(vm), + job->disk->src->nodeformat, + job->data.backup.bitmap); if (qemuDomainObjExitMonitor(driver, vm) < 0) return; -- 2.29.2

Add the migration capability flag and the propagation of the corresponding mapping configuration. The mapping will be produced from the bitmaps on disk depending on both sides of the migration and the necessity to perform merges. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_migration_params.c | 29 +++++++++++++++++++++++++++++ src/qemu/qemu_migration_params.h | 5 +++++ 2 files changed, 34 insertions(+) diff --git a/src/qemu/qemu_migration_params.c b/src/qemu/qemu_migration_params.c index 510dad783a..e9b4601510 100644 --- a/src/qemu/qemu_migration_params.c +++ b/src/qemu/qemu_migration_params.c @@ -63,6 +63,7 @@ struct _qemuMigrationParams { unsigned long long compMethods; /* bit-wise OR of qemuMigrationCompressMethod */ virBitmapPtr caps; qemuMigrationParamValue params[QEMU_MIGRATION_PARAM_LAST]; + virJSONValuePtr blockDirtyBitmapMapping; }; typedef enum { @@ -89,6 +90,7 @@ VIR_ENUM_IMPL(qemuMigrationCapability, "pause-before-switchover", "late-block-activate", "multifd", + "dirty-bitmaps", ); @@ -265,6 +267,7 @@ qemuMigrationParamsFree(qemuMigrationParamsPtr migParams) } virBitmapFree(migParams->caps); + virJSONValueFree(migParams->blockDirtyBitmapMapping); g_free(migParams); } @@ -524,6 +527,20 @@ qemuMigrationParamsSetCompression(virTypedParameterPtr params, } +void +qemuMigrationParamsSetBlockDirtyBitmapMapping(qemuMigrationParamsPtr migParams, + virJSONValuePtr *params) +{ + virJSONValueFree(migParams->blockDirtyBitmapMapping); + migParams->blockDirtyBitmapMapping = g_steal_pointer(params); + + if (migParams->blockDirtyBitmapMapping) + ignore_value(virBitmapSetBit(migParams->caps, QEMU_MIGRATION_CAP_BLOCK_DIRTY_BITMAPS)); + else + ignore_value(virBitmapClearBit(migParams->caps, QEMU_MIGRATION_CAP_BLOCK_DIRTY_BITMAPS)); +} + + qemuMigrationParamsPtr qemuMigrationParamsFromFlags(virTypedParameterPtr params, int nparams, @@ -747,6 +764,17 @@ qemuMigrationParamsToJSON(qemuMigrationParamsPtr migParams) return NULL; } + if (migParams->blockDirtyBitmapMapping) { + g_autoptr(virJSONValue) mapping = virJSONValueCopy(migParams->blockDirtyBitmapMapping); + + if (!mapping) + return NULL; + + if (virJSONValueObjectAppend(params, "block-bitmap-mapping", mapping) < 0) + return NULL; + mapping = NULL; + } + return g_steal_pointer(¶ms); } @@ -1202,6 +1230,7 @@ qemuMigrationParamsReset(virQEMUDriverPtr driver, goto cleanup; qemuMigrationParamsResetTLS(driver, vm, asyncJob, origParams, apiFlags); + /* We don't reset 'block-bitmap-mapping' as it can't be unset */ cleanup: virErrorRestore(&err); diff --git a/src/qemu/qemu_migration_params.h b/src/qemu/qemu_migration_params.h index 9876101bfc..f1db42ce94 100644 --- a/src/qemu/qemu_migration_params.h +++ b/src/qemu/qemu_migration_params.h @@ -39,6 +39,7 @@ typedef enum { QEMU_MIGRATION_CAP_PAUSE_BEFORE_SWITCHOVER, QEMU_MIGRATION_CAP_LATE_BLOCK_ACTIVATE, QEMU_MIGRATION_CAP_MULTIFD, + QEMU_MIGRATION_CAP_BLOCK_DIRTY_BITMAPS, QEMU_MIGRATION_CAP_LAST } qemuMigrationCapability; @@ -132,6 +133,10 @@ qemuMigrationParamsGetULL(qemuMigrationParamsPtr migParams, qemuMigrationParam param, unsigned long long *value); +void +qemuMigrationParamsSetBlockDirtyBitmapMapping(qemuMigrationParamsPtr migParams, + virJSONValuePtr *params); + int qemuMigrationParamsCheck(virQEMUDriverPtr driver, virDomainObjPtr vm, -- 2.29.2

On Fri, Feb 19, 2021 at 12:58:19 +0100, Peter Krempa wrote:
Add the migration capability flag and the propagation of the corresponding mapping configuration. The mapping will be produced from the bitmaps on disk depending on both sides of the migration and the necessity to perform merges.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_migration_params.c | 29 +++++++++++++++++++++++++++++ src/qemu/qemu_migration_params.h | 5 +++++ 2 files changed, 34 insertions(+)
Reviewed-by: Jiri Denemark <jdenemar@redhat.com>

In cases where we are copying the storage we need to ensure that also bitmaps are copied properly. This patch adds migration cookie XML infrastructure which will allow the migration sides reach consensus on which bitmaps to migrate. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_migration_cookie.c | 146 +++++++++++++++++++++++++++++++ src/qemu/qemu_migration_cookie.h | 34 +++++++ 2 files changed, 180 insertions(+) diff --git a/src/qemu/qemu_migration_cookie.c b/src/qemu/qemu_migration_cookie.c index 6f2b1b2f57..0f8555cbb0 100644 --- a/src/qemu/qemu_migration_cookie.c +++ b/src/qemu/qemu_migration_cookie.c @@ -51,6 +51,7 @@ VIR_ENUM_IMPL(qemuMigrationCookieFlag, "cpu", "allowReboot", "capabilities", + "block-dirty-bitmaps", ); @@ -116,6 +117,39 @@ qemuMigrationCookieCapsFree(qemuMigrationCookieCapsPtr caps) G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuMigrationCookieCaps, qemuMigrationCookieCapsFree); +static void +qemuMigrationBlockDirtyBitmapsDiskBitmapFree(qemuMigrationBlockDirtyBitmapsDiskBitmapPtr bmp) +{ + if (!bmp) + return; + + g_free(bmp->bitmapname); + g_free(bmp->alias); + g_free(bmp->sourcebitmap); + g_free(bmp); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuMigrationBlockDirtyBitmapsDiskBitmap, + qemuMigrationBlockDirtyBitmapsDiskBitmapFree); + + +static void +qemuMigrationBlockDirtyBitmapsDiskFree(qemuMigrationBlockDirtyBitmapsDiskPtr dsk) +{ + if (!dsk) + return; + + g_free(dsk->target); + if (dsk->bitmaps) + g_slist_free_full(dsk->bitmaps, + (GDestroyNotify) qemuMigrationBlockDirtyBitmapsDiskBitmapFree); + g_free(dsk); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuMigrationBlockDirtyBitmapsDisk, + qemuMigrationBlockDirtyBitmapsDiskFree); + + void qemuMigrationCookieFree(qemuMigrationCookiePtr mig) { @@ -135,6 +169,9 @@ qemuMigrationCookieFree(qemuMigrationCookiePtr mig) g_clear_pointer(&mig->jobInfo, qemuDomainJobInfoFree); virCPUDefFree(mig->cpu); qemuMigrationCookieCapsFree(mig->caps); + if (mig->blockDirtyBitmaps) + g_slist_free_full(mig->blockDirtyBitmaps, + (GDestroyNotify) qemuMigrationBlockDirtyBitmapsDiskFree); g_free(mig); } @@ -758,6 +795,48 @@ qemuMigrationCookieNBDXMLFormat(qemuMigrationCookieNBDPtr nbd, } +static void +qemuMigrationCookieBlockDirtyBitmapsFormat(virBufferPtr buf, + GSList *bitmaps) +{ + g_auto(virBuffer) disksBuf = VIR_BUFFER_INIT_CHILD(buf); + GSList *nextdisk; + + for (nextdisk = bitmaps; nextdisk; nextdisk = nextdisk->next) { + qemuMigrationBlockDirtyBitmapsDiskPtr disk = nextdisk->data; + g_auto(virBuffer) diskAttrBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) diskChildBuf = VIR_BUFFER_INIT_CHILD(&disksBuf); + bool hasBitmaps = false; + GSList *nextbitmap; + + if (disk->skip || !disk->bitmaps) + continue; + + for (nextbitmap = disk->bitmaps; nextbitmap; nextbitmap = nextbitmap->next) { + qemuMigrationBlockDirtyBitmapsDiskBitmapPtr bitmap = nextbitmap->data; + + if (bitmap->skip) + continue; + + virBufferAsprintf(&diskChildBuf, + "<bitmap name='%s' alias='%s'/>\n", + bitmap->bitmapname, bitmap->alias); + + hasBitmaps = true; + } + + if (!hasBitmaps) + continue; + + virBufferAsprintf(&diskAttrBuf, " target='%s'", disk->target); + virXMLFormatElement(&disksBuf, "disk", &diskAttrBuf, &diskChildBuf); + } + + + virXMLFormatElement(buf, "blockDirtyBitmaps", NULL, &disksBuf); +} + + int qemuMigrationCookieXMLFormat(virQEMUDriverPtr driver, virQEMUCapsPtr qemuCaps, @@ -829,6 +908,9 @@ qemuMigrationCookieXMLFormat(virQEMUDriverPtr driver, if (mig->flags & QEMU_MIGRATION_COOKIE_CAPS) qemuMigrationCookieCapsXMLFormat(buf, mig->caps); + if (mig->flags & QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS) + qemuMigrationCookieBlockDirtyBitmapsFormat(buf, mig->blockDirtyBitmaps); + virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "</qemu-migration>\n"); return 0; @@ -1132,6 +1214,65 @@ qemuMigrationCookieXMLParseMandatoryFeatures(xmlXPathContextPtr ctxt, } +static int +qemuMigrationCookieBlockDirtyBitmapsParse(xmlXPathContextPtr ctxt, + qemuMigrationCookiePtr mig) +{ + g_autoslist(qemuMigrationBlockDirtyBitmapsDisk) disks = NULL; + g_autofree xmlNodePtr *disknodes = NULL; + int ndisknodes; + size_t i; + VIR_XPATH_NODE_AUTORESTORE(ctxt) + + if ((ndisknodes = virXPathNodeSet("./blockDirtyBitmaps/disk", ctxt, &disknodes)) < 0) + return -1; + + for (i = 0; i < ndisknodes; i++) { + g_autoslist(qemuMigrationBlockDirtyBitmapsDiskBitmap) bitmaps = NULL; + qemuMigrationBlockDirtyBitmapsDiskPtr disk; + g_autofree xmlNodePtr *bitmapnodes = NULL; + int nbitmapnodes; + size_t j; + + ctxt->node = disknodes[i]; + + if ((nbitmapnodes = virXPathNodeSet("./bitmap", ctxt, &bitmapnodes)) < 0) + return -1; + + for (j = 0; j < nbitmapnodes; j++) { + qemuMigrationBlockDirtyBitmapsDiskBitmapPtr bitmap; + + bitmap = g_new0(qemuMigrationBlockDirtyBitmapsDiskBitmap, 1); + bitmap->bitmapname = virXMLPropString(bitmapnodes[j], "name"); + bitmap->alias = virXMLPropString(bitmapnodes[j], "alias"); + bitmaps = g_slist_prepend(bitmaps, bitmap); + + if (!bitmap->bitmapname || !bitmap->alias) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("malformed <blockDirtyBitmaps> in migration cookie")); + return -1; + } + } + + disk = g_new0(qemuMigrationBlockDirtyBitmapsDisk, 1); + disk->target = virXMLPropString(disknodes[i], "target"); + disk->bitmaps = g_slist_reverse(g_steal_pointer(&bitmaps)); + + disks = g_slist_prepend(disks, disk); + + if (!disk->target) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("malformed <blockDirtyBitmaps> in migration cookie")); + return -1; + } + } + + mig->blockDirtyBitmaps = g_slist_reverse(g_steal_pointer(&disks)); + + return 0; +} + + static int qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig, virQEMUDriverPtr driver, @@ -1275,6 +1416,11 @@ qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig, !(mig->caps = qemuMigrationCookieCapsXMLParse(ctxt))) return -1; + if (flags & QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS && + virXPathBoolean("boolean(./blockDirtyBitmaps)", ctxt) && + qemuMigrationCookieBlockDirtyBitmapsParse(ctxt, mig) < 0) + return -1; + return 0; } diff --git a/src/qemu/qemu_migration_cookie.h b/src/qemu/qemu_migration_cookie.h index ecd1a01375..8636f955da 100644 --- a/src/qemu/qemu_migration_cookie.h +++ b/src/qemu/qemu_migration_cookie.h @@ -35,6 +35,7 @@ typedef enum { QEMU_MIGRATION_COOKIE_FLAG_CPU, QEMU_MIGRATION_COOKIE_FLAG_ALLOW_REBOOT, QEMU_MIGRATION_COOKIE_FLAG_CAPS, + QEMU_MIGRATION_COOKIE_FLAG_BLOCK_DIRTY_BITMAPS, QEMU_MIGRATION_COOKIE_FLAG_LAST } qemuMigrationCookieFlags; @@ -53,6 +54,7 @@ typedef enum { QEMU_MIGRATION_COOKIE_CPU = (1 << QEMU_MIGRATION_COOKIE_FLAG_CPU), QEMU_MIGRATION_COOKIE_ALLOW_REBOOT = (1 << QEMU_MIGRATION_COOKIE_FLAG_ALLOW_REBOOT), QEMU_MIGRATION_COOKIE_CAPS = (1 << QEMU_MIGRATION_COOKIE_FLAG_CAPS), + QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS = (1 << QEMU_MIGRATION_COOKIE_FLAG_BLOCK_DIRTY_BITMAPS), } qemuMigrationCookieFeatures; typedef struct _qemuMigrationCookieGraphics qemuMigrationCookieGraphics; @@ -107,6 +109,35 @@ struct _qemuMigrationCookieCaps { virBitmapPtr automatic; }; +typedef struct _qemuMigrationBlockDirtyBitmapsDiskBitmap qemuMigrationBlockDirtyBitmapsDiskBitmap; +typedef qemuMigrationBlockDirtyBitmapsDiskBitmap *qemuMigrationBlockDirtyBitmapsDiskBitmapPtr; +struct _qemuMigrationBlockDirtyBitmapsDiskBitmap { + /* config */ + char *bitmapname; + char *alias; + + /* runtime */ + virTristateBool persistent; /* force persisting of the bitmap */ + char *sourcebitmap; /* optional, actual bitmap to migrate in case we needed + to create a temporary one by merging */ + bool skip; /* omit this bitmap */ +}; + + +typedef struct _qemuMigrationBlockDirtyBitmapsDisk qemuMigrationBlockDirtyBitmapsDisk; +typedef qemuMigrationBlockDirtyBitmapsDisk *qemuMigrationBlockDirtyBitmapsDiskPtr; +struct _qemuMigrationBlockDirtyBitmapsDisk { + char *target; + + GSList *bitmaps; + + /* runtime data */ + virDomainDiskDefPtr disk; /* disk object corresponding to 'target' */ + const char *nodename; /* nodename of the top level source of 'disk' */ + bool skip; /* omit this disk */ +}; + + typedef struct _qemuMigrationCookie qemuMigrationCookie; typedef qemuMigrationCookie *qemuMigrationCookiePtr; struct _qemuMigrationCookie { @@ -150,6 +181,9 @@ struct _qemuMigrationCookie { /* If flags & QEMU_MIGRATION_COOKIE_CAPS */ qemuMigrationCookieCapsPtr caps; + + /* If flags & QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS */ + GSList *blockDirtyBitmaps; }; -- 2.29.2

'qemuMigrationCookieBlockDirtyBitmapsMatchDisks' maps the bitmaps from the migration cookie to actual disk objects definition pointers. 'qemuMigrationCookieBlockDirtyBitmapsToParams' converts the bitmap definitions from the migration cookie into parameters for the 'block-bitmap-mapping' migration parameter. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_migration_cookie.c | 115 +++++++++++++++++++++++++++++++ src/qemu/qemu_migration_cookie.h | 8 +++ 2 files changed, 123 insertions(+) diff --git a/src/qemu/qemu_migration_cookie.c b/src/qemu/qemu_migration_cookie.c index 0f8555cbb0..186fe7bc9e 100644 --- a/src/qemu/qemu_migration_cookie.c +++ b/src/qemu/qemu_migration_cookie.c @@ -1580,3 +1580,118 @@ qemuMigrationCookieParse(virQEMUDriverPtr driver, return g_steal_pointer(&mig); } + + +/** + * qemuMigrationCookieBlockDirtyBitmapsMatchDisks: + * @def: domain definition + * @disks: list of qemuMigrationBlockDirtyBitmapsDiskPtr + * + * Matches all of the @disks to the actual domain disk definition objects + * by looking up the target. + */ +int +qemuMigrationCookieBlockDirtyBitmapsMatchDisks(virDomainDefPtr def, + GSList *disks) +{ + GSList *next; + + for (next = disks; next; next = next->next) { + qemuMigrationBlockDirtyBitmapsDiskPtr disk = next->data; + + if (!(disk->disk = virDomainDiskByTarget(def, disk->target))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Can't find disk '%s' in domain definition"), + disk->target); + return -1; + } + + disk->nodename = disk->disk->src->nodeformat; + } + + return 0; +} + + +/** + * qemuMigrationCookieBlockDirtyBitmapsToParams: + * @disks: list of qemuMigrationBlockDirtyBitmapsDisk + * @mapping: filled with resulting mapping + * + * Converts @disks into the arguments for 'block-bitmap-mapping' migration + * parameter. + */ +int +qemuMigrationCookieBlockDirtyBitmapsToParams(GSList *disks, + virJSONValuePtr *mapping) +{ + g_autoptr(virJSONValue) map = virJSONValueNewArray(); + bool hasDisks = false; + GSList *nextdisk; + + for (nextdisk = disks; nextdisk; nextdisk = nextdisk->next) { + qemuMigrationBlockDirtyBitmapsDiskPtr disk = nextdisk->data; + g_autoptr(virJSONValue) jsondisk = NULL; + g_autoptr(virJSONValue) jsonbitmaps = virJSONValueNewArray(); + bool hasBitmaps = false; + GSList *nextbitmap; + + if (disk->skip || !disk->bitmaps) + continue; + + for (nextbitmap = disk->bitmaps; nextbitmap; nextbitmap = nextbitmap->next) { + qemuMigrationBlockDirtyBitmapsDiskBitmapPtr bitmap = nextbitmap->data; + g_autoptr(virJSONValue) jsonbitmap = NULL; + g_autoptr(virJSONValue) transform = NULL; + const char *bitmapname = bitmap->sourcebitmap; + + if (bitmap->skip) + continue; + + /* if there isn't an override, use the real name */ + if (!bitmapname) + bitmapname = bitmap->bitmapname; + + if (bitmap->persistent == VIR_TRISTATE_BOOL_YES) { + if (virJSONValueObjectCreate(&transform, + "b:persistent", true, NULL) < 0) + return -1; + } + + if (virJSONValueObjectCreate(&jsonbitmap, + "s:name", bitmapname, + "s:alias", bitmap->alias, + "A:transform", &transform, + NULL) < 0) + return -1; + + if (virJSONValueArrayAppend(jsonbitmaps, jsonbitmap) < 0) + return -1; + + jsonbitmap = NULL; + hasBitmaps = true; + } + + if (!hasBitmaps) + continue; + + if (virJSONValueObjectCreate(&jsondisk, + "s:node-name", disk->nodename, + "s:alias", disk->target, + "a:bitmaps", &jsonbitmaps, + NULL) < 0) + return -1; + + if (virJSONValueArrayAppend(map, jsondisk) < 0) + return -1; + + jsondisk = NULL; + hasDisks = true; + } + + if (!hasDisks) + return 0; + + *mapping = g_steal_pointer(&map); + return 0; +} diff --git a/src/qemu/qemu_migration_cookie.h b/src/qemu/qemu_migration_cookie.h index 8636f955da..e50dee7ba7 100644 --- a/src/qemu/qemu_migration_cookie.h +++ b/src/qemu/qemu_migration_cookie.h @@ -226,3 +226,11 @@ qemuMigrationCookieXMLFormat(virQEMUDriverPtr driver, virQEMUCapsPtr qemuCaps, virBufferPtr buf, qemuMigrationCookiePtr mig); + +int +qemuMigrationCookieBlockDirtyBitmapsMatchDisks(virDomainDefPtr def, + GSList *disks); + +int +qemuMigrationCookieBlockDirtyBitmapsToParams(GSList *disks, + virJSONValuePtr *mapping); -- 2.29.2

Add status XML infrastructure for storing a list of block dirty bitmaps which are temporarily used when migrating a VM with VIR_MIGRATE_NON_SHARED_DISK for cleanup after a libvirtd restart during migration. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_domain.c | 87 ++++++++++++++++++++++++++++++++++++++++-- src/qemu/qemu_domain.h | 15 ++++++++ 2 files changed, 99 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 541d592bbe..bb14fe2e33 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -86,6 +86,18 @@ qemuJobAllocPrivate(void) } +void +qemuDomainJobPrivateMigrateTempBitmapFree(qemuDomainJobPrivateMigrateTempBitmapPtr bmp) +{ + if (!bmp) + return; + + g_free(bmp->nodename); + g_free(bmp->bitmapname); + g_free(bmp); +} + + static void qemuJobFreePrivate(void *opaque) { @@ -95,6 +107,9 @@ qemuJobFreePrivate(void *opaque) return; qemuMigrationParamsFree(priv->migParams); + if (priv->migTempBitmaps) + g_slist_free_full(priv->migTempBitmaps, + (GDestroyNotify) qemuDomainJobPrivateMigrateTempBitmapFree); g_free(priv); } @@ -165,6 +180,28 @@ qemuDomainObjPrivateXMLFormatNBDMigration(virBufferPtr buf, return 0; } + +static void +qemuDomainObjPrivateXMLFormatMigrateTempBitmap(virBufferPtr buf, + GSList *bitmaps) +{ + g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf); + GSList *next; + + for (next = bitmaps; next; next = next->next) { + qemuDomainJobPrivateMigrateTempBitmapPtr t = next->data; + g_auto(virBuffer) bitmapBuf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&bitmapBuf, " name='%s'", t->bitmapname); + virBufferAsprintf(&bitmapBuf, " nodename='%s'", t->nodename); + + virXMLFormatElement(&childBuf, "bitmap", &bitmapBuf, NULL); + } + + virXMLFormatElement(buf, "tempBlockDirtyBitmaps", NULL, &childBuf); +} + + static int qemuDomainFormatJobPrivate(virBufferPtr buf, qemuDomainJobObjPtr job, @@ -172,9 +209,12 @@ qemuDomainFormatJobPrivate(virBufferPtr buf, { qemuDomainJobPrivatePtr priv = job->privateData; - if (job->asyncJob == QEMU_ASYNC_JOB_MIGRATION_OUT && - qemuDomainObjPrivateXMLFormatNBDMigration(buf, vm) < 0) - return -1; + if (job->asyncJob == QEMU_ASYNC_JOB_MIGRATION_OUT) { + if (qemuDomainObjPrivateXMLFormatNBDMigration(buf, vm) < 0) + return -1; + + qemuDomainObjPrivateXMLFormatMigrateTempBitmap(buf, priv->migTempBitmaps); + } if (priv->migParams) qemuMigrationParamsFormat(buf, priv->migParams); @@ -267,6 +307,44 @@ qemuDomainObjPrivateXMLParseJobNBD(virDomainObjPtr vm, return 0; } + +static int +qemuDomainObjPrivateXMLParseMigrateTempBitmap(qemuDomainJobPrivatePtr jobPriv, + xmlXPathContextPtr ctxt) +{ + g_autoslist(qemuDomainJobPrivateMigrateTempBitmap) bitmaps = NULL; + g_autofree xmlNodePtr *nodes = NULL; + size_t i; + int n; + + if ((n = virXPathNodeSet("./tempBlockDirtyBitmaps/bitmap", ctxt, &nodes)) < 0) + return -1; + + if (n == 0) + return 0; + + for (i = 0; i < n; i++) { + qemuDomainJobPrivateMigrateTempBitmapPtr bmp; + + bmp = g_new0(qemuDomainJobPrivateMigrateTempBitmap, 1); + bmp->nodename = virXMLPropString(nodes[i], "nodename"); + bmp->bitmapname = virXMLPropString(nodes[i], "name"); + + bitmaps = g_slist_prepend(bitmaps, bmp); + + if (!bmp->bitmapname || !bmp->nodename) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("malformed <tempBlockDirtyBitmaps> in status XML")); + return -1; + } + + } + + jobPriv->migTempBitmaps = g_slist_reverse(g_steal_pointer(&bitmaps)); + return 0; +} + + static int qemuDomainParseJobPrivate(xmlXPathContextPtr ctxt, qemuDomainJobObjPtr job, @@ -277,6 +355,9 @@ qemuDomainParseJobPrivate(xmlXPathContextPtr ctxt, if (qemuDomainObjPrivateXMLParseJobNBD(vm, ctxt) < 0) return -1; + if (qemuDomainObjPrivateXMLParseMigrateTempBitmap(priv, ctxt) < 0) + return -1; + if (qemuMigrationParamsParse(ctxt, &priv->migParams) < 0) return -1; diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 9a7d997d65..949307229b 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -487,6 +487,20 @@ struct _qemuDomainXmlNsDef { char **capsdel; }; + +typedef struct _qemuDomainJobPrivateMigrateTempBitmap qemuDomainJobPrivateMigrateTempBitmap; +typedef qemuDomainJobPrivateMigrateTempBitmap *qemuDomainJobPrivateMigrateTempBitmapPtr; + +struct _qemuDomainJobPrivateMigrateTempBitmap { + char *nodename; + char *bitmapname; +}; + +void +qemuDomainJobPrivateMigrateTempBitmapFree(qemuDomainJobPrivateMigrateTempBitmapPtr bmp); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuDomainJobPrivateMigrateTempBitmap, qemuDomainJobPrivateMigrateTempBitmapFree); + + typedef struct _qemuDomainJobPrivate qemuDomainJobPrivate; typedef qemuDomainJobPrivate *qemuDomainJobPrivatePtr; struct _qemuDomainJobPrivate { @@ -495,6 +509,7 @@ struct _qemuDomainJobPrivate { bool spiceMigrated; /* spice migration completed */ bool dumpCompleted; /* dump completed */ qemuMigrationParamsPtr migParams; + GSList *migTempBitmaps; /* temporary block dirty bitmaps - qemuDomainJobPrivateMigrateTempBitmap */ }; int qemuDomainObjStartWorker(virDomainObjPtr dom); -- 2.29.2

The XML sample shows the status XML when migrating with bitmaps including the <tempBlockDirtyBitmaps> element added in previous commit. It will also be used for the migration cookie test. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Jiri Denemark <jdenemar@redhat.com> --- .../migration-out-nbd-bitmaps-in.xml | 574 ++++++++++++++++++ .../migration-out-nbd-bitmaps-out.xml | 1 + tests/qemustatusxml2xmltest.c | 1 + 3 files changed, 576 insertions(+) create mode 100644 tests/qemustatusxml2xmldata/migration-out-nbd-bitmaps-in.xml create mode 120000 tests/qemustatusxml2xmldata/migration-out-nbd-bitmaps-out.xml diff --git a/tests/qemustatusxml2xmldata/migration-out-nbd-bitmaps-in.xml b/tests/qemustatusxml2xmldata/migration-out-nbd-bitmaps-in.xml new file mode 100644 index 0000000000..b3d24eda98 --- /dev/null +++ b/tests/qemustatusxml2xmldata/migration-out-nbd-bitmaps-in.xml @@ -0,0 +1,574 @@ +<domstatus state='paused' reason='migration' pid='2458694'> + <monitor path='/var/lib/libvirt/qemu/domain-11-migr/monitor.sock' type='unix'/> + <namespaces> + <mount/> + </namespaces> + <vcpus> + <vcpu id='0' pid='2458745'/> + </vcpus> + <qemuCaps> + <flag name='kvm'/> + <flag name='no-hpet'/> + <flag name='spice'/> + <flag name='hda-duplex'/> + <flag name='ccid-emulated'/> + <flag name='ccid-passthru'/> + <flag name='virtio-tx-alg'/> + <flag name='virtio-blk-pci.ioeventfd'/> + <flag name='sga'/> + <flag name='virtio-blk-pci.event_idx'/> + <flag name='virtio-net-pci.event_idx'/> + <flag name='piix3-usb-uhci'/> + <flag name='piix4-usb-uhci'/> + <flag name='usb-ehci'/> + <flag name='ich9-usb-ehci1'/> + <flag name='vt82c686b-usb-uhci'/> + <flag name='pci-ohci'/> + <flag name='usb-redir'/> + <flag name='usb-hub'/> + <flag name='ich9-ahci'/> + <flag name='no-acpi'/> + <flag name='virtio-blk-pci.scsi'/> + <flag name='scsi-disk.channel'/> + <flag name='scsi-block'/> + <flag name='hda-micro'/> + <flag name='dump-guest-memory'/> + <flag name='nec-usb-xhci'/> + <flag name='lsi'/> + <flag name='virtio-scsi-pci'/> + <flag name='blockio'/> + <flag name='disable-s3'/> + <flag name='disable-s4'/> + <flag name='usb-redir.filter'/> + <flag name='ide-drive.wwn'/> + <flag name='scsi-disk.wwn'/> + <flag name='seccomp-sandbox'/> + <flag name='reboot-timeout'/> + <flag name='vnc'/> + <flag name='qxl'/> + <flag name='VGA'/> + <flag name='cirrus-vga'/> + <flag name='vmware-svga'/> + <flag name='device-video-primary'/> + <flag name='usb-serial'/> + <flag name='nbd-server'/> + <flag name='virtio-rng'/> + <flag name='rng-random'/> + <flag name='rng-egd'/> + <flag name='megasas'/> + <flag name='tpm-passthrough'/> + <flag name='tpm-tis'/> + <flag name='pci-bridge'/> + <flag name='vfio-pci'/> + <flag name='mem-merge'/> + <flag name='drive-discard'/> + <flag name='dmi-to-pci-bridge'/> + <flag name='i440fx-pci-hole64-size'/> + <flag name='q35-pci-hole64-size'/> + <flag name='usb-storage'/> + <flag name='usb-storage.removable'/> + <flag name='virtio-mmio'/> + <flag name='ich9-intel-hda'/> + <flag name='kvm-pit-lost-tick-policy'/> + <flag name='boot-strict'/> + <flag name='pvpanic'/> + <flag name='spice-file-xfer-disable'/> + <flag name='usb-kbd'/> + <flag name='msg-timestamp'/> + <flag name='active-commit'/> + <flag name='change-backing-file'/> + <flag name='memory-backend-ram'/> + <flag name='numa'/> + <flag name='memory-backend-file'/> + <flag name='usb-audio'/> + <flag name='rtc-reset-reinjection'/> + <flag name='splash-timeout'/> + <flag name='iothread'/> + <flag name='migrate-rdma'/> + <flag name='drive-iotune-max'/> + <flag name='VGA.vgamem_mb'/> + <flag name='vmware-svga.vgamem_mb'/> + <flag name='qxl.vgamem_mb'/> + <flag name='pc-dimm'/> + <flag name='machine-vmport-opt'/> + <flag name='aes-key-wrap'/> + <flag name='dea-key-wrap'/> + <flag name='pci-serial'/> + <flag name='vhost-user-multiqueue'/> + <flag name='migration-event'/> + <flag name='gpex-pcihost'/> + <flag name='ioh3420'/> + <flag name='x3130-upstream'/> + <flag name='xio3130-downstream'/> + <flag name='rtl8139'/> + <flag name='e1000'/> + <flag name='virtio-net'/> + <flag name='gic-version'/> + <flag name='incoming-defer'/> + <flag name='virtio-gpu'/> + <flag name='virtio-gpu.virgl'/> + <flag name='virtio-keyboard'/> + <flag name='virtio-mouse'/> + <flag name='virtio-tablet'/> + <flag name='virtio-input-host'/> + <flag name='chardev-file-append'/> + <flag name='ich9-disable-s3'/> + <flag name='ich9-disable-s4'/> + <flag name='vserport-change-event'/> + <flag name='virtio-balloon-pci.deflate-on-oom'/> + <flag name='mptsas1068'/> + <flag name='spice-gl'/> + <flag name='qxl.vram64_size_mb'/> + <flag name='chardev-logfile'/> + <flag name='debug-threads'/> + <flag name='secret'/> + <flag name='pxb'/> + <flag name='pxb-pcie'/> + <flag name='nec-usb-xhci-ports'/> + <flag name='virtio-scsi-pci.iothread'/> + <flag name='name-guest'/> + <flag name='qxl.max_outputs'/> + <flag name='spice-unix'/> + <flag name='drive-detect-zeroes'/> + <flag name='tls-creds-x509'/> + <flag name='intel-iommu'/> + <flag name='smm'/> + <flag name='virtio-pci-disable-legacy'/> + <flag name='query-hotpluggable-cpus'/> + <flag name='virtio-net.rx_queue_size'/> + <flag name='virtio-vga'/> + <flag name='drive-iotune-max-length'/> + <flag name='ivshmem-plain'/> + <flag name='ivshmem-doorbell'/> + <flag name='query-qmp-schema'/> + <flag name='gluster.debug_level'/> + <flag name='vhost-scsi'/> + <flag name='drive-iotune-group'/> + <flag name='query-cpu-model-expansion'/> + <flag name='virtio-net.host_mtu'/> + <flag name='spice-rendernode'/> + <flag name='nvdimm'/> + <flag name='pcie-root-port'/> + <flag name='query-cpu-definitions'/> + <flag name='block-write-threshold'/> + <flag name='query-named-block-nodes'/> + <flag name='cpu-cache'/> + <flag name='qemu-xhci'/> + <flag name='kernel-irqchip'/> + <flag name='kernel-irqchip.split'/> + <flag name='intel-iommu.intremap'/> + <flag name='intel-iommu.caching-mode'/> + <flag name='intel-iommu.eim'/> + <flag name='intel-iommu.device-iotlb'/> + <flag name='virtio.iommu_platform'/> + <flag name='virtio.ats'/> + <flag name='loadparm'/> + <flag name='vnc-multi-servers'/> + <flag name='virtio-net.tx_queue_size'/> + <flag name='chardev-reconnect'/> + <flag name='virtio-gpu.max_outputs'/> + <flag name='virtio-blk.num-queues'/> + <flag name='vmcoreinfo'/> + <flag name='numa.dist'/> + <flag name='disk-share-rw'/> + <flag name='iscsi.password-secret'/> + <flag name='isa-serial'/> + <flag name='dump-completed'/> + <flag name='qcow2-luks'/> + <flag name='pcie-pci-bridge'/> + <flag name='seccomp-blacklist'/> + <flag name='query-cpus-fast'/> + <flag name='disk-write-cache'/> + <flag name='nbd-tls'/> + <flag name='tpm-crb'/> + <flag name='pr-manager-helper'/> + <flag name='qom-list-properties'/> + <flag name='memory-backend-file.discard-data'/> + <flag name='sdl-gl'/> + <flag name='screendump_device'/> + <flag name='hda-output'/> + <flag name='blockdev-del'/> + <flag name='vmgenid'/> + <flag name='vhost-vsock'/> + <flag name='chardev-fd-pass'/> + <flag name='tpm-emulator'/> + <flag name='mch'/> + <flag name='mch.extended-tseg-mbytes'/> + <flag name='usb-storage.werror'/> + <flag name='egl-headless'/> + <flag name='vfio-pci.display'/> + <flag name='blockdev'/> + <flag name='memory-backend-memfd'/> + <flag name='memory-backend-memfd.hugetlb'/> + <flag name='iothread.poll-max-ns'/> + <flag name='egl-headless.rendernode'/> + <flag name='memory-backend-file.align'/> + <flag name='memory-backend-file.pmem'/> + <flag name='nvdimm.unarmed'/> + <flag name='scsi-disk.device_id'/> + <flag name='virtio-pci-non-transitional'/> + <flag name='overcommit'/> + <flag name='query-current-machine'/> + <flag name='bitmap-merge'/> + <flag name='nbd-bitmap'/> + <flag name='x86-max-cpu'/> + <flag name='cpu-unavailable-features'/> + <flag name='canonical-cpu-features'/> + <flag name='bochs-display'/> + <flag name='migration-file-drop-cache'/> + <flag name='dbus-vmstate'/> + <flag name='vhost-user-gpu'/> + <flag name='vhost-user-vga'/> + <flag name='incremental-backup'/> + <flag name='ramfb'/> + <flag name='blockdev-file-dynamic-auto-read-only'/> + <flag name='savevm-monitor-nodes'/> + <flag name='drive-nvme'/> + <flag name='smp-dies'/> + <flag name='i8042'/> + <flag name='rng-builtin'/> + <flag name='virtio-net.failover'/> + <flag name='vhost-user-fs'/> + <flag name='query-named-block-nodes.flat'/> + <flag name='blockdev-snapshot.allow-write-only-overlay'/> + <flag name='blockdev-reopen'/> + <flag name='storage.werror'/> + <flag name='fsdev.multidevs'/> + <flag name='virtio.packed'/> + <flag name='pcie-root-port.hotplug'/> + <flag name='aio.io_uring'/> + <flag name='tcg'/> + <flag name='virtio-blk-pci.scsi.default.disabled'/> + <flag name='pvscsi'/> + <flag name='cpu.migratable'/> + <flag name='query-cpu-model-expansion.migratable'/> + <flag name='fw_cfg'/> + <flag name='migration-param.bandwidth'/> + <flag name='migration-param.downtime'/> + <flag name='migration-param.xbzrle-cache-size'/> + <flag name='intel-iommu.aw-bits'/> + <flag name='numa.hmat'/> + <flag name='blockdev-hostdev-scsi'/> + <flag name='usb-host.hostdevice'/> + <flag name='virtio-balloon.free-page-reporting'/> + <flag name='block-export-add'/> + <flag name='netdev.vhost-vdpa'/> + <flag name='fsdev.createmode'/> + <flag name='dc390'/> + <flag name='am53c974'/> + <flag name='virtio-pmem-pci'/> + <flag name='vhost-user-fs.bootindex'/> + <flag name='vhost-user-blk'/> + <flag name='cpu-max'/> + <flag name='migration-param.block-bitmap-mapping'/> + </qemuCaps> + <job type='none' async='migration out' phase='perform3' flags='0x42'> + <disk dev='hda' migrating='no'/> + <disk dev='vda' migrating='yes'> + <migrationSource type='network' format='raw'> + <source protocol='nbd' name='drive-virtio-disk0' tlsFromConfig='0'> + <host name='migr' port='49153'/> + <privateData> + <nodenames> + <nodename type='storage' name='migration-vda-storage'/> + <nodename type='format' name='migration-vda-format'/> + </nodenames> + </privateData> + </source> + </migrationSource> + </disk> + <tempBlockDirtyBitmaps> + <bitmap name='libvirt-migration-libvirt-vda-c' nodename='libvirt-1-format'/> + <bitmap name='libvirt-migration-libvirt-vda-b' nodename='libvirt-1-format'/> + <bitmap name='libvirt-migration-libvirt-vda-a' nodename='libvirt-1-format'/> + </tempBlockDirtyBitmaps> + <migParams> + <param name='compress-level' value='1'/> + <param name='compress-threads' value='8'/> + <param name='decompress-threads' value='2'/> + <param name='cpu-throttle-initial' value='20'/> + <param name='cpu-throttle-increment' value='10'/> + <param name='tls-creds' value=''/> + <param name='tls-hostname' value=''/> + <param name='max-bandwidth' value='134217728'/> + <param name='downtime-limit' value='300'/> + <param name='block-incremental' value='no'/> + <param name='xbzrle-cache-size' value='67108864'/> + <param name='max-postcopy-bandwidth' value='0'/> + <param name='multifd-channels' value='2'/> + </migParams> + </job> + <devices> + <device alias='sound0'/> + <device alias='sata1'/> + <device alias='ide0-0-0'/> + <device alias='video0'/> + <device alias='redir1'/> + <device alias='serial0'/> + <device alias='balloon0'/> + <device alias='sata0'/> + <device alias='usb'/> + <device alias='scsi0'/> + <device alias='redir0'/> + <device alias='channel1'/> + <device alias='virtio-disk0'/> + <device alias='sound0-codec0'/> + <device alias='channel0'/> + <device alias='virtio-serial0'/> + <device alias='input0'/> + <device alias='rng0'/> + </devices> + <libDir path='/var/lib/libvirt/qemu/domain-11-migr'/> + <channelTargetDir path='/var/lib/libvirt/qemu/channel/target/domain-11-migr'/> + <cpu mode='custom' match='exact' check='partial'> + <model fallback='forbid'>EPYC-Rome</model> + <vendor>AMD</vendor> + <feature policy='require' name='x2apic'/> + <feature policy='require' name='tsc-deadline'/> + <feature policy='require' name='hypervisor'/> + <feature policy='require' name='tsc_adjust'/> + <feature policy='require' name='stibp'/> + <feature policy='require' name='arch-capabilities'/> + <feature policy='require' name='ssbd'/> + <feature policy='require' name='xsaves'/> + <feature policy='require' name='cmp_legacy'/> + <feature policy='require' name='amd-ssbd'/> + <feature policy='require' name='virt-ssbd'/> + <feature policy='require' name='rdctl-no'/> + <feature policy='require' name='skip-l1dfl-vmentry'/> + <feature policy='require' name='mds-no'/> + <feature policy='require' name='pschange-mc-no'/> + </cpu> + <chardevStdioLogd/> + <rememberOwner/> + <allowReboot value='yes'/> + <nodename index='3'/> + <blockjobs active='yes'> + <blockjob name='drive-virtio-disk0' type='copy' state='ready' jobflags='0x0'> + <disk dst='vda'/> + </blockjob> + </blockjobs> + <agentTimeout>-2</agentTimeout> + <domain type='kvm' id='11'> + <name>migr</name> + <uuid>10b01607-0323-486b-afe2-3014a8a5b98b</uuid> + <memory unit='KiB'>1024000</memory> + <currentMemory unit='KiB'>1024000</currentMemory> + <vcpu placement='static'>1</vcpu> + <resource> + <partition>/machine</partition> + </resource> + <os> + <type arch='x86_64' machine='pc-i440fx-2.9'>hvm</type> + <bootmenu enable='yes'/> + </os> + <features> + <acpi/> + <apic/> + <vmport state='off'/> + </features> + <cpu mode='custom' match='exact' check='full'> + <model fallback='forbid'>EPYC-Rome</model> + <vendor>AMD</vendor> + <feature policy='require' name='x2apic'/> + <feature policy='require' name='tsc-deadline'/> + <feature policy='require' name='hypervisor'/> + <feature policy='require' name='tsc_adjust'/> + <feature policy='require' name='stibp'/> + <feature policy='require' name='arch-capabilities'/> + <feature policy='require' name='ssbd'/> + <feature policy='require' name='xsaves'/> + <feature policy='require' name='cmp_legacy'/> + <feature policy='require' name='amd-ssbd'/> + <feature policy='require' name='virt-ssbd'/> + <feature policy='require' name='rdctl-no'/> + <feature policy='require' name='skip-l1dfl-vmentry'/> + <feature policy='require' name='mds-no'/> + <feature policy='require' name='pschange-mc-no'/> + <feature policy='disable' name='svm'/> + <feature policy='disable' name='npt'/> + <feature policy='disable' name='nrip-save'/> + </cpu> + <clock offset='utc'> + <timer name='rtc' tickpolicy='catchup'/> + <timer name='pit' tickpolicy='delay'/> + <timer name='hpet' present='no'/> + </clock> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>restart</on_crash> + <pm> + <suspend-to-mem enabled='no'/> + <suspend-to-disk enabled='no'/> + </pm> + <devices> + <emulator>/home/pipo/git/qemu.git/build/x86_64-softmmu/qemu-system-x86_64</emulator> + <disk type='file' device='cdrom'> + <driver name='qemu' type='raw'/> + <source file='/var/lib/libvirt/images/systemrescuecd-amd64-6.1.2.iso' index='2'> + <privateData> + <nodenames> + <nodename type='storage' name='libvirt-2-storage'/> + <nodename type='format' name='libvirt-2-format'/> + </nodenames> + </privateData> + </source> + <backingStore/> + <target dev='hda' bus='ide'/> + <readonly/> + <boot order='1'/> + <alias name='ide0-0-0'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + <privateData> + <qom name='ide0-0-0'/> + </privateData> + </disk> + <disk type='file' device='disk'> + <driver name='qemu' type='qcow2'/> + <source file='/tmp/migr.qcow2' index='1'> + <privateData> + <nodenames> + <nodename type='storage' name='libvirt-1-storage'/> + <nodename type='format' name='libvirt-1-format'/> + </nodenames> + </privateData> + </source> + <backingStore type='file' index='3'> + <format type='qcow2'/> + <source file='/tmp/migr-base.qcow2'> + <privateData> + <nodenames> + <nodename type='storage' name='libvirt-3-storage'/> + <nodename type='format' name='libvirt-3-format'/> + </nodenames> + </privateData> + </source> + <backingStore/> + </backingStore> + <target dev='vda' bus='virtio'/> + <alias name='virtio-disk0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x08' function='0x0'/> + <privateData> + <qom name='/machine/peripheral/virtio-disk0/virtio-backend'/> + </privateData> + </disk> + <controller type='usb' index='0' model='ich9-ehci1'> + <alias name='usb'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x7'/> + </controller> + <controller type='usb' index='0' model='ich9-uhci1'> + <alias name='usb'/> + <master startport='0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0' multifunction='on'/> + </controller> + <controller type='usb' index='0' model='ich9-uhci2'> + <alias name='usb'/> + <master startport='2'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x1'/> + </controller> + <controller type='usb' index='0' model='ich9-uhci3'> + <alias name='usb'/> + <master startport='4'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'> + <alias name='pci.0'/> + </controller> + <controller type='virtio-serial' index='0'> + <alias name='virtio-serial0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> + </controller> + <controller type='ide' index='0'> + <alias name='ide'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='scsi' index='0' model='virtio-scsi'> + <alias name='scsi0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x09' function='0x0'/> + </controller> + <controller type='sata' index='0'> + <alias name='sata0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x0b' function='0x0'/> + </controller> + <controller type='sata' index='1'> + <alias name='sata1'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </controller> + <controller type='fdc' index='0'> + <alias name='fdc0'/> + </controller> + <serial type='pty'> + <source path='/dev/pts/47'/> + <target type='isa-serial' port='0'> + <model name='isa-serial'/> + </target> + <alias name='serial0'/> + </serial> + <console type='pty' tty='/dev/pts/47'> + <source path='/dev/pts/47'/> + <target type='serial' port='0'/> + <alias name='serial0'/> + </console> + <channel type='unix'> + <source mode='bind' path='/var/lib/libvirt/qemu/channel/target/domain-11-migr/org.qemu.guest_agent.0'/> + <target type='virtio' name='org.qemu.guest_agent.0' state='disconnected'/> + <alias name='channel0'/> + <address type='virtio-serial' controller='0' bus='0' port='1'/> + </channel> + <channel type='spicevmc'> + <target type='virtio' name='com.redhat.spice.0' state='disconnected'/> + <alias name='channel1'/> + <address type='virtio-serial' controller='0' bus='0' port='2'/> + </channel> + <input type='tablet' bus='usb'> + <alias name='input0'/> + <address type='usb' bus='0' port='1'/> + </input> + <input type='mouse' bus='ps2'> + <alias name='input1'/> + </input> + <input type='keyboard' bus='ps2'> + <alias name='input2'/> + </input> + <graphics type='spice' port='5902' autoport='yes' listen='127.0.0.1'> + <listen type='address' address='127.0.0.1' fromConfig='1' autoGenerated='no'/> + <image compression='off'/> + <gl enable='no'/> + </graphics> + <sound model='ich6'> + <alias name='sound0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </sound> + <video> + <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/> + <alias name='video0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> + </video> + <redirdev bus='usb' type='spicevmc'> + <alias name='redir0'/> + <address type='usb' bus='0' port='2'/> + </redirdev> + <redirdev bus='usb' type='spicevmc'> + <alias name='redir1'/> + <address type='usb' bus='0' port='3'/> + </redirdev> + <memballoon model='virtio'> + <alias name='balloon0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x0a' function='0x0'/> + </memballoon> + <rng model='virtio'> + <backend model='random'>/dev/random</backend> + <alias name='rng0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/> + </rng> + </devices> + <seclabel type='dynamic' model='selinux' relabel='yes'> + <label>unconfined_u:unconfined_r:svirt_t:s0:c38,c915</label> + <imagelabel>unconfined_u:object_r:svirt_image_t:s0:c38,c915</imagelabel> + </seclabel> + <seclabel type='dynamic' model='dac' relabel='yes'> + <label>+107:+107</label> + <imagelabel>+107:+107</imagelabel> + </seclabel> + </domain> +</domstatus> diff --git a/tests/qemustatusxml2xmldata/migration-out-nbd-bitmaps-out.xml b/tests/qemustatusxml2xmldata/migration-out-nbd-bitmaps-out.xml new file mode 120000 index 0000000000..a3d38478d5 --- /dev/null +++ b/tests/qemustatusxml2xmldata/migration-out-nbd-bitmaps-out.xml @@ -0,0 +1 @@ +migration-out-nbd-bitmaps-in.xml \ No newline at end of file diff --git a/tests/qemustatusxml2xmltest.c b/tests/qemustatusxml2xmltest.c index 67a070c986..39be0edf69 100644 --- a/tests/qemustatusxml2xmltest.c +++ b/tests/qemustatusxml2xmltest.c @@ -133,6 +133,7 @@ mymain(void) DO_TEST_STATUS("migration-in-params"); DO_TEST_STATUS("migration-out-params"); DO_TEST_STATUS("migration-out-nbd-tls"); + DO_TEST_STATUS("migration-out-nbd-bitmaps"); DO_TEST_STATUS("upgrade"); DO_TEST_STATUS("blockjob-blockdev"); -- 2.29.2

Test the XML infrastructure for <blockDirtyBitmaps> migration cookie element as well as the conversion to migration parameters for QMP schema validation. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Jiri Denemark <jdenemar@redhat.com> --- tests/meson.build | 2 +- .../nbd-bitmaps-xml2xml-in.xml | 52 ++++++ .../nbd-bitmaps-xml2xml-migparams.json | 25 +++ .../nbd-bitmaps-xml2xml-out.xml | 51 ++++++ tests/qemumigrationcookiexmltest.c | 166 +++++++++++++++--- 5 files changed, 269 insertions(+), 27 deletions(-) create mode 100644 tests/qemumigrationcookiexmldata/nbd-bitmaps-xml2xml-in.xml create mode 100644 tests/qemumigrationcookiexmldata/nbd-bitmaps-xml2xml-migparams.json create mode 100644 tests/qemumigrationcookiexmldata/nbd-bitmaps-xml2xml-out.xml diff --git a/tests/meson.build b/tests/meson.build index 0de0783839..b9b2255666 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -452,7 +452,7 @@ if conf.has('WITH_QEMU') { 'name': 'qemuhotplugtest', 'link_with': [ test_qemu_driver_lib, test_utils_qemu_monitor_lib ], 'link_whole': [ test_utils_qemu_lib ] }, { 'name': 'qemumemlocktest', 'link_with': [ test_qemu_driver_lib ], 'link_whole': [ test_utils_qemu_lib ] }, { 'name': 'qemumigparamstest', 'link_with': [ test_qemu_driver_lib, test_utils_qemu_monitor_lib ], 'link_whole': [ test_utils_qemu_lib ] }, - { 'name': 'qemumigrationcookiexmltest', 'link_with': [ test_qemu_driver_lib ], 'link_whole': [ test_utils_qemu_lib, test_file_wrapper_lib ] }, + { 'name': 'qemumigrationcookiexmltest', 'link_with': [ test_qemu_driver_lib, test_utils_qemu_monitor_lib ], 'link_whole': [ test_utils_qemu_lib, test_file_wrapper_lib ] }, { 'name': 'qemumonitorjsontest', 'link_with': [ test_qemu_driver_lib, test_utils_qemu_monitor_lib ], 'link_whole': [ test_utils_qemu_lib ] }, { 'name': 'qemusecuritytest', 'sources': [ 'qemusecuritytest.c', 'qemusecuritymock.c' ], 'link_with': [ test_qemu_driver_lib ], 'link_whole': [ test_utils_qemu_lib ] }, { 'name': 'qemustatusxml2xmltest', 'link_with': [ test_qemu_driver_lib ], 'link_whole': [ test_utils_qemu_lib, test_file_wrapper_lib ] }, diff --git a/tests/qemumigrationcookiexmldata/nbd-bitmaps-xml2xml-in.xml b/tests/qemumigrationcookiexmldata/nbd-bitmaps-xml2xml-in.xml new file mode 100644 index 0000000000..b219c25f27 --- /dev/null +++ b/tests/qemumigrationcookiexmldata/nbd-bitmaps-xml2xml-in.xml @@ -0,0 +1,52 @@ +<qemu-migration> + <name>migr</name> + <uuid>10b01607-0323-486b-afe2-3014a8a5b98b</uuid> + <hostname>sourcehost</hostname> + <hostuuid>1f5e0da0-fecf-413f-9bf1-1aa9c21e71e4</hostuuid> + <feature name='lockstate'/> + <nbd> + <disk target='hda' capacity='708837376'/> + <disk target='vda' capacity='10485760'/> + </nbd> + <cpu mode='custom' match='exact' check='full'> + <model fallback='forbid'>EPYC-Rome</model> + <vendor>AMD</vendor> + <feature policy='require' name='x2apic'/> + <feature policy='require' name='tsc-deadline'/> + <feature policy='require' name='hypervisor'/> + <feature policy='require' name='tsc_adjust'/> + <feature policy='require' name='stibp'/> + <feature policy='require' name='arch-capabilities'/> + <feature policy='require' name='ssbd'/> + <feature policy='require' name='xsaves'/> + <feature policy='require' name='cmp_legacy'/> + <feature policy='require' name='amd-ssbd'/> + <feature policy='require' name='virt-ssbd'/> + <feature policy='require' name='rdctl-no'/> + <feature policy='require' name='skip-l1dfl-vmentry'/> + <feature policy='require' name='mds-no'/> + <feature policy='require' name='pschange-mc-no'/> + <feature policy='disable' name='svm'/> + <feature policy='disable' name='npt'/> + <feature policy='disable' name='nrip-save'/> + </cpu> + <allowReboot value='yes'/> + <capabilities> + <cap name='xbzrle' auto='no'/> + <cap name='auto-converge' auto='no'/> + <cap name='rdma-pin-all' auto='no'/> + <cap name='postcopy-ram' auto='no'/> + <cap name='compress' auto='no'/> + <cap name='pause-before-switchover' auto='yes'/> + <cap name='late-block-activate' auto='no'/> + <cap name='multifd' auto='no'/> + <cap name='dirty-bitmaps' auto='no'/> + </capabilities> + <blockDirtyBitmaps> + <disk target='vda'> + <bitmap name='a' alias='libvirt-vda-a'/> + <bitmap name='b' alias='libvirt-vda-b'/> + <bitmap name='c' alias='libvirt-vda-c'/> + </disk> + </blockDirtyBitmaps> +</qemu-migration> diff --git a/tests/qemumigrationcookiexmldata/nbd-bitmaps-xml2xml-migparams.json b/tests/qemumigrationcookiexmldata/nbd-bitmaps-xml2xml-migparams.json new file mode 100644 index 0000000000..100da7c270 --- /dev/null +++ b/tests/qemumigrationcookiexmldata/nbd-bitmaps-xml2xml-migparams.json @@ -0,0 +1,25 @@ +{ + "block-bitmap-mapping": [ + { + "node-name": "libvirt-1-format", + "alias": "vda", + "bitmaps": [ + { + "name": "a", + "alias": "libvirt-vda-a", + "transform": { + "persistent": true + } + }, + { + "name": "b", + "alias": "libvirt-vda-b" + }, + { + "name": "c", + "alias": "libvirt-vda-c" + } + ] + } + ] +} diff --git a/tests/qemumigrationcookiexmldata/nbd-bitmaps-xml2xml-out.xml b/tests/qemumigrationcookiexmldata/nbd-bitmaps-xml2xml-out.xml new file mode 100644 index 0000000000..09b6fa291c --- /dev/null +++ b/tests/qemumigrationcookiexmldata/nbd-bitmaps-xml2xml-out.xml @@ -0,0 +1,51 @@ +<qemu-migration> + <name>migr</name> + <uuid>10b01607-0323-486b-afe2-3014a8a5b98b</uuid> + <hostname>hostname</hostname> + <hostuuid>4a802f00-4cba-5df6-9679-a08c4c5b577f</hostuuid> + <nbd> + <disk target='hda' capacity='708837376'/> + <disk target='vda' capacity='10485760'/> + </nbd> + <cpu mode='custom' match='exact' check='full'> + <model fallback='forbid'>EPYC-Rome</model> + <vendor>AMD</vendor> + <feature policy='require' name='x2apic'/> + <feature policy='require' name='tsc-deadline'/> + <feature policy='require' name='hypervisor'/> + <feature policy='require' name='tsc_adjust'/> + <feature policy='require' name='stibp'/> + <feature policy='require' name='arch-capabilities'/> + <feature policy='require' name='ssbd'/> + <feature policy='require' name='xsaves'/> + <feature policy='require' name='cmp_legacy'/> + <feature policy='require' name='amd-ssbd'/> + <feature policy='require' name='virt-ssbd'/> + <feature policy='require' name='rdctl-no'/> + <feature policy='require' name='skip-l1dfl-vmentry'/> + <feature policy='require' name='mds-no'/> + <feature policy='require' name='pschange-mc-no'/> + <feature policy='disable' name='svm'/> + <feature policy='disable' name='npt'/> + <feature policy='disable' name='nrip-save'/> + </cpu> + <allowReboot value='yes'/> + <capabilities> + <cap name='xbzrle' auto='no'/> + <cap name='auto-converge' auto='no'/> + <cap name='rdma-pin-all' auto='no'/> + <cap name='postcopy-ram' auto='no'/> + <cap name='compress' auto='no'/> + <cap name='pause-before-switchover' auto='yes'/> + <cap name='late-block-activate' auto='no'/> + <cap name='multifd' auto='no'/> + <cap name='dirty-bitmaps' auto='no'/> + </capabilities> + <blockDirtyBitmaps> + <disk target='vda'> + <bitmap name='a' alias='libvirt-vda-a'/> + <bitmap name='b' alias='libvirt-vda-b'/> + <bitmap name='c' alias='libvirt-vda-c'/> + </disk> + </blockDirtyBitmaps> +</qemu-migration> diff --git a/tests/qemumigrationcookiexmltest.c b/tests/qemumigrationcookiexmltest.c index 5fe0ba8a8a..7f2437a7fe 100644 --- a/tests/qemumigrationcookiexmltest.c +++ b/tests/qemumigrationcookiexmltest.c @@ -25,9 +25,13 @@ #include "internal.h" #include "testutilsqemu.h" +#include "testutilsqemuschema.h" #include "configmake.h" +#define LIBVIRT_QEMU_MIGRATION_PARAMSPRIV_H_ALLOW + #include "qemu/qemu_migration_cookie.h" +#include "qemu/qemu_migration_paramspriv.h" #define VIR_FROM_THIS VIR_FROM_NONE @@ -61,13 +65,33 @@ struct testQemuMigrationCookieData { qemuMigrationParty cookiePopulateParty; + qemuMigrationCookiePtr cookie; + char *xmlstr; int xmlstrlen; char *infile; char *outfile; + char *outmigparamsfile; }; +static void +testQemuMigrationCookieDataFree(struct testQemuMigrationCookieData *data) +{ + if (!data) + return; + + qemuMigrationCookieFree(data->cookie); + g_free(data->xmlstr); + g_free(data->outfile); + g_free(data->infile); + g_free(data->outmigparamsfile); + g_free(data->inStatus); + virDomainObjEndAPI(&data->vm); + g_free(data); +} + + static int testQemuMigrationCookiePopulate(const void *opaque) { @@ -115,26 +139,25 @@ testQemuMigrationCookieParse(const void *opaque) struct testQemuMigrationCookieData *data = (struct testQemuMigrationCookieData *) opaque; qemuDomainObjPrivatePtr priv = data->vm->privateData; g_auto(virBuffer) actual = VIR_BUFFER_INITIALIZER; - g_autoptr(qemuMigrationCookie) cookie = NULL; - if (!(cookie = qemuMigrationCookieParse(&driver, - data->vm->def, - NULL, - priv, - data->xmlstr, - data->xmlstrlen, - data->cookieParseFlags))) { + if (!(data->cookie = qemuMigrationCookieParse(&driver, + data->vm->def, + NULL, + priv, + data->xmlstr, + data->xmlstrlen, + data->cookieParseFlags))) { VIR_TEST_DEBUG("\nfailed to parse qemu migration cookie:\n%s\n", data->xmlstr); return -1; } /* set all flags so that formatter attempts to format everything */ - cookie->flags = ~0; + data->cookie->flags = ~0; if (qemuMigrationCookieXMLFormat(&driver, priv->qemuCaps, &actual, - cookie) < 0) { + data->cookie) < 0) { VIR_TEST_DEBUG("\nfailed to format back qemu migration cookie"); return -1; } @@ -179,21 +202,6 @@ testQemuMigrationCookieXMLLoad(const void *opaque) } -static void -testQemuMigrationCookieDataFree(struct testQemuMigrationCookieData *data) -{ - if (!data) - return; - - g_free(data->xmlstr); - g_free(data->outfile); - g_free(data->infile); - g_free(data->inStatus); - virDomainObjEndAPI(&data->vm); - g_free(data); -} - - static int testQemuMigrationCookieDom2XML(const char *namesuffix, const char *domxml, @@ -207,9 +215,11 @@ testQemuMigrationCookieDom2XML(const char *namesuffix, /* flags unsupported by default: * - lockstate: internals are NULL in tests, causes crash * - nbd: monitor not present + * - dirty bitmaps: monitor not present */ unsigned int cookiePopulateFlagMask = QEMU_MIGRATION_COOKIE_LOCKSTATE | - QEMU_MIGRATION_COOKIE_NBD; + QEMU_MIGRATION_COOKIE_NBD | + QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS; data->cookiePopulateFlags = ~cookiePopulateFlagMask; } @@ -286,6 +296,107 @@ testQemuMigrationCookieXML2XML(const char *name, } +static int +testQemuMigrationCookieBlockDirtyBitmaps(const void *opaque) +{ + const struct testQemuMigrationCookieData *data = opaque; + g_autoptr(virJSONValue) migParamsBitmaps = NULL; + g_autofree char *actualJSON = NULL; + g_autoptr(virJSONValue) paramsOut = NULL; + g_auto(virBuffer) debug = VIR_BUFFER_INITIALIZER; + g_autoptr(qemuMigrationParams) migParams = NULL; + g_autoptr(GHashTable) qmpschema = NULL; + GSList *next; + + if (!(qmpschema = testQEMUSchemaLoadLatest("x86_64"))) { + VIR_TEST_VERBOSE("failed to load QMP schema"); + return -1; + } + + if (qemuMigrationCookieBlockDirtyBitmapsMatchDisks(data->vm->def, + data->cookie->blockDirtyBitmaps) < 0) + return -1; + + for (next = data->cookie->blockDirtyBitmaps; next; next = next->next) { + qemuMigrationBlockDirtyBitmapsDiskPtr disk = next->data; + qemuMigrationBlockDirtyBitmapsDiskBitmapPtr bitmap = disk->bitmaps->data; + + bitmap->persistent = VIR_TRISTATE_BOOL_YES; + } + + if (qemuMigrationCookieBlockDirtyBitmapsToParams(data->cookie->blockDirtyBitmaps, + &migParamsBitmaps)) + return -1; + + if (!(migParams = qemuMigrationParamsNew())) + return -1; + + qemuMigrationParamsSetBlockDirtyBitmapMapping(migParams, &migParamsBitmaps); + + if (!(paramsOut = qemuMigrationParamsToJSON(migParams)) || + !(actualJSON = virJSONValueToString(paramsOut, true))) + return -1; + + if (testQEMUSchemaValidateCommand("migrate-set-parameters", + paramsOut, + qmpschema, + false, + false, + &debug) < 0) { + VIR_TEST_VERBOSE("failed to validate migration params '%s' against QMP schema: %s", + actualJSON, virBufferCurrentContent(&debug)); + return -1; + } + + if (virTestCompareToFile(actualJSON, data->outmigparamsfile) < 0) + return -1; + + return 0; +} + + +/* tests also the conversion to list of migrated bitmaps */ +static int +testQemuMigrationCookieXML2XMLBitmaps(const char *name, + const char *statusxml, + unsigned int cookieParseFlags) +{ + struct testQemuMigrationCookieData *data = g_new0(struct testQemuMigrationCookieData, 1); + int ret = 0; + + if (cookieParseFlags == 0) + data->cookieParseFlags = ~0; + + data->inStatus = g_strconcat(abs_srcdir, "/", statusxml, NULL); + data->infile = g_strconcat(abs_srcdir, "/qemumigrationcookiexmldata/", + name, "-xml2xml-in.xml", NULL); + data->outfile = g_strconcat(abs_srcdir, "/qemumigrationcookiexmldata/", + name, "-xml2xml-out.xml", NULL); + data->outmigparamsfile = g_strconcat(abs_srcdir, "/qemumigrationcookiexmldata/", + name, "-xml2xml-migparams.json", NULL); + + if (virTestRun(tn("qemumigrationcookieXML2XML-dom-", name, NULL), + testQemuMigrationCookieDomInit, data) < 0) + ret = -1; + + if (virTestRun(tn("qemumigrationcookieXML2XML-load-", name, NULL), + testQemuMigrationCookieXMLLoad, data) < 0) + ret = -1; + + if (virTestRun(tn("qemumigrationcookieXML2XML-parse-", name, NULL), + testQemuMigrationCookieParse, data) < 0) + ret = -1; + + if (virTestRun(tn("qemumigrationcookieXML2XML-migparams-", name, NULL), + testQemuMigrationCookieBlockDirtyBitmaps, data) < 0) + ret = -1; + + testQemuMigrationCookieDataFree(data); + + return ret; +} + + static int mymain(void) { @@ -321,6 +432,9 @@ mymain(void) testQemuMigrationCookieXML2XML("full", "qemustatusxml2xmldata/modern-in.xml", 0) < 0) ret = -1; + if (testQemuMigrationCookieXML2XMLBitmaps("nbd-bitmaps", "qemustatusxml2xmldata/migration-out-nbd-bitmaps-in.xml", 0) < 0) + ret = -1; + virBufferFreeAndReset(&testnamebuf); cleanup: -- 2.29.2

In case when the block migration job required temporary bitmaps for merging the appropriate checkpoints we need to clean them up when cancelling the job. On success we don't need to do that though as the bitmaps are just temporary thus are not written to disk. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_migration.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 94b9b34ca0..4e69fab384 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -840,6 +840,29 @@ qemuMigrationSrcNBDCopyCancel(virQEMUDriverPtr driver, } +static int +qemuMigrationSrcCancelRemoveTempBitmaps(virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virQEMUDriverPtr driver = priv->driver; + qemuDomainJobPrivatePtr jobPriv = priv->job.privateData; + GSList *next; + + for (next = jobPriv->migTempBitmaps; next; next = next->next) { + qemuDomainJobPrivateMigrateTempBitmapPtr t = next->data; + + if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) + return -1; + qemuMonitorBitmapRemove(priv->mon, t->nodename, t->bitmapname); + if (qemuDomainObjExitMonitor(driver, vm) < 0) + return -1; + } + + return 0; +} + + static virStorageSourcePtr qemuMigrationSrcNBDStorageCopyBlockdevPrepareSource(virDomainDiskDefPtr disk, const char *host, @@ -4003,6 +4026,8 @@ qemuMigrationSrcRun(virQEMUDriverPtr driver, QEMU_ASYNC_JOB_MIGRATION_OUT, dconn); + qemuMigrationSrcCancelRemoveTempBitmaps(vm, QEMU_ASYNC_JOB_MIGRATION_OUT); + if (priv->job.current->status != QEMU_DOMAIN_JOB_STATUS_CANCELED) priv->job.current->status = QEMU_DOMAIN_JOB_STATUS_FAILED; } @@ -5705,6 +5730,9 @@ qemuMigrationSrcCancel(virQEMUDriverPtr driver, QEMU_ASYNC_JOB_NONE, NULL) < 0) return -1; + if (qemuMigrationSrcCancelRemoveTempBitmaps(vm, QEMU_ASYNC_JOB_NONE) < 0) + return -1; + return 0; } -- 2.29.2

Preserve block dirty bitmaps after migration with QEMU_MONITOR_MIGRATE_NON_SHARED_(DISK|INC). This patch implements functions which offer the bitmaps to the destination, check for eligibility on destination and then configure source for the migration. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_migration.c | 333 +++++++++++++++++++++++++++++++++++++- 1 file changed, 331 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 4e69fab384..08f60c6db3 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -2205,6 +2205,91 @@ qemuMigrationSrcCleanup(virDomainObjPtr vm, } +/** + * qemuMigrationSrcBeginPhaseBlockDirtyBitmaps: + * @mig: migration cookie struct + * @vm: domain object + * @migrate_disks: disks which are being migrated + * @nmigrage_disks: number of @migrate_disks + * + * Enumerates block dirty bitmaps on disks which will undergo storage migration + * and fills them into @mig to be offered to the destination. + */ +static int +qemuMigrationSrcBeginPhaseBlockDirtyBitmaps(qemuMigrationCookiePtr mig, + virDomainObjPtr vm, + const char **migrate_disks, + size_t nmigrate_disks) + +{ + GSList *disks = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; + size_t i; + + g_autoptr(GHashTable) blockNamedNodeData = NULL; + + if (!(blockNamedNodeData = qemuBlockGetNamedNodeData(vm, priv->job.asyncJob))) + return -1; + + for (i = 0; i < vm->def->ndisks; i++) { + qemuMigrationBlockDirtyBitmapsDiskPtr disk; + GSList *bitmaps = NULL; + virDomainDiskDefPtr diskdef = vm->def->disks[i]; + qemuBlockNamedNodeDataPtr nodedata = virHashLookup(blockNamedNodeData, diskdef->src->nodeformat); + size_t j; + + if (!nodedata) + continue; + + if (migrate_disks) { + bool migrating = false; + + for (j = 0; j < nmigrate_disks; j++) { + if (STREQ(migrate_disks[j], diskdef->dst)) { + migrating = true; + break; + } + } + + if (!migrating) + continue; + } + + for (j = 0; j < nodedata->nbitmaps; j++) { + qemuMigrationBlockDirtyBitmapsDiskBitmapPtr bitmap; + + if (!qemuBlockBitmapChainIsValid(diskdef->src, + nodedata->bitmaps[j]->name, + blockNamedNodeData)) + continue; + + bitmap = g_new0(qemuMigrationBlockDirtyBitmapsDiskBitmap, 1); + bitmap->bitmapname = g_strdup(nodedata->bitmaps[j]->name); + bitmap->alias = g_strdup_printf("libvirt-%s-%s", + diskdef->dst, + nodedata->bitmaps[j]->name); + bitmaps = g_slist_prepend(bitmaps, bitmap); + } + + if (!bitmaps) + continue; + + disk = g_new0(qemuMigrationBlockDirtyBitmapsDisk, 1); + disk->target = g_strdup(diskdef->dst); + disk->bitmaps = bitmaps; + disks = g_slist_prepend(disks, disk); + } + + if (!disks) + return 0; + + mig->blockDirtyBitmaps = disks; + mig->flags |= QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS; + + return 0; +} + + /* The caller is supposed to lock the vm and start a migration job. */ static char * qemuMigrationSrcBeginPhase(virQEMUDriverPtr driver, @@ -2317,6 +2402,12 @@ qemuMigrationSrcBeginPhase(virQEMUDriverPtr driver, if (!(mig = qemuMigrationCookieNew(vm->def, priv->origname))) return NULL; + if (cookieFlags & QEMU_MIGRATION_COOKIE_NBD && + virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_MIGRATION_PARAM_BLOCK_BITMAP_MAPPING) && + qemuMigrationSrcBeginPhaseBlockDirtyBitmaps(mig, vm, migrate_disks, + nmigrate_disks) < 0) + return NULL; + if (qemuMigrationCookieFormat(mig, driver, vm, QEMU_MIGRATION_SOURCE, cookieout, cookieoutlen, @@ -2530,6 +2621,92 @@ qemuMigrationDstPrepare(virDomainObjPtr vm, migrateFrom, fd, NULL); } + +/** + * qemuMigrationDstPrepareAnyBlockDirtyBitmaps: + * @vm: domain object + * @mig: migration cookie + * @migParams: migration parameters + * @flags: migration flags + * + * Checks whether block dirty bitmaps offered by the migration source are + * to be migrated (e.g. they don't exist, the destination is compatible etc) + * and sets up destination qemu for migrating the bitmaps as well as updates the + * list of eligible bitmaps in the migration cookie to be sent back to the + * source. + */ +static int +qemuMigrationDstPrepareAnyBlockDirtyBitmaps(virDomainObjPtr vm, + qemuMigrationCookiePtr mig, + qemuMigrationParamsPtr migParams, + unsigned int flags) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + g_autoptr(virJSONValue) mapping = NULL; + g_autoptr(GHashTable) blockNamedNodeData = NULL; + GSList *nextdisk; + + if (!mig->nbd || + !mig->blockDirtyBitmaps || + !(flags & (VIR_MIGRATE_NON_SHARED_DISK | VIR_MIGRATE_NON_SHARED_INC)) || + !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_MIGRATION_PARAM_BLOCK_BITMAP_MAPPING)) + return 0; + + if (qemuMigrationCookieBlockDirtyBitmapsMatchDisks(vm->def, mig->blockDirtyBitmaps) < 0) + return -1; + + if (!(blockNamedNodeData = qemuBlockGetNamedNodeData(vm, QEMU_ASYNC_JOB_MIGRATION_IN))) + return -1; + + for (nextdisk = mig->blockDirtyBitmaps; nextdisk; nextdisk = nextdisk->next) { + qemuMigrationBlockDirtyBitmapsDiskPtr disk = nextdisk->data; + qemuBlockNamedNodeDataPtr nodedata; + GSList *nextbitmap; + + if (!(nodedata = virHashLookup(blockNamedNodeData, disk->nodename))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to find data for block node '%s'"), + disk->nodename); + return -1; + } + + /* Bitmaps can only be migrated to qcow2 v3+ */ + if (disk->disk->src->format != VIR_STORAGE_FILE_QCOW2 || + nodedata->qcow2v2) { + disk->skip = true; + continue; + } + + for (nextbitmap = disk->bitmaps; nextbitmap; nextbitmap = nextbitmap->next) { + qemuMigrationBlockDirtyBitmapsDiskBitmapPtr bitmap = nextbitmap->data; + size_t k; + + /* don't migrate into existing bitmaps */ + for (k = 0; k < nodedata->nbitmaps; k++) { + if (STREQ(bitmap->bitmapname, nodedata->bitmaps[k]->name)) { + bitmap->skip = true; + break; + } + } + + if (bitmap->skip) + continue; + } + } + + if (qemuMigrationCookieBlockDirtyBitmapsToParams(mig->blockDirtyBitmaps, + &mapping) < 0) + return -1; + + if (!mapping) + return 0; + + qemuMigrationParamsSetBlockDirtyBitmapMapping(migParams, &mapping); + mig->flags |= QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS; + return 0; +} + + static int qemuMigrationDstPrepareAny(virQEMUDriverPtr driver, virConnectPtr dconn, @@ -2679,7 +2856,8 @@ qemuMigrationDstPrepareAny(virQEMUDriverPtr driver, QEMU_MIGRATION_COOKIE_CPU_HOTPLUG | QEMU_MIGRATION_COOKIE_CPU | QEMU_MIGRATION_COOKIE_ALLOW_REBOOT | - QEMU_MIGRATION_COOKIE_CAPS))) + QEMU_MIGRATION_COOKIE_CAPS | + QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS))) goto cleanup; if (!(vm = virDomainObjListAdd(driver->domains, *def, @@ -2772,6 +2950,9 @@ qemuMigrationDstPrepareAny(virQEMUDriverPtr driver, goto stopjob; } + if (qemuMigrationDstPrepareAnyBlockDirtyBitmaps(vm, mig, migParams, flags) < 0) + goto stopjob; + if (qemuMigrationParamsCheck(driver, vm, QEMU_ASYNC_JOB_MIGRATION_IN, migParams, mig->caps->automatic) < 0) goto stopjob; @@ -3654,6 +3835,145 @@ qemuMigrationSetDBusVMState(virQEMUDriverPtr driver, } +/** + * qemuMigrationSrcRunPrepareBlockDirtyBitmapsMerge: + * @vm: domain object + * @mig: migration cookie + * + * When migrating full disks, which means that the backing chain of the disk + * will be squashed into a single image we need to calculate bitmaps + * corresponding to the checkpoints which express the same set of changes + * for migration. + * + * This function prepares temporary bitmaps and corresponding merges, updates + * the data so that the temporary bitmaps are used and registers the temporary + * bitmaps for deletion on failed migration. + */ +static int +qemuMigrationSrcRunPrepareBlockDirtyBitmapsMerge(virDomainObjPtr vm, + qemuMigrationCookiePtr mig) +{ + g_autoslist(qemuDomainJobPrivateMigrateTempBitmap) tmpbitmaps = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; + qemuDomainJobPrivatePtr jobPriv = priv->job.privateData; + virQEMUDriverPtr driver = priv->driver; + g_autoptr(virJSONValue) actions = virJSONValueNewArray(); + g_autoptr(GHashTable) blockNamedNodeData = NULL; + GSList *nextdisk; + int rc; + + if (!(blockNamedNodeData = qemuBlockGetNamedNodeData(vm, QEMU_ASYNC_JOB_MIGRATION_OUT))) + return -1; + + for (nextdisk = mig->blockDirtyBitmaps; nextdisk; nextdisk = nextdisk->next) { + qemuMigrationBlockDirtyBitmapsDiskPtr disk = nextdisk->data; + GSList *nextbitmap; + + /* if a disk doesn't have a backing chain we don't need the code below */ + if (!virStorageSourceHasBacking(disk->disk->src)) + continue; + + for (nextbitmap = disk->bitmaps; nextbitmap; nextbitmap = nextbitmap->next) { + qemuMigrationBlockDirtyBitmapsDiskBitmapPtr bitmap = nextbitmap->data; + qemuDomainJobPrivateMigrateTempBitmapPtr tmpbmp; + virStorageSourcePtr n; + unsigned long long granularity = 0; + g_autoptr(virJSONValue) merge = virJSONValueNewArray(); + + for (n = disk->disk->src; virStorageSourceIsBacking(n); n = n->backingStore) { + qemuBlockNamedNodeDataBitmapPtr b; + + if (!(b = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData, n, + bitmap->bitmapname))) + break; + + if (granularity == 0) + granularity = b->granularity; + + if (qemuMonitorTransactionBitmapMergeSourceAddBitmap(merge, + n->nodeformat, + b->name) < 0) + return -1; + } + + bitmap->sourcebitmap = g_strdup_printf("libvirt-migration-%s", bitmap->alias); + bitmap->persistent = VIR_TRISTATE_BOOL_YES; + + if (qemuMonitorTransactionBitmapAdd(actions, + disk->disk->src->nodeformat, + bitmap->sourcebitmap, + false, false, granularity) < 0) + return -1; + + if (qemuMonitorTransactionBitmapMerge(actions, + disk->disk->src->nodeformat, + bitmap->sourcebitmap, + &merge) < 0) + return -1; + + tmpbmp = g_new0(qemuDomainJobPrivateMigrateTempBitmap, 1); + tmpbmp->nodename = g_strdup(disk->disk->src->nodeformat); + tmpbmp->bitmapname = g_strdup(bitmap->sourcebitmap); + tmpbitmaps = g_slist_prepend(tmpbitmaps, tmpbmp); + } + } + + if (qemuDomainObjEnterMonitorAsync(driver, vm, QEMU_ASYNC_JOB_MIGRATION_OUT) < 0) + return -1; + + rc = qemuMonitorTransaction(priv->mon, &actions); + + if (qemuDomainObjExitMonitor(driver, vm) < 0 || rc < 0) + return -1; + + jobPriv->migTempBitmaps = g_steal_pointer(&tmpbitmaps); + + return 0; +} + + +/** + * qemuMigrationSrcRunPrepareBlockDirtyBitmaps: + * @vm: domain object + * @mig: migration cookie + * @migParams: migration parameters + * @flags: migration flags + * + * Configures the source for bitmap migration when the destination asks + * for bitmaps. + */ +static int +qemuMigrationSrcRunPrepareBlockDirtyBitmaps(virDomainObjPtr vm, + qemuMigrationCookiePtr mig, + qemuMigrationParamsPtr migParams, + unsigned int flags) + +{ + g_autoptr(virJSONValue) mapping = NULL; + + if (!mig->blockDirtyBitmaps) + return 0; + + if (qemuMigrationCookieBlockDirtyBitmapsMatchDisks(vm->def, mig->blockDirtyBitmaps) < 0) + return -1; + + /* For QEMU_MONITOR_MIGRATE_NON_SHARED_INC we can migrate the bitmaps + * directly, otherwise we must create merged bitmaps from the whole + * chain */ + + if (!(flags & QEMU_MONITOR_MIGRATE_NON_SHARED_INC) && + qemuMigrationSrcRunPrepareBlockDirtyBitmapsMerge(vm, mig) < 0) + return -1; + + if (qemuMigrationCookieBlockDirtyBitmapsToParams(mig->blockDirtyBitmaps, + &mapping) < 0) + return -1; + + qemuMigrationParamsSetBlockDirtyBitmapMapping(migParams, &mapping); + return 0; +} + + static int qemuMigrationSrcRun(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -3710,6 +4030,10 @@ qemuMigrationSrcRun(virQEMUDriverPtr driver, cookieFlags |= QEMU_MIGRATION_COOKIE_NBD; } + if (cookieFlags & QEMU_MIGRATION_COOKIE_NBD && + virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_MIGRATION_PARAM_BLOCK_BITMAP_MAPPING)) + cookieFlags |= QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS; + if (virLockManagerPluginUsesState(driver->lockManager) && !cookieout) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -3742,13 +4066,18 @@ qemuMigrationSrcRun(virQEMUDriverPtr driver, cookiein, cookieinlen, cookieFlags | QEMU_MIGRATION_COOKIE_GRAPHICS | - QEMU_MIGRATION_COOKIE_CAPS); + QEMU_MIGRATION_COOKIE_CAPS | + QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS); if (!mig) goto error; if (qemuMigrationSrcGraphicsRelocate(driver, vm, mig, graphicsuri) < 0) VIR_WARN("unable to provide data for graphics client relocation"); + if (mig->blockDirtyBitmaps && + qemuMigrationSrcRunPrepareBlockDirtyBitmaps(vm, mig, migParams, flags) < 0) + goto error; + if (qemuMigrationParamsCheck(driver, vm, QEMU_ASYNC_JOB_MIGRATION_OUT, migParams, mig->caps->automatic) < 0) goto error; -- 2.29.2

On Fri, Feb 19, 2021 at 12:58:26 +0100, Peter Krempa wrote:
Preserve block dirty bitmaps after migration with QEMU_MONITOR_MIGRATE_NON_SHARED_(DISK|INC).
This patch implements functions which offer the bitmaps to the destination, check for eligibility on destination and then configure source for the migration.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_migration.c | 333 +++++++++++++++++++++++++++++++++++++- 1 file changed, 331 insertions(+), 2 deletions(-)
Reviewed-by: Jiri Denemark <jdenemar@redhat.com>

For incremental backup we need QEMU_CAPS_BLOCKDEV, QEMU_CAPS_BLOCKDEV_REOPEN, QEMU_CAPS_MIGRATION_PARAM_BLOCK_BITMAP_MAPPING. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_capabilities.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 600952a53a..f40d6d77be 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -5167,8 +5167,10 @@ virQEMUCapsInitQMPVersionCaps(virQEMUCapsPtr qemuCaps) void virQEMUCapsInitProcessCapsInterlock(virQEMUCapsPtr qemuCaps) { - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_BLOCKDEV)) - virQEMUCapsClear(qemuCaps, QEMU_CAPS_INCREMENTAL_BACKUP); + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_BLOCKDEV) && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_BLOCKDEV_REOPEN) && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_MIGRATION_PARAM_BLOCK_BITMAP_MAPPING)) + virQEMUCapsSet(qemuCaps, QEMU_CAPS_INCREMENTAL_BACKUP); if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_STORAGE) && !virQEMUCapsGet(qemuCaps, QEMU_CAPS_USB_STORAGE_WERROR)) { -- 2.29.2
participants (2)
-
Jiri Denemark
-
Peter Krempa