* [PATCH 1/5] migration/qapi: Add memfd migration transport
2026-08-12 23:27 [PATCH 0/5] Support diskless memfd transport for cpr-exec Dongli Zhang
@ 2026-08-12 23:27 ` Dongli Zhang
2026-08-20 7:09 ` Markus Armbruster
2026-08-12 23:27 ` [PATCH 2/5] migration: Add generic memfd channel helpers Dongli Zhang
` (3 subsequent siblings)
4 siblings, 1 reply; 7+ messages in thread
From: Dongli Zhang @ 2026-08-12 23:27 UTC (permalink / raw)
To: qemu-devel
Cc: peterx, farosas, eblake, armbru, maciej.szmigiero, mark.kanda,
bchaney, pierrick.bouvier, joe.jin
Add a memfd migration transport to the MigrationAddress QAPI schema. The
new transport has no user-supplied arguments because QEMU creates and
passes the backing memfd internally.
The initial user is cpr-exec, where the main VM/device migration stream can
be stored in an inherited memfd instead of an external file.
Signed-off-by: Dongli Zhang <dongli.zhang@oracle.com>
---
qapi/migration.json | 32 +++++++++++++++++++++++++++-----
1 file changed, 27 insertions(+), 5 deletions(-)
diff --git a/qapi/migration.json b/qapi/migration.json
index b1eaf7b054..1505c201ca 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -702,9 +702,11 @@
# @cpr-exec: The migrate command stops the VM, saves state to the
# migration channel, directly exec's a new version of QEMU on the
# same host, replacing the original process while retaining its
-# PID, and loads state from the channel. Guest RAM is preserved
-# in place. Devices and their pinned pages are also preserved for
-# VFIO and IOMMUFD.
+# PID, and loads state from the channel. With the @memfd
+# migration transport, QEMU saves VM/device state to an internal
+# memfd instead of an external main migration channel. Guest RAM
+# is preserved in place. Devices and their pinned pages are also
+# preserved for VFIO and IOMMUFD.
#
# Old QEMU starts new QEMU by exec'ing the command specified by
# the @cpr-exec-command parameter. The command may be a direct
@@ -716,6 +718,13 @@
# as a file, that accepts all data before old QEMU exits.
# Otherwise, old QEMU may quietly block writing to the channel.
#
+# Alternatively, use the @memfd migration transport to save
+# VM/device state to an internal memfd inherited by new QEMU.
+#
+# With @memfd, the new QEMU command may include '-incoming memfd:'
+# to load the migration stream automatically, or '-incoming defer'
+# to load it later with `migrate-incoming`.
+#
# Memory-backend objects must have the share=on attribute, but
# memory-backend-epc is not supported. The VM must be started
# with the '-machine aux-ram-share=on' option.
@@ -1309,10 +1318,14 @@
#
# @file: Direct the migration stream to a file.
#
+# @memfd: Direct the migration stream to an internal memfd. This
+# transport is currently only valid with cpr-exec migration mode.
+# (Since 11.2)
+#
# Since: 8.2
##
{ 'enum': 'MigrationAddressType',
- 'data': [ 'socket', 'exec', 'rdma', 'file' ] }
+ 'data': [ 'socket', 'exec', 'rdma', 'file', 'memfd' ] }
##
# @FileMigrationArgs:
@@ -1337,6 +1350,14 @@
{ 'struct': 'MigrationExecCommand',
'data': {'args': [ 'str' ] } }
+##
+# @MemfdMigrationArgs:
+#
+# Since: 11.2
+##
+{ 'struct': 'MemfdMigrationArgs',
+ 'data': { } }
+
##
# @MigrationAddress:
#
@@ -1353,7 +1374,8 @@
'socket': 'SocketAddress',
'exec': 'MigrationExecCommand',
'rdma': 'InetSocketAddress',
- 'file': 'FileMigrationArgs' } }
+ 'file': 'FileMigrationArgs',
+ 'memfd': 'MemfdMigrationArgs' } }
##
# @MigrationChannelType:
--
2.43.5
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH 1/5] migration/qapi: Add memfd migration transport
2026-08-12 23:27 ` [PATCH 1/5] migration/qapi: Add memfd migration transport Dongli Zhang
@ 2026-08-20 7:09 ` Markus Armbruster
0 siblings, 0 replies; 7+ messages in thread
From: Markus Armbruster @ 2026-08-20 7:09 UTC (permalink / raw)
To: Dongli Zhang
Cc: qemu-devel, peterx, farosas, eblake, maciej.szmigiero, mark.kanda,
bchaney, pierrick.bouvier, joe.jin
Dongli Zhang <dongli.zhang@oracle.com> writes:
> Add a memfd migration transport to the MigrationAddress QAPI schema. The
> new transport has no user-supplied arguments because QEMU creates and
> passes the backing memfd internally.
>
> The initial user is cpr-exec, where the main VM/device migration stream can
> be stored in an inherited memfd instead of an external file.
>
> Signed-off-by: Dongli Zhang <dongli.zhang@oracle.com>
> ---
> qapi/migration.json | 32 +++++++++++++++++++++++++++-----
> 1 file changed, 27 insertions(+), 5 deletions(-)
>
> diff --git a/qapi/migration.json b/qapi/migration.json
> index b1eaf7b054..1505c201ca 100644
> --- a/qapi/migration.json
> +++ b/qapi/migration.json
> @@ -702,9 +702,11 @@
> # @cpr-exec: The migrate command stops the VM, saves state to the
> # migration channel, directly exec's a new version of QEMU on the
> # same host, replacing the original process while retaining its
> -# PID, and loads state from the channel. Guest RAM is preserved
> -# in place. Devices and their pinned pages are also preserved for
> -# VFIO and IOMMUFD.
> +# PID, and loads state from the channel. With the @memfd
> +# migration transport, QEMU saves VM/device state to an internal
> +# memfd instead of an external main migration channel. Guest RAM
> +# is preserved in place. Devices and their pinned pages are also
> +# preserved for VFIO and IOMMUFD.
> #
> # Old QEMU starts new QEMU by exec'ing the command specified by
> # the @cpr-exec-command parameter. The command may be a direct
> @@ -716,6 +718,13 @@
> # as a file, that accepts all data before old QEMU exits.
> # Otherwise, old QEMU may quietly block writing to the channel.
> #
> +# Alternatively, use the @memfd migration transport to save
> +# VM/device state to an internal memfd inherited by new QEMU.
> +#
Any guidance on when to use @memfd?
> +# With @memfd, the new QEMU command may include '-incoming memfd:'
> +# to load the migration stream automatically, or '-incoming defer'
> +# to load it later with `migrate-incoming`.
> +#
Isn't that the same for any transport? I.e. old and new QEMU must use a
common transport, and you configure it for the new one with -incoming
right away, or later with migrate-incoming.
> # Memory-backend objects must have the share=on attribute, but
> # memory-backend-epc is not supported. The VM must be started
> # with the '-machine aux-ram-share=on' option.
> @@ -1309,10 +1318,14 @@
> #
> # @file: Direct the migration stream to a file.
> #
> +# @memfd: Direct the migration stream to an internal memfd. This
> +# transport is currently only valid with cpr-exec migration mode.
> +# (Since 11.2)
> +#
> # Since: 8.2
> ##
> { 'enum': 'MigrationAddressType',
> - 'data': [ 'socket', 'exec', 'rdma', 'file' ] }
> + 'data': [ 'socket', 'exec', 'rdma', 'file', 'memfd' ] }
>
> ##
> # @FileMigrationArgs:
> @@ -1337,6 +1350,14 @@
> { 'struct': 'MigrationExecCommand',
> 'data': {'args': [ 'str' ] } }
>
> +##
> +# @MemfdMigrationArgs:
> +#
> +# Since: 11.2
> +##
> +{ 'struct': 'MemfdMigrationArgs',
> + 'data': { } }
> +
> ##
> # @MigrationAddress:
> #
> @@ -1353,7 +1374,8 @@
> 'socket': 'SocketAddress',
> 'exec': 'MigrationExecCommand',
> 'rdma': 'InetSocketAddress',
> - 'file': 'FileMigrationArgs' } }
> + 'file': 'FileMigrationArgs',
> + 'memfd': 'MemfdMigrationArgs' } }
Simpler: omit branch @memfd, no need for struct MemfdMigrationArgs.
> ##
> # @MigrationChannelType:
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 2/5] migration: Add generic memfd channel helpers
2026-08-12 23:27 [PATCH 0/5] Support diskless memfd transport for cpr-exec Dongli Zhang
2026-08-12 23:27 ` [PATCH 1/5] migration/qapi: Add memfd migration transport Dongli Zhang
@ 2026-08-12 23:27 ` Dongli Zhang
2026-08-12 23:27 ` [PATCH 3/5] migration/cpr-exec: Add memfd helpers Dongli Zhang
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Dongli Zhang @ 2026-08-12 23:27 UTC (permalink / raw)
To: qemu-devel
Cc: peterx, farosas, eblake, armbru, maciej.szmigiero, mark.kanda,
bchaney, pierrick.bouvier, joe.jin
Add helpers for creating an outgoing memfd-backed migration channel and for
wrapping an existing memfd fd as an incoming migration channel. The
outgoing helper can optionally duplicate the memfd fd for the caller.
Signed-off-by: Dongli Zhang <dongli.zhang@oracle.com>
---
migration/memfd.c | 67 +++++++++++++++++++++++++++++++++++++++++++
migration/memfd.h | 17 +++++++++++
migration/meson.build | 1 +
3 files changed, 85 insertions(+)
create mode 100644 migration/memfd.c
create mode 100644 migration/memfd.h
diff --git a/migration/memfd.c b/migration/memfd.c
new file mode 100644
index 0000000000..0b5ef0234f
--- /dev/null
+++ b/migration/memfd.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * QEMU live migration via memfd
+ *
+ * Copyright (c) 2026 Oracle and/or its affiliates.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/cutils.h"
+#include "qemu/memfd.h"
+#include "memfd.h"
+
+QIOChannel *memfd_create_outgoing(const char *name, int *dup_fdp,
+ Error **errp)
+{
+ QIOChannelFile *fioc;
+ QIOChannel *ioc;
+ int fd;
+
+#ifdef CONFIG_LINUX
+ fd = qemu_memfd_create(name, 0, false, 0, 0, errp);
+#else
+ fd = -1;
+ error_setg(errp, "memfd migration is only supported on Linux");
+#endif
+
+ if (fd < 0) {
+ return NULL;
+ }
+
+ if (dup_fdp) {
+ int dup_fd = dup(fd);
+
+ if (dup_fd < 0) {
+ error_setg_errno(errp, errno,
+ "failed to duplicate memfd migration fd");
+ close(fd);
+ return NULL;
+ }
+
+ qemu_set_cloexec(dup_fd);
+ *dup_fdp = dup_fd;
+ }
+
+ fioc = qio_channel_file_new_fd(fd);
+ ioc = QIO_CHANNEL(fioc);
+ qio_channel_set_name(ioc, name);
+ return ioc;
+}
+
+QIOChannel *memfd_open_incoming(int fd, const char *name, Error **errp)
+{
+ QIOChannelFile *fioc;
+ QIOChannel *ioc;
+
+ if (lseek(fd, 0, SEEK_SET) < 0) {
+ error_setg_errno(errp, errno, "failed to rewind memfd migration fd");
+ close(fd);
+ return NULL;
+ }
+
+ fioc = qio_channel_file_new_fd(fd);
+ ioc = QIO_CHANNEL(fioc);
+ qio_channel_set_name(ioc, name);
+ return ioc;
+}
diff --git a/migration/memfd.h b/migration/memfd.h
new file mode 100644
index 0000000000..cb6afe3eeb
--- /dev/null
+++ b/migration/memfd.h
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * QEMU live migration via memfd
+ *
+ * Copyright (c) 2026 Oracle and/or its affiliates.
+ */
+
+#ifndef QEMU_MIGRATION_MEMFD_H
+#define QEMU_MIGRATION_MEMFD_H
+
+#include "io/channel-file.h"
+
+QIOChannel *memfd_create_outgoing(const char *name, int *dup_fdp,
+ Error **errp);
+QIOChannel *memfd_open_incoming(int fd, const char *name, Error **errp);
+
+#endif
diff --git a/migration/meson.build b/migration/meson.build
index 0a9a5d0d37..1d852b8c6d 100644
--- a/migration/meson.build
+++ b/migration/meson.build
@@ -23,6 +23,7 @@ system_ss.add(files(
'fd.c',
'file.c',
'global_state.c',
+ 'memfd.c',
'migration.c',
'multifd.c',
'multifd-device-state.c',
--
2.43.5
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 3/5] migration/cpr-exec: Add memfd helpers
2026-08-12 23:27 [PATCH 0/5] Support diskless memfd transport for cpr-exec Dongli Zhang
2026-08-12 23:27 ` [PATCH 1/5] migration/qapi: Add memfd migration transport Dongli Zhang
2026-08-12 23:27 ` [PATCH 2/5] migration: Add generic memfd channel helpers Dongli Zhang
@ 2026-08-12 23:27 ` Dongli Zhang
2026-08-12 23:27 ` [PATCH 4/5] migration/cpr-exec: Support diskless memfd transport Dongli Zhang
2026-08-12 23:27 ` [PATCH 5/5] migration: cpr-exec memfd transport documentation Dongli Zhang
4 siblings, 0 replies; 7+ messages in thread
From: Dongli Zhang @ 2026-08-12 23:27 UTC (permalink / raw)
To: qemu-devel
Cc: peterx, farosas, eblake, armbru, maciej.szmigiero, mark.kanda,
bchaney, pierrick.bouvier, joe.jin
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 <dongli.zhang@oracle.com>
---
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
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 4/5] migration/cpr-exec: Support diskless memfd transport
2026-08-12 23:27 [PATCH 0/5] Support diskless memfd transport for cpr-exec Dongli Zhang
` (2 preceding siblings ...)
2026-08-12 23:27 ` [PATCH 3/5] migration/cpr-exec: Add memfd helpers Dongli Zhang
@ 2026-08-12 23:27 ` Dongli Zhang
2026-08-12 23:27 ` [PATCH 5/5] migration: cpr-exec memfd transport documentation Dongli Zhang
4 siblings, 0 replies; 7+ messages in thread
From: Dongli Zhang @ 2026-08-12 23:27 UTC (permalink / raw)
To: qemu-devel
Cc: peterx, farosas, eblake, armbru, maciej.szmigiero, mark.kanda,
bchaney, pierrick.bouvier, joe.jin
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 <dongli.zhang@oracle.com>
---
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
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 5/5] migration: cpr-exec memfd transport documentation
2026-08-12 23:27 [PATCH 0/5] Support diskless memfd transport for cpr-exec Dongli Zhang
` (3 preceding siblings ...)
2026-08-12 23:27 ` [PATCH 4/5] migration/cpr-exec: Support diskless memfd transport Dongli Zhang
@ 2026-08-12 23:27 ` Dongli Zhang
4 siblings, 0 replies; 7+ messages in thread
From: Dongli Zhang @ 2026-08-12 23:27 UTC (permalink / raw)
To: qemu-devel
Cc: peterx, farosas, eblake, armbru, maciej.szmigiero, mark.kanda,
bchaney, pierrick.bouvier, joe.jin
Document the cpr-exec flow using the memfd migration transport. The example
keeps migrate_incoming explicit so the command sequence mirrors the
existing deferred incoming flow.
Signed-off-by: Dongli Zhang <dongli.zhang@oracle.com>
---
docs/devel/migration/CPR.rst | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/docs/devel/migration/CPR.rst b/docs/devel/migration/CPR.rst
index b6178568a8..8afc83a08d 100644
--- a/docs/devel/migration/CPR.rst
+++ b/docs/devel/migration/CPR.rst
@@ -369,6 +369,10 @@ Outgoing:
a ``file`` type, but one can use other types such as ``exec``,
provided the command captures all the data from the outgoing side,
and provides all the data to the incoming side.
+ * Alternatively, use the ``memfd:`` URI to save VM/device state
+ to an internal memfd inherited by the new QEMU. The new QEMU may
+ use ``-incoming memfd:`` to load it automatically, or ``-incoming
+ defer`` to load it later with ``migrate_incoming memfd:``.
Incoming:
* You do not need to explicitly start new QEMU. It is started as
@@ -424,6 +428,26 @@ Example 2: incoming defer
(qemu) info status
VM status: running
+Example 3: main state memfd
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+::
+
+ # qemu-kvm -monitor stdio
+ -machine q35,memory-backend=ram0,aux-ram-share=on
+ -object memory-backend-memfd,id=ram0,size=8G,share=on
+ ... ...
+
+ QEMU 11.1.50 monitor - type 'help' for more information
+ (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
+
Caveats
^^^^^^^
--
2.43.5
^ permalink raw reply related [flat|nested] 7+ messages in thread