When a timer's expiration time is changed we may need to rearm the alarm timer. Rearming is necessary when the nearest deadline changes.
This patch introduces the qemu_alarm_timer->timer_modified boolean and a lock to protect it. It moves rearming the alarm timer from inside qemu_mod_timer_ns() to qemu_run_all_timers() since we cannot rearm outside the QEMU global mutex. The following code is dropped because we always kick the main loop now: /* Interrupt execution to force deadline recalculation. */ qemu_clock_warp(ts->clock); if (use_icount) { qemu_notify_event(); } cpus.c will invoke qemu_clock_warp() again since the main loop ran. It is now safe to call qemu_mod_timer_ns() for outside the QEMU global mutex. Signed-off-by: Stefan Hajnoczi <stefa...@redhat.com> --- qemu-timer.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/qemu-timer.c b/qemu-timer.c index c773af0..9500d12 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -78,6 +78,10 @@ struct qemu_alarm_timer { #endif bool expired; bool pending; + + /* Was the nearest deadline timer modified (possibly by another thread)? */ + QemuMutex timer_modified_lock; + bool timer_modified; }; static struct qemu_alarm_timer *alarm_timer; @@ -371,14 +375,10 @@ void qemu_mod_timer_ns(QEMUTimer *ts, int64_t expire_time) /* Rearm if necessary */ if (pt == &ts->clock->active_timers) { - if (!alarm_timer->pending) { - qemu_rearm_alarm_timer(alarm_timer); - } - /* Interrupt execution to force deadline recalculation. */ - qemu_clock_warp(ts->clock); - if (use_icount) { - qemu_notify_event(); - } + qemu_mutex_lock(&alarm_timer->timer_modified_lock); + alarm_timer->timer_modified = true; + qemu_mutex_unlock(&alarm_timer->timer_modified_lock); + qemu_notify_event(); } } @@ -484,6 +484,8 @@ uint64_t qemu_timer_expire_time_ns(QEMUTimer *ts) void qemu_run_all_timers(void) { + bool timer_modified; + alarm_timer->pending = false; /* vm time timers */ @@ -491,11 +493,20 @@ void qemu_run_all_timers(void) qemu_run_timers(rt_clock); qemu_run_timers(host_clock); + /* Check if qemu_mod_timer_ns() has been called */ + qemu_mutex_lock(&alarm_timer->timer_modified_lock); + timer_modified = alarm_timer->timer_modified; + alarm_timer->timer_modified = false; + qemu_mutex_unlock(&alarm_timer->timer_modified_lock); + /* rearm timer, if not periodic */ if (alarm_timer->expired) { alarm_timer->expired = false; qemu_rearm_alarm_timer(alarm_timer); + } else if (timer_modified) { + qemu_rearm_alarm_timer(alarm_timer); } + } #ifdef _WIN32 @@ -805,6 +816,8 @@ int init_timer_alarm(void) goto fail; } + qemu_mutex_init(&t->timer_modified_lock); + atexit(quit_timers); #ifdef CONFIG_POSIX pthread_atfork(NULL, NULL, reinit_timers); -- 1.8.1.4