diff --git a/SOURCES/libvirt-Add-VIR_MIGRATE_ZEROCOPY-flag.patch b/SOURCES/libvirt-Add-VIR_MIGRATE_ZEROCOPY-flag.patch
new file mode 100644
index 0000000..e1bf11f
--- /dev/null
+++ b/SOURCES/libvirt-Add-VIR_MIGRATE_ZEROCOPY-flag.patch
@@ -0,0 +1,49 @@
+From 2f3e89c97c7babc1d4da579eadf34979c8fc1725 Mon Sep 17 00:00:00 2001
+Message-Id: <2f3e89c97c7babc1d4da579eadf34979c8fc1725@dist-git>
+From: Jiri Denemark <jdenemar@redhat.com>
+Date: Wed, 22 Jun 2022 16:35:50 +0200
+Subject: [PATCH] Add VIR_MIGRATE_ZEROCOPY flag
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The flag can be used to enable zero-copy mechanism for migrating memory
+pages.
+
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+Reviewed-by: Ján Tomko <jtomko@redhat.com>
+(cherry picked from commit 8744beecb36600e773c8a8c4823db2bf4b3e262d)
+
+https://bugzilla.redhat.com/show_bug.cgi?id=2117272
+
+Conflicts:
+	include/libvirt/libvirt-domain.h
+            - post-copy recovery not backported
+
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ include/libvirt/libvirt-domain.h | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h
+index 5f0a9b7572..792973ce2d 100644
+--- a/include/libvirt/libvirt-domain.h
++++ b/include/libvirt/libvirt-domain.h
+@@ -860,6 +860,14 @@ typedef enum {
+       */
+     VIR_MIGRATE_NON_SHARED_SYNCHRONOUS_WRITES = (1 << 18),
+ 
++    /* Use zero-copy mechanism for migrating memory pages. For QEMU/KVM this
++     * means QEMU will be temporarily allowed to lock all guest pages in host's
++     * memory, although only those that are queued for transfer will be locked
++     * at the same time.
++     *
++     * Since: 8.5.0
++     */
++    VIR_MIGRATE_ZEROCOPY = (1 << 20),
+ } virDomainMigrateFlags;
+ 
+ 
+-- 
+2.35.1
+
diff --git a/SOURCES/libvirt-conf-Move-virDomainObj-originalMemlock-into-qemuDomainObjPrivate.patch b/SOURCES/libvirt-conf-Move-virDomainObj-originalMemlock-into-qemuDomainObjPrivate.patch
new file mode 100644
index 0000000..6cd0e37
--- /dev/null
+++ b/SOURCES/libvirt-conf-Move-virDomainObj-originalMemlock-into-qemuDomainObjPrivate.patch
@@ -0,0 +1,91 @@
+From b4469bda27e54a948f0d2750637226afc2b2be61 Mon Sep 17 00:00:00 2001
+Message-Id: <b4469bda27e54a948f0d2750637226afc2b2be61@dist-git>
+From: Michal Privoznik <mprivozn@redhat.com>
+Date: Wed, 11 May 2022 16:27:18 +0200
+Subject: [PATCH] conf: Move virDomainObj::originalMemlock into
+ qemuDomainObjPrivate
+
+Since v1.3.0-90-gafbe1d4c56 the original value of memlock limit
+is stored inside virDomainObj struct directly (under
+originalMemlock member). This is needless because the value is
+used only inside QEMU driver and thus can reside in
+qemuDomainObjPrivate struct.
+
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+Reviewed-by: Peter Krempa <pkrempa@redhat.com>
+(cherry picked from commit 75df6d2c291c48d65c1e54dd93e3d2d3cb0712e7)
+
+https://bugzilla.redhat.com/show_bug.cgi?id=2117272
+
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/conf/domain_conf.h | 3 ---
+ src/qemu/qemu_domain.c | 9 +++++----
+ src/qemu/qemu_domain.h | 3 +++
+ 3 files changed, 8 insertions(+), 7 deletions(-)
+
+diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
+index 10af94e2e4..7e3255e41a 100644
+--- a/src/conf/domain_conf.h
++++ b/src/conf/domain_conf.h
+@@ -3030,9 +3030,6 @@ struct _virDomainObj {
+     int taint;
+     size_t ndeprecations;
+     char **deprecations;
+-
+-    unsigned long long originalMemlock; /* Original RLIMIT_MEMLOCK, zero if no
+-                                         * restore will be required later */
+ };
+ 
+ G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainObj, virObjectUnref);
+diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
+index 40fe9985e6..86d673dafa 100644
+--- a/src/qemu/qemu_domain.c
++++ b/src/qemu/qemu_domain.c
+@@ -9269,6 +9269,7 @@ int
+ qemuDomainAdjustMaxMemLock(virDomainObj *vm,
+                            bool forceVFIO)
+ {
++    qemuDomainObjPrivate *priv = vm->privateData;
+     unsigned long long currentMemLock = 0;
+     unsigned long long desiredMemLock = 0;
+ 
+@@ -9281,8 +9282,8 @@ qemuDomainAdjustMaxMemLock(virDomainObj *vm,
+             /* If this is the first time adjusting the limit, save the current
+              * value so that we can restore it once memory locking is no longer
+              * required */
+-            if (vm->originalMemlock == 0) {
+-                vm->originalMemlock = currentMemLock;
++            if (priv->originalMemlock == 0) {
++                priv->originalMemlock = currentMemLock;
+             }
+         } else {
+             /* If the limit is already high enough, we can assume
+@@ -9295,8 +9296,8 @@ qemuDomainAdjustMaxMemLock(virDomainObj *vm,
+     } else {
+         /* Once memory locking is no longer required, we can restore the
+          * original, usually very low, limit */
+-        desiredMemLock = vm->originalMemlock;
+-        vm->originalMemlock = 0;
++        desiredMemLock = priv->originalMemlock;
++        priv->originalMemlock = 0;
+     }
+ 
+     if (desiredMemLock > 0 &&
+diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
+index e5046367e3..e9497d20de 100644
+--- a/src/qemu/qemu_domain.h
++++ b/src/qemu/qemu_domain.h
+@@ -241,6 +241,9 @@ struct _qemuDomainObjPrivate {
+     GSList *dbusVMStateIds;
+     /* true if -object dbus-vmstate was added */
+     bool dbusVMState;
++
++    unsigned long long originalMemlock; /* Original RLIMIT_MEMLOCK, zero if no
++                                         * restore will be required later */
+ };
+ 
+ #define QEMU_DOMAIN_PRIVATE(vm) \
+-- 
+2.35.1
+
diff --git a/SOURCES/libvirt-domain_validate-Split-out-validation-of-disk-startup-policy.patch b/SOURCES/libvirt-domain_validate-Split-out-validation-of-disk-startup-policy.patch
new file mode 100644
index 0000000..4783fab
--- /dev/null
+++ b/SOURCES/libvirt-domain_validate-Split-out-validation-of-disk-startup-policy.patch
@@ -0,0 +1,112 @@
+From bfd8d181d45a22731ae5b1f05f3cb9488a2c7939 Mon Sep 17 00:00:00 2001
+Message-Id: <bfd8d181d45a22731ae5b1f05f3cb9488a2c7939@dist-git>
+From: Peter Krempa <pkrempa@redhat.com>
+Date: Tue, 14 Jun 2022 13:23:29 +0200
+Subject: [PATCH] domain_validate: Split out validation of disk startup policy
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Move the code into 'virDomainDiskDefValidateStartupPolicy' which will be
+later reused in the qemu driver.
+
+Signed-off-by: Peter Krempa <pkrempa@redhat.com>
+Reviewed-by: Ján Tomko <jtomko@redhat.com>
+(cherry picked from commit 3603a18bcec18842cedecbd8329723062b87795c)
+https://bugzilla.redhat.com/show_bug.cgi?id=2095758
+https://bugzilla.redhat.com/show_bug.cgi?id=2109571
+---
+ src/conf/domain_validate.c | 45 ++++++++++++++++++++++++--------------
+ src/conf/domain_validate.h |  2 ++
+ src/libvirt_private.syms   |  1 +
+ 3 files changed, 31 insertions(+), 17 deletions(-)
+
+diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c
+index 452742e67c..bfff7339ef 100644
+--- a/src/conf/domain_validate.c
++++ b/src/conf/domain_validate.c
+@@ -598,6 +598,32 @@ virDomainDiskDefSourceLUNValidate(const virStorageSource *src)
+ }
+ 
+ 
++int
++virDomainDiskDefValidateStartupPolicy(const virDomainDiskDef *disk)
++{
++    if (disk->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_DEFAULT)
++        return 0;
++
++    if (disk->src->type == VIR_STORAGE_TYPE_NETWORK) {
++        virReportError(VIR_ERR_XML_ERROR,
++                       _("disk startupPolicy '%s' is not allowed for disk of '%s' type"),
++                       virDomainStartupPolicyTypeToString(disk->startupPolicy),
++                       virStorageTypeToString(disk->src->type));
++        return -1;
++    }
++
++    if (disk->device != VIR_DOMAIN_DISK_DEVICE_CDROM &&
++        disk->device != VIR_DOMAIN_DISK_DEVICE_FLOPPY &&
++        disk->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_REQUISITE) {
++        virReportError(VIR_ERR_XML_ERROR, "%s",
++                       _("disk startupPolicy 'requisite' is allowed only for cdrom or floppy"));
++        return -1;
++    }
++
++    return 0;
++}
++
++
+ static int
+ virDomainDiskDefValidate(const virDomainDef *def,
+                          const virDomainDiskDef *disk)
+@@ -775,23 +801,8 @@ virDomainDiskDefValidate(const virDomainDef *def,
+         return -1;
+     }
+ 
+-    if (disk->startupPolicy != VIR_DOMAIN_STARTUP_POLICY_DEFAULT) {
+-        if (disk->src->type == VIR_STORAGE_TYPE_NETWORK) {
+-            virReportError(VIR_ERR_XML_ERROR,
+-                           _("disk startupPolicy '%s' is not allowed for disk of '%s' type"),
+-                           virDomainStartupPolicyTypeToString(disk->startupPolicy),
+-                           virStorageTypeToString(disk->src->type));
+-            return -1;
+-        }
+-
+-        if (disk->device != VIR_DOMAIN_DISK_DEVICE_CDROM &&
+-            disk->device != VIR_DOMAIN_DISK_DEVICE_FLOPPY &&
+-            disk->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_REQUISITE) {
+-            virReportError(VIR_ERR_XML_ERROR, "%s",
+-                           _("disk startupPolicy 'requisite' is allowed only for cdrom or floppy"));
+-            return -1;
+-        }
+-    }
++    if (virDomainDiskDefValidateStartupPolicy(disk) < 0)
++        return -1;
+ 
+     if (disk->wwn && !virValidateWWN(disk->wwn))
+         return -1;
+diff --git a/src/conf/domain_validate.h b/src/conf/domain_validate.h
+index 430d61fd3c..07b99195e3 100644
+--- a/src/conf/domain_validate.h
++++ b/src/conf/domain_validate.h
+@@ -41,4 +41,6 @@ int virDomainDeviceDefValidate(const virDomainDeviceDef *dev,
+ 
+ int virDomainDiskDefValidateSource(const virStorageSource *src);
+ 
++int virDomainDiskDefValidateStartupPolicy(const virDomainDiskDef *disk);
++
+ int virDomainDiskDefSourceLUNValidate(const virStorageSource *src);
+diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
+index 2c42e2a5e8..5b7a056151 100644
+--- a/src/libvirt_private.syms
++++ b/src/libvirt_private.syms
+@@ -777,6 +777,7 @@ virDomainActualNetDefValidate;
+ virDomainDefValidate;
+ virDomainDeviceValidateAliasForHotplug;
+ virDomainDiskDefSourceLUNValidate;
++virDomainDiskDefValidateStartupPolicy;
+ 
+ 
+ # conf/interface_conf.h
+-- 
+2.35.1
+
diff --git a/SOURCES/libvirt-qemu-Add-qemuDomainSetMaxMemLock-helper.patch b/SOURCES/libvirt-qemu-Add-qemuDomainSetMaxMemLock-helper.patch
new file mode 100644
index 0000000..740f4f1
--- /dev/null
+++ b/SOURCES/libvirt-qemu-Add-qemuDomainSetMaxMemLock-helper.patch
@@ -0,0 +1,155 @@
+From 7f5b89a15bfcd964c7f2b6ccbf3c03fd867f93b5 Mon Sep 17 00:00:00 2001
+Message-Id: <7f5b89a15bfcd964c7f2b6ccbf3c03fd867f93b5@dist-git>
+From: Jiri Denemark <jdenemar@redhat.com>
+Date: Wed, 22 Jun 2022 15:21:30 +0200
+Subject: [PATCH] qemu: Add qemuDomainSetMaxMemLock helper
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+qemuDomainAdjustMaxMemLock combined computing the desired limit with
+applying it. This patch separates the code to apply a memory locking
+limit to a new qemuDomainSetMaxMemLock helper for better reusability.
+
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+Reviewed-by: Ján Tomko <jtomko@redhat.com>
+(cherry picked from commit dff51c7f5760ded8235076f55d082fe4363f2f78)
+
+https://bugzilla.redhat.com/show_bug.cgi?id=2117272
+
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/qemu/qemu_domain.c | 95 ++++++++++++++++++++++++++----------------
+ src/qemu/qemu_domain.h |  3 ++
+ 2 files changed, 61 insertions(+), 37 deletions(-)
+
+diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
+index ee7d310903..a81789f194 100644
+--- a/src/qemu/qemu_domain.c
++++ b/src/qemu/qemu_domain.c
+@@ -9261,6 +9261,61 @@ qemuDomainGetMemLockLimitBytes(virDomainDef *def,
+ }
+ 
+ 
++/**
++ * qemuDomainSetMaxMemLock:
++ * @vm: domain
++ * @limit: the desired memory locking limit
++ * @origPtr: where to store (or load from) the original value of the limit
++ *
++ * Set the memory locking limit for @vm unless it's already big enough. If
++ * @origPtr is non-NULL, the original value of the limit will be store there
++ * and can be restored by calling this function with @limit == 0.
++ *
++ * Returns: 0 on success, -1 otherwise.
++ */
++int
++qemuDomainSetMaxMemLock(virDomainObj *vm,
++                        unsigned long long limit,
++                        unsigned long long *origPtr)
++{
++    unsigned long long current = 0;
++
++    if (virProcessGetMaxMemLock(vm->pid, &current) < 0)
++        return -1;
++
++    if (limit > 0) {
++        VIR_DEBUG("Requested memory lock limit: %llu", limit);
++        /* If the limit is already high enough, we can assume
++         * that some external process is taking care of managing
++         * process limits and we shouldn't do anything ourselves:
++         * we're probably running in a containerized environment
++         * where we don't have enough privilege anyway */
++        if (current >= limit) {
++            VIR_DEBUG("Current limit %llu is big enough", current);
++            return 0;
++        }
++
++        /* If this is the first time adjusting the limit, save the current
++         * value so that we can restore it once memory locking is no longer
++         * required */
++        if (origPtr && *origPtr == 0)
++            *origPtr = current;
++    } else {
++        /* Once memory locking is no longer required, we can restore the
++         * original, usually very low, limit. But only if we actually stored
++         * the original limit before. */
++        if (!origPtr || *origPtr == 0)
++            return 0;
++
++        limit = *origPtr;
++        *origPtr = 0;
++        VIR_DEBUG("Resetting memory lock limit back to %llu", limit);
++    }
++
++    return virProcessSetMaxMemLock(vm->pid, limit);
++}
++
++
+ /**
+  * qemuDomainAdjustMaxMemLock:
+  * @vm: domain
+@@ -9282,43 +9337,9 @@ int
+ qemuDomainAdjustMaxMemLock(virDomainObj *vm,
+                            bool forceVFIO)
+ {
+-    qemuDomainObjPrivate *priv = vm->privateData;
+-    unsigned long long currentMemLock = 0;
+-    unsigned long long desiredMemLock = 0;
+-
+-    desiredMemLock = qemuDomainGetMemLockLimitBytes(vm->def, forceVFIO);
+-    if (virProcessGetMaxMemLock(vm->pid, &currentMemLock) < 0)
+-        return -1;
+-
+-    if (desiredMemLock > 0) {
+-        if (currentMemLock < desiredMemLock) {
+-            /* If this is the first time adjusting the limit, save the current
+-             * value so that we can restore it once memory locking is no longer
+-             * required */
+-            if (priv->originalMemlock == 0) {
+-                priv->originalMemlock = currentMemLock;
+-            }
+-        } else {
+-            /* If the limit is already high enough, we can assume
+-             * that some external process is taking care of managing
+-             * process limits and we shouldn't do anything ourselves:
+-             * we're probably running in a containerized environment
+-             * where we don't have enough privilege anyway */
+-            desiredMemLock = 0;
+-        }
+-    } else {
+-        /* Once memory locking is no longer required, we can restore the
+-         * original, usually very low, limit */
+-        desiredMemLock = priv->originalMemlock;
+-        priv->originalMemlock = 0;
+-    }
+-
+-    if (desiredMemLock > 0 &&
+-        virProcessSetMaxMemLock(vm->pid, desiredMemLock) < 0) {
+-        return -1;
+-    }
+-
+-    return 0;
++    return qemuDomainSetMaxMemLock(vm,
++                                   qemuDomainGetMemLockLimitBytes(vm->def, forceVFIO),
++                                   &QEMU_DOMAIN_PRIVATE(vm)->originalMemlock);
+ }
+ 
+ 
+diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
+index e9497d20de..6d1d23439a 100644
+--- a/src/qemu/qemu_domain.h
++++ b/src/qemu/qemu_domain.h
+@@ -789,6 +789,9 @@ int qemuDomainAdjustMaxMemLock(virDomainObj *vm,
+                                bool forceVFIO);
+ int qemuDomainAdjustMaxMemLockHostdev(virDomainObj *vm,
+                                       virDomainHostdevDef *hostdev);
++int qemuDomainSetMaxMemLock(virDomainObj *vm,
++                            unsigned long long limit,
++                            unsigned long long *origPtr);
+ 
+ int qemuDomainDefValidateMemoryHotplug(const virDomainDef *def,
+                                        const virDomainMemoryDef *mem);
+-- 
+2.35.1
+
diff --git a/SOURCES/libvirt-qemu_domain-Format-qemuDomainObjPrivate-originalMemlock.patch b/SOURCES/libvirt-qemu_domain-Format-qemuDomainObjPrivate-originalMemlock.patch
new file mode 100644
index 0000000..b1c20ff
--- /dev/null
+++ b/SOURCES/libvirt-qemu_domain-Format-qemuDomainObjPrivate-originalMemlock.patch
@@ -0,0 +1,55 @@
+From cbfe812a4affe5be7677bf28764b58dc7f99c969 Mon Sep 17 00:00:00 2001
+Message-Id: <cbfe812a4affe5be7677bf28764b58dc7f99c969@dist-git>
+From: Michal Privoznik <mprivozn@redhat.com>
+Date: Wed, 11 May 2022 16:37:27 +0200
+Subject: [PATCH] qemu_domain: Format qemuDomainObjPrivate::originalMemlock
+
+Now that qemuDomainObjPrivate struct gained new member format it
+into XML and parse it so that the value is preserved across
+daemon restarts.
+
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+Reviewed-by: Peter Krempa <pkrempa@redhat.com>
+(cherry picked from commit 21aec91790ae14d24512856b20cff49764ede637)
+
+https://bugzilla.redhat.com/show_bug.cgi?id=2117272
+
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/qemu/qemu_domain.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
+index 86d673dafa..ee7d310903 100644
+--- a/src/qemu/qemu_domain.c
++++ b/src/qemu/qemu_domain.c
+@@ -2383,6 +2383,12 @@ qemuDomainObjPrivateXMLFormat(virBuffer *buf,
+     if (qemuDomainObjPrivateXMLFormatBackups(buf, vm) < 0)
+         return -1;
+ 
++    if (priv->originalMemlock > 0) {
++        virBufferAsprintf(buf,
++                          "<originalMemlock>%llu</originalMemlock>\n",
++                          priv->originalMemlock);
++    }
++
+     return 0;
+ }
+ 
+@@ -3104,6 +3110,13 @@ qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt,
+ 
+     priv->memPrealloc = virXPathBoolean("boolean(./memPrealloc)", ctxt) == 1;
+ 
++    if (virXPathULongLong("string(./originalMemlock)",
++                          ctxt, &priv->originalMemlock) == -2) {
++        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
++                       _("failed to parse original memlock size"));
++        goto error;
++    }
++
+     return 0;
+ 
+  error:
+-- 
+2.35.1
+
diff --git a/SOURCES/libvirt-qemu_migration-Implement-VIR_MIGRATE_ZEROCOPY-flag.patch b/SOURCES/libvirt-qemu_migration-Implement-VIR_MIGRATE_ZEROCOPY-flag.patch
new file mode 100644
index 0000000..e1d38c3
--- /dev/null
+++ b/SOURCES/libvirt-qemu_migration-Implement-VIR_MIGRATE_ZEROCOPY-flag.patch
@@ -0,0 +1,120 @@
+From 34dc905251ca0f00d92e8419adc63580c6266394 Mon Sep 17 00:00:00 2001
+Message-Id: <34dc905251ca0f00d92e8419adc63580c6266394@dist-git>
+From: Jiri Denemark <jdenemar@redhat.com>
+Date: Wed, 22 Jun 2022 16:37:31 +0200
+Subject: [PATCH] qemu_migration: Implement VIR_MIGRATE_ZEROCOPY flag
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Resolves: https://gitlab.com/libvirt/libvirt/-/issues/306
+
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+Reviewed-by: Ján Tomko <jtomko@redhat.com>
+(cherry picked from commit d375993ab314a41bca7ef6c846e07afc18c37774)
+
+https://bugzilla.redhat.com/show_bug.cgi?id=2117272
+
+Conflicts:
+	src/qemu/qemu_migration.c
+	src/qemu/qemu_migration.h
+            - post-copy recovery not bacported
+
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/qemu/qemu_migration.c        | 21 +++++++++++++++++++++
+ src/qemu/qemu_migration.h        |  1 +
+ src/qemu/qemu_migration_params.c |  6 ++++++
+ src/qemu/qemu_migration_params.h |  1 +
+ 4 files changed, 29 insertions(+)
+
+diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
+index 01102c4300..11f87296d6 100644
+--- a/src/qemu/qemu_migration.c
++++ b/src/qemu/qemu_migration.c
+@@ -2366,6 +2366,12 @@ qemuMigrationSrcBeginPhase(virQEMUDriver *driver,
+         return NULL;
+     }
+ 
++    if (flags & VIR_MIGRATE_ZEROCOPY && !(flags & VIR_MIGRATE_PARALLEL)) {
++        virReportError(VIR_ERR_OPERATION_INVALID, "%s",
++                       _("zero-copy is only available for parallel migration"));
++        return NULL;
++    }
++
+     if (flags & (VIR_MIGRATE_NON_SHARED_DISK | VIR_MIGRATE_NON_SHARED_INC)) {
+         if (flags & VIR_MIGRATE_NON_SHARED_SYNCHRONOUS_WRITES &&
+             !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV)) {
+@@ -4137,6 +4143,21 @@ qemuMigrationSrcRun(virQEMUDriver *driver,
+                                  migParams) < 0)
+         goto error;
+ 
++    if (flags & VIR_MIGRATE_ZEROCOPY) {
++        /* Zero-copy requires pages in transfer to be locked in host memory.
++         * Unfortunately, we have no reliable way of computing how many pages
++         * will need to be locked at the same time. Thus we set the limit to
++         * the whole guest memory and reset it back once migration is done. */
++        unsigned long long limit;
++
++        if (virMemoryLimitIsSet(vm->def->mem.hard_limit))
++            limit = vm->def->mem.hard_limit;
++        else
++            limit = virDomainDefGetMemoryTotal(vm->def);
++
++        if (qemuDomainSetMaxMemLock(vm, limit << 10, &priv->preMigrationMemlock) < 0)
++            goto error;
++    }
+ 
+     if (storageMigration) {
+         if (mig->nbd) {
+diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h
+index b233358a51..6f737f7b4c 100644
+--- a/src/qemu/qemu_migration.h
++++ b/src/qemu/qemu_migration.h
+@@ -60,6 +60,7 @@
+      VIR_MIGRATE_TLS | \
+      VIR_MIGRATE_PARALLEL | \
+      VIR_MIGRATE_NON_SHARED_SYNCHRONOUS_WRITES | \
++     VIR_MIGRATE_ZEROCOPY | \
+      0)
+ 
+ /* All supported migration parameters and their types. */
+diff --git a/src/qemu/qemu_migration_params.c b/src/qemu/qemu_migration_params.c
+index 7b225fdf4b..c985583861 100644
+--- a/src/qemu/qemu_migration_params.c
++++ b/src/qemu/qemu_migration_params.c
+@@ -96,6 +96,7 @@ VIR_ENUM_IMPL(qemuMigrationCapability,
+               "multifd",
+               "dirty-bitmaps",
+               "return-path",
++              "zero-copy-send",
+ );
+ 
+ 
+@@ -177,6 +178,11 @@ static const qemuMigrationParamsFlagMapItem qemuMigrationParamsFlagMap[] = {
+      VIR_MIGRATE_TUNNELLED,
+      QEMU_MIGRATION_CAP_RETURN_PATH,
+      QEMU_MIGRATION_SOURCE | QEMU_MIGRATION_DESTINATION},
++
++    {QEMU_MIGRATION_FLAG_REQUIRED,
++     VIR_MIGRATE_ZEROCOPY,
++     QEMU_MIGRATION_CAP_ZERO_COPY_SEND,
++     QEMU_MIGRATION_SOURCE},
+ };
+ 
+ /* Translation from VIR_MIGRATE_PARAM_* typed parameters to
+diff --git a/src/qemu/qemu_migration_params.h b/src/qemu/qemu_migration_params.h
+index b4de8dda7b..caa5e47f0f 100644
+--- a/src/qemu/qemu_migration_params.h
++++ b/src/qemu/qemu_migration_params.h
+@@ -41,6 +41,7 @@ typedef enum {
+     QEMU_MIGRATION_CAP_MULTIFD,
+     QEMU_MIGRATION_CAP_BLOCK_DIRTY_BITMAPS,
+     QEMU_MIGRATION_CAP_RETURN_PATH,
++    QEMU_MIGRATION_CAP_ZERO_COPY_SEND,
+ 
+     QEMU_MIGRATION_CAP_LAST
+ } qemuMigrationCapability;
+-- 
+2.35.1
+
diff --git a/SOURCES/libvirt-qemu_migration-Restore-original-memory-locking-limit.patch b/SOURCES/libvirt-qemu_migration-Restore-original-memory-locking-limit.patch
new file mode 100644
index 0000000..3011e5f
--- /dev/null
+++ b/SOURCES/libvirt-qemu_migration-Restore-original-memory-locking-limit.patch
@@ -0,0 +1,138 @@
+From 7cbfdb081de6e4eb684447ba48869082df798419 Mon Sep 17 00:00:00 2001
+Message-Id: <7cbfdb081de6e4eb684447ba48869082df798419@dist-git>
+From: Jiri Denemark <jdenemar@redhat.com>
+Date: Wed, 22 Jun 2022 16:12:02 +0200
+Subject: [PATCH] qemu_migration: Restore original memory locking limit
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+For RDMA migration we update memory locking limit, but never set it back
+once migration finishes (on the destination host) or aborts (on the
+source host).
+
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+Reviewed-by: Ján Tomko <jtomko@redhat.com>
+(cherry picked from commit d4d3bb813031275c2c7cf72724b83c97ce82ab7a)
+
+https://bugzilla.redhat.com/show_bug.cgi?id=2117272
+
+Conflicts:
+	src/qemu/qemu_migration.c
+            - post-copy resovery not backported
+
+The original 8.7.0 backport contained a bug which was later fixed by
+"qemu_migration: Fix restoring memlock limit on destination". This
+backport includes the follow up fix squashed in.
+
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/qemu/qemu_domain.c    | 12 ++++++++++++
+ src/qemu/qemu_domain.h    |  3 +++
+ src/qemu/qemu_migration.c | 11 +++++++++--
+ 3 files changed, 24 insertions(+), 2 deletions(-)
+
+diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
+index a81789f194..c24d1e4d53 100644
+--- a/src/qemu/qemu_domain.c
++++ b/src/qemu/qemu_domain.c
+@@ -2389,6 +2389,11 @@ qemuDomainObjPrivateXMLFormat(virBuffer *buf,
+                           priv->originalMemlock);
+     }
+ 
++    if (priv->preMigrationMemlock > 0) {
++        virBufferAsprintf(buf, "<preMigrationMemlock>%llu</preMigrationMemlock>\n",
++                          priv->preMigrationMemlock);
++    }
++
+     return 0;
+ }
+ 
+@@ -3117,6 +3122,13 @@ qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt,
+         goto error;
+     }
+ 
++    if (virXPathULongLong("string(./preMigrationMemlock)", ctxt,
++                          &priv->preMigrationMemlock) == -2) {
++        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
++                       _("failed to parse pre-migration memlock limit"));
++        return -1;
++    }
++
+     return 0;
+ 
+  error:
+diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
+index 6d1d23439a..d6e8a7a0fb 100644
+--- a/src/qemu/qemu_domain.h
++++ b/src/qemu/qemu_domain.h
+@@ -146,6 +146,9 @@ struct _qemuDomainObjPrivate {
+     int nbdPort; /* Port used for migration with NBD */
+     unsigned short migrationPort;
+     int preMigrationState;
++    unsigned long long preMigrationMemlock; /* Original RLIMIT_MEMLOCK in case
++                                               it was changed for the current
++                                               migration job. */
+ 
+     virChrdevs *devs;
+ 
+diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
+index 10338f8e87..01102c4300 100644
+--- a/src/qemu/qemu_migration.c
++++ b/src/qemu/qemu_migration.c
+@@ -2974,7 +2974,8 @@ qemuMigrationDstPrepareAny(virQEMUDriver *driver,
+ 
+     if (STREQ_NULLABLE(protocol, "rdma") &&
+         vm->def->mem.hard_limit > 0 &&
+-        qemuDomainSetMaxMemLock(vm, vm->def->mem.hard_limit << 10, NULL) < 0) {
++        qemuDomainSetMaxMemLock(vm, vm->def->mem.hard_limit << 10,
++                                &priv->preMigrationMemlock) < 0) {
+         goto stopjob;
+     }
+ 
+@@ -3451,6 +3452,7 @@ qemuMigrationSrcConfirmPhase(virQEMUDriver *driver,
+                                          VIR_DOMAIN_EVENT_STOPPED_MIGRATED);
+         virObjectEventStateQueue(driver->domainEventState, event);
+         qemuDomainEventEmitJobCompleted(driver, vm);
++        priv->preMigrationMemlock = 0;
+     } else {
+         virErrorPtr orig_err;
+         int reason;
+@@ -3471,6 +3473,7 @@ qemuMigrationSrcConfirmPhase(virQEMUDriver *driver,
+ 
+         qemuMigrationParamsReset(driver, vm, QEMU_ASYNC_JOB_MIGRATION_OUT,
+                                  jobPriv->migParams, priv->job.apiFlags);
++        qemuDomainSetMaxMemLock(vm, 0, &priv->preMigrationMemlock);
+ 
+         qemuDomainSaveStatus(vm);
+     }
+@@ -4224,7 +4227,8 @@ qemuMigrationSrcRun(virQEMUDriver *driver,
+     case MIGRATION_DEST_HOST:
+         if (STREQ(spec->dest.host.protocol, "rdma") &&
+             vm->def->mem.hard_limit > 0 &&
+-            qemuDomainSetMaxMemLock(vm, vm->def->mem.hard_limit << 10, NULL) < 0) {
++            qemuDomainSetMaxMemLock(vm, vm->def->mem.hard_limit << 10,
++                                    &priv->preMigrationMemlock) < 0) {
+             goto exit_monitor;
+         }
+         rc = qemuMonitorMigrateToHost(priv->mon, migrate_flags,
+@@ -5408,6 +5412,7 @@ qemuMigrationSrcPerformPhase(virQEMUDriver *driver,
+     if (ret < 0) {
+         qemuMigrationParamsReset(driver, vm, QEMU_ASYNC_JOB_MIGRATION_OUT,
+                                  jobPriv->migParams, priv->job.apiFlags);
++        qemuDomainSetMaxMemLock(vm, 0, &priv->preMigrationMemlock);
+         qemuMigrationJobFinish(driver, vm);
+     } else {
+         qemuMigrationJobContinue(vm);
+@@ -5869,6 +5874,8 @@ qemuMigrationDstFinish(virQEMUDriver *driver,
+          */
+         if (inPostCopy)
+             g_clear_pointer(&priv->job.completed, qemuDomainJobInfoFree);
++
++        qemuDomainSetMaxMemLock(vm, 0, &priv->preMigrationMemlock);
+     }
+ 
+     qemuMigrationParamsReset(driver, vm, QEMU_ASYNC_JOB_MIGRATION_IN,
+-- 
+2.35.1
+
diff --git a/SOURCES/libvirt-qemu_migration-Use-qemuDomainSetMaxMemLock.patch b/SOURCES/libvirt-qemu_migration-Use-qemuDomainSetMaxMemLock.patch
new file mode 100644
index 0000000..2b853a2
--- /dev/null
+++ b/SOURCES/libvirt-qemu_migration-Use-qemuDomainSetMaxMemLock.patch
@@ -0,0 +1,53 @@
+From ad3f1dceac21369b9c18b6032a4ff859dc79bbc7 Mon Sep 17 00:00:00 2001
+Message-Id: <ad3f1dceac21369b9c18b6032a4ff859dc79bbc7@dist-git>
+From: Jiri Denemark <jdenemar@redhat.com>
+Date: Wed, 22 Jun 2022 09:04:04 +0200
+Subject: [PATCH] qemu_migration: Use qemuDomainSetMaxMemLock
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This helper will not try to set the limit if it is already big enough,
+which may be useful when libvirt daemon is running in a containerized
+environment and is not allowed to change memory locking limit.
+
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+Reviewed-by: Ján Tomko <jtomko@redhat.com>
+(cherry picked from commit 22ee8cbf090c45f999b76e3f8dc7a45065fc9edf)
+
+https://bugzilla.redhat.com/show_bug.cgi?id=2117272
+
+Conflicts:
+	src/qemu/qemu_migration.c
+            - refactoring for post-copy recovery not backported
+
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/qemu/qemu_migration.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
+index 2635ef1162..10338f8e87 100644
+--- a/src/qemu/qemu_migration.c
++++ b/src/qemu/qemu_migration.c
+@@ -2974,7 +2974,7 @@ qemuMigrationDstPrepareAny(virQEMUDriver *driver,
+ 
+     if (STREQ_NULLABLE(protocol, "rdma") &&
+         vm->def->mem.hard_limit > 0 &&
+-        virProcessSetMaxMemLock(vm->pid, vm->def->mem.hard_limit << 10) < 0) {
++        qemuDomainSetMaxMemLock(vm, vm->def->mem.hard_limit << 10, NULL) < 0) {
+         goto stopjob;
+     }
+ 
+@@ -4224,7 +4224,7 @@ qemuMigrationSrcRun(virQEMUDriver *driver,
+     case MIGRATION_DEST_HOST:
+         if (STREQ(spec->dest.host.protocol, "rdma") &&
+             vm->def->mem.hard_limit > 0 &&
+-            virProcessSetMaxMemLock(vm->pid, vm->def->mem.hard_limit << 10) < 0) {
++            qemuDomainSetMaxMemLock(vm, vm->def->mem.hard_limit << 10, NULL) < 0) {
+             goto exit_monitor;
+         }
+         rc = qemuMonitorMigrateToHost(priv->mon, migrate_flags,
+-- 
+2.35.1
+
diff --git a/SOURCES/libvirt-virDomainDiskDefValidate-Improve-error-messages-for-startupPolicy-checks.patch b/SOURCES/libvirt-virDomainDiskDefValidate-Improve-error-messages-for-startupPolicy-checks.patch
new file mode 100644
index 0000000..015d22d
--- /dev/null
+++ b/SOURCES/libvirt-virDomainDiskDefValidate-Improve-error-messages-for-startupPolicy-checks.patch
@@ -0,0 +1,52 @@
+From 4c57d8399b86c07ee0b1bafd2f8bf0ba10ff384f Mon Sep 17 00:00:00 2001
+Message-Id: <4c57d8399b86c07ee0b1bafd2f8bf0ba10ff384f@dist-git>
+From: Peter Krempa <pkrempa@redhat.com>
+Date: Tue, 14 Jun 2022 13:13:48 +0200
+Subject: [PATCH] virDomainDiskDefValidate: Improve error messages for
+ 'startupPolicy' checks
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Remove linebreak and mention the attribute name. Also prepare the error
+messages for future by substituting the type of offending access.
+
+Signed-off-by: Peter Krempa <pkrempa@redhat.com>
+Reviewed-by: Ján Tomko <jtomko@redhat.com>
+(cherry picked from commit 9a480737c8adf92c332aa850c5269191ceb04eb9)
+https://bugzilla.redhat.com/show_bug.cgi?id=2095758
+https://bugzilla.redhat.com/show_bug.cgi?id=2109571
+---
+ src/conf/domain_validate.c | 9 ++++-----
+ 1 file changed, 4 insertions(+), 5 deletions(-)
+
+diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c
+index a4271f1247..452742e67c 100644
+--- a/src/conf/domain_validate.c
++++ b/src/conf/domain_validate.c
+@@ -778,9 +778,9 @@ virDomainDiskDefValidate(const virDomainDef *def,
+     if (disk->startupPolicy != VIR_DOMAIN_STARTUP_POLICY_DEFAULT) {
+         if (disk->src->type == VIR_STORAGE_TYPE_NETWORK) {
+             virReportError(VIR_ERR_XML_ERROR,
+-                           _("Setting disk %s is not allowed for "
+-                             "disk of network type"),
+-                           virDomainStartupPolicyTypeToString(disk->startupPolicy));
++                           _("disk startupPolicy '%s' is not allowed for disk of '%s' type"),
++                           virDomainStartupPolicyTypeToString(disk->startupPolicy),
++                           virStorageTypeToString(disk->src->type));
+             return -1;
+         }
+ 
+@@ -788,8 +788,7 @@ virDomainDiskDefValidate(const virDomainDef *def,
+             disk->device != VIR_DOMAIN_DISK_DEVICE_FLOPPY &&
+             disk->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_REQUISITE) {
+             virReportError(VIR_ERR_XML_ERROR, "%s",
+-                           _("Setting disk 'requisite' is allowed only for "
+-                             "cdrom or floppy"));
++                           _("disk startupPolicy 'requisite' is allowed only for cdrom or floppy"));
+             return -1;
+         }
+     }
+-- 
+2.35.1
+
diff --git a/SOURCES/libvirt-virDomainDiskDefValidateStartupPolicy-Validate-disk-type-better.patch b/SOURCES/libvirt-virDomainDiskDefValidateStartupPolicy-Validate-disk-type-better.patch
new file mode 100644
index 0000000..78b4dd1
--- /dev/null
+++ b/SOURCES/libvirt-virDomainDiskDefValidateStartupPolicy-Validate-disk-type-better.patch
@@ -0,0 +1,43 @@
+From a5cdca9995a05ac45e882ad3e1ca1e4ab53a2c34 Mon Sep 17 00:00:00 2001
+Message-Id: <a5cdca9995a05ac45e882ad3e1ca1e4ab53a2c34@dist-git>
+From: Peter Krempa <pkrempa@redhat.com>
+Date: Tue, 14 Jun 2022 14:07:47 +0200
+Subject: [PATCH] virDomainDiskDefValidateStartupPolicy: Validate disk type
+ better
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Our startup policy checkers work only for local paths, so disk sources
+such as NVMe, or vhost-user can't be used with startup policy.
+
+Unfortunately the validation did not catch these cases. Fix it.
+
+Signed-off-by: Peter Krempa <pkrempa@redhat.com>
+Reviewed-by: Ján Tomko <jtomko@redhat.com>
+(cherry picked from commit b90d0f0a1e4ee52c828fb683c14c14e241e6fcbb)
+https://bugzilla.redhat.com/show_bug.cgi?id=2095758
+https://bugzilla.redhat.com/show_bug.cgi?id=2109571
+---
+ src/conf/domain_validate.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c
+index bfff7339ef..55759af9f8 100644
+--- a/src/conf/domain_validate.c
++++ b/src/conf/domain_validate.c
+@@ -604,7 +604,10 @@ virDomainDiskDefValidateStartupPolicy(const virDomainDiskDef *disk)
+     if (disk->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_DEFAULT)
+         return 0;
+ 
+-    if (disk->src->type == VIR_STORAGE_TYPE_NETWORK) {
++    /* We want to allow any startup policy for un-translated _TYPE_VOLUME disks.
++     * virStorageSourceGetActualType returns _TYPE_VOLUME in such case */
++    if (virStorageSourceGetActualType(disk->src) != VIR_STORAGE_TYPE_VOLUME &&
++        !virStorageSourceIsLocalStorage(disk->src)) {
+         virReportError(VIR_ERR_XML_ERROR,
+                        _("disk startupPolicy '%s' is not allowed for disk of '%s' type"),
+                        virDomainStartupPolicyTypeToString(disk->startupPolicy),
+-- 
+2.35.1
+
diff --git a/SOURCES/libvirt-virDomainDiskTranslateSourcePool-Fix-check-of-startupPolicy-definition.patch b/SOURCES/libvirt-virDomainDiskTranslateSourcePool-Fix-check-of-startupPolicy-definition.patch
new file mode 100644
index 0000000..8db5803
--- /dev/null
+++ b/SOURCES/libvirt-virDomainDiskTranslateSourcePool-Fix-check-of-startupPolicy-definition.patch
@@ -0,0 +1,57 @@
+From d56c0a4b1b57d9547d40088b6787d7503c09e2b9 Mon Sep 17 00:00:00 2001
+Message-Id: <d56c0a4b1b57d9547d40088b6787d7503c09e2b9@dist-git>
+From: Peter Krempa <pkrempa@redhat.com>
+Date: Tue, 14 Jun 2022 14:21:33 +0200
+Subject: [PATCH] virDomainDiskTranslateSourcePool: Fix check of
+ 'startupPolicy' definition
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The check was historically done only for _TYPE_VOLUME disks, but
+refactors to allow _TYPE_VOLUME disks in the backing chain caused a
+regression where we'd reject startupPolicy also for _TYPE_BLOCK disks
+which historically worked well.
+
+Fix it by using the 'virDomainDiskDefValidateStartupPolicy' helper and
+use it only when the top level image is a _TYPE_VOLUME as in other cases
+it was already validated. This also allows _TYPE_BLOCK volumes to use
+startup policy.
+
+Fixes: 37f01262eed9f37dd5eb7de8b83edd2fea741054
+Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2095758
+Signed-off-by: Peter Krempa <pkrempa@redhat.com>
+Reviewed-by: Ján Tomko <jtomko@redhat.com>
+(cherry picked from commit ed8984306e1cd44c424fda3ed412a4177dd7b84d)
+https://bugzilla.redhat.com/show_bug.cgi?id=2109571
+---
+ src/conf/domain_conf.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
+index 805a15848e..92510973e6 100644
+--- a/src/conf/domain_conf.c
++++ b/src/conf/domain_conf.c
+@@ -31311,13 +31311,13 @@ virDomainDiskTranslateSourcePool(virDomainDiskDef *def)
+ 
+         if (virDomainStorageSourceTranslateSourcePool(n, conn) < 0)
+             return -1;
+-    }
+ 
+-    if (def->startupPolicy != 0 &&
+-        virStorageSourceGetActualType(def->src) != VIR_STORAGE_TYPE_FILE) {
+-        virReportError(VIR_ERR_XML_ERROR, "%s",
+-                       _("'startupPolicy' is only valid for 'file' type volume"));
+-        return -1;
++        /* The validity of 'startupPolicy' setting is checked only for the top
++         * level image. For any other subsequent images we honour it only if
++         * possible */
++        if (n == def->src &&
++            virDomainDiskDefValidateStartupPolicy(def) < 0)
++            return -1;
+     }
+ 
+     return 0;
+-- 
+2.35.1
+
diff --git a/SOURCES/libvirt-virsh-Add-support-for-VIR_MIGRATE_ZEROCOPY-flag.patch b/SOURCES/libvirt-virsh-Add-support-for-VIR_MIGRATE_ZEROCOPY-flag.patch
new file mode 100644
index 0000000..a3211db
--- /dev/null
+++ b/SOURCES/libvirt-virsh-Add-support-for-VIR_MIGRATE_ZEROCOPY-flag.patch
@@ -0,0 +1,80 @@
+From eb87264dc41a7147fb88fdc4d666ef8d0e9bb882 Mon Sep 17 00:00:00 2001
+Message-Id: <eb87264dc41a7147fb88fdc4d666ef8d0e9bb882@dist-git>
+From: Jiri Denemark <jdenemar@redhat.com>
+Date: Wed, 22 Jun 2022 16:36:53 +0200
+Subject: [PATCH] virsh: Add support for VIR_MIGRATE_ZEROCOPY flag
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+Reviewed-by: Ján Tomko <jtomko@redhat.com>
+(cherry picked from commit efa3baeae70fbdf4ab032ca485cb9272ee96bd50)
+
+https://bugzilla.redhat.com/show_bug.cgi?id=2117272
+
+Conflicts:
+	docs/manpages/virsh.rst
+	tools/virsh-domain.c
+            - post-copy recovery not backported
+
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ docs/manpages/virsh.rst | 8 +++++++-
+ tools/virsh-domain.c    | 7 +++++++
+ 2 files changed, 14 insertions(+), 1 deletion(-)
+
+diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst
+index dd534c10cb..d24e7774a6 100644
+--- a/docs/manpages/virsh.rst
++++ b/docs/manpages/virsh.rst
+@@ -3225,7 +3225,8 @@ migrate
+    migrate [--live] [--offline] [--direct] [--p2p [--tunnelled]]
+       [--persistent] [--undefinesource] [--suspend] [--copy-storage-all]
+       [--copy-storage-inc] [--change-protection] [--unsafe] [--verbose]
+-      [--rdma-pin-all] [--abort-on-error] [--postcopy] [--postcopy-after-precopy]
++      [--rdma-pin-all] [--abort-on-error] [--postcopy]
++      [--postcopy-after-precopy] [--zerocopy]
+       domain desturi [migrateuri] [graphicsuri] [listen-address] [dname]
+       [--timeout seconds [--timeout-suspend | --timeout-postcopy]]
+       [--xml file] [--migrate-disks disk-list] [--disks-port port]
+@@ -3298,6 +3299,11 @@ high (and thus allowing the domain to lock most of the host's memory). Doing so
+ may be dangerous to both the domain and the host itself since the host's kernel
+ may run out of memory.
+ 
++*--zerocopy* requests zero-copy mechanism to be used for migrating memory pages.
++For QEMU/KVM this means QEMU will be temporarily allowed to lock all guest
++pages in host's memory, although only those that are queued for transfer will
++be locked at the same time.
++
+ ``Note``: Individual hypervisors usually do not support all possible types of
+ migration. For example, QEMU does not support direct migration.
+ 
+diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
+index b56f6a90f5..c5bade1dbf 100644
+--- a/tools/virsh-domain.c
++++ b/tools/virsh-domain.c
+@@ -10730,6 +10730,10 @@ static const vshCmdOptDef opts_migrate[] = {
+      .type = VSH_OT_BOOL,
+      .help = N_("automatically switch to post-copy migration after one pass of pre-copy")
+     },
++    {.name = "zerocopy",
++     .type = VSH_OT_BOOL,
++     .help = N_("use zero-copy mechanism for migrating memory pages")
++    },
+     {.name = "migrateuri",
+      .type = VSH_OT_STRING,
+      .completer = virshCompleteEmpty,
+@@ -11133,6 +11137,9 @@ doMigrate(void *opaque)
+     if (vshCommandOptBool(cmd, "postcopy"))
+         flags |= VIR_MIGRATE_POSTCOPY;
+ 
++    if (vshCommandOptBool(cmd, "zerocopy"))
++        flags |= VIR_MIGRATE_ZEROCOPY;
++
+     if (vshCommandOptBool(cmd, "tls"))
+         flags |= VIR_MIGRATE_TLS;
+ 
+-- 
+2.35.1
+
diff --git a/SPECS/libvirt.spec b/SPECS/libvirt.spec
index 0d357d8..3d6c119 100644
--- a/SPECS/libvirt.spec
+++ b/SPECS/libvirt.spec
@@ -210,7 +210,7 @@
 Summary: Library providing a simple virtualization API
 Name: libvirt
 Version: 8.0.0
-Release: 5.2%{?dist}%{?extra_release}
+Release: 5.4%{?dist}%{?extra_release}
 License: LGPLv2+
 URL: https://libvirt.org/
 
@@ -250,6 +250,18 @@ Patch27: libvirt-cpu_x86-Consolidate-signature-match-in-x86DecodeUseCandidate.pa
 Patch28: libvirt-cpu_x86-Refactor-feature-list-comparison-in-x86DecodeUseCandidate.patch
 Patch29: libvirt-cpu_x86-Penalize-disabled-features-when-computing-CPU-model.patch
 Patch30: libvirt-cpu_x86-Ignore-enabled-features-for-input-models-in-x86DecodeUseCandidate.patch
+Patch31: libvirt-virDomainDiskDefValidate-Improve-error-messages-for-startupPolicy-checks.patch
+Patch32: libvirt-domain_validate-Split-out-validation-of-disk-startup-policy.patch
+Patch33: libvirt-virDomainDiskDefValidateStartupPolicy-Validate-disk-type-better.patch
+Patch34: libvirt-virDomainDiskTranslateSourcePool-Fix-check-of-startupPolicy-definition.patch
+Patch35: libvirt-conf-Move-virDomainObj-originalMemlock-into-qemuDomainObjPrivate.patch
+Patch36: libvirt-qemu_domain-Format-qemuDomainObjPrivate-originalMemlock.patch
+Patch37: libvirt-qemu-Add-qemuDomainSetMaxMemLock-helper.patch
+Patch38: libvirt-qemu_migration-Use-qemuDomainSetMaxMemLock.patch
+Patch39: libvirt-qemu_migration-Restore-original-memory-locking-limit.patch
+Patch40: libvirt-Add-VIR_MIGRATE_ZEROCOPY-flag.patch
+Patch41: libvirt-virsh-Add-support-for-VIR_MIGRATE_ZEROCOPY-flag.patch
+Patch42: libvirt-qemu_migration-Implement-VIR_MIGRATE_ZEROCOPY-flag.patch
 
 Requires: libvirt-daemon = %{version}-%{release}
 Requires: libvirt-daemon-config-network = %{version}-%{release}
@@ -2123,6 +2135,22 @@ exit 0
 
 
 %changelog
+* Thu Aug 18 2022 Jiri Denemark <jdenemar@redhat.com> - 8.0.0-5.4.el8
+- conf: Move virDomainObj::originalMemlock into qemuDomainObjPrivate (rhbz#2117272)
+- qemu_domain: Format qemuDomainObjPrivate::originalMemlock (rhbz#2117272)
+- qemu: Add qemuDomainSetMaxMemLock helper (rhbz#2117272)
+- qemu_migration: Use qemuDomainSetMaxMemLock (rhbz#2117272)
+- qemu_migration: Restore original memory locking limit (rhbz#2117272)
+- Add VIR_MIGRATE_ZEROCOPY flag (rhbz#2117272)
+- virsh: Add support for VIR_MIGRATE_ZEROCOPY flag (rhbz#2117272)
+- qemu_migration: Implement VIR_MIGRATE_ZEROCOPY flag (rhbz#2117272)
+
+* Fri Jul 29 2022 Jiri Denemark <jdenemar@redhat.com> - 8.0.0-5.3.el8
+- virDomainDiskDefValidate: Improve error messages for 'startupPolicy' checks (rhbz#2109571)
+- domain_validate: Split out validation of disk startup policy (rhbz#2109571)
+- virDomainDiskDefValidateStartupPolicy: Validate disk type better (rhbz#2109571)
+- virDomainDiskTranslateSourcePool: Fix check of 'startupPolicy' definition (rhbz#2109571)
+
 * Tue May 17 2022 Jiri Denemark <jdenemar@redhat.com> - 8.0.0-5.2.el8
 - cpu_map: Disable cpu64-rhel* for host-model and baseline (rhbz#2084030)
 - cputest: Drop some old artificial baseline tests (rhbz#2084030)