migration: cpr-exec save and load

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>
Link: https://lore.kernel.org/r/1759332851-370353-6-git-send-email-steven.sistare@oracle.com
[peterx: fix build for Windows]
Signed-off-by: Peter Xu <peterx@redhat.com>
This commit is contained in:
Steve Sistare
2025-10-01 08:33:57 -07:00
committed by Peter Xu
parent f57ff59f1e
commit efc6587313
3 changed files with 105 additions and 0 deletions

View File

@@ -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

99
migration/cpr-exec.c Normal file
View File

@@ -0,0 +1,99 @@
/*
* 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;
#ifdef CONFIG_LINUX
mfd = qemu_memfd_create(CPR_EXEC_STATE_NAME, 0, false, 0, 0, errp);
#else
mfd = -1;
#endif
if (mfd < 0) {
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);
}

View File

@@ -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',