You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
qemu/migration/cpr.c

226 lines
5.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 "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 "system/runstate.h"
#include "trace.h"
/*************************************************************************/
/* cpr state container for all information to be saved. */
typedef QLIST_HEAD(CprFdList, CprFd) CprFdList;
typedef struct CprState {
CprFdList fds;
} CprState;
static 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;
}
/*************************************************************************/
#define CPR_STATE "CprState"
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()
}
};
/*************************************************************************/
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;
}
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;
}
}