[PATCH RFC 6/9] qemu: Add support for mapped-ram on save

Jim Fehlig via Devel posted 9 patches 3 months ago
[PATCH RFC 6/9] qemu: Add support for mapped-ram on save
Posted by Jim Fehlig via Devel 3 months ago
Signed-off-by: Jim Fehlig <jfehlig@suse.com>
---
 src/qemu/qemu_driver.c    |   2 +-
 src/qemu/qemu_migration.c |  79 ++++++++++++++++++++++++++++
 src/qemu/qemu_migration.h |   7 +++
 src/qemu/qemu_monitor.c   |  32 ++++++++++++
 src/qemu/qemu_monitor.h   |   4 ++
 src/qemu/qemu_saveimage.c | 105 ++++++++++++++++++++++++++++++--------
 src/qemu/qemu_saveimage.h |   1 +
 src/qemu/qemu_snapshot.c  |   2 +-
 8 files changed, 208 insertions(+), 24 deletions(-)

diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index f9761242d2..34f37210d9 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -2696,7 +2696,7 @@ qemuDomainSaveInternal(virQEMUDriver *driver,
     if (!(cookie = qemuDomainSaveCookieNew(vm)))
         goto endjob;
 
-    if (!(data = virQEMUSaveDataNew(driver, xml, cookie, was_running, compressed)))
+    if (!(data = virQEMUSaveDataNew(driver, vm, xml, cookie, was_running, compressed)))
         goto endjob;
     xml = NULL;
 
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 1faab5dd23..3110ef2621 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -7072,6 +7072,85 @@ qemuMigrationSrcToFile(virQEMUDriver *driver, virDomainObj *vm,
 }
 
 
+int
+qemuMigrationSrcToMappedFile(virQEMUDriver *driver, virDomainObj *vm,
+                             int fd,
+                             virDomainAsyncJob asyncJob)
+{
+    qemuDomainObjPrivate *priv = vm->privateData;
+    g_autoptr(qemuMigrationParams) saveParams = NULL;
+    unsigned long saveMigBandwidth = priv->migMaxBandwidth;
+    int rc;
+    int ret = -1;
+    virErrorPtr orig_err = NULL;
+
+    if (qemuMigrationSetDBusVMState(driver, vm) < 0)
+        return -1;
+
+    if (!(saveParams = qemuMigrationParamsForMappedSave()))
+        return -1;
+
+    /* Increase migration bandwidth to unlimited since target is a file.
+     * Failure to change migration speed is not fatal. */
+    if (qemuMigrationParamsSetULL(saveParams,
+                                  QEMU_MIGRATION_PARAM_MAX_BANDWIDTH,
+                                  QEMU_DOMAIN_MIG_BANDWIDTH_MAX * 1024 * 1024) < 0)
+        return -1;
+
+    if (qemuMigrationParamsApply(vm, asyncJob, saveParams, 0) < 0)
+        return -1;
+
+    priv->migMaxBandwidth = QEMU_DOMAIN_MIG_BANDWIDTH_MAX;
+
+    if (!virDomainObjIsActive(vm)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("guest unexpectedly quit"));
+        /* nothing to tear down */
+        return -1;
+    }
+
+    if (qemuDomainObjEnterMonitorAsync(vm, asyncJob) < 0)
+        goto cleanup;
+
+    rc = qemuMonitorMigrateToFdSet(vm, 0, fd);
+    qemuDomainObjExitMonitor(vm);
+    if (rc < 0)
+        goto cleanup;
+
+    rc = qemuMigrationSrcWaitForCompletion(vm, asyncJob, NULL, 0);
+
+    if (rc < 0) {
+        if (rc == -2) {
+            virErrorPreserveLast(&orig_err);
+            if (virDomainObjIsActive(vm))
+                qemuMigrationSrcCancel(vm, asyncJob, true);
+        }
+        goto cleanup;
+    }
+
+    qemuDomainEventEmitJobCompleted(driver, vm);
+    ret = 0;
+
+ cleanup:
+    if (ret < 0 && !orig_err)
+        virErrorPreserveLast(&orig_err);
+
+    /* Restore max migration bandwidth */
+    if (virDomainObjIsActive(vm)) {
+        if (qemuMigrationParamsSetULL(saveParams,
+                                      QEMU_MIGRATION_PARAM_MAX_BANDWIDTH,
+                                      saveMigBandwidth * 1024 * 1024) == 0)
+            ignore_value(qemuMigrationParamsApply(vm, asyncJob,
+                                                  saveParams, 0));
+        priv->migMaxBandwidth = saveMigBandwidth;
+    }
+
+    virErrorRestore(&orig_err);
+
+    return ret;
+}
+
+
 /**
  * This function is supposed to be used only to while reconnecting to a domain
  * with an active migration job.
diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h
index ed62fd4a91..f845a0198b 100644
--- a/src/qemu/qemu_migration.h
+++ b/src/qemu/qemu_migration.h
@@ -241,6 +241,13 @@ qemuMigrationSrcToFile(virQEMUDriver *driver,
                        virDomainAsyncJob asyncJob)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
 
+int
+qemuMigrationSrcToMappedFile(virQEMUDriver *driver,
+                             virDomainObj *vm,
+                             int fd,
+                             virDomainAsyncJob asyncJob)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
+
 int
 qemuMigrationSrcCancelUnattended(virDomainObj *vm,
                                  virDomainJobObj *oldJob);
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 34e2ccab97..4c92bd740a 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -2237,6 +2237,38 @@ qemuMonitorMigrateToFd(qemuMonitor *mon,
 }
 
 
+int
+qemuMonitorMigrateToFdSet(virDomainObj *vm,
+                          unsigned int flags,
+                          int fd)
+{
+    qemuDomainObjPrivate *priv = vm->privateData;
+    qemuMonitor *mon = priv->mon;
+    off_t offset;
+    g_autoptr(qemuFDPass) fdPassMigrate = NULL;
+    unsigned int setId;
+    g_autofree char *uri = NULL;
+
+    VIR_DEBUG("fd=%d flags=0x%x", fd, flags);
+
+    QEMU_CHECK_MONITOR(mon);
+
+    if ((offset = lseek(fd, 0, SEEK_CUR)) == -1)
+        return -1;
+
+    fdPassMigrate = qemuFDPassNew("migrate", priv);
+    qemuFDPassAddFD(fdPassMigrate, &fd, "-fd");
+    qemuFDPassTransferMonitor(fdPassMigrate, mon);
+
+    if (qemuFDPassGetId(fdPassMigrate, &setId) < 0)
+        return -1;
+
+    uri = g_strdup_printf("file:/dev/fdset/%u,offset=%#lx", setId, offset);
+
+    return qemuMonitorJSONMigrate(mon, flags, uri);
+}
+
+
 int
 qemuMonitorMigrateToHost(qemuMonitor *mon,
                          unsigned int flags,
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 6e81945201..c477def138 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -843,6 +843,10 @@ int qemuMonitorMigrateToFd(qemuMonitor *mon,
                            unsigned int flags,
                            int fd);
 
+int qemuMonitorMigrateToFdSet(virDomainObj *vm,
+                              unsigned int flags,
+                              int fd);
+
 int qemuMonitorMigrateToHost(qemuMonitor *mon,
                              unsigned int flags,
                              const char *protocol,
diff --git a/src/qemu/qemu_saveimage.c b/src/qemu/qemu_saveimage.c
index 30085dc7bc..8f28770086 100644
--- a/src/qemu/qemu_saveimage.c
+++ b/src/qemu/qemu_saveimage.c
@@ -96,6 +96,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(virQEMUSaveData, virQEMUSaveDataFree);
  */
 virQEMUSaveData *
 virQEMUSaveDataNew(virQEMUDriver *driver,
+                   virDomainObj *vm,
                    char *domXML,
                    qemuDomainSaveCookie *cookieObj,
                    bool running,
@@ -122,6 +123,10 @@ virQEMUSaveDataNew(virQEMUDriver *driver,
         goto error;
     }
     header->version = cfg->saveImageVersion;
+    /* Enable mapped-ram feature if available and save version >= 3 */
+    if (header->version >= QEMU_SAVE_VERSION &&
+        qemuMigrationCapsGet(vm, QEMU_MIGRATION_CAP_MAPPED_RAM))
+        header->features |= QEMU_SAVE_FEATURE_MAPPED_RAM;
 
     header->was_running = running ? 1 : 0;
     header->compressed = compressed;
@@ -369,6 +374,79 @@ qemuSaveImageDecompressionStop(virCommand *cmd,
 }
 
 
+static int
+qemuSaveImageCreateSequential(virQEMUDriver *driver,
+                              virDomainObj *vm,
+                              const char *path,
+                              int fd,
+                              virQEMUSaveData *data,
+                              virCommand *compressor,
+                              unsigned int flags,
+                              virDomainAsyncJob asyncJob)
+{
+    int ret = -1;
+    virFileWrapperFd *wrapperFd = NULL;
+    unsigned int wrapperFlags = VIR_FILE_WRAPPER_NON_BLOCKING;
+
+    if ((flags & VIR_DOMAIN_SAVE_BYPASS_CACHE))
+        wrapperFlags |= VIR_FILE_WRAPPER_BYPASS_CACHE;
+
+    if (!(wrapperFd = virFileWrapperFdNew(&fd, path, wrapperFlags)))
+        goto cleanup;
+
+    if (virQEMUSaveDataWrite(data, fd, path) < 0)
+        goto cleanup;
+
+    /* Perform the migration */
+    if (qemuMigrationSrcToFile(driver, vm, fd, compressor, asyncJob) < 0)
+        goto cleanup;
+
+    if (VIR_CLOSE(fd) < 0) {
+        virReportSystemError(errno, _("unable to close %1$s"), path);
+        goto cleanup;
+    }
+
+    if (qemuDomainFileWrapperFDClose(vm, wrapperFd) < 0)
+        goto cleanup;
+
+    ret = 0;
+
+ cleanup:
+    VIR_FORCE_CLOSE(fd);
+    if (qemuDomainFileWrapperFDClose(vm, wrapperFd) < 0)
+        ret = -1;
+    virFileWrapperFdFree(wrapperFd);
+
+    return ret;
+}
+
+
+static int
+qemuSaveImageCreateMapped(virQEMUDriver *driver,
+                          virDomainObj *vm,
+                          const char *path,
+                          int fd,
+                          virQEMUSaveData *data,
+                          unsigned int flags,
+                          virDomainAsyncJob asyncJob)
+{
+    g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
+
+    /* mapped-ram does not support directIO */
+    if ((flags & VIR_DOMAIN_SAVE_BYPASS_CACHE)) {
+        virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+                       _("bypass cache unsupported by this system"));
+        return -1;
+    }
+
+    if (virQEMUSaveDataWrite(data, fd, path) < 0)
+        return -1;
+
+    /* Perform the migration */
+    return qemuMigrationSrcToMappedFile(driver, vm, fd, asyncJob);
+}
+
+
 /* Helper function to execute a migration to file with a correct save header
  * the caller needs to make sure that the processors are stopped and do all other
  * actions besides saving memory */
