From: Steve Sistare Date: Wed, 1 Oct 2025 15:33:57 +0000 (-0700) Subject: migration: cpr-exec save and load X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=efc65873131dac48d2f7620adfcd683834acc94b;p=thirdparty%2Fqemu.git 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 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 --- diff --git a/include/migration/cpr.h b/include/migration/cpr.h index 2b074d7a654..b84389ff04a 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 00000000000..81d84425e1b --- /dev/null +++ b/migration/cpr-exec.c @@ -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); +} diff --git a/migration/meson.build b/migration/meson.build index 0f71544a825..16909d54c51 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',