mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-03 15:53:54 -06:00

Define a list of vfio devices in CPR state, in a subsection so that older QEMU can be live updated to this version. However, new QEMU will not be live updateable to old QEMU. This is acceptable because CPR is not yet commonly used, and updates to older versions are unusual. The contents of each device object will be defined by the vfio subsystem in a subsequent patch. Signed-off-by: Steve Sistare <steven.sistare@oracle.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Link: https://lore.kernel.org/qemu-devel/1751493538-202042-14-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater <clg@redhat.com>
300 lines
7.1 KiB
C
300 lines
7.1 KiB
C
/*
|
|
* Copyright (c) 2021-2024 Oracle and/or its affiliates.
|
|
*
|
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
* See the COPYING file in the top-level directory.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qapi/error.h"
|
|
#include "hw/vfio/vfio-device.h"
|
|
#include "migration/cpr.h"
|
|
#include "migration/misc.h"
|
|
#include "migration/options.h"
|
|
#include "migration/qemu-file.h"
|
|
#include "migration/savevm.h"
|
|
#include "migration/vmstate.h"
|
|
#include "monitor/monitor.h"
|
|
#include "system/runstate.h"
|
|
#include "trace.h"
|
|
|
|
/*************************************************************************/
|
|
/* cpr state container for all information to be saved. */
|
|
|
|
CprState cpr_state;
|
|
|
|
/****************************************************************************/
|
|
|
|
typedef struct CprFd {
|
|
char *name;
|
|
unsigned int namelen;
|
|
int id;
|
|
int fd;
|
|
QLIST_ENTRY(CprFd) next;
|
|
} CprFd;
|
|
|
|
static const VMStateDescription vmstate_cpr_fd = {
|
|
.name = "cpr fd",
|
|
.version_id = 1,
|
|
.minimum_version_id = 1,
|
|
.fields = (VMStateField[]) {
|
|
VMSTATE_UINT32(namelen, CprFd),
|
|
VMSTATE_VBUFFER_ALLOC_UINT32(name, CprFd, 0, NULL, namelen),
|
|
VMSTATE_INT32(id, CprFd),
|
|
VMSTATE_FD(fd, CprFd),
|
|
VMSTATE_END_OF_LIST()
|
|
}
|
|
};
|
|
|
|
void cpr_save_fd(const char *name, int id, int fd)
|
|
{
|
|
CprFd *elem = g_new0(CprFd, 1);
|
|
|
|
trace_cpr_save_fd(name, id, fd);
|
|
elem->name = g_strdup(name);
|
|
elem->namelen = strlen(name) + 1;
|
|
elem->id = id;
|
|
elem->fd = fd;
|
|
QLIST_INSERT_HEAD(&cpr_state.fds, elem, next);
|
|
}
|
|
|
|
static CprFd *find_fd(CprFdList *head, const char *name, int id)
|
|
{
|
|
CprFd *elem;
|
|
|
|
QLIST_FOREACH(elem, head, next) {
|
|
if (!strcmp(elem->name, name) && elem->id == id) {
|
|
return elem;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void cpr_delete_fd(const char *name, int id)
|
|
{
|
|
CprFd *elem = find_fd(&cpr_state.fds, name, id);
|
|
|
|
if (elem) {
|
|
QLIST_REMOVE(elem, next);
|
|
g_free(elem->name);
|
|
g_free(elem);
|
|
}
|
|
|
|
trace_cpr_delete_fd(name, id);
|
|
}
|
|
|
|
int cpr_find_fd(const char *name, int id)
|
|
{
|
|
CprFd *elem = find_fd(&cpr_state.fds, name, id);
|
|
int fd = elem ? elem->fd : -1;
|
|
|
|
trace_cpr_find_fd(name, id, fd);
|
|
return fd;
|
|
}
|
|
|
|
void cpr_resave_fd(const char *name, int id, int fd)
|
|
{
|
|
CprFd *elem = find_fd(&cpr_state.fds, name, id);
|
|
int old_fd = elem ? elem->fd : -1;
|
|
|
|
if (old_fd < 0) {
|
|
cpr_save_fd(name, id, fd);
|
|
} else if (old_fd != fd) {
|
|
error_setg(&error_fatal,
|
|
"internal error: cpr fd '%s' id %d value %d "
|
|
"already saved with a different value %d",
|
|
name, id, fd, old_fd);
|
|
}
|
|
}
|
|
|
|
int cpr_open_fd(const char *path, int flags, const char *name, int id,
|
|
Error **errp)
|
|
{
|
|
int fd = cpr_find_fd(name, id);
|
|
|
|
if (fd < 0) {
|
|
fd = qemu_open(path, flags, errp);
|
|
if (fd >= 0) {
|
|
cpr_save_fd(name, id, fd);
|
|
}
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
static const VMStateDescription vmstate_cpr_state = {
|
|
.name = CPR_STATE,
|
|
.version_id = 1,
|
|
.minimum_version_id = 1,
|
|
.fields = (VMStateField[]) {
|
|
VMSTATE_QLIST_V(fds, CprState, 1, vmstate_cpr_fd, CprFd, next),
|
|
VMSTATE_END_OF_LIST()
|
|
},
|
|
.subsections = (const VMStateDescription * const []) {
|
|
&vmstate_cpr_vfio_devices,
|
|
NULL
|
|
}
|
|
};
|
|
/*************************************************************************/
|
|
|
|
static QEMUFile *cpr_state_file;
|
|
|
|
QIOChannel *cpr_state_ioc(void)
|
|
{
|
|
return qemu_file_get_ioc(cpr_state_file);
|
|
}
|
|
|
|
static MigMode incoming_mode = MIG_MODE_NONE;
|
|
|
|
MigMode cpr_get_incoming_mode(void)
|
|
{
|
|
return incoming_mode;
|
|
}
|
|
|
|
void cpr_set_incoming_mode(MigMode mode)
|
|
{
|
|
incoming_mode = mode;
|
|
}
|
|
|
|
bool cpr_is_incoming(void)
|
|
{
|
|
return incoming_mode != MIG_MODE_NONE;
|
|
}
|
|
|
|
int cpr_state_save(MigrationChannel *channel, Error **errp)
|
|
{
|
|
int ret;
|
|
QEMUFile *f;
|
|
MigMode mode = migrate_mode();
|
|
|
|
trace_cpr_state_save(MigMode_str(mode));
|
|
|
|
if (mode == MIG_MODE_CPR_TRANSFER) {
|
|
g_assert(channel);
|
|
f = cpr_transfer_output(channel, errp);
|
|
} else {
|
|
return 0;
|
|
}
|
|
if (!f) {
|
|
return -1;
|
|
}
|
|
|
|
qemu_put_be32(f, QEMU_CPR_FILE_MAGIC);
|
|
qemu_put_be32(f, QEMU_CPR_FILE_VERSION);
|
|
|
|
ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0);
|
|
if (ret) {
|
|
error_setg(errp, "vmstate_save_state error %d", ret);
|
|
qemu_fclose(f);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Close the socket only partially so we can later detect when the other
|
|
* end closes by getting a HUP event.
|
|
*/
|
|
qemu_fflush(f);
|
|
qio_channel_shutdown(qemu_file_get_ioc(f), QIO_CHANNEL_SHUTDOWN_WRITE,
|
|
NULL);
|
|
cpr_state_file = f;
|
|
return 0;
|
|
}
|
|
|
|
int cpr_state_load(MigrationChannel *channel, Error **errp)
|
|
{
|
|
int ret;
|
|
uint32_t v;
|
|
QEMUFile *f;
|
|
MigMode mode = 0;
|
|
|
|
if (channel) {
|
|
mode = MIG_MODE_CPR_TRANSFER;
|
|
cpr_set_incoming_mode(mode);
|
|
f = cpr_transfer_input(channel, errp);
|
|
} else {
|
|
return 0;
|
|
}
|
|
if (!f) {
|
|
return -1;
|
|
}
|
|
|
|
trace_cpr_state_load(MigMode_str(mode));
|
|
|
|
v = qemu_get_be32(f);
|
|
if (v != QEMU_CPR_FILE_MAGIC) {
|
|
error_setg(errp, "Not a migration stream (bad magic %x)", v);
|
|
qemu_fclose(f);
|
|
return -EINVAL;
|
|
}
|
|
v = qemu_get_be32(f);
|
|
if (v != QEMU_CPR_FILE_VERSION) {
|
|
error_setg(errp, "Unsupported migration stream version %d", v);
|
|
qemu_fclose(f);
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
ret = vmstate_load_state(f, &vmstate_cpr_state, &cpr_state, 1);
|
|
if (ret) {
|
|
error_setg(errp, "vmstate_load_state error %d", ret);
|
|
qemu_fclose(f);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Let the caller decide when to close the socket (and generate a HUP event
|
|
* for the sending side).
|
|
*/
|
|
cpr_state_file = f;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void cpr_state_close(void)
|
|
{
|
|
if (cpr_state_file) {
|
|
qemu_fclose(cpr_state_file);
|
|
cpr_state_file = NULL;
|
|
}
|
|
}
|
|
|
|
bool cpr_incoming_needed(void *opaque)
|
|
{
|
|
MigMode mode = migrate_mode();
|
|
return mode == MIG_MODE_CPR_TRANSFER;
|
|
}
|
|
|
|
/*
|
|
* cpr_get_fd_param: find a descriptor and return its value.
|
|
*
|
|
* @name: CPR name for the descriptor
|
|
* @fdname: An integer-valued string, or a name passed to a getfd command
|
|
* @index: CPR index of the descriptor
|
|
* @errp: returned error message
|
|
*
|
|
* If CPR is not being performed, then use @fdname to find the fd.
|
|
* If CPR is being performed, then ignore @fdname, and look for @name
|
|
* and @index in CPR state.
|
|
*
|
|
* On success returns the fd value, else returns -1.
|
|
*/
|
|
int cpr_get_fd_param(const char *name, const char *fdname, int index,
|
|
Error **errp)
|
|
{
|
|
ERRP_GUARD();
|
|
int fd;
|
|
|
|
if (cpr_is_incoming()) {
|
|
fd = cpr_find_fd(name, index);
|
|
if (fd < 0) {
|
|
error_setg(errp, "cannot find saved value for fd %s", fdname);
|
|
}
|
|
} else {
|
|
fd = monitor_fd_param(monitor_cur(), fdname, errp);
|
|
if (fd >= 0) {
|
|
cpr_save_fd(name, index, fd);
|
|
} else {
|
|
error_prepend(errp, "Could not parse object fd %s:", fdname);
|
|
}
|
|
}
|
|
return fd;
|
|
}
|