To preserve CPR state across exec, create a QEMUFile based on a memfd, and
keep the memfd open across exec. Save the value of the memfd in an
environment variable so post-exec QEMU can find it.
These new functions are called in a subsequent patch.
Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
---
include/migration/cpr.h | 5 +++
migration/cpr-exec.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++
migration/meson.build | 1 +
3 files changed, 100 insertions(+)
create mode 100644 migration/cpr-exec.c
diff --git a/include/migration/cpr.h b/include/migration/cpr.h
index 2b074d7..b84389f 100644
--- a/include/migration/cpr.h
+++ b/include/migration/cpr.h
@@ -53,4 +53,9 @@ int cpr_get_fd_param(const char *name, const char *fdname, int index,
QEMUFile *cpr_transfer_output(MigrationChannel *channel, Error **errp);
QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp);
+QEMUFile *cpr_exec_output(Error **errp);
+QEMUFile *cpr_exec_input(Error **errp);
+void cpr_exec_persist_state(QEMUFile *f);
+bool cpr_exec_has_state(void);
+void cpr_exec_unpersist_state(void);
#endif
diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
new file mode 100644
index 0000000..2c32e9c
--- /dev/null
+++ b/migration/cpr-exec.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2021-2025 Oracle and/or its affiliates.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "qemu/memfd.h"
+#include "qapi/error.h"
+#include "io/channel-file.h"
+#include "io/channel-socket.h"
+#include "migration/cpr.h"
+#include "migration/qemu-file.h"
+#include "migration/misc.h"
+#include "migration/vmstate.h"
+#include "system/runstate.h"
+
+#define CPR_EXEC_STATE_NAME "QEMU_CPR_EXEC_STATE"
+
+static QEMUFile *qemu_file_new_fd_input(int fd, const char *name)
+{
+ g_autoptr(QIOChannelFile) fioc = qio_channel_file_new_fd(fd);
+ QIOChannel *ioc = QIO_CHANNEL(fioc);
+ qio_channel_set_name(ioc, name);
+ return qemu_file_new_input(ioc);
+}
+
+static QEMUFile *qemu_file_new_fd_output(int fd, const char *name)
+{
+ g_autoptr(QIOChannelFile) fioc = qio_channel_file_new_fd(fd);
+ QIOChannel *ioc = QIO_CHANNEL(fioc);
+ qio_channel_set_name(ioc, name);
+ return qemu_file_new_output(ioc);
+}
+
+void cpr_exec_persist_state(QEMUFile *f)
+{
+ QIOChannelFile *fioc = QIO_CHANNEL_FILE(qemu_file_get_ioc(f));
+ int mfd = dup(fioc->fd);
+ char val[16];
+
+ /* Remember mfd in environment for post-exec load */
+ qemu_clear_cloexec(mfd);
+ snprintf(val, sizeof(val), "%d", mfd);
+ g_setenv(CPR_EXEC_STATE_NAME, val, 1);
+}
+
+static int cpr_exec_find_state(void)
+{
+ const char *val = g_getenv(CPR_EXEC_STATE_NAME);
+ int mfd;
+
+ assert(val);
+ g_unsetenv(CPR_EXEC_STATE_NAME);
+ assert(!qemu_strtoi(val, NULL, 10, &mfd));
+ return mfd;
+}
+
+bool cpr_exec_has_state(void)
+{
+ return g_getenv(CPR_EXEC_STATE_NAME) != NULL;
+}
+
+void cpr_exec_unpersist_state(void)
+{
+ int mfd;
+ const char *val = g_getenv(CPR_EXEC_STATE_NAME);
+
+ g_unsetenv(CPR_EXEC_STATE_NAME);
+ assert(val);
+ assert(!qemu_strtoi(val, NULL, 10, &mfd));
+ close(mfd);
+}
+
+QEMUFile *cpr_exec_output(Error **errp)
+{
+ int mfd = memfd_create(CPR_EXEC_STATE_NAME, 0);
+
+ if (mfd < 0) {
+ error_setg_errno(errp, errno, "memfd_create failed");
+ return NULL;
+ }
+
+ return qemu_file_new_fd_output(mfd, CPR_EXEC_STATE_NAME);
+}
+
+QEMUFile *cpr_exec_input(Error **errp)
+{
+ int mfd = cpr_exec_find_state();
+
+ lseek(mfd, 0, SEEK_SET);
+ return qemu_file_new_fd_input(mfd, CPR_EXEC_STATE_NAME);
+}
diff --git a/migration/meson.build b/migration/meson.build
index 0f71544..16909d5 100644
--- a/migration/meson.build
+++ b/migration/meson.build
@@ -16,6 +16,7 @@ system_ss.add(files(
'channel-block.c',
'cpr.c',
'cpr-transfer.c',
+ 'cpr-exec.c',
'cpu-throttle.c',
'dirtyrate.c',
'exec.c',
--
1.8.3.1
On 9/22/25 15:49, Steve Sistare wrote:
> To preserve CPR state across exec, create a QEMUFile based on a memfd, and
> keep the memfd open across exec. Save the value of the memfd in an
> environment variable so post-exec QEMU can find it.
Couldn't we preserve some memory to hand off to QEMU ? Like firmwares
An environment variable is a limited method.
Thanks,
C.
That's a short term hack right ? it's not even documented. I am sure
you something else in mind.
> These new functions are called in a subsequent patch.
>
> Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
> ---
> include/migration/cpr.h | 5 +++
> migration/cpr-exec.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++
> migration/meson.build | 1 +
> 3 files changed, 100 insertions(+)
> create mode 100644 migration/cpr-exec.c
>
> diff --git a/include/migration/cpr.h b/include/migration/cpr.h
> index 2b074d7..b84389f 100644
> --- a/include/migration/cpr.h
> +++ b/include/migration/cpr.h
> @@ -53,4 +53,9 @@ int cpr_get_fd_param(const char *name, const char *fdname, int index,
> QEMUFile *cpr_transfer_output(MigrationChannel *channel, Error **errp);
> QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp);
>
> +QEMUFile *cpr_exec_output(Error **errp);
> +QEMUFile *cpr_exec_input(Error **errp);
> +void cpr_exec_persist_state(QEMUFile *f);
> +bool cpr_exec_has_state(void);
> +void cpr_exec_unpersist_state(void);
> #endif
> diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
> new file mode 100644
> index 0000000..2c32e9c
> --- /dev/null
> +++ b/migration/cpr-exec.c
> @@ -0,0 +1,94 @@
> +/*
> + * Copyright (c) 2021-2025 Oracle and/or its affiliates.
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/cutils.h"
> +#include "qemu/memfd.h"
> +#include "qapi/error.h"
> +#include "io/channel-file.h"
> +#include "io/channel-socket.h"
> +#include "migration/cpr.h"
> +#include "migration/qemu-file.h"
> +#include "migration/misc.h"
> +#include "migration/vmstate.h"
> +#include "system/runstate.h"
> +
> +#define CPR_EXEC_STATE_NAME "QEMU_CPR_EXEC_STATE"
> +
> +static QEMUFile *qemu_file_new_fd_input(int fd, const char *name)
> +{
> + g_autoptr(QIOChannelFile) fioc = qio_channel_file_new_fd(fd);
> + QIOChannel *ioc = QIO_CHANNEL(fioc);
> + qio_channel_set_name(ioc, name);
> + return qemu_file_new_input(ioc);
> +}
> +
> +static QEMUFile *qemu_file_new_fd_output(int fd, const char *name)
> +{
> + g_autoptr(QIOChannelFile) fioc = qio_channel_file_new_fd(fd);
> + QIOChannel *ioc = QIO_CHANNEL(fioc);
> + qio_channel_set_name(ioc, name);
> + return qemu_file_new_output(ioc);
> +}
> +
> +void cpr_exec_persist_state(QEMUFile *f)
> +{
> + QIOChannelFile *fioc = QIO_CHANNEL_FILE(qemu_file_get_ioc(f));
> + int mfd = dup(fioc->fd);
> + char val[16];
> +
> + /* Remember mfd in environment for post-exec load */
> + qemu_clear_cloexec(mfd);
> + snprintf(val, sizeof(val), "%d", mfd);
> + g_setenv(CPR_EXEC_STATE_NAME, val, 1);
> +}
> +
> +static int cpr_exec_find_state(void)
> +{
> + const char *val = g_getenv(CPR_EXEC_STATE_NAME);
> + int mfd;
> +
> + assert(val);
> + g_unsetenv(CPR_EXEC_STATE_NAME);
> + assert(!qemu_strtoi(val, NULL, 10, &mfd));
> + return mfd;
> +}
> +
> +bool cpr_exec_has_state(void)
> +{
> + return g_getenv(CPR_EXEC_STATE_NAME) != NULL;
> +}
> +
> +void cpr_exec_unpersist_state(void)
> +{
> + int mfd;
> + const char *val = g_getenv(CPR_EXEC_STATE_NAME);
> +
> + g_unsetenv(CPR_EXEC_STATE_NAME);
> + assert(val);
> + assert(!qemu_strtoi(val, NULL, 10, &mfd));
> + close(mfd);
> +}
> +
> +QEMUFile *cpr_exec_output(Error **errp)
> +{
> + int mfd = memfd_create(CPR_EXEC_STATE_NAME, 0);
The build should be adjusted for Linux only.
Thanks,
C.
> +
> + if (mfd < 0) {
> + error_setg_errno(errp, errno, "memfd_create failed");
> + return NULL;
> + }
> +
> + return qemu_file_new_fd_output(mfd, CPR_EXEC_STATE_NAME);
> +}
> +
> +QEMUFile *cpr_exec_input(Error **errp)
> +{
> + int mfd = cpr_exec_find_state();
> +
> + lseek(mfd, 0, SEEK_SET);
> + return qemu_file_new_fd_input(mfd, CPR_EXEC_STATE_NAME);
> +}
> diff --git a/migration/meson.build b/migration/meson.build
> index 0f71544..16909d5 100644
> --- a/migration/meson.build
> +++ b/migration/meson.build
> @@ -16,6 +16,7 @@ system_ss.add(files(
> 'channel-block.c',
> 'cpr.c',
> 'cpr-transfer.c',
> + 'cpr-exec.c',
> 'cpu-throttle.c',
> 'dirtyrate.c',
> 'exec.c',
On 9/22/2025 12:00 PM, Cédric Le Goater wrote:
> On 9/22/25 15:49, Steve Sistare wrote:
>> To preserve CPR state across exec, create a QEMUFile based on a memfd, and
>> keep the memfd open across exec. Save the value of the memfd in an
>> environment variable so post-exec QEMU can find it.
>
> Couldn't we preserve some memory to hand off to QEMU ? Like firmwares
> An environment variable is a limited method.
There is no upside in making this more complicated. We only need to
pass one tidbit of information -- the file descriptor number of the memfd
that contains all other information.
> Thanks,
>
> C.
>
> That's a short term hack right ? it's not even documented.
It is an implementation detail, known only to the matched saving
and loading functions inside qemu. No one else needs to know, so
no documentation.
- Steve
>I am sure
> you something else in mind.
>
>> These new functions are called in a subsequent patch.
>>
>> Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
>> ---
>> include/migration/cpr.h | 5 +++
>> migration/cpr-exec.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++
>> migration/meson.build | 1 +
>> 3 files changed, 100 insertions(+)
>> create mode 100644 migration/cpr-exec.c
>>
>> diff --git a/include/migration/cpr.h b/include/migration/cpr.h
>> index 2b074d7..b84389f 100644
>> --- a/include/migration/cpr.h
>> +++ b/include/migration/cpr.h
>> @@ -53,4 +53,9 @@ int cpr_get_fd_param(const char *name, const char *fdname, int index,
>> QEMUFile *cpr_transfer_output(MigrationChannel *channel, Error **errp);
>> QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp);
>> +QEMUFile *cpr_exec_output(Error **errp);
>> +QEMUFile *cpr_exec_input(Error **errp);
>> +void cpr_exec_persist_state(QEMUFile *f);
>> +bool cpr_exec_has_state(void);
>> +void cpr_exec_unpersist_state(void);
>> #endif
>> diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
>> new file mode 100644
>> index 0000000..2c32e9c
>> --- /dev/null
>> +++ b/migration/cpr-exec.c
>> @@ -0,0 +1,94 @@
>> +/*
>> + * Copyright (c) 2021-2025 Oracle and/or its affiliates.
>> + *
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qemu/cutils.h"
>> +#include "qemu/memfd.h"
>> +#include "qapi/error.h"
>> +#include "io/channel-file.h"
>> +#include "io/channel-socket.h"
>> +#include "migration/cpr.h"
>> +#include "migration/qemu-file.h"
>> +#include "migration/misc.h"
>> +#include "migration/vmstate.h"
>> +#include "system/runstate.h"
>> +
>> +#define CPR_EXEC_STATE_NAME "QEMU_CPR_EXEC_STATE"
>> +
>> +static QEMUFile *qemu_file_new_fd_input(int fd, const char *name)
>> +{
>> + g_autoptr(QIOChannelFile) fioc = qio_channel_file_new_fd(fd);
>> + QIOChannel *ioc = QIO_CHANNEL(fioc);
>> + qio_channel_set_name(ioc, name);
>> + return qemu_file_new_input(ioc);
>> +}
>> +
>> +static QEMUFile *qemu_file_new_fd_output(int fd, const char *name)
>> +{
>> + g_autoptr(QIOChannelFile) fioc = qio_channel_file_new_fd(fd);
>> + QIOChannel *ioc = QIO_CHANNEL(fioc);
>> + qio_channel_set_name(ioc, name);
>> + return qemu_file_new_output(ioc);
>> +}
>> +
>> +void cpr_exec_persist_state(QEMUFile *f)
>> +{
>> + QIOChannelFile *fioc = QIO_CHANNEL_FILE(qemu_file_get_ioc(f));
>> + int mfd = dup(fioc->fd);
>> + char val[16];
>> +
>> + /* Remember mfd in environment for post-exec load */
>> + qemu_clear_cloexec(mfd);
>> + snprintf(val, sizeof(val), "%d", mfd);
>> + g_setenv(CPR_EXEC_STATE_NAME, val, 1);
>> +}
>> +
>> +static int cpr_exec_find_state(void)
>> +{
>> + const char *val = g_getenv(CPR_EXEC_STATE_NAME);
>> + int mfd;
>> +
>> + assert(val);
>> + g_unsetenv(CPR_EXEC_STATE_NAME);
>> + assert(!qemu_strtoi(val, NULL, 10, &mfd));
>> + return mfd;
>> +}
>> +
>> +bool cpr_exec_has_state(void)
>> +{
>> + return g_getenv(CPR_EXEC_STATE_NAME) != NULL;
>> +}
>> +
>> +void cpr_exec_unpersist_state(void)
>> +{
>> + int mfd;
>> + const char *val = g_getenv(CPR_EXEC_STATE_NAME);
>> +
>> + g_unsetenv(CPR_EXEC_STATE_NAME);
>> + assert(val);
>> + assert(!qemu_strtoi(val, NULL, 10, &mfd));
>> + close(mfd);
>> +}
>> +
>> +QEMUFile *cpr_exec_output(Error **errp)
>> +{
>> + int mfd = memfd_create(CPR_EXEC_STATE_NAME, 0);
>
> The build should be adjusted for Linux only.
>
> Thanks,
>
> C.
>
>
>
>> +
>> + if (mfd < 0) {
>> + error_setg_errno(errp, errno, "memfd_create failed");
>> + return NULL;
>> + }
>> +
>> + return qemu_file_new_fd_output(mfd, CPR_EXEC_STATE_NAME);
>> +}
>> +
>> +QEMUFile *cpr_exec_input(Error **errp)
>> +{
>> + int mfd = cpr_exec_find_state();
>> +
>> + lseek(mfd, 0, SEEK_SET);
>> + return qemu_file_new_fd_input(mfd, CPR_EXEC_STATE_NAME);
>> +}
>> diff --git a/migration/meson.build b/migration/meson.build
>> index 0f71544..16909d5 100644
>> --- a/migration/meson.build
>> +++ b/migration/meson.build
>> @@ -16,6 +16,7 @@ system_ss.add(files(
>> 'channel-block.c',
>> 'cpr.c',
>> 'cpr-transfer.c',
>> + 'cpr-exec.c',
>> 'cpu-throttle.c',
>> 'dirtyrate.c',
>> 'exec.c',
>
On 9/24/25 20:16, Steven Sistare wrote:
> On 9/22/2025 12:00 PM, Cédric Le Goater wrote:
>> On 9/22/25 15:49, Steve Sistare wrote:
>>> To preserve CPR state across exec, create a QEMUFile based on a memfd, and
>>> keep the memfd open across exec. Save the value of the memfd in an
>>> environment variable so post-exec QEMU can find it.
>>
>> Couldn't we preserve some memory to hand off to QEMU ? Like firmwares
>> An environment variable is a limited method.
>
> There is no upside in making this more complicated. We only need to
> pass one tidbit of information -- the file descriptor number of the memfd
> that contains all other information.
Please adjust the build for windows, memfd is Linux only.
>> Thanks,
>>
>> C.
>>
>> That's a short term hack right ? it's not even documented.
>
> It is an implementation detail, known only to the matched saving
> and loading functions inside qemu. No one else needs to know, so
> no documentation.
ok. Fair enough.
Thanks,
C.
>
> - Steve
>
>> I am sure
>> you something else in mind.
>>
>>> These new functions are called in a subsequent patch.
>>>
>>> Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
>>> ---
>>> include/migration/cpr.h | 5 +++
>>> migration/cpr-exec.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++
>>> migration/meson.build | 1 +
>>> 3 files changed, 100 insertions(+)
>>> create mode 100644 migration/cpr-exec.c
>>>
>>> diff --git a/include/migration/cpr.h b/include/migration/cpr.h
>>> index 2b074d7..b84389f 100644
>>> --- a/include/migration/cpr.h
>>> +++ b/include/migration/cpr.h
>>> @@ -53,4 +53,9 @@ int cpr_get_fd_param(const char *name, const char *fdname, int index,
>>> QEMUFile *cpr_transfer_output(MigrationChannel *channel, Error **errp);
>>> QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp);
>>> +QEMUFile *cpr_exec_output(Error **errp);
>>> +QEMUFile *cpr_exec_input(Error **errp);
>>> +void cpr_exec_persist_state(QEMUFile *f);
>>> +bool cpr_exec_has_state(void);
>>> +void cpr_exec_unpersist_state(void);
>>> #endif
>>> diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
>>> new file mode 100644
>>> index 0000000..2c32e9c
>>> --- /dev/null
>>> +++ b/migration/cpr-exec.c
>>> @@ -0,0 +1,94 @@
>>> +/*
>>> + * Copyright (c) 2021-2025 Oracle and/or its affiliates.
>>> + *
>>> + * SPDX-License-Identifier: GPL-2.0-or-later
>>> + */
>>> +
>>> +#include "qemu/osdep.h"
>>> +#include "qemu/cutils.h"
>>> +#include "qemu/memfd.h"
>>> +#include "qapi/error.h"
>>> +#include "io/channel-file.h"
>>> +#include "io/channel-socket.h"
>>> +#include "migration/cpr.h"
>>> +#include "migration/qemu-file.h"
>>> +#include "migration/misc.h"
>>> +#include "migration/vmstate.h"
>>> +#include "system/runstate.h"
>>> +
>>> +#define CPR_EXEC_STATE_NAME "QEMU_CPR_EXEC_STATE"
>>> +
>>> +static QEMUFile *qemu_file_new_fd_input(int fd, const char *name)
>>> +{
>>> + g_autoptr(QIOChannelFile) fioc = qio_channel_file_new_fd(fd);
>>> + QIOChannel *ioc = QIO_CHANNEL(fioc);
>>> + qio_channel_set_name(ioc, name);
>>> + return qemu_file_new_input(ioc);
>>> +}
>>> +
>>> +static QEMUFile *qemu_file_new_fd_output(int fd, const char *name)
>>> +{
>>> + g_autoptr(QIOChannelFile) fioc = qio_channel_file_new_fd(fd);
>>> + QIOChannel *ioc = QIO_CHANNEL(fioc);
>>> + qio_channel_set_name(ioc, name);
>>> + return qemu_file_new_output(ioc);
>>> +}
>>> +
>>> +void cpr_exec_persist_state(QEMUFile *f)
>>> +{
>>> + QIOChannelFile *fioc = QIO_CHANNEL_FILE(qemu_file_get_ioc(f));
>>> + int mfd = dup(fioc->fd);
>>> + char val[16];
>>> +
>>> + /* Remember mfd in environment for post-exec load */
>>> + qemu_clear_cloexec(mfd);
>>> + snprintf(val, sizeof(val), "%d", mfd);
>>> + g_setenv(CPR_EXEC_STATE_NAME, val, 1);
>>> +}
>>> +
>>> +static int cpr_exec_find_state(void)
>>> +{
>>> + const char *val = g_getenv(CPR_EXEC_STATE_NAME);
>>> + int mfd;
>>> +
>>> + assert(val);
>>> + g_unsetenv(CPR_EXEC_STATE_NAME);
>>> + assert(!qemu_strtoi(val, NULL, 10, &mfd));
>>> + return mfd;
>>> +}
>>> +
>>> +bool cpr_exec_has_state(void)
>>> +{
>>> + return g_getenv(CPR_EXEC_STATE_NAME) != NULL;
>>> +}
>>> +
>>> +void cpr_exec_unpersist_state(void)
>>> +{
>>> + int mfd;
>>> + const char *val = g_getenv(CPR_EXEC_STATE_NAME);
>>> +
>>> + g_unsetenv(CPR_EXEC_STATE_NAME);
>>> + assert(val);
>>> + assert(!qemu_strtoi(val, NULL, 10, &mfd));
>>> + close(mfd);
>>> +}
>>> +
>>> +QEMUFile *cpr_exec_output(Error **errp)
>>> +{
>>> + int mfd = memfd_create(CPR_EXEC_STATE_NAME, 0);
>>
>> The build should be adjusted for Linux only.
>>
>> Thanks,
>>
>> C.
>>
>>
>>
>>> +
>>> + if (mfd < 0) {
>>> + error_setg_errno(errp, errno, "memfd_create failed");
>>> + return NULL;
>>> + }
>>> +
>>> + return qemu_file_new_fd_output(mfd, CPR_EXEC_STATE_NAME);
>>> +}
>>> +
>>> +QEMUFile *cpr_exec_input(Error **errp)
>>> +{
>>> + int mfd = cpr_exec_find_state();
>>> +
>>> + lseek(mfd, 0, SEEK_SET);
>>> + return qemu_file_new_fd_input(mfd, CPR_EXEC_STATE_NAME);
>>> +}
>>> diff --git a/migration/meson.build b/migration/meson.build
>>> index 0f71544..16909d5 100644
>>> --- a/migration/meson.build
>>> +++ b/migration/meson.build
>>> @@ -16,6 +16,7 @@ system_ss.add(files(
>>> 'channel-block.c',
>>> 'cpr.c',
>>> 'cpr-transfer.c',
>>> + 'cpr-exec.c',
>>> 'cpu-throttle.c',
>>> 'dirtyrate.c',
>>> 'exec.c',
>>
>
On Thu, Sep 25, 2025 at 09:11:33AM +0200, Cédric Le Goater wrote: > > > That's a short term hack right ? it's not even documented. > > > > It is an implementation detail, known only to the matched saving > > and loading functions inside qemu. No one else needs to know, so > > no documentation. > > ok. Fair enough. IMHO Cedric's ask is fair. At least when people reading the doc may get confused of why cpr channel isn't needed for the exec mode in its API. Could we still add one liner into the doc to describe it? Something that would mention a temp memfd and passing it over using environment vars would help. Thanks, -- Peter Xu
On 9/30/2025 12:19 PM, Peter Xu wrote: > On Thu, Sep 25, 2025 at 09:11:33AM +0200, Cédric Le Goater wrote: >>>> That's a short term hack right ? it's not even documented. >>> >>> It is an implementation detail, known only to the matched saving >>> and loading functions inside qemu. No one else needs to know, so >>> no documentation. >> >> ok. Fair enough. > > IMHO Cedric's ask is fair. At least when people reading the doc may get > confused of why cpr channel isn't needed for the exec mode in its API. > > Could we still add one liner into the doc to describe it? Something that > would mention a temp memfd and passing it over using environment vars would > help. Sure. I will add to CPR.rst: This mode does not require a channel of type ``cpr``. The information that is passed over that channel for cpr-transfer mode is instead serialized to a memfd, the number of the fd is saved in the QEMU_CPR_EXEC_STATE environment variable during the exec of new QEMU. and new QEMU mmaps the memfd. - Steve
On 9/25/2025 3:11 AM, Cédric Le Goater wrote:
> On 9/24/25 20:16, Steven Sistare wrote:
>> On 9/22/2025 12:00 PM, Cédric Le Goater wrote:
>>> On 9/22/25 15:49, Steve Sistare wrote:
>>>> To preserve CPR state across exec, create a QEMUFile based on a memfd, and
>>>> keep the memfd open across exec. Save the value of the memfd in an
>>>> environment variable so post-exec QEMU can find it.
>>>
>>> Couldn't we preserve some memory to hand off to QEMU ? Like firmwares
>>> An environment variable is a limited method.
>>
>> There is no upside in making this more complicated. We only need to
>> pass one tidbit of information -- the file descriptor number of the memfd
>> that contains all other information.
>
> Please adjust the build for windows, memfd is Linux only.
Will do, thanks. I will call qemu_memfd_create, which is defined for posix
and windows but returns error for the latter, instead of memfd_create.
- Steve
>>> That's a short term hack right ? it's not even documented.
>>
>> It is an implementation detail, known only to the matched saving
>> and loading functions inside qemu. No one else needs to know, so
>> no documentation.
>
> ok. Fair enough.
>
> Thanks,
>
> C.
>
>
>>
>> - Steve
>>
>>> I am sure
>>> you something else in mind.
>>>
>>>> These new functions are called in a subsequent patch.
>>>>
>>>> Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
>>>> ---
>>>> include/migration/cpr.h | 5 +++
>>>> migration/cpr-exec.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++
>>>> migration/meson.build | 1 +
>>>> 3 files changed, 100 insertions(+)
>>>> create mode 100644 migration/cpr-exec.c
>>>>
>>>> diff --git a/include/migration/cpr.h b/include/migration/cpr.h
>>>> index 2b074d7..b84389f 100644
>>>> --- a/include/migration/cpr.h
>>>> +++ b/include/migration/cpr.h
>>>> @@ -53,4 +53,9 @@ int cpr_get_fd_param(const char *name, const char *fdname, int index,
>>>> QEMUFile *cpr_transfer_output(MigrationChannel *channel, Error **errp);
>>>> QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp);
>>>> +QEMUFile *cpr_exec_output(Error **errp);
>>>> +QEMUFile *cpr_exec_input(Error **errp);
>>>> +void cpr_exec_persist_state(QEMUFile *f);
>>>> +bool cpr_exec_has_state(void);
>>>> +void cpr_exec_unpersist_state(void);
>>>> #endif
>>>> diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
>>>> new file mode 100644
>>>> index 0000000..2c32e9c
>>>> --- /dev/null
>>>> +++ b/migration/cpr-exec.c
>>>> @@ -0,0 +1,94 @@
>>>> +/*
>>>> + * Copyright (c) 2021-2025 Oracle and/or its affiliates.
>>>> + *
>>>> + * SPDX-License-Identifier: GPL-2.0-or-later
>>>> + */
>>>> +
>>>> +#include "qemu/osdep.h"
>>>> +#include "qemu/cutils.h"
>>>> +#include "qemu/memfd.h"
>>>> +#include "qapi/error.h"
>>>> +#include "io/channel-file.h"
>>>> +#include "io/channel-socket.h"
>>>> +#include "migration/cpr.h"
>>>> +#include "migration/qemu-file.h"
>>>> +#include "migration/misc.h"
>>>> +#include "migration/vmstate.h"
>>>> +#include "system/runstate.h"
>>>> +
>>>> +#define CPR_EXEC_STATE_NAME "QEMU_CPR_EXEC_STATE"
>>>> +
>>>> +static QEMUFile *qemu_file_new_fd_input(int fd, const char *name)
>>>> +{
>>>> + g_autoptr(QIOChannelFile) fioc = qio_channel_file_new_fd(fd);
>>>> + QIOChannel *ioc = QIO_CHANNEL(fioc);
>>>> + qio_channel_set_name(ioc, name);
>>>> + return qemu_file_new_input(ioc);
>>>> +}
>>>> +
>>>> +static QEMUFile *qemu_file_new_fd_output(int fd, const char *name)
>>>> +{
>>>> + g_autoptr(QIOChannelFile) fioc = qio_channel_file_new_fd(fd);
>>>> + QIOChannel *ioc = QIO_CHANNEL(fioc);
>>>> + qio_channel_set_name(ioc, name);
>>>> + return qemu_file_new_output(ioc);
>>>> +}
>>>> +
>>>> +void cpr_exec_persist_state(QEMUFile *f)
>>>> +{
>>>> + QIOChannelFile *fioc = QIO_CHANNEL_FILE(qemu_file_get_ioc(f));
>>>> + int mfd = dup(fioc->fd);
>>>> + char val[16];
>>>> +
>>>> + /* Remember mfd in environment for post-exec load */
>>>> + qemu_clear_cloexec(mfd);
>>>> + snprintf(val, sizeof(val), "%d", mfd);
>>>> + g_setenv(CPR_EXEC_STATE_NAME, val, 1);
>>>> +}
>>>> +
>>>> +static int cpr_exec_find_state(void)
>>>> +{
>>>> + const char *val = g_getenv(CPR_EXEC_STATE_NAME);
>>>> + int mfd;
>>>> +
>>>> + assert(val);
>>>> + g_unsetenv(CPR_EXEC_STATE_NAME);
>>>> + assert(!qemu_strtoi(val, NULL, 10, &mfd));
>>>> + return mfd;
>>>> +}
>>>> +
>>>> +bool cpr_exec_has_state(void)
>>>> +{
>>>> + return g_getenv(CPR_EXEC_STATE_NAME) != NULL;
>>>> +}
>>>> +
>>>> +void cpr_exec_unpersist_state(void)
>>>> +{
>>>> + int mfd;
>>>> + const char *val = g_getenv(CPR_EXEC_STATE_NAME);
>>>> +
>>>> + g_unsetenv(CPR_EXEC_STATE_NAME);
>>>> + assert(val);
>>>> + assert(!qemu_strtoi(val, NULL, 10, &mfd));
>>>> + close(mfd);
>>>> +}
>>>> +
>>>> +QEMUFile *cpr_exec_output(Error **errp)
>>>> +{
>>>> + int mfd = memfd_create(CPR_EXEC_STATE_NAME, 0);
>>>
>>> The build should be adjusted for Linux only.
>>>
>>> Thanks,
>>>
>>> C.
>>>
>>>
>>>
>>>> +
>>>> + if (mfd < 0) {
>>>> + error_setg_errno(errp, errno, "memfd_create failed");
>>>> + return NULL;
>>>> + }
>>>> +
>>>> + return qemu_file_new_fd_output(mfd, CPR_EXEC_STATE_NAME);
>>>> +}
>>>> +
>>>> +QEMUFile *cpr_exec_input(Error **errp)
>>>> +{
>>>> + int mfd = cpr_exec_find_state();
>>>> +
>>>> + lseek(mfd, 0, SEEK_SET);
>>>> + return qemu_file_new_fd_input(mfd, CPR_EXEC_STATE_NAME);
>>>> +}
>>>> diff --git a/migration/meson.build b/migration/meson.build
>>>> index 0f71544..16909d5 100644
>>>> --- a/migration/meson.build
>>>> +++ b/migration/meson.build
>>>> @@ -16,6 +16,7 @@ system_ss.add(files(
>>>> 'channel-block.c',
>>>> 'cpr.c',
>>>> 'cpr-transfer.c',
>>>> + 'cpr-exec.c',
>>>> 'cpu-throttle.c',
>>>> 'dirtyrate.c',
>>>> 'exec.c',
>>>
>>
>
© 2016 - 2026 Red Hat, Inc.