There patch protects vmstop_requested with a lock and introduces qemu_system_vmstop_request_prepare.
Together with the new call to qemu_vmstop_requested in vm_start, qemu_system_vmstop_request_prepare avoids a race where the VM could remain stopped even though the iostatus of a block device has already been set (for example). qemu_system_vmstop_request_prepare however also lets the caller thread delay observation of the state change until it has itself communicated that change to the user. This delay avoids any possibility of a wrong reordering of the BLOCK_IO_ERROR event and the subsequent STOP event. Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> --- cpus.c | 1 + include/sysemu/sysemu.h | 1 + target-lm32/op_helper.c | 2 +- vl.c | 85 +++++++++++++++++++++++++++++++------------------ 4 files changed, 57 insertions(+), 32 deletions(-) diff --git a/cpus.c b/cpus.c index dd7ac13..f566a88 100644 --- a/cpus.c +++ b/cpus.c @@ -1206,6 +1206,7 @@ void cpu_stop_current(void) int vm_stop(RunState state) { if (qemu_in_vcpu_thread()) { + qemu_system_vmstop_request_prepare(); qemu_system_vmstop_request(state); /* * FIXME: should not return to device code in case diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index ba5c7f8..0a2f2bb 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -60,6 +60,7 @@ void qemu_system_powerdown_request(void); void qemu_register_powerdown_notifier(Notifier *notifier); void qemu_system_debug_request(void); void qemu_system_vmstop_request(RunState reason); +void qemu_system_vmstop_request_prepare(void); int qemu_shutdown_requested_get(void); int qemu_reset_requested_get(void); void qemu_system_killed(int signal, pid_t pid); diff --git a/target-lm32/op_helper.c b/target-lm32/op_helper.c index 40fbed6..25ad09a 100644 --- a/target-lm32/op_helper.c +++ b/target-lm32/op_helper.c @@ -52,7 +52,7 @@ void HELPER(ill)(CPULM32State *env) fprintf(stderr, "VM paused due to illegal instruction. " "Connect a debugger or switch to the monitor console " "to find out more.\n"); - qemu_system_vmstop_request(RUN_STATE_PAUSED); + vm_stop(RUN_STATE_PAUSED); cs->halted = 1; raise_exception(env, EXCP_HALTED); #endif diff --git a/vl.c b/vl.c index 0c15608..229a8d1 100644 --- a/vl.c +++ b/vl.c @@ -567,6 +567,10 @@ static int default_driver_check(QemuOpts *opts, void *opaque) static RunState current_run_state = RUN_STATE_PRELAUNCH; +/* We use RUN_STATE_MAX but any invalid value will do */ +static RunState vmstop_requested = RUN_STATE_MAX; +static QemuMutex vmstop_lock; + typedef struct { RunState from; RunState to; @@ -643,10 +647,11 @@ static void runstate_init(void) const RunStateTransition *p; memset(&runstate_valid_transitions, 0, sizeof(runstate_valid_transitions)); - for (p = &runstate_transitions_def[0]; p->from != RUN_STATE_MAX; p++) { runstate_valid_transitions[p->from][p->to] = true; } + + qemu_mutex_init(&vmstop_lock); } /* This function will abort() on invalid state transitions */ @@ -686,6 +691,54 @@ StatusInfo *qmp_query_status(Error **errp) return info; } +static bool qemu_vmstop_requested(RunState *r) +{ + qemu_mutex_lock(&vmstop_lock); + *r = vmstop_requested; + vmstop_requested = RUN_STATE_MAX; + qemu_mutex_unlock(&vmstop_lock); + return *r < RUN_STATE_MAX; +} + +void qemu_system_vmstop_request_prepare(void) +{ + qemu_mutex_lock(&vmstop_lock); +} + +void qemu_system_vmstop_request(RunState state) +{ + vmstop_requested = state; + qemu_mutex_unlock(&vmstop_lock); + qemu_notify_event(); +} + +void vm_start(void) +{ + RunState requested; + + qemu_vmstop_requested(&requested); + if (runstate_is_running() && requested == RUN_STATE_MAX) { + return; + } + + /* Ensure that a STOP/RESUME pair of events is emitted if a + * vmstop request was pending. The BLOCK_IO_ERROR event, for + * example, according to documentation is always followed by + * the STOP event. + */ + if (runstate_is_running()) { + monitor_protocol_event(QEVENT_STOP, NULL); + } else { + cpu_enable_ticks(); + runstate_set(RUN_STATE_RUNNING); + vm_state_notify(1, RUN_STATE_RUNNING); + resume_all_vcpus(); + } + + monitor_protocol_event(QEVENT_RESUME, NULL); +} + + /***********************************************************/ /* real time host monotonic timer */ @@ -1747,17 +1800,6 @@ void vm_state_notify(int running, RunState state) } } -void vm_start(void) -{ - if (!runstate_is_running()) { - cpu_enable_ticks(); - runstate_set(RUN_STATE_RUNNING); - vm_state_notify(1, RUN_STATE_RUNNING); - resume_all_vcpus(); - monitor_protocol_event(QEVENT_RESUME, NULL); - } -} - /* reset/shutdown handler */ typedef struct QEMUResetEntry { @@ -1782,7 +1824,6 @@ static NotifierList suspend_notifiers = static NotifierList wakeup_notifiers = NOTIFIER_LIST_INITIALIZER(wakeup_notifiers); static uint32_t wakeup_reason_mask = ~(1 << QEMU_WAKEUP_REASON_NONE); -static RunState vmstop_requested = RUN_STATE_MAX; int qemu_shutdown_requested_get(void) { @@ -1850,18 +1891,6 @@ static int qemu_debug_requested(void) return r; } -/* We use RUN_STATE_MAX but any invalid value will do */ -static bool qemu_vmstop_requested(RunState *r) -{ - if (vmstop_requested < RUN_STATE_MAX) { - *r = vmstop_requested; - vmstop_requested = RUN_STATE_MAX; - return true; - } - - return false; -} - void qemu_register_reset(QEMUResetHandler *func, void *opaque) { QEMUResetEntry *re = g_malloc0(sizeof(QEMUResetEntry)); @@ -2011,12 +2040,6 @@ void qemu_system_debug_request(void) qemu_notify_event(); } -void qemu_system_vmstop_request(RunState state) -{ - vmstop_requested = state; - qemu_notify_event(); -} - static bool main_loop_should_exit(void) { RunState r; -- 1.8.3.1