diff options
author | Anthony Liguori <aliguori@amazon.com> | 2013-10-18 10:01:49 -0700 |
---|---|---|
committer | Anthony Liguori <aliguori@amazon.com> | 2013-10-18 10:01:49 -0700 |
commit | 989644915c281ac83f06f65923d716272ede1ed8 (patch) | |
tree | 574ac7adcfdb572fd735b7f509c6ba95aabe4b95 /qemu-timer.c | |
parent | 1cb9b64df380f232bcd142ab27c085cff0add1d8 (diff) | |
parent | 041603fe5d4537cd165941f96bd76a31f7f662fd (diff) |
Merge remote-tracking branch 'bonzini/iommu-for-anthony' into staging
# By Paolo Bonzini (10) and others
# Via Paolo Bonzini
* bonzini/iommu-for-anthony:
exec: remove qemu_safe_ram_ptr
icount: make it thread-safe
icount: document (future) locking rules for icount
icount: prepare the code for future races in calling qemu_clock_warp
icount: reorganize icount_warp_rt
icount: use cpu_get_icount() directly
timer: add timer_mod_anticipate and timer_mod_anticipate_ns
timer: extract timer_mod_ns_locked and timerlist_rearm
timer: make qemu_clock_enable sync between disable and timer's cb
qemu-thread: add QemuEvent
timer: protect timers_state's clock with seqlock
seqlock: introduce read-write seqlock
vga: Mark relevant portio lists regions as coalesced MMIO flushing
cirrus: Mark vga io region as coalesced MMIO flushing
portio: Allow to mark portio lists as coalesced MMIO flushing
compatfd: switch to QemuThread
memory: fix 128 arithmetic in info mtree
Message-id: 1382024935-28297-1-git-send-email-pbonzini@redhat.com
Signed-off-by: Anthony Liguori <aliguori@amazon.com>
Diffstat (limited to 'qemu-timer.c')
-rw-r--r-- | qemu-timer.c | 97 |
1 files changed, 80 insertions, 17 deletions
diff --git a/qemu-timer.c b/qemu-timer.c index 6b62e88669..e15ce477cc 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -45,6 +45,7 @@ /* timers */ typedef struct QEMUClock { + /* We rely on BQL to protect the timerlists */ QLIST_HEAD(, QEMUTimerList) timerlists; NotifierList reset_notifiers; @@ -71,6 +72,9 @@ struct QEMUTimerList { QLIST_ENTRY(QEMUTimerList) list; QEMUTimerListNotifyCB *notify_cb; void *notify_opaque; + + /* lightweight method to mark the end of timerlist's running */ + QemuEvent timers_done_ev; }; /** @@ -99,6 +103,7 @@ QEMUTimerList *timerlist_new(QEMUClockType type, QEMUClock *clock = qemu_clock_ptr(type); timer_list = g_malloc0(sizeof(QEMUTimerList)); + qemu_event_init(&timer_list->timers_done_ev, false); timer_list->clock = clock; timer_list->notify_cb = cb; timer_list->notify_opaque = opaque; @@ -143,13 +148,25 @@ void qemu_clock_notify(QEMUClockType type) } } +/* Disabling the clock will wait for related timerlists to stop + * executing qemu_run_timers. Thus, this functions should not + * be used from the callback of a timer that is based on @clock. + * Doing so would cause a deadlock. + * + * Caller should hold BQL. + */ void qemu_clock_enable(QEMUClockType type, bool enabled) { QEMUClock *clock = qemu_clock_ptr(type); + QEMUTimerList *tl; bool old = clock->enabled; clock->enabled = enabled; if (enabled && !old) { qemu_clock_notify(type); + } else if (!enabled && old) { + QLIST_FOREACH(tl, &clock->timerlists, list) { + qemu_event_wait(&tl->timers_done_ev); + } } } @@ -338,6 +355,34 @@ static void timer_del_locked(QEMUTimerList *timer_list, QEMUTimer *ts) } } +static bool timer_mod_ns_locked(QEMUTimerList *timer_list, + QEMUTimer *ts, int64_t expire_time) +{ + QEMUTimer **pt, *t; + + /* add the timer in the sorted list */ + pt = &timer_list->active_timers; + for (;;) { + t = *pt; + if (!timer_expired_ns(t, expire_time)) { + break; + } + pt = &t->next; + } + ts->expire_time = MAX(expire_time, 0); + ts->next = *pt; + *pt = ts; + + return pt == &timer_list->active_timers; +} + +static void timerlist_rearm(QEMUTimerList *timer_list) +{ + /* Interrupt execution to force deadline recalculation. */ + qemu_clock_warp(timer_list->clock->type); + timerlist_notify(timer_list); +} + /* stop a timer, but do not dealloc it */ void timer_del(QEMUTimer *ts) { @@ -353,30 +398,39 @@ void timer_del(QEMUTimer *ts) void timer_mod_ns(QEMUTimer *ts, int64_t expire_time) { QEMUTimerList *timer_list = ts->timer_list; - QEMUTimer **pt, *t; + bool rearm; qemu_mutex_lock(&timer_list->active_timers_lock); timer_del_locked(timer_list, ts); + rearm = timer_mod_ns_locked(timer_list, ts, expire_time); + qemu_mutex_unlock(&timer_list->active_timers_lock); - /* add the timer in the sorted list */ - pt = &timer_list->active_timers; - for(;;) { - t = *pt; - if (!timer_expired_ns(t, expire_time)) { - break; + if (rearm) { + timerlist_rearm(timer_list); + } +} + +/* modify the current timer so that it will be fired when current_time + >= expire_time or the current deadline, whichever comes earlier. + The corresponding callback will be called. */ +void timer_mod_anticipate_ns(QEMUTimer *ts, int64_t expire_time) +{ + QEMUTimerList *timer_list = ts->timer_list; + bool rearm; + + qemu_mutex_lock(&timer_list->active_timers_lock); + if (ts->expire_time == -1 || ts->expire_time > expire_time) { + if (ts->expire_time != -1) { + timer_del_locked(timer_list, ts); } - pt = &t->next; + rearm = timer_mod_ns_locked(timer_list, ts, expire_time); + } else { + rearm = false; } - ts->expire_time = MAX(expire_time, 0); - ts->next = *pt; - *pt = ts; qemu_mutex_unlock(&timer_list->active_timers_lock); - /* Rearm if necessary */ - if (pt == &timer_list->active_timers) { - /* Interrupt execution to force deadline recalculation. */ - qemu_clock_warp(timer_list->clock->type); - timerlist_notify(timer_list); + if (rearm) { + timerlist_rearm(timer_list); } } @@ -385,6 +439,11 @@ void timer_mod(QEMUTimer *ts, int64_t expire_time) timer_mod_ns(ts, expire_time * ts->scale); } +void timer_mod_anticipate(QEMUTimer *ts, int64_t expire_time) +{ + timer_mod_anticipate_ns(ts, expire_time * ts->scale); +} + bool timer_pending(QEMUTimer *ts) { return ts->expire_time >= 0; @@ -403,8 +462,9 @@ bool timerlist_run_timers(QEMUTimerList *timer_list) QEMUTimerCB *cb; void *opaque; + qemu_event_reset(&timer_list->timers_done_ev); if (!timer_list->clock->enabled) { - return progress; + goto out; } current_time = qemu_clock_get_ns(timer_list->clock->type); @@ -428,6 +488,9 @@ bool timerlist_run_timers(QEMUTimerList *timer_list) cb(opaque); progress = true; } + +out: + qemu_event_set(&timer_list->timers_done_ev); return progress; } |