There are some situations during migration when we want to change the runstate of the VM, but don't actually want the new runstate to be put on the wire to be restored on the destination VM. In those cases, the pattern is to use global_state_store() to save the state for migration before changing it. One scenario where this happens is when switching the source VM into the FINISH_MIGRATE state. This state only makes sense on the source VM. Another situation is when pausing the source VM prior to migration completion. We are about to introduce a third scenario when the whole migration should be performed with a paused VM. In this case we will want to save the VM runstate at the very start of the migration and that state will be the one restored on the destination regardless of all the runstate changes that happen in between. To achieve that we need to make sure that the other two calls to global_state_store() do not overwrite the state that is to be migrated. Introduce a version of global_state_store() that only saves the state if no other state has already been saved. Signed-off-by: Fabiano Rosas <farosas@suse.de>
164 lines
3.8 KiB
C
164 lines
3.8 KiB
C
/*
|
|
* Global State configuration
|
|
*
|
|
* Copyright (c) 2014-2017 Red Hat Inc
|
|
*
|
|
* Authors:
|
|
* Juan Quintela <quintela@redhat.com>
|
|
*
|
|
* 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 "qemu/cutils.h"
|
|
#include "qemu/error-report.h"
|
|
#include "sysemu/runstate.h"
|
|
#include "qapi/error.h"
|
|
#include "migration.h"
|
|
#include "migration/global_state.h"
|
|
#include "migration/vmstate.h"
|
|
#include "trace.h"
|
|
|
|
typedef struct {
|
|
uint32_t size;
|
|
uint8_t runstate[100];
|
|
RunState state;
|
|
bool received;
|
|
} GlobalState;
|
|
|
|
static GlobalState global_state;
|
|
|
|
static void global_state_do_store(RunState state)
|
|
{
|
|
const char *state_str = RunState_str(state);
|
|
assert(strlen(state_str) < sizeof(global_state.runstate));
|
|
strpadcpy((char *)global_state.runstate, sizeof(global_state.runstate),
|
|
state_str, '\0');
|
|
}
|
|
|
|
RunState global_state_store(void)
|
|
{
|
|
RunState r = runstate_get();
|
|
|
|
global_state_do_store(r);
|
|
return r;
|
|
}
|
|
|
|
RunState global_state_store_once(void)
|
|
{
|
|
int r;
|
|
char *runstate = (char *)global_state.runstate;
|
|
|
|
r = qapi_enum_parse(&RunState_lookup, runstate, -1, NULL);
|
|
if (r < 0) {
|
|
return global_state_store();
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
void global_state_store_running(void)
|
|
{
|
|
global_state_do_store(RUN_STATE_RUNNING);
|
|
}
|
|
|
|
bool global_state_received(void)
|
|
{
|
|
return global_state.received;
|
|
}
|
|
|
|
RunState global_state_get_runstate(void)
|
|
{
|
|
return global_state.state;
|
|
}
|
|
|
|
static bool global_state_needed(void *opaque)
|
|
{
|
|
GlobalState *s = opaque;
|
|
char *runstate = (char *)s->runstate;
|
|
|
|
/* If it is not optional, it is mandatory */
|
|
|
|
if (migrate_get_current()->store_global_state) {
|
|
return true;
|
|
}
|
|
|
|
/* If state is running or paused, it is not needed */
|
|
|
|
if (strcmp(runstate, "running") == 0 ||
|
|
strcmp(runstate, "paused") == 0) {
|
|
return false;
|
|
}
|
|
|
|
/* for any other state it is needed */
|
|
return true;
|
|
}
|
|
|
|
static int global_state_post_load(void *opaque, int version_id)
|
|
{
|
|
GlobalState *s = opaque;
|
|
Error *local_err = NULL;
|
|
int r;
|
|
char *runstate = (char *)s->runstate;
|
|
|
|
s->received = true;
|
|
trace_migrate_global_state_post_load(runstate);
|
|
|
|
if (strnlen((char *)s->runstate,
|
|
sizeof(s->runstate)) == sizeof(s->runstate)) {
|
|
/*
|
|
* This condition should never happen during migration, because
|
|
* all runstate names are shorter than 100 bytes (the size of
|
|
* s->runstate). However, a malicious stream could overflow
|
|
* the qapi_enum_parse() call, so we force the last character
|
|
* to a NUL byte.
|
|
*/
|
|
s->runstate[sizeof(s->runstate) - 1] = '\0';
|
|
}
|
|
r = qapi_enum_parse(&RunState_lookup, runstate, -1, &local_err);
|
|
|
|
if (r == -1) {
|
|
if (local_err) {
|
|
error_report_err(local_err);
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
s->state = r;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int global_state_pre_save(void *opaque)
|
|
{
|
|
GlobalState *s = opaque;
|
|
|
|
trace_migrate_global_state_pre_save((char *)s->runstate);
|
|
s->size = strnlen((char *)s->runstate, sizeof(s->runstate)) + 1;
|
|
assert(s->size <= sizeof(s->runstate));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const VMStateDescription vmstate_globalstate = {
|
|
.name = "globalstate",
|
|
.version_id = 1,
|
|
.minimum_version_id = 1,
|
|
.post_load = global_state_post_load,
|
|
.pre_save = global_state_pre_save,
|
|
.needed = global_state_needed,
|
|
.fields = (VMStateField[]) {
|
|
VMSTATE_UINT32(size, GlobalState),
|
|
VMSTATE_BUFFER(runstate, GlobalState),
|
|
VMSTATE_END_OF_LIST()
|
|
},
|
|
};
|
|
|
|
void register_global_state(void)
|
|
{
|
|
/* We would use it independently that we receive it */
|
|
strcpy((char *)&global_state.runstate, "");
|
|
global_state.received = false;
|
|
vmstate_register(NULL, 0, &vmstate_globalstate, &global_state);
|
|
}
|