Currently cpr-exec uses a file to pass the VM and device state between the
old and new QEMU processes. Support using memfd as a migration transport so
this state can be passed without creating a state file.

Here is an example of using the new memfd transport with cpr-exec.

(qemu) migrate_set_parameter mode cpr-exec
(qemu) migrate_set_parameter cpr-exec-command qemu-kvm ... -incoming defer
(qemu) migrate -d memfd:
QEMU 11.1.50 monitor - type 'help' for more information

(qemu) info status
VM status: paused (inmigrate)
(qemu) migrate_incoming memfd:
(qemu) info status
VM status: running

Signed-off-by: Dongli Zhang <[email protected]>
---
 migration/channel.c   |  7 +++++++
 migration/cpr-exec.c  |  9 +++++++++
 migration/memfd.c     | 23 +++++++++++++++++++++++
 migration/memfd.h     |  2 ++
 migration/migration.c |  9 +++++++++
 5 files changed, 50 insertions(+)

diff --git a/migration/channel.c b/migration/channel.c
index 1e2935f926..96de40c108 100644
--- a/migration/channel.c
+++ b/migration/channel.c
@@ -18,6 +18,7 @@
 #include "file.h"
 #include "io/channel-socket.h"
 #include "io/channel-tls.h"
+#include "memfd.h"
 #include "migration.h"
 #include "multifd.h"
 #include "options.h"
@@ -62,6 +63,8 @@ void migration_connect_outgoing(MigrationState *s, 
MigrationAddress *addr,
         ioc = exec_connect_outgoing(s, addr->u.exec.args, errp);
     } else if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) {
         ioc = file_connect_outgoing(s, &addr->u.file, errp);
+    } else if (addr->transport == MIGRATION_ADDRESS_TYPE_MEMFD) {
+        ioc = memfd_connect_outgoing(errp);
     } else {
         error_setg(errp, "uri is not a valid migration protocol");
     }
@@ -92,6 +95,8 @@ void migration_connect_incoming(MigrationAddress *addr, Error 
**errp)
         exec_connect_incoming(addr->u.exec.args, errp);
     } else if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) {
         file_connect_incoming(&addr->u.file, errp);
+    } else if (addr->transport == MIGRATION_ADDRESS_TYPE_MEMFD) {
+        memfd_connect_incoming(errp);
     } else {
         error_setg(errp, "unknown migration protocol");
     }
@@ -394,6 +399,8 @@ bool migrate_uri_parse(const char *uri, MigrationChannel 
**channel,
                               errp)) {
             return false;
         }
+    } else if (g_str_equal(uri, "memfd:")) {
+        addr->transport = MIGRATION_ADDRESS_TYPE_MEMFD;
     } else {
         error_setg(errp, "unknown migration protocol: %s", uri);
         return false;
diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
index daa50916d2..388cfba80d 100644
--- a/migration/cpr-exec.c
+++ b/migration/cpr-exec.c
@@ -21,6 +21,7 @@
 #include "migration/vmstate.h"
 #include "system/runstate.h"
 #include "trace.h"
+#include "cpr-exec-memfd.h"
 
 #define CPR_EXEC_STATE_NAME "QEMU_CPR_EXEC_STATE"
 
@@ -143,6 +144,11 @@ static void cpr_exec_cb(void *opaque)
      * earlier because they should not persist across miscellaneous fork and
      * exec calls that are performed during normal operation.
      */
+    if (!cpr_exec_memfd_preserve_fd(&err)) {
+        g_clear_pointer(&argv, g_strfreev);
+        cpr_exec_memfd_cleanup();
+        goto fail;
+    }
     cpr_exec_preserve_fds();
 
     trace_cpr_exec();
@@ -155,7 +161,9 @@ static void cpr_exec_cb(void *opaque)
     error_setg_errno(&err, errno, "execvp %s failed", argv[0]);
     g_clear_pointer(&argv, g_strfreev);
     cpr_exec_unpreserve_fds();
+    cpr_exec_memfd_cleanup();
 
+fail:
     error_report_err(error_copy(err));
     migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
 
@@ -189,6 +197,7 @@ static int cpr_exec_notifier(NotifierWithReturn *notifier, 
MigrationEvent *e,
         qemu_notify_event();
     } else if (e->type == MIG_EVENT_FAILED) {
         cpr_exec_unpersist_state();
+        cpr_exec_memfd_cleanup();
     }
     return 0;
 }
diff --git a/migration/memfd.c b/migration/memfd.c
index 0b5ef0234f..4ed185d844 100644
--- a/migration/memfd.c
+++ b/migration/memfd.c
@@ -10,6 +10,9 @@
 #include "qemu/cutils.h"
 #include "qemu/memfd.h"
 #include "memfd.h"
+#include "cpr-exec-memfd.h"
+#include "migration/cpr.h"
+#include "options.h"
 
 QIOChannel *memfd_create_outgoing(const char *name, int *dup_fdp,
                                   Error **errp)
@@ -65,3 +68,23 @@ QIOChannel *memfd_open_incoming(int fd, const char *name, 
Error **errp)
     qio_channel_set_name(ioc, name);
     return ioc;
 }
+
+QIOChannel *memfd_connect_outgoing(Error **errp)
+{
+    if (migrate_mode() == MIG_MODE_CPR_EXEC) {
+        return cpr_exec_memfd_connect_outgoing(errp);
+    }
+
+    error_setg(errp, "memfd transport is only supported with cpr-exec");
+    return NULL;
+}
+
+void memfd_connect_incoming(Error **errp)
+{
+    if (cpr_get_incoming_mode() == MIG_MODE_CPR_EXEC) {
+        cpr_exec_memfd_connect_incoming(errp);
+        return;
+    }
+
+    error_setg(errp, "memfd transport is only supported with cpr-exec");
+}
diff --git a/migration/memfd.h b/migration/memfd.h
index cb6afe3eeb..9c50c84ad5 100644
--- a/migration/memfd.h
+++ b/migration/memfd.h
@@ -13,5 +13,7 @@
 QIOChannel *memfd_create_outgoing(const char *name, int *dup_fdp,
                                   Error **errp);
 QIOChannel *memfd_open_incoming(int fd, const char *name, Error **errp);
+QIOChannel *memfd_connect_outgoing(Error **errp);
+void memfd_connect_incoming(Error **errp);
 
 #endif
diff --git a/migration/migration.c b/migration/migration.c
index 9df6da131c..9f3afe5a3a 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -248,6 +248,15 @@ 
migration_channels_and_transport_compatible(MigrationAddress *addr,
         return false;
     }
 
+    if (addr->transport == MIGRATION_ADDRESS_TYPE_MEMFD) {
+        if (migrate_mode() != MIG_MODE_CPR_EXEC &&
+            cpr_get_incoming_mode() != MIG_MODE_CPR_EXEC) {
+            error_setg(errp,
+                       "memfd transport is only supported with cpr-exec");
+            return false;
+        }
+    }
+
     return true;
 }
 
-- 
2.43.5


Reply via email to