[PATCH V4 5/8] migration: cpr-exec save and load

Steve Sistare posted 8 patches 6 days, 1 hour ago
Maintainers: Steve Sistare <steven.sistare@oracle.com>, Peter Xu <peterx@redhat.com>, Fabiano Rosas <farosas@suse.de>, "Dr. David Alan Gilbert" <dave@treblig.org>, Alex Williamson <alex.williamson@redhat.com>, "Cédric Le Goater" <clg@redhat.com>, Eric Blake <eblake@redhat.com>, Markus Armbruster <armbru@redhat.com>, Paolo Bonzini <pbonzini@redhat.com>, Stefan Weil <sw@weilnetz.de>
[PATCH V4 5/8] migration: cpr-exec save and load
Posted by Steve Sistare 6 days, 1 hour ago
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
Re: [PATCH V4 5/8] migration: cpr-exec save and load
Posted by Cédric Le Goater 5 days, 23 hours ago
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',
Re: [PATCH V4 5/8] migration: cpr-exec save and load
Posted by Steven Sistare 3 days, 21 hours ago
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',
> 


Re: [PATCH V4 5/8] migration: cpr-exec save and load
Posted by Cédric Le Goater 3 days, 8 hours ago
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',
>>
> 


Re: [PATCH V4 5/8] migration: cpr-exec save and load
Posted by Steven Sistare 2 days, 18 hours ago
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',
>>>
>>
>