Add cpr-exec specific helpers for using a memfd as the main migration state
channel. The helpers create the outgoing memfd channel, preserve the
duplicated fd across exec via an environment variable, claim the fd on the
incoming side, and clean up the saved fd on failure paths.

The generic memfd helper remains unchanged. The cpr-exec specific helpers
use it for channel creation and keep exec inheritance local to cpr-exec.

Signed-off-by: Dongli Zhang <[email protected]>
---
 migration/cpr-exec-memfd.c | 90 ++++++++++++++++++++++++++++++++++++++
 migration/cpr-exec-memfd.h | 18 ++++++++
 migration/meson.build      |  1 +
 3 files changed, 109 insertions(+)
 create mode 100644 migration/cpr-exec-memfd.c
 create mode 100644 migration/cpr-exec-memfd.h

diff --git a/migration/cpr-exec-memfd.c b/migration/cpr-exec-memfd.c
new file mode 100644
index 0000000000..2bc15f644e
--- /dev/null
+++ b/migration/cpr-exec-memfd.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * CPR exec migration via memfd
+ *
+ * Copyright (c) 2026 Oracle and/or its affiliates.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/cutils.h"
+#include "channel.h"
+#include "cpr-exec-memfd.h"
+#include "memfd.h"
+
+#define CPR_EXEC_MEMFD_NAME "qemu-cpr-exec-migration"
+#define CPR_EXEC_MEMFD_ENV "QEMU_CPR_EXEC_MIGRATION_FD"
+
+static int cpr_exec_memfd_fd = -1;
+
+static int cpr_exec_memfd_claim_fd(Error **errp)
+{
+    const char *val = g_getenv(CPR_EXEC_MEMFD_ENV);
+    int fd;
+
+    if (!val) {
+        error_setg(errp, "missing %s environment variable",
+                   CPR_EXEC_MEMFD_ENV);
+        return -1;
+    }
+
+    if (qemu_strtoi(val, NULL, 10, &fd) < 0) {
+        error_setg(errp, "bad %s value %s", CPR_EXEC_MEMFD_ENV, val);
+        return -1;
+    }
+
+    g_unsetenv(CPR_EXEC_MEMFD_ENV);
+    return fd;
+}
+
+QIOChannel *cpr_exec_memfd_connect_outgoing(Error **errp)
+{
+    return memfd_create_outgoing(CPR_EXEC_MEMFD_NAME, &cpr_exec_memfd_fd,
+                                 errp);
+}
+
+void cpr_exec_memfd_connect_incoming(Error **errp)
+{
+    g_autoptr(QIOChannel) ioc = NULL;
+    int fd = cpr_exec_memfd_claim_fd(errp);
+
+    if (fd < 0) {
+        return;
+    }
+
+    ioc = memfd_open_incoming(fd, CPR_EXEC_MEMFD_NAME, errp);
+    if (!ioc) {
+        return;
+    }
+
+    migration_channel_process_incoming(ioc);
+}
+
+bool cpr_exec_memfd_preserve_fd(Error **errp)
+{
+    char val[16];
+
+    if (cpr_exec_memfd_fd < 0) {
+        return true;
+    }
+
+    qemu_clear_cloexec(cpr_exec_memfd_fd);
+    snprintf(val, sizeof(val), "%d", cpr_exec_memfd_fd);
+    if (!g_setenv(CPR_EXEC_MEMFD_ENV, val, 1)) {
+        error_setg(errp, "Setting env %s = %s failed",
+                   CPR_EXEC_MEMFD_ENV, val);
+        return false;
+    }
+
+    return true;
+}
+
+void cpr_exec_memfd_cleanup(void)
+{
+    g_unsetenv(CPR_EXEC_MEMFD_ENV);
+
+    if (cpr_exec_memfd_fd >= 0) {
+        close(cpr_exec_memfd_fd);
+        cpr_exec_memfd_fd = -1;
+    }
+}
diff --git a/migration/cpr-exec-memfd.h b/migration/cpr-exec-memfd.h
new file mode 100644
index 0000000000..1c74c7bfde
--- /dev/null
+++ b/migration/cpr-exec-memfd.h
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * CPR exec migration via memfd
+ *
+ * Copyright (c) 2026 Oracle and/or its affiliates.
+ */
+
+#ifndef QEMU_MIGRATION_CPR_EXEC_MEMFD_H
+#define QEMU_MIGRATION_CPR_EXEC_MEMFD_H
+
+#include "io/channel-file.h"
+
+QIOChannel *cpr_exec_memfd_connect_outgoing(Error **errp);
+void cpr_exec_memfd_connect_incoming(Error **errp);
+bool cpr_exec_memfd_preserve_fd(Error **errp);
+void cpr_exec_memfd_cleanup(void);
+
+#endif
diff --git a/migration/meson.build b/migration/meson.build
index 1d852b8c6d..2e8a89637e 100644
--- a/migration/meson.build
+++ b/migration/meson.build
@@ -17,6 +17,7 @@ system_ss.add(files(
   'cpr.c',
   'cpr-transfer.c',
   'cpr-exec.c',
+  'cpr-exec-memfd.c',
   'cpu-throttle.c',
   'dirtyrate.c',
   'exec.c',
-- 
2.43.5


Reply via email to