cpus: stop vm in suspended runstate

Currently, a vm in the suspended state is not completely stopped.  The VCPUs
have been paused, but the cpu clock still runs, and runstate notifiers for
the transition to stopped have not been called.  This causes problems for
live migration.  Stale cpu timers_state is saved to the migration stream,
causing time errors in the guest when it wakes from suspend, and state that
would have been modified by runstate notifiers is wrong.

Modify vm_stop to completely stop the vm if the current state is suspended,
transition to RUN_STATE_PAUSED, and remember that the machine was suspended.
Modify vm_start to restore the suspended state.

This affects all callers of vm_stop and vm_start, notably, the qapi stop and
cont commands:

  old behavior:
    RUN_STATE_SUSPENDED --> stop --> RUN_STATE_SUSPENDED

  new behavior:
    RUN_STATE_SUSPENDED --> stop --> RUN_STATE_PAUSED
    RUN_STATE_PAUSED    --> cont --> RUN_STATE_SUSPENDED

For example:

    (qemu) info status
    VM status: paused (suspended)

    (qemu) stop
    (qemu) info status
    VM status: paused

    (qemu) system_wakeup
    Error: Unable to wake up: guest is not in suspended state

    (qemu) cont
    (qemu) info status
    VM status: paused (suspended)

    (qemu) system_wakeup
    (qemu) info status
    VM status: running

Suggested-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Peter Xu <peterx@redhat.com>
Link: https://lore.kernel.org/r/1704312341-66640-3-git-send-email-steven.sistare@oracle.com
Signed-off-by: Peter Xu <peterx@redhat.com>
This commit is contained in:
Steve Sistare
2024-01-03 12:05:31 -08:00
committed by Peter Xu
parent f06f316d3e
commit b9ae473d80
5 changed files with 39 additions and 13 deletions

View File

@ -277,11 +277,15 @@ bool vm_get_suspended(void)
static int do_vm_stop(RunState state, bool send_stop)
{
int ret = 0;
RunState oldstate = runstate_get();
if (runstate_is_running()) {
if (runstate_is_live(oldstate)) {
vm_was_suspended = (oldstate == RUN_STATE_SUSPENDED);
runstate_set(state);
cpu_disable_ticks();
pause_all_vcpus();
if (oldstate == RUN_STATE_RUNNING) {
pause_all_vcpus();
}
vm_state_notify(0, state);
if (send_stop) {
qapi_event_send_stop();
@ -694,11 +698,13 @@ int vm_stop(RunState state)
/**
* Prepare for (re)starting the VM.
* Returns -1 if the vCPUs are not to be restarted (e.g. if they are already
* running or in case of an error condition), 0 otherwise.
* Returns 0 if the vCPUs should be restarted, -1 on an error condition,
* and 1 otherwise.
*/
int vm_prepare_start(bool step_pending)
{
int ret = vm_was_suspended ? 1 : 0;
RunState state = vm_was_suspended ? RUN_STATE_SUSPENDED : RUN_STATE_RUNNING;
RunState requested;
qemu_vmstop_requested(&requested);
@ -729,9 +735,10 @@ int vm_prepare_start(bool step_pending)
qapi_event_send_resume();
cpu_enable_ticks();
runstate_set(RUN_STATE_RUNNING);
vm_state_notify(1, RUN_STATE_RUNNING);
return 0;
runstate_set(state);
vm_state_notify(1, state);
vm_was_suspended = false;
return ret;
}
void vm_start(void)
@ -745,7 +752,7 @@ void vm_start(void)
current state is forgotten forever */
int vm_stop_force_state(RunState state)
{
if (runstate_is_running()) {
if (runstate_is_live(runstate_get())) {
return vm_stop(state);
} else {
int ret;

View File

@ -108,6 +108,7 @@ static const RunStateTransition runstate_transitions_def[] = {
{ RUN_STATE_PAUSED, RUN_STATE_POSTMIGRATE },
{ RUN_STATE_PAUSED, RUN_STATE_PRELAUNCH },
{ RUN_STATE_PAUSED, RUN_STATE_COLO},
{ RUN_STATE_PAUSED, RUN_STATE_SUSPENDED},
{ RUN_STATE_POSTMIGRATE, RUN_STATE_RUNNING },
{ RUN_STATE_POSTMIGRATE, RUN_STATE_FINISH_MIGRATE },
@ -161,6 +162,7 @@ static const RunStateTransition runstate_transitions_def[] = {
{ RUN_STATE_SUSPENDED, RUN_STATE_FINISH_MIGRATE },
{ RUN_STATE_SUSPENDED, RUN_STATE_PRELAUNCH },
{ RUN_STATE_SUSPENDED, RUN_STATE_COLO},
{ RUN_STATE_SUSPENDED, RUN_STATE_PAUSED},
{ RUN_STATE_WATCHDOG, RUN_STATE_RUNNING },
{ RUN_STATE_WATCHDOG, RUN_STATE_FINISH_MIGRATE },
@ -502,6 +504,7 @@ void qemu_system_reset(ShutdownCause reason)
qapi_event_send_reset(shutdown_caused_by_guest(reason), reason);
}
cpu_synchronize_all_post_reset();
vm_set_suspended(false);
}
/*