[PATCH 0/7] qemu: Add support for declaring that disk copy target is pre-zeroed by the user
Pre-zeroing allows preserve sparseness on some storage technologies where efficient zeroing is not supported, yet the images are created cleared. The support is added both for virDomainBlockCopy as well as the non-shared storage migration. Peter Krempa (7): qemuMigrationSrcPerformTunnel: Remove 'migrate_disks' argument qemu: capabilities: Introduce QEMU_CAPS_BLOCKDEV_MIRROR_TARGET_IS_ZERO qemu: monitor: Add support for 'target-is-zero' option of 'blockdev-mirror' qemu: Add VIR_DOMAIN_BLOCK_COPY_TARGET_ZEROED flag for virDomainBlockCopy virsh: Add support for 'VIR_DOMAIN_BLOCK_COPY_TARGET_ZEROED' as '--dest-is-zero' qemu: migration: Introduce 'VIR_MIGRATE_PARAM_MIGRATE_DISKS_TARGET_ZERO' virsh: migrate: Add support for VIR_MIGRATE_PARAM_MIGRATE_DISKS_TARGET_ZERO docs/manpages/virsh.rst | 10 +- include/libvirt/libvirt-domain.h | 19 +++ src/libvirt-domain.c | 4 + src/qemu/qemu_capabilities.c | 4 + src/qemu/qemu_capabilities.h | 3 + src/qemu/qemu_driver.c | 38 ++++-- src/qemu/qemu_migration.c | 118 +++++++++++++----- src/qemu/qemu_migration.h | 4 + src/qemu/qemu_monitor.c | 9 +- src/qemu/qemu_monitor.h | 3 +- src/qemu/qemu_monitor_json.c | 4 +- src/qemu/qemu_monitor_json.h | 3 +- .../caps_10.1.0_s390x.xml | 1 + .../caps_10.1.0_x86_64+inteltdx.xml | 1 + .../caps_10.1.0_x86_64.xml | 1 + .../caps_10.2.0_aarch64.xml | 1 + .../caps_10.2.0_x86_64+mshv.xml | 1 + .../caps_10.2.0_x86_64.xml | 1 + .../caps_11.0.0_aarch64.xml | 1 + .../caps_11.0.0_x86_64.xml | 1 + tests/qemumonitorjsontest.c | 2 +- tools/virsh-domain.c | 35 +++++- 22 files changed, 217 insertions(+), 47 deletions(-) -- 2.53.0
From: Peter Krempa <pkrempa@redhat.com> Any QEMU we support requires use of NBD for disk migration which is not supported on tunnelled migration. This is validated in 'qemuMigrationSrcBeginPhase'. Passing the list of disks to migrate is thus pointless. Remove the argument. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_migration.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index fec808ccfb..4d136e259b 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -5428,7 +5428,6 @@ qemuMigrationSrcPerformTunnel(virQEMUDriver *driver, unsigned long bandwidth, virConnectPtr dconn, const char *graphicsuri, - const char **migrate_disks, qemuMigrationParams *migParams) { int ret = -1; @@ -5437,10 +5436,10 @@ qemuMigrationSrcPerformTunnel(virQEMUDriver *driver, VIR_DEBUG("driver=%p, vm=%p, st=%p, cookiein=%s, cookieinlen=%d, " "cookieout=%p, cookieoutlen=%p, flags=0x%x, bandwidth=%lu, " - "graphicsuri=%s, migrate_disks=%p", + "graphicsuri=%s", driver, vm, st, NULLSTR(cookiein), cookieinlen, cookieout, cookieoutlen, flags, bandwidth, - NULLSTR(graphicsuri), migrate_disks); + NULLSTR(graphicsuri)); spec.fwdType = MIGRATION_FWD_STREAM; spec.fwd.stream = st; @@ -5464,11 +5463,11 @@ qemuMigrationSrcPerformTunnel(virQEMUDriver *driver, goto cleanup; } - /* Migration with NBD is not supported with _TUNNELLED, thus - * 'migrate_disks_detect_zeroes' is NULL here */ + /* Migration with NBD is not supported with _TUNNELLED, thus all corresponding + * parameters are NULL here */ ret = qemuMigrationSrcRun(driver, vm, persist_xml, cookiein, cookieinlen, cookieout, cookieoutlen, flags, bandwidth, &spec, - dconn, graphicsuri, migrate_disks, NULL, + dconn, graphicsuri, NULL, NULL, migParams, NULL); cleanup: @@ -5610,7 +5609,7 @@ qemuMigrationSrcPerformPeer2Peer2(virQEMUDriver *driver, ret = qemuMigrationSrcPerformTunnel(driver, vm, st, NULL, NULL, 0, NULL, NULL, flags, bandwidth, dconn, - NULL, NULL, migParams); + NULL, migParams); else ret = qemuMigrationSrcPerformNative(driver, vm, NULL, uri_out, cookie, cookielen, @@ -5886,7 +5885,6 @@ qemuMigrationSrcPerformPeer2Peer3(virQEMUDriver *driver, cookiein, cookieinlen, &cookieout, &cookieoutlen, flags, bandwidth, dconn, graphicsuri, - migrate_disks, migParams); } else { ret = qemuMigrationSrcPerformNative(driver, vm, persist_xml, uri, -- 2.53.0
On a Wednesday in 2026, Peter Krempa via Devel wrote:
From: Peter Krempa <pkrempa@redhat.com>
Any QEMU we support requires use of NBD for disk migration which is not supported on tunnelled migration. This is validated in
s/on/with/ ?
'qemuMigrationSrcBeginPhase'. Passing the list of disks to migrate is thus pointless. Remove the argument.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_migration.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano
From: Peter Krempa <pkrempa@redhat.com> The 'target-is-zero' option of 'blockdev-mirror' allows telling qemu to skip zeroing the mirror target if the user certifies that it's empty. It was introduced in qemu-10.1. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_capabilities.c | 4 ++++ src/qemu/qemu_capabilities.h | 3 +++ tests/qemucapabilitiesdata/caps_10.1.0_s390x.xml | 1 + tests/qemucapabilitiesdata/caps_10.1.0_x86_64+inteltdx.xml | 1 + tests/qemucapabilitiesdata/caps_10.1.0_x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_10.2.0_aarch64.xml | 1 + tests/qemucapabilitiesdata/caps_10.2.0_x86_64+mshv.xml | 1 + tests/qemucapabilitiesdata/caps_10.2.0_x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_11.0.0_aarch64.xml | 1 + tests/qemucapabilitiesdata/caps_11.0.0_x86_64.xml | 1 + 10 files changed, 15 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 65d8aac4fb..2911982678 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -764,6 +764,9 @@ VIR_ENUM_IMPL(virQEMUCaps, "uefi-vars", /* QEMU_CAPS_DEVICE_UEFI_VARS */ "query-block-flat", /* QEMU_CAPS_QUERY_BLOCK_FLAT */ "amd-iommu.xtsup", /* QEMU_CAPS_AMD_IOMMU_XTSUP */ + + /* 495 */ + "blockdev-mirror.target-is-zero", /* QEMU_CAPS_BLOCKDEV_MIRROR_TARGET_IS_ZERO */ ); @@ -1648,6 +1651,7 @@ static struct virQEMUCapsStringFlags virQEMUCapsQMPSchemaQueries[] = { { "blockdev-add/arg-type/+nbd/tls-hostname", QEMU_CAPS_BLOCKDEV_NBD_TLS_HOSTNAME }, { "blockdev-add/arg-type/+qcow2/discard-no-unref", QEMU_CAPS_QCOW2_DISCARD_NO_UNREF }, { "blockdev-add/arg-type/+virtio-blk-vhost-vdpa/$fdset", QEMU_CAPS_DEVICE_VIRTIO_BLK_VHOST_VDPA}, + { "blockdev-mirror/arg-type/target-is-zero", QEMU_CAPS_BLOCKDEV_MIRROR_TARGET_IS_ZERO }, { "calc-dirty-rate/arg-type/mode", QEMU_CAPS_DIRTYRATE_MODE }, { "chardev-add/arg-type/backend/+socket/data/reconnect-ms", QEMU_CAPS_CHARDEV_RECONNECT_MILISECONDS }, { "chardev-add/arg-type/backend/+qemu-vdagent", QEMU_CAPS_CHARDEV_QEMU_VDAGENT }, diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index b93d1532ff..c26e06104d 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -739,6 +739,9 @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ QEMU_CAPS_QUERY_BLOCK_FLAT, /* 'query-block' command supports 'flat' */ QEMU_CAPS_AMD_IOMMU_XTSUP, /* amd-iommu.xtsup */ + /* 495 */ + QEMU_CAPS_BLOCKDEV_MIRROR_TARGET_IS_ZERO, /* 'blockdev-mirror' supports 'target-is-zero' */ + QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; diff --git a/tests/qemucapabilitiesdata/caps_10.1.0_s390x.xml b/tests/qemucapabilitiesdata/caps_10.1.0_s390x.xml index f33b366db8..584a1b304c 100644 --- a/tests/qemucapabilitiesdata/caps_10.1.0_s390x.xml +++ b/tests/qemucapabilitiesdata/caps_10.1.0_s390x.xml @@ -143,6 +143,7 @@ <flag name='virtio-iommu.aw-bits'/> <flag name='iommufd'/> <flag name='uefi-vars'/> + <flag name='blockdev-mirror.target-is-zero'/> <version>10001000</version> <microcodeVersion>39100286</microcodeVersion> <package>v10.1.0</package> diff --git a/tests/qemucapabilitiesdata/caps_10.1.0_x86_64+inteltdx.xml b/tests/qemucapabilitiesdata/caps_10.1.0_x86_64+inteltdx.xml index 05de206987..6dd00c360f 100644 --- a/tests/qemucapabilitiesdata/caps_10.1.0_x86_64+inteltdx.xml +++ b/tests/qemucapabilitiesdata/caps_10.1.0_x86_64+inteltdx.xml @@ -196,6 +196,7 @@ <flag name='iommufd'/> <flag name='uefi-vars'/> <flag name='amd-iommu.xtsup'/> + <flag name='blockdev-mirror.target-is-zero'/> <version>10001000</version> <microcodeVersion>43100286</microcodeVersion> <package>v10.1.0</package> diff --git a/tests/qemucapabilitiesdata/caps_10.1.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_10.1.0_x86_64.xml index 5a2bc0d66f..e7a0818697 100644 --- a/tests/qemucapabilitiesdata/caps_10.1.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_10.1.0_x86_64.xml @@ -218,6 +218,7 @@ <flag name='iommufd'/> <flag name='uefi-vars'/> <flag name='amd-iommu.xtsup'/> + <flag name='blockdev-mirror.target-is-zero'/> <version>10001000</version> <microcodeVersion>43100286</microcodeVersion> <package>v10.1.0</package> diff --git a/tests/qemucapabilitiesdata/caps_10.2.0_aarch64.xml b/tests/qemucapabilitiesdata/caps_10.2.0_aarch64.xml index ac8f50b984..df3e2a8275 100644 --- a/tests/qemucapabilitiesdata/caps_10.2.0_aarch64.xml +++ b/tests/qemucapabilitiesdata/caps_10.2.0_aarch64.xml @@ -185,6 +185,7 @@ <flag name='virtio-iommu.aw-bits'/> <flag name='iommufd'/> <flag name='uefi-vars'/> + <flag name='blockdev-mirror.target-is-zero'/> <version>10002000</version> <microcodeVersion>61700287</microcodeVersion> <package>v10.2.0</package> diff --git a/tests/qemucapabilitiesdata/caps_10.2.0_x86_64+mshv.xml b/tests/qemucapabilitiesdata/caps_10.2.0_x86_64+mshv.xml index 05a28f6ddc..54018639ad 100644 --- a/tests/qemucapabilitiesdata/caps_10.2.0_x86_64+mshv.xml +++ b/tests/qemucapabilitiesdata/caps_10.2.0_x86_64+mshv.xml @@ -206,6 +206,7 @@ <flag name='iommufd'/> <flag name='uefi-vars'/> <flag name='amd-iommu.xtsup'/> + <flag name='blockdev-mirror.target-is-zero'/> <version>10002000</version> <microcodeVersion>43100287</microcodeVersion> <package>v10.2.0</package> diff --git a/tests/qemucapabilitiesdata/caps_10.2.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_10.2.0_x86_64.xml index 29ca620345..d5cc279e13 100644 --- a/tests/qemucapabilitiesdata/caps_10.2.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_10.2.0_x86_64.xml @@ -219,6 +219,7 @@ <flag name='iommufd'/> <flag name='uefi-vars'/> <flag name='amd-iommu.xtsup'/> + <flag name='blockdev-mirror.target-is-zero'/> <version>10002000</version> <microcodeVersion>43100287</microcodeVersion> <package>v10.2.0</package> diff --git a/tests/qemucapabilitiesdata/caps_11.0.0_aarch64.xml b/tests/qemucapabilitiesdata/caps_11.0.0_aarch64.xml index bca13980ce..28cd421f12 100644 --- a/tests/qemucapabilitiesdata/caps_11.0.0_aarch64.xml +++ b/tests/qemucapabilitiesdata/caps_11.0.0_aarch64.xml @@ -187,6 +187,7 @@ <flag name='iommufd'/> <flag name='uefi-vars'/> <flag name='query-block-flat'/> + <flag name='blockdev-mirror.target-is-zero'/> <version>10002050</version> <microcodeVersion>61700286</microcodeVersion> <package>v10.2.0-2062-g084a6c6e73</package> diff --git a/tests/qemucapabilitiesdata/caps_11.0.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_11.0.0_x86_64.xml index 2e20a6f3b2..d8c39368d2 100644 --- a/tests/qemucapabilitiesdata/caps_11.0.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_11.0.0_x86_64.xml @@ -221,6 +221,7 @@ <flag name='uefi-vars'/> <flag name='query-block-flat'/> <flag name='amd-iommu.xtsup'/> + <flag name='blockdev-mirror.target-is-zero'/> <version>10002050</version> <microcodeVersion>43100286</microcodeVersion> <package>v10.2.0-2062-g084a6c6e73</package> -- 2.53.0
From: Peter Krempa <pkrempa@redhat.com> Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_driver.c | 2 +- src/qemu/qemu_migration.c | 2 +- src/qemu/qemu_monitor.c | 9 +++++---- src/qemu/qemu_monitor.h | 3 ++- src/qemu/qemu_monitor_json.c | 4 +++- src/qemu/qemu_monitor_json.h | 3 ++- tests/qemumonitorjsontest.c | 2 +- 7 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 8b148b33b4..0ba40e0665 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -14535,7 +14535,7 @@ qemuDomainBlockCopyCommon(virDomainObj *vm, qemuBlockStorageSourceGetEffectiveNodename(disk->src), bandwidth, granularity, buf_size, mirror_shallow, - syncWrites); + syncWrites, false); virDomainAuditDisk(vm, NULL, mirror, "mirror", ret >= 0); qemuDomainObjExitMonitor(vm); diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 4d136e259b..bb62b1d48c 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -1173,7 +1173,7 @@ qemuMigrationSrcNBDStorageCopyBlockdev(virDomainObj *vm, qemuBlockStorageSourceGetEffectiveNodename(copysrc), NULL, mirror_speed, 0, 0, mirror_shallow, - syncWrites); + syncWrites, false); if (mon_ret != 0) qemuBlockStorageSourceAttachRollback(qemuDomainGetMonitor(vm), data); diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index b94aaf741b..ef83e7b69a 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2845,18 +2845,19 @@ qemuMonitorBlockdevMirror(qemuMonitor *mon, unsigned int granularity, unsigned long long buf_size, bool shallow, - bool syncWrite) + bool syncWrite, + bool targetIsZero) { VIR_DEBUG("jobname=%s, persistjob=%d, device=%s, target=%s, replaces=%s, bandwidth=%lld, " - "granularity=%#x, buf_size=%lld, shallow=%d syncWrite=%d", + "granularity=%#x, buf_size=%lld, shallow=%d syncWrite=%d targetIsZero=%d", NULLSTR(jobname), persistjob, device, target, NULLSTR(replaces), - bandwidth, granularity, buf_size, shallow, syncWrite); + bandwidth, granularity, buf_size, shallow, syncWrite, targetIsZero); QEMU_CHECK_MONITOR(mon); return qemuMonitorJSONBlockdevMirror(mon, jobname, persistjob, device, target, replaces, bandwidth, granularity, buf_size, shallow, - syncWrite); + syncWrite, targetIsZero); } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index d3611d9713..dfa25fc7ba 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1245,7 +1245,8 @@ qemuMonitorBlockdevMirror(qemuMonitor *mon, unsigned int granularity, unsigned long long buf_size, bool shallow, - bool syncWrite) + bool syncWrite, + bool targetIsZero) ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(5); int diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index f9b0470ed4..532bb885a3 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -4301,7 +4301,8 @@ qemuMonitorJSONBlockdevMirror(qemuMonitor *mon, unsigned int granularity, unsigned long long buf_size, bool shallow, - bool syncWrite) + bool syncWrite, + bool targetIsZero) { g_autoptr(virJSONValue) cmd = NULL; g_autoptr(virJSONValue) reply = NULL; @@ -4333,6 +4334,7 @@ qemuMonitorJSONBlockdevMirror(qemuMonitor *mon, "S:copy-mode", copymode, "T:auto-finalize", autofinalize, "T:auto-dismiss", autodismiss, + "B:target-is-zero", targetIsZero, NULL); if (!cmd) return -1; diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index cf9e341fe3..5034c8d23d 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -273,7 +273,8 @@ qemuMonitorJSONBlockdevMirror(qemuMonitor *mon, unsigned int granularity, unsigned long long buf_size, bool shallow, - bool syncWrite) + bool syncWrite, + bool targetIsZero) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(5); int diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index 8911895c7e..e34dbad7cd 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -1107,7 +1107,7 @@ GEN_TEST_FUNC(qemuMonitorJSONGraphicsRelocate, VIR_DOMAIN_GRAPHICS_TYPE_SPICE, "localhost", 12345, 12346, "certsubjectval") GEN_TEST_FUNC(qemuMonitorJSONRemoveNetdev, "net0") GEN_TEST_FUNC(qemuMonitorJSONDelDevice, "ide0") -GEN_TEST_FUNC(qemuMonitorJSONBlockdevMirror, "jobname", true, "vdb", "targetnode", "replacenode", 1024, 1234, 31234, true, true) +GEN_TEST_FUNC(qemuMonitorJSONBlockdevMirror, "jobname", true, "vdb", "targetnode", "replacenode", 1024, 1234, 31234, true, true, true) GEN_TEST_FUNC(qemuMonitorJSONBlockStream, "vdb", "jobname", "backingnode", "backingfilename", 1024) GEN_TEST_FUNC(qemuMonitorJSONBlockCommit, "vdb", "jobname", "topnode", "basenode", "backingfilename", 1024, VIR_TRISTATE_BOOL_YES) GEN_TEST_FUNC(qemuMonitorJSONScreendump, "devicename", 1, NULL, "/foo/bar") -- 2.53.0
From: Peter Krempa <pkrempa@redhat.com> Allow the hypervisor to assume that the user already passed a zeroed-out image to optimize the copy. Implement the feature for the qemu driver. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- include/libvirt/libvirt-domain.h | 7 +++++++ src/libvirt-domain.c | 4 ++++ src/qemu/qemu_driver.c | 16 +++++++++++++--- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index ccc2c87ea8..a0e0b84eb9 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -5004,6 +5004,13 @@ typedef enum { * Since: 8.0.0 */ VIR_DOMAIN_BLOCK_COPY_SYNCHRONOUS_WRITES = 1 << 3, + + /* Destination of the copy is zeroed (any read returns only 0x00 bytes) so + * the hypervisor may optimize out clearing of the target image. + * + * Since: 12.2.0 */ + VIR_DOMAIN_BLOCK_COPY_TARGET_ZEROED = 1 << 4, + } virDomainBlockCopyFlags; /** diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 6b8783d57f..e1571cbda7 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -11261,6 +11261,10 @@ virDomainBlockRebase(virDomainPtr dom, const char *disk, * the destination storage is slower. This may impact performance of writes * while the blockjob is running. * + * If @flags contains VIR_DOMAIN_BLOCK_COPY_TARGET_ZEROED the hypervisor may + * assume that the target image was already zeroed out (any read will return + * 0x00 bytes) and thus may skip this step. + * * The @disk parameter is either an unambiguous source name of the * block device (the <source file='...'/> sub-element, such as * "/path/to/image"), or the device target shorthand (the diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0ba40e0665..42d0f46405 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -14285,13 +14285,15 @@ qemuDomainBlockCopyCommon(virDomainObj *vm, virStorageSource *mirrorBacking = NULL; g_autoptr(GHashTable) blockNamedNodeData = NULL; bool syncWrites = !!(flags & VIR_DOMAIN_BLOCK_COPY_SYNCHRONOUS_WRITES); + bool targetIsZero = !!(flags & VIR_DOMAIN_BLOCK_COPY_TARGET_ZEROED); int rc = 0; /* Preliminaries: find the disk we are editing, sanity checks */ virCheckFlags(VIR_DOMAIN_BLOCK_COPY_SHALLOW | VIR_DOMAIN_BLOCK_COPY_REUSE_EXT | VIR_DOMAIN_BLOCK_COPY_TRANSIENT_JOB | - VIR_DOMAIN_BLOCK_COPY_SYNCHRONOUS_WRITES, -1); + VIR_DOMAIN_BLOCK_COPY_SYNCHRONOUS_WRITES | + VIR_DOMAIN_BLOCK_COPY_TARGET_ZEROED, -1); if (virStorageSourceIsRelative(mirror)) { virReportError(VIR_ERR_INVALID_ARG, "%s", @@ -14327,6 +14329,13 @@ qemuDomainBlockCopyCommon(virDomainObj *vm, goto endjob; } + if (targetIsZero && + !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV_MIRROR_TARGET_IS_ZERO)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("this qemu doesn't support 'VIR_DOMAIN_BLOCK_COPY_TARGET_ZEROED'")); + goto endjob; + } + if (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN && virDomainDiskDefSourceLUNValidate(mirror) < 0) goto endjob; @@ -14535,7 +14544,7 @@ qemuDomainBlockCopyCommon(virDomainObj *vm, qemuBlockStorageSourceGetEffectiveNodename(disk->src), bandwidth, granularity, buf_size, mirror_shallow, - syncWrites, false); + syncWrites, targetIsZero); virDomainAuditDisk(vm, NULL, mirror, "mirror", ret >= 0); qemuDomainObjExitMonitor(vm); @@ -14670,7 +14679,8 @@ qemuDomainBlockCopy(virDomainPtr dom, virCheckFlags(VIR_DOMAIN_BLOCK_COPY_SHALLOW | VIR_DOMAIN_BLOCK_COPY_REUSE_EXT | VIR_DOMAIN_BLOCK_COPY_TRANSIENT_JOB | - VIR_DOMAIN_BLOCK_COPY_SYNCHRONOUS_WRITES, -1); + VIR_DOMAIN_BLOCK_COPY_SYNCHRONOUS_WRITES | + VIR_DOMAIN_BLOCK_COPY_TARGET_ZEROED, -1); if (virTypedParamsValidate(params, nparams, VIR_DOMAIN_BLOCK_COPY_BANDWIDTH, -- 2.53.0
From: Peter Krempa <pkrempa@redhat.com> Add the aforementioned flag for 'virsh blockcopy'. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- docs/manpages/virsh.rst | 6 +++++- tools/virsh-domain.c | 9 ++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index 591c47a7ce..da9e9f8658 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -1472,7 +1472,7 @@ blockcopy [--shallow] [--reuse-external] [bandwidth] [--wait [--async] [--verbose]] [{--pivot | --finish}] [--timeout seconds] [granularity] [buf-size] [--bytes] - [--transient-job] [--synchronous-writes] [--print-xml] + [--transient-job] [--synchronous-writes] [--dest-is-zero] [--print-xml] Copy a disk backing image chain to a destination. Either *dest* as the destination file name, or *--xml* with the name of an XML file containing @@ -1537,6 +1537,10 @@ to be propagated both to the original image and to the destination of the copy so that it's guaranteed that the job converges if the destination storage is slower. This may impact performance of writes while the blockjob is running. +If *--dest-is-zero* is specified the hypervisor may assume that the target +image was already cleared (any offset reads 0x00 bytes) and thus may skip +clearing it. + If *--print-xml* is specified, then the XML used to start the block copy job is printed instead of starting the job. diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 5a2ae75379..1af7f9eb0e 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -2714,6 +2714,10 @@ static const vshCmdOptDef opts_blockcopy[] = { .type = VSH_OT_BOOL, .help = N_("print the XML used to start the copy job instead of starting the job") }, + {.name = "dest-is-zero", + .type = VSH_OT_BOOL, + .help = N_("the destination image is already zeroed; hypervisor may skip pre-zeroing") + }, {.name = NULL} }; @@ -2737,6 +2741,7 @@ cmdBlockcopy(vshControl *ctl, const vshCmd *cmd) bool bytes = vshCommandOptBool(cmd, "bytes"); bool transientjob = vshCommandOptBool(cmd, "transient-job"); bool syncWrites = vshCommandOptBool(cmd, "synchronous-writes"); + bool destIsZero = vshCommandOptBool(cmd, "dest-is-zero"); int timeout = 0; const char *path = NULL; int abort_flags = 0; @@ -2773,6 +2778,8 @@ cmdBlockcopy(vshControl *ctl, const vshCmd *cmd) flags |= VIR_DOMAIN_BLOCK_COPY_SYNCHRONOUS_WRITES; if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) return false; + if (destIsZero) + flags |= VIR_DOMAIN_BLOCK_COPY_TARGET_ZEROED; if (timeout) blocking = true; @@ -2818,7 +2825,7 @@ cmdBlockcopy(vshControl *ctl, const vshCmd *cmd) } if (granularity || buf_size || (format && STRNEQ(format, "raw")) || xml || - transientjob || syncWrites || print_xml) { + transientjob || syncWrites || destIsZero || print_xml) { /* New API */ if (bandwidth || granularity || buf_size) { params = g_new0(virTypedParameter, 3); -- 2.53.0
From: Peter Krempa <pkrempa@redhat.com> The migration parameter allows enumerating disks selected for migration where the hypervisor may assume that the user pre-cleared the destination images of the block copy so that all offsets read 0x00 and thus optimize clearing of such targets. This patch adds the 'VIR_MIGRATE_PARAM_MIGRATE_DISKS_TARGET_ZERO' parameter and also plumbs it for the qemu driver (mirrors plumbing for 'migrate_disks_detect_zeroes'). Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- include/libvirt/libvirt-domain.h | 12 ++++ src/qemu/qemu_driver.c | 22 +++++-- src/qemu/qemu_migration.c | 106 ++++++++++++++++++++++++------- src/qemu/qemu_migration.h | 4 ++ 4 files changed, 117 insertions(+), 27 deletions(-) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index a0e0b84eb9..09a797cab6 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -1285,6 +1285,18 @@ typedef enum { */ # define VIR_MIGRATE_PARAM_MIGRATE_DISKS_DETECT_ZEROES "migrate_disks_detect_zeroes" +/** + * VIR_MIGRATE_PARAM_MIGRATE_DISKS_TARGET_ZERO: + * + * virDomainMigrate* params multiple field: The multiple values that list + * the block devices for which the hypervisor is allowed to assume that the + * destination image was zeroed out and thus may skip zeroing it beforehand. + * At the moment this is only supported by the QEMU driver. + * + * Since: 12.2.0 + */ +# define VIR_MIGRATE_PARAM_MIGRATE_DISKS_TARGET_ZERO "migrate_disks_target_zero" + /** * VIR_MIGRATE_PARAM_DISKS_PORT: * diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 42d0f46405..6d333275b1 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10913,7 +10913,7 @@ qemuDomainMigratePerform(virDomainPtr dom, * Consume any cookie we were able to decode though */ ret = qemuMigrationSrcPerform(driver, dom->conn, vm, NULL, - NULL, dconnuri, uri, NULL, NULL, NULL, NULL, 0, + NULL, dconnuri, uri, NULL, NULL, NULL, NULL, NULL, 0, NULL, migParams, cookie, cookielen, NULL, NULL, /* No output cookies in v2 */ @@ -10989,7 +10989,7 @@ qemuDomainMigrateBegin3(virDomainPtr domain, } return qemuMigrationSrcBegin(domain->conn, vm, xmlin, dname, - cookieout, cookieoutlen, NULL, NULL, flags); + cookieout, cookieoutlen, NULL, NULL, NULL, flags); } static char * @@ -11004,6 +11004,7 @@ qemuDomainMigrateBegin3Params(virDomainPtr domain, const char *dname = NULL; g_autofree const char **migrate_disks = NULL; g_autofree const char **migrate_disks_detect_zeroes = NULL; + g_autofree const char **migrate_disks_target_zero = NULL; virDomainObj *vm; virCheckFlags(QEMU_MIGRATION_FLAGS, NULL); @@ -11025,6 +11026,10 @@ qemuDomainMigrateBegin3Params(virDomainPtr domain, VIR_MIGRATE_PARAM_MIGRATE_DISKS_DETECT_ZEROES, &migrate_disks_detect_zeroes); + virTypedParamsGetStringList(params, nparams, + VIR_MIGRATE_PARAM_MIGRATE_DISKS_TARGET_ZERO, + &migrate_disks_target_zero); + if (!(vm = qemuDomainObjFromDomain(domain))) return NULL; @@ -11036,6 +11041,7 @@ qemuDomainMigrateBegin3Params(virDomainPtr domain, return qemuMigrationSrcBegin(domain->conn, vm, xmlin, dname, cookieout, cookieoutlen, migrate_disks, migrate_disks_detect_zeroes, + migrate_disks_target_zero, flags); } @@ -11313,7 +11319,7 @@ qemuDomainMigratePerform3(virDomainPtr dom, goto cleanup; ret = qemuMigrationSrcPerform(driver, dom->conn, vm, xmlin, NULL, - dconnuri, uri, NULL, NULL, NULL, NULL, 0, + dconnuri, uri, NULL, NULL, NULL, NULL, NULL, 0, NULL, migParams, cookiein, cookieinlen, cookieout, cookieoutlen, @@ -11345,6 +11351,7 @@ qemuDomainMigratePerform3Params(virDomainPtr dom, const char *listenAddress = NULL; g_autofree const char **migrate_disks = NULL; g_autofree const char **migrate_disks_detect_zeroes = NULL; + g_autofree const char **migrate_disks_target_zero = NULL; unsigned long long bandwidth = 0; int nbdPort = 0; g_autoptr(qemuMigrationParams) migParams = NULL; @@ -11404,6 +11411,9 @@ qemuDomainMigratePerform3Params(virDomainPtr dom, virTypedParamsGetStringList(params, nparams, VIR_MIGRATE_PARAM_MIGRATE_DISKS_DETECT_ZEROES, &migrate_disks_detect_zeroes); + virTypedParamsGetStringList(params, nparams, + VIR_MIGRATE_PARAM_MIGRATE_DISKS_TARGET_ZERO, + &migrate_disks_target_zero); if (flags & (VIR_MIGRATE_NON_SHARED_DISK | VIR_MIGRATE_NON_SHARED_INC) || migrate_disks) { @@ -11426,8 +11436,10 @@ qemuDomainMigratePerform3Params(virDomainPtr dom, ret = qemuMigrationSrcPerform(driver, dom->conn, vm, dom_xml, persist_xml, dconnuri, uri, graphicsuri, listenAddress, - migrate_disks, migrate_disks_detect_zeroes, nbdPort, - nbdURI, migParams, + migrate_disks, + migrate_disks_detect_zeroes, + migrate_disks_target_zero, + nbdPort, nbdURI, migParams, cookiein, cookieinlen, cookieout, cookieoutlen, flags, dname, bandwidth, true); cleanup: diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index bb62b1d48c..09985779df 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -1140,7 +1140,8 @@ qemuMigrationSrcNBDStorageCopyBlockdev(virDomainObj *vm, const char *tlsAlias, const char *tlsHostname, bool syncWrites, - bool detect_zeroes) + bool detect_zeroes, + bool target_zeroed) { g_autoptr(qemuBlockStorageSourceAttachData) data = NULL; qemuDomainDiskPrivate *diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); @@ -1173,7 +1174,7 @@ qemuMigrationSrcNBDStorageCopyBlockdev(virDomainObj *vm, qemuBlockStorageSourceGetEffectiveNodename(copysrc), NULL, mirror_speed, 0, 0, mirror_shallow, - syncWrites, false); + syncWrites, target_zeroed); if (mon_ret != 0) qemuBlockStorageSourceAttachRollback(qemuDomainGetMonitor(vm), data); @@ -1199,6 +1200,7 @@ qemuMigrationSrcNBDStorageCopyOne(virDomainObj *vm, const char *tlsAlias, const char *tlsHostname, bool detect_zeroes, + bool target_zeroed, unsigned int flags) { qemuDomainDiskPrivate *diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); @@ -1224,7 +1226,8 @@ qemuMigrationSrcNBDStorageCopyOne(virDomainObj *vm, tlsAlias, tlsHostname, syncWrites, - detect_zeroes); + detect_zeroes, + target_zeroed); if (rc == 0) { diskPriv->migrating = true; @@ -1261,6 +1264,7 @@ qemuMigrationSrcNBDStorageCopy(virQEMUDriver *driver, unsigned long speed, const char **migrate_disks, const char **migrate_disks_detect_zeroes, + const char **migrate_disks_target_zero, virConnectPtr dconn, const char *tlsAlias, const char *tlsHostname, @@ -1333,6 +1337,7 @@ qemuMigrationSrcNBDStorageCopy(virQEMUDriver *driver, for (i = 0; i < vm->def->ndisks; i++) { virDomainDiskDef *disk = vm->def->disks[i]; bool detect_zeroes = false; + bool target_zero = false; /* check whether disk should be migrated */ if (!qemuMigrationAnyCopyDisk(disk, migrate_disks)) @@ -1341,11 +1346,14 @@ qemuMigrationSrcNBDStorageCopy(virQEMUDriver *driver, if (migrate_disks_detect_zeroes) detect_zeroes = g_strv_contains(migrate_disks_detect_zeroes, disk->dst); + if (migrate_disks_target_zero) + target_zero = g_strv_contains(migrate_disks_target_zero, disk->dst); + if (qemuMigrationSrcNBDStorageCopyOne(vm, disk, host, port, socket, mirror_speed, mirror_shallow, tlsAlias, tlsHostname, detect_zeroes, - flags) < 0) + target_zero, flags) < 0) return -1; if (virDomainObjSave(vm, driver->xmlopt, cfg->stateDir) < 0) { @@ -2760,6 +2768,7 @@ qemuMigrationSrcBeginPhase(virQEMUDriver *driver, int *cookieoutlen, const char **migrate_disks, const char **migrate_disks_detect_zeroes, + const char **migrate_disks_target_zero, unsigned int flags) { qemuDomainObjPrivate *priv = vm->privateData; @@ -2767,10 +2776,13 @@ qemuMigrationSrcBeginPhase(virQEMUDriver *driver, VIR_DEBUG("driver=%p, vm=%p, xmlin=%s, dname=%s," " cookieout=%p, cookieoutlen=%p," - " migrate_disks=%p, migrate_disks_detect_zeroes=%p, flags=0x%x", + " migrate_disks=%p, migrate_disks_detect_zeroes=%p," + " migrate_disks_target_zero=%p, flags=0x%x", driver, vm, NULLSTR(xmlin), NULLSTR(dname), cookieout, cookieoutlen, - migrate_disks, migrate_disks_detect_zeroes, flags); + migrate_disks, migrate_disks_detect_zeroes, + migrate_disks_target_zero, + flags); /* Only set the phase if we are inside VIR_ASYNC_JOB_MIGRATION_OUT. * Otherwise we will start the async job later in the perform phase losing @@ -2841,6 +2853,17 @@ qemuMigrationSrcBeginPhase(virQEMUDriver *driver, qemuMigrationSrcBeginPhaseValidateDiskTargetList(vm, migrate_disks_detect_zeroes) < 0) return NULL; + if (migrate_disks_target_zero) { + if (qemuMigrationSrcBeginPhaseValidateDiskTargetList(vm, migrate_disks_target_zero) < 0) + return NULL; + + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV_MIRROR_TARGET_IS_ZERO)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("use of 'VIR_MIGRATE_PARAM_MIGRATE_DISKS_TARGET_ZERO' is not supported by this QEMU")); + return NULL; + } + } + priv->nbdPort = 0; if (qemuMigrationHasAnyStorageMigrationDisks(vm->def, migrate_disks)) @@ -2858,6 +2881,11 @@ qemuMigrationSrcBeginPhase(virQEMUDriver *driver, return NULL; } + if (migrate_disks_target_zero) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("use of 'VIR_MIGRATE_PARAM_MIGRATE_DISKS_TARGET_ZERO' requires use of 'VIR_MIGRATE_NON_SHARED_DISK' or 'VIR_MIGRATE_NON_SHARED_INC' flag")); + return NULL; + } } if (virDomainDefHasMemoryHotplug(vm->def) || @@ -3010,6 +3038,7 @@ qemuMigrationSrcBegin(virConnectPtr conn, int *cookieoutlen, const char **migrate_disks, const char **migrate_disks_detect_zeroes, + const char **migrate_disks_target_zero, unsigned int flags) { virQEMUDriver *driver = conn->privateData; @@ -3051,6 +3080,7 @@ qemuMigrationSrcBegin(virConnectPtr conn, cookieout, cookieoutlen, migrate_disks, migrate_disks_detect_zeroes, + migrate_disks_target_zero, flags))) goto endjob; @@ -4909,6 +4939,7 @@ qemuMigrationSrcRun(virQEMUDriver *driver, const char *graphicsuri, const char **migrate_disks, const char **migrate_disks_detect_zeroes, + const char **migrate_disks_target_zero, qemuMigrationParams *migParams, const char *nbdURI) { @@ -4934,11 +4965,13 @@ qemuMigrationSrcRun(virQEMUDriver *driver, VIR_DEBUG("driver=%p, vm=%p, cookiein=%s, cookieinlen=%d, " "cookieout=%p, cookieoutlen=%p, flags=0x%x, bandwidth=%lu, " "spec=%p (dest=%d, fwd=%d), dconn=%p, graphicsuri=%s, " - "migrate_disks=%p, migrate_disks_detect_zeroes=%p", + "migrate_disks=%p, migrate_disks_detect_zeroes=%p" + "migrate_disks_target_zero=%p", driver, vm, NULLSTR(cookiein), cookieinlen, cookieout, cookieoutlen, flags, bandwidth, spec, spec->destType, spec->fwdType, dconn, - NULLSTR(graphicsuri), migrate_disks, migrate_disks_detect_zeroes); + NULLSTR(graphicsuri), migrate_disks, migrate_disks_detect_zeroes, + migrate_disks_target_zero); if (storageMigration) storageMigration = qemuMigrationHasAnyStorageMigrationDisks(vm->def, @@ -5063,6 +5096,7 @@ qemuMigrationSrcRun(virQEMUDriver *driver, priv->migMaxBandwidth, migrate_disks, migrate_disks_detect_zeroes, + migrate_disks_target_zero, dconn, tlsAlias, tlsHostname, nbdURI, flags) < 0) { goto error; @@ -5331,6 +5365,7 @@ qemuMigrationSrcPerformNative(virQEMUDriver *driver, const char *graphicsuri, const char **migrate_disks, const char **migrate_disks_detect_zeroes, + const char **migrate_disks_target_zero, qemuMigrationParams *migParams, const char *nbdURI) { @@ -5340,10 +5375,12 @@ qemuMigrationSrcPerformNative(virQEMUDriver *driver, VIR_DEBUG("driver=%p, vm=%p, uri=%s, cookiein=%s, cookieinlen=%d, " "cookieout=%p, cookieoutlen=%p, flags=0x%x, bandwidth=%lu, " - "graphicsuri=%s, migrate_disks=%p, migrate_disks_detect_zeroes=%p", + "graphicsuri=%s, migrate_disks=%p, migrate_disks_detect_zeroes=%p " + "migrate_disks_target_zero=%p", driver, vm, uri, NULLSTR(cookiein), cookieinlen, cookieout, cookieoutlen, flags, bandwidth, - NULLSTR(graphicsuri), migrate_disks, migrate_disks_detect_zeroes); + NULLSTR(graphicsuri), migrate_disks, migrate_disks_detect_zeroes, + migrate_disks_target_zero); if (!(uribits = qemuMigrationAnyParseURI(uri, NULL))) return -1; @@ -5405,6 +5442,7 @@ qemuMigrationSrcPerformNative(virQEMUDriver *driver, cookieout, cookieoutlen, flags, bandwidth, &spec, dconn, graphicsuri, migrate_disks, migrate_disks_detect_zeroes, + migrate_disks_target_zero, migParams, nbdURI); } @@ -5467,7 +5505,7 @@ qemuMigrationSrcPerformTunnel(virQEMUDriver *driver, * parameters are NULL here */ ret = qemuMigrationSrcRun(driver, vm, persist_xml, cookiein, cookieinlen, cookieout, cookieoutlen, flags, bandwidth, &spec, - dconn, graphicsuri, NULL, NULL, + dconn, graphicsuri, NULL, NULL, NULL, migParams, NULL); cleanup: @@ -5507,7 +5545,7 @@ qemuMigrationSrcPerformResume(virQEMUDriver *driver, ret = qemuMigrationSrcPerformNative(driver, vm, NULL, uri, cookiein, cookieinlen, cookieout, cookieoutlen, flags, - 0, NULL, NULL, NULL, NULL, migParams, NULL); + 0, NULL, NULL, NULL, NULL, NULL, migParams, NULL); virCloseCallbacksDomainAdd(vm, conn, qemuMigrationAnyConnectionClosed); @@ -5614,7 +5652,7 @@ qemuMigrationSrcPerformPeer2Peer2(virQEMUDriver *driver, ret = qemuMigrationSrcPerformNative(driver, vm, NULL, uri_out, cookie, cookielen, NULL, NULL, /* No out cookie with v2 migration */ - flags, bandwidth, dconn, NULL, NULL, + flags, bandwidth, dconn, NULL, NULL, NULL, NULL, migParams, NULL); /* Perform failed. Make sure Finish doesn't overwrite the error */ @@ -5679,6 +5717,7 @@ qemuMigrationSrcPerformPeer2Peer3(virQEMUDriver *driver, const char *listenAddress, const char **migrate_disks, const char **migrate_disks_detect_zeroes, + const char **migrate_disks_target_zero, int nbdPort, const char *nbdURI, qemuMigrationParams *migParams, @@ -5705,11 +5744,13 @@ qemuMigrationSrcPerformPeer2Peer3(virQEMUDriver *driver, VIR_DEBUG("driver=%p, sconn=%p, dconn=%p, dconnuri=%s, vm=%p, xmlin=%s, " "dname=%s, uri=%s, graphicsuri=%s, listenAddress=%s, " - "migrate_disks=%p, migrate_disks_detect_zeroes=%p, nbdPort=%d, nbdURI=%s, " + "migrate_disks=%p, migrate_disks_detect_zeroes=%p, " + "migrate_disks_target_zero=%p, nbdPort=%d, nbdURI=%s, " "bandwidth=%llu, useParams=%d, flags=0x%x", driver, sconn, dconn, NULLSTR(dconnuri), vm, NULLSTR(xmlin), NULLSTR(dname), NULLSTR(uri), NULLSTR(graphicsuri), - NULLSTR(listenAddress), migrate_disks, migrate_disks_detect_zeroes, nbdPort, + NULLSTR(listenAddress), migrate_disks, migrate_disks_detect_zeroes, + migrate_disks_target_zero, nbdPort, NULLSTR(nbdURI), bandwidth, useParams, flags); /* Unlike the virDomainMigrateVersion3 counterpart, we don't need @@ -5725,6 +5766,7 @@ qemuMigrationSrcPerformPeer2Peer3(virQEMUDriver *driver, &cookieout, &cookieoutlen, migrate_disks, migrate_disks_detect_zeroes, + migrate_disks_target_zero, flags); } if (!dom_xml) @@ -5779,6 +5821,15 @@ qemuMigrationSrcPerformPeer2Peer3(virQEMUDriver *driver, *d) < 0) goto cleanup; } + if (migrate_disks_target_zero) { + const char **d; + + for (d = migrate_disks_target_zero; *d; d++) + if (virTypedParamsAddString(¶ms, &nparams, &maxparams, + VIR_MIGRATE_PARAM_MIGRATE_DISKS_TARGET_ZERO, + *d) < 0) + goto cleanup; + } if (nbdPort && virTypedParamsAddInt(¶ms, &nparams, &maxparams, VIR_MIGRATE_PARAM_DISKS_PORT, @@ -5892,6 +5943,7 @@ qemuMigrationSrcPerformPeer2Peer3(virQEMUDriver *driver, &cookieout, &cookieoutlen, flags, bandwidth, dconn, graphicsuri, migrate_disks, migrate_disks_detect_zeroes, + migrate_disks_target_zero, migParams, nbdURI); } @@ -6066,6 +6118,7 @@ qemuMigrationSrcPerformPeer2Peer(virQEMUDriver *driver, const char *listenAddress, const char **migrate_disks, const char **migrate_disks_detect_zeroes, + const char **migrate_disks_target_zero, int nbdPort, const char *nbdURI, qemuMigrationParams *migParams, @@ -6191,6 +6244,7 @@ qemuMigrationSrcPerformPeer2Peer(virQEMUDriver *driver, ret = qemuMigrationSrcPerformPeer2Peer3(driver, sconn, dconn, dconnuri, vm, xmlin, persist_xml, dname, uri, graphicsuri, listenAddress, migrate_disks, migrate_disks_detect_zeroes, + migrate_disks_target_zero, nbdPort, nbdURI, migParams, bandwidth, !!useParams, flags); } else { @@ -6228,6 +6282,7 @@ qemuMigrationSrcPerformJob(virQEMUDriver *driver, const char *listenAddress, const char **migrate_disks, const char **migrate_disks_detect_zeroes, + const char **migrate_disks_target_zero, int nbdPort, const char *nbdURI, qemuMigrationParams *migParams, @@ -6277,8 +6332,9 @@ qemuMigrationSrcPerformJob(virQEMUDriver *driver, if ((flags & (VIR_MIGRATE_TUNNELLED | VIR_MIGRATE_PEER2PEER))) { ret = qemuMigrationSrcPerformPeer2Peer(driver, conn, vm, xmlin, persist_xml, dconnuri, uri, graphicsuri, listenAddress, - migrate_disks, migrate_disks_detect_zeroes, nbdPort, - nbdURI, + migrate_disks, migrate_disks_detect_zeroes, + migrate_disks_target_zero, + nbdPort, nbdURI, migParams, flags, dname, bandwidth, &v3proto); } else { @@ -6287,7 +6343,7 @@ qemuMigrationSrcPerformJob(virQEMUDriver *driver, ret = qemuMigrationSrcPerformNative(driver, vm, persist_xml, uri, cookiein, cookieinlen, cookieout, cookieoutlen, - flags, bandwidth, NULL, NULL, NULL, NULL, + flags, bandwidth, NULL, NULL, NULL, NULL, NULL, migParams, nbdURI); } if (ret < 0) @@ -6355,6 +6411,7 @@ qemuMigrationSrcPerformPhase(virQEMUDriver *driver, const char *graphicsuri, const char **migrate_disks, const char **migrate_disks_detect_zeroes, + const char **migrate_disks_target_zero, qemuMigrationParams *migParams, const char *cookiein, int cookieinlen, @@ -6391,6 +6448,7 @@ qemuMigrationSrcPerformPhase(virQEMUDriver *driver, cookieout, cookieoutlen, flags, bandwidth, NULL, graphicsuri, migrate_disks, migrate_disks_detect_zeroes, + migrate_disks_target_zero, migParams, nbdURI) < 0) goto cleanup; @@ -6434,6 +6492,7 @@ qemuMigrationSrcPerform(virQEMUDriver *driver, const char *listenAddress, const char **migrate_disks, const char **migrate_disks_detect_zeroes, + const char **migrate_disks_target_zero, int nbdPort, const char *nbdURI, qemuMigrationParams *migParams, @@ -6477,8 +6536,9 @@ qemuMigrationSrcPerform(virQEMUDriver *driver, return qemuMigrationSrcPerformJob(driver, conn, vm, xmlin, persist_xml, dconnuri, uri, graphicsuri, listenAddress, - migrate_disks, migrate_disks_detect_zeroes, nbdPort, - nbdURI, migParams, + migrate_disks, migrate_disks_detect_zeroes, + migrate_disks_target_zero, + nbdPort, nbdURI, migParams, cookiein, cookieinlen, cookieout, cookieoutlen, flags, dname, bandwidth, v3proto); @@ -6494,6 +6554,7 @@ qemuMigrationSrcPerform(virQEMUDriver *driver, return qemuMigrationSrcPerformPhase(driver, conn, vm, persist_xml, uri, graphicsuri, migrate_disks, migrate_disks_detect_zeroes, + migrate_disks_target_zero, migParams, cookiein, cookieinlen, cookieout, cookieoutlen, @@ -6502,8 +6563,9 @@ qemuMigrationSrcPerform(virQEMUDriver *driver, return qemuMigrationSrcPerformJob(driver, conn, vm, xmlin, persist_xml, NULL, uri, graphicsuri, listenAddress, - migrate_disks, migrate_disks_detect_zeroes, nbdPort, - nbdURI, migParams, + migrate_disks, migrate_disks_detect_zeroes, + migrate_disks_target_zero, + nbdPort, nbdURI, migParams, cookiein, cookieinlen, cookieout, cookieoutlen, flags, dname, bandwidth, v3proto); diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h index db03144207..ef6a1563a0 100644 --- a/src/qemu/qemu_migration.h +++ b/src/qemu/qemu_migration.h @@ -76,6 +76,8 @@ VIR_TYPED_PARAM_MULTIPLE, \ VIR_MIGRATE_PARAM_MIGRATE_DISKS_DETECT_ZEROES, VIR_TYPED_PARAM_STRING | \ VIR_TYPED_PARAM_MULTIPLE, \ + VIR_MIGRATE_PARAM_MIGRATE_DISKS_TARGET_ZERO, VIR_TYPED_PARAM_STRING | \ + VIR_TYPED_PARAM_MULTIPLE, \ VIR_MIGRATE_PARAM_DISKS_PORT, VIR_TYPED_PARAM_INT, \ VIR_MIGRATE_PARAM_COMPRESSION, VIR_TYPED_PARAM_STRING | \ VIR_TYPED_PARAM_MULTIPLE, \ @@ -127,6 +129,7 @@ qemuMigrationSrcBegin(virConnectPtr conn, int *cookieoutlen, const char **migrate_disks, const char **migrate_disks_detect_zeroes, + const char **migrate_disks_target_zero, unsigned int flags); virDomainDef * @@ -181,6 +184,7 @@ qemuMigrationSrcPerform(virQEMUDriver *driver, const char *listenAddress, const char **migrate_disks, const char **migrate_disks_detect_zeroes, + const char **migrate_disks_target_zero, int nbdPort, const char *nbdURI, qemuMigrationParams *migParams, -- 2.53.0
From: Peter Krempa <pkrempa@redhat.com> Add '--migrate-disks-target-zero' to pass the list of pre-zeroed disk images. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- docs/manpages/virsh.rst | 4 ++++ tools/virsh-domain.c | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index da9e9f8658..4b8bbec7d6 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -3722,6 +3722,7 @@ migrate [--timeout seconds [--timeout-suspend | --timeout-postcopy]] [--xml file] [--migrate-disks disk-list] [--migrate-disks-detect-zeroes disk-list] + [--migrate-disks-target-zero disk-list] [--disks-port port] [--compressed] [--comp-methods method-list] [--comp-mt-level] [--comp-mt-threads] [--comp-mt-dthreads] @@ -3760,6 +3761,9 @@ disk target names enables zeroed block detection for the listed migrated disks. These blocks are not transferred or allocated (requires that 'discard' option on given disk is set to 'unmap') on destination, effectively sparsifying the disk at the cost of CPU overhead. +The *--migrate-disks-target-zero* option which takes a comma separated list of +disk target names specifies disk images where the target was zeroed out prior +to the migration and thus hypervisor will not attempt to zero them. With *--copy-storage-synchronous-writes* flag used the disk data migration will synchronously handle guest disk writes to both the original source and the destination to ensure that the disk migration converges at the price of possibly diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 1af7f9eb0e..a17c8b42e2 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -11218,6 +11218,11 @@ static const vshCmdOptDef opts_migrate[] = { .completer = virshDomainMigrateDisksCompleter, .help = N_("comma separated list of disks to be migrated with zero detection enabled") }, + {.name = "migrate-disks-target-zero", + .type = VSH_OT_STRING, + .completer = virshDomainMigrateDisksCompleter, + .help = N_("comma separated list of disks to be migrated with assumption that target image is zeroed") + }, {.name = "disks-port", .type = VSH_OT_INT, .unwanted_positional = true, @@ -11461,6 +11466,27 @@ doMigrate(void *opaque) } } + if (vshCommandOptString(ctl, cmd, "migrate-disks-target-zero", &opt) < 0) + goto out; + if (opt) { + g_autofree char **val = NULL; + + if (!(flags & (VIR_MIGRATE_NON_SHARED_DISK | VIR_MIGRATE_NON_SHARED_INC))) { + vshError(ctl, "%s", _("'--migrate-disks-target-zero' requires one of '--copy-storage-all', '--copy-storage-inc'")); + goto out; + } + + val = g_strsplit(opt, ",", 0); + + if (virTypedParamsAddStringList(¶ms, + &nparams, + &maxparams, + VIR_MIGRATE_PARAM_MIGRATE_DISKS_TARGET_ZERO, + (const char **)val) < 0) { + goto save_error; + } + } + if (vshCommandOptString(ctl, cmd, "comp-methods", &opt) < 0) goto out; if (opt) { -- 2.53.0
On a Wednesday in 2026, Peter Krempa via Devel wrote:
Pre-zeroing allows preserve sparseness on some storage technologies where efficient zeroing is not supported, yet the images are created cleared.
The support is added both for virDomainBlockCopy as well as the non-shared storage migration.
Peter Krempa (7): qemuMigrationSrcPerformTunnel: Remove 'migrate_disks' argument qemu: capabilities: Introduce QEMU_CAPS_BLOCKDEV_MIRROR_TARGET_IS_ZERO qemu: monitor: Add support for 'target-is-zero' option of 'blockdev-mirror' qemu: Add VIR_DOMAIN_BLOCK_COPY_TARGET_ZEROED flag for virDomainBlockCopy virsh: Add support for 'VIR_DOMAIN_BLOCK_COPY_TARGET_ZEROED' as '--dest-is-zero' qemu: migration: Introduce 'VIR_MIGRATE_PARAM_MIGRATE_DISKS_TARGET_ZERO' virsh: migrate: Add support for VIR_MIGRATE_PARAM_MIGRATE_DISKS_TARGET_ZERO
docs/manpages/virsh.rst | 10 +- include/libvirt/libvirt-domain.h | 19 +++ src/libvirt-domain.c | 4 + src/qemu/qemu_capabilities.c | 4 + src/qemu/qemu_capabilities.h | 3 + src/qemu/qemu_driver.c | 38 ++++-- src/qemu/qemu_migration.c | 118 +++++++++++++----- src/qemu/qemu_migration.h | 4 + src/qemu/qemu_monitor.c | 9 +- src/qemu/qemu_monitor.h | 3 +- src/qemu/qemu_monitor_json.c | 4 +- src/qemu/qemu_monitor_json.h | 3 +- .../caps_10.1.0_s390x.xml | 1 + .../caps_10.1.0_x86_64+inteltdx.xml | 1 + .../caps_10.1.0_x86_64.xml | 1 + .../caps_10.2.0_aarch64.xml | 1 + .../caps_10.2.0_x86_64+mshv.xml | 1 + .../caps_10.2.0_x86_64.xml | 1 + .../caps_11.0.0_aarch64.xml | 1 + .../caps_11.0.0_x86_64.xml | 1 + tests/qemumonitorjsontest.c | 2 +- tools/virsh-domain.c | 35 +++++- 22 files changed, 217 insertions(+), 47 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano
participants (2)
-
Ján Tomko -
Peter Krempa