@@ -386,12 +464,9 @@ qemuSaveImageCreate(virQEMUDriver *driver,
     int ret = -1;
     int fd = -1;
     int directFlag = 0;
-    virFileWrapperFd *wrapperFd = NULL;
-    unsigned int wrapperFlags = VIR_FILE_WRAPPER_NON_BLOCKING;
 
     /* Obtain the file handle.  */
     if ((flags & VIR_DOMAIN_SAVE_BYPASS_CACHE)) {
-        wrapperFlags |= VIR_FILE_WRAPPER_BYPASS_CACHE;
         directFlag = virFileDirectFdFlag();
         if (directFlag < 0) {
             virReportError(VIR_ERR_OPERATION_FAILED, "%s",
@@ -409,14 +484,12 @@ qemuSaveImageCreate(virQEMUDriver *driver,
     if (qemuSecuritySetImageFDLabel(driver->securityManager, vm->def, fd) < 0)
         goto cleanup;
 
-    if (!(wrapperFd = virFileWrapperFdNew(&fd, path, wrapperFlags)))
-        goto cleanup;
+    if (data->header.features & QEMU_SAVE_FEATURE_MAPPED_RAM)
+        ret = qemuSaveImageCreateMapped(driver, vm, path, fd, data, flags, asyncJob);
+    else
+        ret = qemuSaveImageCreateSequential(driver, vm, path, fd, data, compressor, flags, asyncJob);
 
-    if (virQEMUSaveDataWrite(data, fd, path) < 0)
-        goto cleanup;
-
-    /* Perform the migration */
-    if (qemuMigrationSrcToFile(driver, vm, fd, compressor, asyncJob) < 0)
+    if (ret < 0)
         goto cleanup;
 
     /* Touch up file header to mark image complete. */
@@ -425,14 +498,6 @@ qemuSaveImageCreate(virQEMUDriver *driver,
      * up to seek backwards on wrapperFd.  The reopened fd will
      * trigger a single page of file system cache pollution, but
      * that's acceptable.  */
-    if (VIR_CLOSE(fd) < 0) {
-        virReportSystemError(errno, _("unable to close %1$s"), path);
-        goto cleanup;
-    }
-
-    if (qemuDomainFileWrapperFDClose(vm, wrapperFd) < 0)
-        goto cleanup;
-
     if ((fd = qemuDomainOpenFile(cfg, vm->def, path, O_WRONLY, NULL)) < 0 ||
         virQEMUSaveDataFinish(data, &fd, path) < 0)
         goto cleanup;
@@ -441,10 +506,6 @@ qemuSaveImageCreate(virQEMUDriver *driver,
 
  cleanup:
     VIR_FORCE_CLOSE(fd);
-    if (qemuDomainFileWrapperFDClose(vm, wrapperFd) < 0)
-        ret = -1;
-    virFileWrapperFdFree(wrapperFd);
-
     if (ret < 0 && needUnlink)
         unlink(path);
 
diff --git a/src/qemu/qemu_saveimage.h b/src/qemu/qemu_saveimage.h
index 63ad5508ed..81d93bf33c 100644
--- a/src/qemu/qemu_saveimage.h
+++ b/src/qemu/qemu_saveimage.h
@@ -124,6 +124,7 @@ virQEMUSaveDataWrite(virQEMUSaveData *data,
 
 virQEMUSaveData *
 virQEMUSaveDataNew(virQEMUDriver *driver,
+                   virDomainObj *vm,
                    char *domXML,
                    qemuDomainSaveCookie *cookieObj,
                    bool running,
diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c
index 1d75208814..1e9e0e31d7 100644
--- a/src/qemu/qemu_snapshot.c
+++ b/src/qemu/qemu_snapshot.c
@@ -1390,7 +1390,7 @@ qemuSnapshotCreateActiveExternal(virQEMUDriver *driver,
             !(snapdef->cookie = (virObject *) qemuDomainSaveCookieNew(vm)))
             goto cleanup;
 
-        if (!(data = virQEMUSaveDataNew(driver, xml,
+        if (!(data = virQEMUSaveDataNew(driver, vm, xml,
                                         (qemuDomainSaveCookie *) snapdef->cookie,
                                         resume, compressed)))
             goto cleanup;
-- 
2.44.0
Re: [PATCH RFC 6/9] qemu: Add support for mapped-ram on save
Posted by Martin Kletzander 1 month, 1 week ago
On Thu, Jun 13, 2024 at 04:43:20PM -0600, Jim Fehlig via Devel wrote:
>Signed-off-by: Jim Fehlig <jfehlig@suse.com>
>---
> src/qemu/qemu_driver.c    |   2 +-
> src/qemu/qemu_migration.c |  79 ++++++++++++++++++++++++++++
> src/qemu/qemu_migration.h |   7 +++
> src/qemu/qemu_monitor.c   |  32 ++++++++++++
> src/qemu/qemu_monitor.h   |   4 ++
> src/qemu/qemu_saveimage.c | 105 ++++++++++++++++++++++++++++++--------
> src/qemu/qemu_saveimage.h |   1 +
> src/qemu/qemu_snapshot.c  |   2 +-
> 8 files changed, 208 insertions(+), 24 deletions(-)
>
>diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
>index f9761242d2..34f37210d9 100644
>--- a/src/qemu/qemu_driver.c
>+++ b/src/qemu/qemu_driver.c
>@@ -2696,7 +2696,7 @@ qemuDomainSaveInternal(virQEMUDriver *driver,
>     if (!(cookie = qemuDomainSaveCookieNew(vm)))
>         goto endjob;
>
>-    if (!(data = virQEMUSaveDataNew(driver, xml, cookie, was_running, compressed)))
>+    if (!(data = virQEMUSaveDataNew(driver, vm, xml, cookie, was_running, compressed)))
>         goto endjob;
>     xml = NULL;
>
>diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
>index 1faab5dd23..3110ef2621 100644
>--- a/src/qemu/qemu_migration.c
>+++ b/src/qemu/qemu_migration.c
>@@ -7072,6 +7072,85 @@ qemuMigrationSrcToFile(virQEMUDriver *driver, virDomainObj *vm,
> }
>
>
>+int
>+qemuMigrationSrcToMappedFile(virQEMUDriver *driver, virDomainObj *vm,
>+                             int fd,
>+                             virDomainAsyncJob asyncJob)
>+{
>+    qemuDomainObjPrivate *priv = vm->privateData;
>+    g_autoptr(qemuMigrationParams) saveParams = NULL;
>+    unsigned long saveMigBandwidth = priv->migMaxBandwidth;
>+    int rc;
>+    int ret = -1;
>+    virErrorPtr orig_err = NULL;
>+
>+    if (qemuMigrationSetDBusVMState(driver, vm) < 0)
>+        return -1;
>+
>+    if (!(saveParams = qemuMigrationParamsForMappedSave()))
>+        return -1;
>+
>+    /* Increase migration bandwidth to unlimited since target is a file.
>+     * Failure to change migration speed is not fatal. */
>+    if (qemuMigrationParamsSetULL(saveParams,
>+                                  QEMU_MIGRATION_PARAM_MAX_BANDWIDTH,
>+                                  QEMU_DOMAIN_MIG_BANDWIDTH_MAX * 1024 * 1024) < 0)
>+        return -1;
>+
>+    if (qemuMigrationParamsApply(vm, asyncJob, saveParams, 0) < 0)
>+        return -1;
>+
>+    priv->migMaxBandwidth = QEMU_DOMAIN_MIG_BANDWIDTH_MAX;
>+
>+    if (!virDomainObjIsActive(vm)) {
>+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>+                       _("guest unexpectedly quit"));
>+        /* nothing to tear down */
>+        return -1;
>+    }
>+
>+    if (qemuDomainObjEnterMonitorAsync(vm, asyncJob) < 0)
>+        goto cleanup;
>+
>+    rc = qemuMonitorMigrateToFdSet(vm, 0, fd);
>+    qemuDomainObjExitMonitor(vm);
>+    if (rc < 0)
>+        goto cleanup;
>+
>+    rc = qemuMigrationSrcWaitForCompletion(vm, asyncJob, NULL, 0);
>+
>+    if (rc < 0) {
>+        if (rc == -2) {
>+            virErrorPreserveLast(&orig_err);
>+            if (virDomainObjIsActive(vm))
>+                qemuMigrationSrcCancel(vm, asyncJob, true);
>+        }
>+        goto cleanup;
>+    }
>+
>+    qemuDomainEventEmitJobCompleted(driver, vm);
>+    ret = 0;
>+
>+ cleanup:
>+    if (ret < 0 && !orig_err)
>+        virErrorPreserveLast(&orig_err);
>+
>+    /* Restore max migration bandwidth */
>+    if (virDomainObjIsActive(vm)) {
>+        if (qemuMigrationParamsSetULL(saveParams,
>+                                      QEMU_MIGRATION_PARAM_MAX_BANDWIDTH,
>+                                      saveMigBandwidth * 1024 * 1024) == 0)
>+            ignore_value(qemuMigrationParamsApply(vm, asyncJob,
>+                                                  saveParams, 0));
>+        priv->migMaxBandwidth = saveMigBandwidth;
>+    }
>+
>+    virErrorRestore(&orig_err);
>+
>+    return ret;
>+}
>+
>+
> /**
>  * This function is supposed to be used only to while reconnecting to a domain
>  * with an active migration job.
>diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h
>index ed62fd4a91..f845a0198b 100644
>--- a/src/qemu/qemu_migration.h
>+++ b/src/qemu/qemu_migration.h
>@@ -241,6 +241,13 @@ qemuMigrationSrcToFile(virQEMUDriver *driver,
>                        virDomainAsyncJob asyncJob)
>     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
>
>+int
>+qemuMigrationSrcToMappedFile(virQEMUDriver *driver,
>+                             virDomainObj *vm,
>+                             int fd,
>+                             virDomainAsyncJob asyncJob)
>+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
>+
> int
> qemuMigrationSrcCancelUnattended(virDomainObj *vm,
>                                  virDomainJobObj *oldJob);
>diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
>index 34e2ccab97..4c92bd740a 100644
>--- a/src/qemu/qemu_monitor.c
>+++ b/src/qemu/qemu_monitor.c
>@@ -2237,6 +2237,38 @@ qemuMonitorMigrateToFd(qemuMonitor *mon,
> }
>
>
>+int
>+qemuMonitorMigrateToFdSet(virDomainObj *vm,
>+                          unsigned int flags,
>+                          int fd)
>+{
>+    qemuDomainObjPrivate *priv = vm->privateData;
>+    qemuMonitor *mon = priv->mon;
>+    off_t offset;
>+    g_autoptr(qemuFDPass) fdPassMigrate = NULL;
>+    unsigned int setId;
>+    g_autofree char *uri = NULL;
>+
>+    VIR_DEBUG("fd=%d flags=0x%x", fd, flags);
>+
>+    QEMU_CHECK_MONITOR(mon);
>+
>+    if ((offset = lseek(fd, 0, SEEK_CUR)) == -1)
>+        return -1;

virReportSystemError() should probably be here or this could be
checked/gathered before entering the monitor

>+
>+    fdPassMigrate = qemuFDPassNew("migrate", priv);
>+    qemuFDPassAddFD(fdPassMigrate, &fd, "-fd");
>+    qemuFDPassTransferMonitor(fdPassMigrate, mon);

This function can fail (and set an error)

>+
>+    if (qemuFDPassGetId(fdPassMigrate, &setId) < 0)
>+        return -1;

No error set here, but I see no way for it failing in this case.

>+
>+    uri = g_strdup_printf("file:/dev/fdset/%u,offset=%#lx", setId, offset);
>+
>+    return qemuMonitorJSONMigrate(mon, flags, uri);
>+}
>+
>+
> int
> qemuMonitorMigrateToHost(qemuMonitor *mon,
>                          unsigned int flags,
>diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
>index 6e81945201..c477def138 100644
>--- a/src/qemu/qemu_monitor.h
>+++ b/src/qemu/qemu_monitor.h
>@@ -843,6 +843,10 @@ int qemuMonitorMigrateToFd(qemuMonitor *mon,
>                            unsigned int flags,
>                            int fd);
>
>+int qemuMonitorMigrateToFdSet(virDomainObj *vm,
>+                              unsigned int flags,
>+                              int fd);
>+
> int qemuMonitorMigrateToHost(qemuMonitor *mon,
>                              unsigned int flags,
>                              const char *protocol,
>diff --git a/src/qemu/qemu_saveimage.c b/src/qemu/qemu_saveimage.c
>index 30085dc7bc..8f28770086 100644
>--- a/src/qemu/qemu_saveimage.c
>+++ b/src/qemu/qemu_saveimage.c
>@@ -96,6 +96,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(virQEMUSaveData, virQEMUSaveDataFree);
>  */
> virQEMUSaveData *
> virQEMUSaveDataNew(virQEMUDriver *driver,
>+                   virDomainObj *vm,
>                    char *domXML,
>                    qemuDomainSaveCookie *cookieObj,
>                    bool running,
>@@ -122,6 +123,10 @@ virQEMUSaveDataNew(virQEMUDriver *driver,
>         goto error;
>     }
>     header->version = cfg->saveImageVersion;
>+    /* Enable mapped-ram feature if available and save version >= 3 */
>+    if (header->version >= QEMU_SAVE_VERSION &&

This should really be "3" and not the save version in case we increase
it, but to be readable without magic numbers and also more error proof,
there should be a mapping of feature->minVersion maybe?

>+        qemuMigrationCapsGet(vm, QEMU_MIGRATION_CAP_MAPPED_RAM))
>+        header->features |= QEMU_SAVE_FEATURE_MAPPED_RAM;
>
>     header->was_running = running ? 1 : 0;
>     header->compressed = compressed;

Also in case we add support for compressed mapped-ram save images this
might screw up later since you record the compression, but don't execute
it (as mentioned in the cover letter).