From 74892d2468b9f0c56b915ce94848d6f7fac39740 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 5 Jun 2014 14:53:58 +0200 Subject: [PATCH] vl: allow other threads to do qemu_system_vmstop_request 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 Reviewed-by: Eric Blake Signed-off-by: Stefan Hajnoczi --- 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 1ec3a9e..06da4e7 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 277230d..6b4cc13 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -62,6 +62,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 308742a..61209c1 100644 --- a/target-lm32/op_helper.c +++ b/target-lm32/op_helper.c @@ -42,7 +42,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 54b4627..ab8f152 100644 --- a/vl.c +++ b/vl.c @@ -574,6 +574,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; @@ -650,10 +654,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 */ @@ -693,6 +698,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 */ @@ -1658,17 +1711,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 { @@ -1693,7 +1735,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) { @@ -1761,18 +1802,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)); @@ -1922,12 +1951,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; -- 2.7.4