X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=util%2Fqemu-timer.c;h=1cc1b2f2c378ad2012f4aa2ea215fc9c1aae1b71;hb=HEAD;hp=b0e40a90877b815a5585e448fad7202478b5601f;hpb=95a9457fd44ad97c518858a4e1586a5498f9773c;p=mirror_qemu.git diff --git a/util/qemu-timer.c b/util/qemu-timer.c index b0e40a9087..6a0de33dd2 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -25,6 +25,8 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qemu/timer.h" +#include "qemu/lockable.h" +#include "sysemu/cpu-timers.h" #include "sysemu/replay.h" #include "sysemu/cpus.h" @@ -47,9 +49,6 @@ typedef struct QEMUClock { /* We rely on BQL to protect the timerlists */ QLIST_HEAD(, QEMUTimerList) timerlists; - NotifierList reset_notifiers; - int64_t last; - QEMUClockType type; bool enabled; } QEMUClock; @@ -101,7 +100,7 @@ QEMUTimerList *timerlist_new(QEMUClockType type, QEMUTimerList *timer_list; QEMUClock *clock = qemu_clock_ptr(type); - timer_list = g_malloc0(sizeof(QEMUTimerList)); + timer_list = g_new0(QEMUTimerList, 1); qemu_event_init(&timer_list->timers_done_ev, true); timer_list->clock = clock; timer_list->notify_cb = cb; @@ -130,15 +129,13 @@ static void qemu_clock_init(QEMUClockType type, QEMUTimerListNotifyCB *notify_cb clock->type = type; clock->enabled = (type == QEMU_CLOCK_VIRTUAL ? false : true); - clock->last = INT64_MIN; QLIST_INIT(&clock->timerlists); - notifier_list_init(&clock->reset_notifiers); main_loop_tlg.tl[type] = timerlist_new(type, notify_cb, NULL); } bool qemu_clock_use_for_deadline(QEMUClockType type) { - return !(use_icount && (type == QEMU_CLOCK_VIRTUAL)); + return !(icount_enabled() && (type == QEMU_CLOCK_VIRTUAL)); } void qemu_clock_notify(QEMUClockType type) @@ -174,7 +171,7 @@ void qemu_clock_enable(QEMUClockType type, bool enabled) bool timerlist_has_timers(QEMUTimerList *timer_list) { - return !!atomic_read(&timer_list->active_timers); + return !!qatomic_read(&timer_list->active_timers); } bool qemu_clock_has_timers(QEMUClockType type) @@ -187,17 +184,16 @@ bool timerlist_expired(QEMUTimerList *timer_list) { int64_t expire_time; - if (!atomic_read(&timer_list->active_timers)) { + if (!qatomic_read(&timer_list->active_timers)) { return false; } - qemu_mutex_lock(&timer_list->active_timers_lock); - if (!timer_list->active_timers) { - qemu_mutex_unlock(&timer_list->active_timers_lock); - return false; + WITH_QEMU_LOCK_GUARD(&timer_list->active_timers_lock) { + if (!timer_list->active_timers) { + return false; + } + expire_time = timer_list->active_timers->expire_time; } - expire_time = timer_list->active_timers->expire_time; - qemu_mutex_unlock(&timer_list->active_timers_lock); return expire_time <= qemu_clock_get_ns(timer_list->clock->type); } @@ -218,7 +214,7 @@ int64_t timerlist_deadline_ns(QEMUTimerList *timer_list) int64_t delta; int64_t expire_time; - if (!atomic_read(&timer_list->active_timers)) { + if (!qatomic_read(&timer_list->active_timers)) { return -1; } @@ -230,13 +226,12 @@ int64_t timerlist_deadline_ns(QEMUTimerList *timer_list) * value but ->notify_cb() is called when the deadline changes. Therefore * the caller should notice the change and there is no race condition. */ - qemu_mutex_lock(&timer_list->active_timers_lock); - if (!timer_list->active_timers) { - qemu_mutex_unlock(&timer_list->active_timers_lock); - return -1; + WITH_QEMU_LOCK_GUARD(&timer_list->active_timers_lock) { + if (!timer_list->active_timers) { + return -1; + } + expire_time = timer_list->active_timers->expire_time; } - expire_time = timer_list->active_timers->expire_time; - qemu_mutex_unlock(&timer_list->active_timers_lock); delta = expire_time - qemu_clock_get_ns(timer_list->clock->type); @@ -252,14 +247,41 @@ int64_t timerlist_deadline_ns(QEMUTimerList *timer_list) * ignore whether or not the clock should be used in deadline * calculations. */ -int64_t qemu_clock_deadline_ns_all(QEMUClockType type) +int64_t qemu_clock_deadline_ns_all(QEMUClockType type, int attr_mask) { int64_t deadline = -1; + int64_t delta; + int64_t expire_time; + QEMUTimer *ts; QEMUTimerList *timer_list; QEMUClock *clock = qemu_clock_ptr(type); + + if (!clock->enabled) { + return -1; + } + QLIST_FOREACH(timer_list, &clock->timerlists, list) { - deadline = qemu_soonest_timeout(deadline, - timerlist_deadline_ns(timer_list)); + if (!qatomic_read(&timer_list->active_timers)) { + continue; + } + qemu_mutex_lock(&timer_list->active_timers_lock); + ts = timer_list->active_timers; + /* Skip all external timers */ + while (ts && (ts->attributes & ~attr_mask)) { + ts = ts->next; + } + if (!ts) { + qemu_mutex_unlock(&timer_list->active_timers_lock); + continue; + } + expire_time = ts->expire_time; + qemu_mutex_unlock(&timer_list->active_timers_lock); + + delta = expire_time - qemu_clock_get_ns(type); + if (delta <= 0) { + delta = 0; + } + deadline = qemu_soonest_timeout(deadline, delta); } return deadline; } @@ -303,11 +325,7 @@ int qemu_timeout_ns_to_ms(int64_t ns) ms = DIV_ROUND_UP(ns, SCALE_MS); /* To avoid overflow problems, limit this to 2^31, i.e. approx 25 days */ - if (ms > (int64_t) INT32_MAX) { - ms = INT32_MAX; - } - - return (int) ms; + return MIN(ms, INT32_MAX); } @@ -371,7 +389,7 @@ static void timer_del_locked(QEMUTimerList *timer_list, QEMUTimer *ts) if (!t) break; if (t == ts) { - atomic_set(pt, t->next); + qatomic_set(pt, t->next); break; } pt = &t->next; @@ -394,7 +412,7 @@ static bool timer_mod_ns_locked(QEMUTimerList *timer_list, } ts->expire_time = MAX(expire_time, 0); ts->next = *pt; - atomic_set(pt, ts); + qatomic_set(pt, ts); return pt == &timer_list->active_timers; } @@ -402,8 +420,8 @@ static bool timer_mod_ns_locked(QEMUTimerList *timer_list, static void timerlist_rearm(QEMUTimerList *timer_list) { /* Interrupt execution to force deadline recalculation. */ - if (timer_list->clock->type == QEMU_CLOCK_VIRTUAL) { - qemu_start_warp_timer(); + if (icount_enabled() && timer_list->clock->type == QEMU_CLOCK_VIRTUAL) { + icount_start_warp_timer(); } timerlist_notify(timer_list); } @@ -445,17 +463,16 @@ 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); + WITH_QEMU_LOCK_GUARD(&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); + } + rearm = timer_mod_ns_locked(timer_list, ts, expire_time); + } else { + rearm = false; } - rearm = timer_mod_ns_locked(timer_list, ts, expire_time); - } else { - rearm = false; } - qemu_mutex_unlock(&timer_list->active_timers_lock); - if (rearm) { timerlist_rearm(timer_list); } @@ -488,9 +505,8 @@ bool timerlist_run_timers(QEMUTimerList *timer_list) bool progress = false; QEMUTimerCB *cb; void *opaque; - bool need_replay_checkpoint = false; - if (!atomic_read(&timer_list->active_timers)) { + if (!qatomic_read(&timer_list->active_timers)) { return false; } @@ -504,16 +520,6 @@ bool timerlist_run_timers(QEMUTimerList *timer_list) break; default: case QEMU_CLOCK_VIRTUAL: - if (replay_mode != REPLAY_MODE_NONE) { - /* Checkpoint for virtual clock is redundant in cases where - * it's being triggered with only non-EXTERNAL timers, because - * these timers don't change guest state directly. - * Since it has conditional dependence on specific timers, it is - * subject to race conditions and requires special handling. - * See below. - */ - need_replay_checkpoint = true; - } break; case QEMU_CLOCK_HOST: if (!replay_checkpoint(CHECKPOINT_CLOCK_HOST)) { @@ -528,7 +534,7 @@ bool timerlist_run_timers(QEMUTimerList *timer_list) } /* - * Extract expired timers from active timers list and and process them. + * Extract expired timers from active timers list and process them. * * In rr mode we need "filtered" checkpointing for virtual clock. The * checkpoint must be recorded/replayed before processing any non-EXTERNAL timer, @@ -546,19 +552,16 @@ bool timerlist_run_timers(QEMUTimerList *timer_list) */ break; } - if (need_replay_checkpoint - && !(ts->attributes & QEMU_TIMER_ATTR_EXTERNAL)) { - /* once we got here, checkpoint clock only once */ - need_replay_checkpoint = false; + /* Checkpoint for virtual clock is redundant in cases where + * it's being triggered with only non-EXTERNAL timers, because + * these timers don't change guest state directly. + */ + if (replay_mode != REPLAY_MODE_NONE + && timer_list->clock->type == QEMU_CLOCK_VIRTUAL + && !(ts->attributes & QEMU_TIMER_ATTR_EXTERNAL) + && !replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL)) { qemu_mutex_unlock(&timer_list->active_timers_lock); - if (!replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL)) { - goto out; - } - qemu_mutex_lock(&timer_list->active_timers_lock); - /* The lock was released; start over again in case the list was - * modified. - */ - continue; + goto out; } /* remove timer from the list before calling the callback */ @@ -629,57 +632,19 @@ int64_t timerlistgroup_deadline_ns(QEMUTimerListGroup *tlg) int64_t qemu_clock_get_ns(QEMUClockType type) { - int64_t now, last; - QEMUClock *clock = qemu_clock_ptr(type); - switch (type) { case QEMU_CLOCK_REALTIME: return get_clock(); default: case QEMU_CLOCK_VIRTUAL: - if (use_icount) { - return cpu_get_icount(); - } else { - return cpu_get_clock(); - } + return cpus_get_virtual_clock(); case QEMU_CLOCK_HOST: - now = REPLAY_CLOCK(REPLAY_CLOCK_HOST, get_clock_realtime()); - last = clock->last; - clock->last = now; - if (now < last || now > (last + get_max_clock_jump())) { - notifier_list_notify(&clock->reset_notifiers, &now); - } - return now; + return REPLAY_CLOCK(REPLAY_CLOCK_HOST, get_clock_realtime()); case QEMU_CLOCK_VIRTUAL_RT: return REPLAY_CLOCK(REPLAY_CLOCK_VIRTUAL_RT, cpu_get_clock()); } } -uint64_t qemu_clock_get_last(QEMUClockType type) -{ - QEMUClock *clock = qemu_clock_ptr(type); - return clock->last; -} - -void qemu_clock_set_last(QEMUClockType type, uint64_t last) -{ - QEMUClock *clock = qemu_clock_ptr(type); - clock->last = last; -} - -void qemu_clock_register_reset_notifier(QEMUClockType type, - Notifier *notifier) -{ - QEMUClock *clock = qemu_clock_ptr(type); - notifier_list_add(&clock->reset_notifiers, notifier); -} - -void qemu_clock_unregister_reset_notifier(QEMUClockType type, - Notifier *notifier) -{ - notifier_remove(notifier); -} - void init_clocks(QEMUTimerListNotifyCB *notify_cb) { QEMUClockType type;