This is an incremental step in converting vmstate loading code to report error via Error objects instead of directly printing it to console/monitor. It is ensured that vmstate_load_state() must report an error in errp, in case of failure. The errors are temporarily reported using error_report_err(). This is removed in the subsequent patches in this series, when we are actually able to propagate the error to the calling function using errp. Whereas, if we want the function to exit on error, then error_fatal is passed. Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Fabiano Rosas <farosas@suse.de> Signed-off-by: Arun Menon <armenon@redhat.com> Tested-by: Fabiano Rosas <farosas@suse.de> Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Link: https://lore.kernel.org/r/20250918-propagate_tpm_error-v14-2-36f11a6fb9d3@redhat.com Signed-off-by: Peter Xu <peterx@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 "qemu/error-report.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_report("internal error: cpr fd '%s' id %d value %d "
|
|
"already saved with a different value %d",
|
|
name, id, fd, old_fd);
|
|
g_assert_not_reached();
|
|
}
|
|
}
|
|
|
|
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, errp);
|
|
if (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;
|
|
}
|