/* * QEMU System Emulator * * Copyright (c) 2003-2008 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #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" #ifdef CONFIG_POSIX #include #endif #ifdef CONFIG_PPOLL #include #endif #ifdef CONFIG_PRCTL_PR_SET_TIMERSLACK #include #endif /***********************************************************/ /* timers */ typedef struct QEMUClock { /* We rely on BQL to protect the timerlists */ QLIST_HEAD(, QEMUTimerList) timerlists; QEMUClockType type; bool enabled; } QEMUClock; QEMUTimerListGroup main_loop_tlg; static QEMUClock qemu_clocks[QEMU_CLOCK_MAX]; /* A QEMUTimerList is a list of timers attached to a clock. More * than one QEMUTimerList can be attached to each clock, for instance * used by different AioContexts / threads. Each clock also has * a list of the QEMUTimerLists associated with it, in order that * reenabling the clock can call all the notifiers. */ struct QEMUTimerList { QEMUClock *clock; QemuMutex active_timers_lock; QEMUTimer *active_timers; QLIST_ENTRY(QEMUTimerList) list; QEMUTimerListNotifyCB *notify_cb; void *notify_opaque; /* lightweight method to mark the end of timerlist's running */ QemuEvent timers_done_ev; }; /** * qemu_clock_ptr: * @type: type of clock * * Translate a clock type into a pointer to QEMUClock object. * * Returns: a pointer to the QEMUClock object */ static inline QEMUClock *qemu_clock_ptr(QEMUClockType type) { return &qemu_clocks[type]; } static bool timer_expired_ns(QEMUTimer *timer_head, int64_t current_time) { return timer_head && (timer_head->expire_time <= current_time); } QEMUTimerList *timerlist_new(QEMUClockType type, QEMUTimerListNotifyCB *cb, void *opaque) { QEMUTimerList *timer_list; QEMUClock *clock = qemu_clock_ptr(type); timer_list = g_new0(QEMUTimerList, 1); qemu_event_init(&timer_list->timers_done_ev, true); timer_list->clock = clock; timer_list->notify_cb = cb; timer_list->notify_opaque = opaque; qemu_mutex_init(&timer_list->active_timers_lock); QLIST_INSERT_HEAD(&clock->timerlists, timer_list, list); return timer_list; } void timerlist_free(QEMUTimerList *timer_list) { assert(!timerlist_has_timers(timer_list)); if (timer_list->clock) { QLIST_REMOVE(timer_list, list); } qemu_mutex_destroy(&timer_list->active_timers_lock); g_free(timer_list); } static void qemu_clock_init(QEMUClockType type, QEMUTimerListNotifyCB *notify_cb) { QEMUClock *clock = qemu_clock_ptr(type); /* Assert that the clock of type TYPE has not been initialized yet. */ assert(main_loop_tlg.tl[type] == NULL); clock->type = type; clock->enabled = (type == QEMU_CLOCK_VIRTUAL ? false : true); QLIST_INIT(&clock->timerlists); main_loop_tlg.tl[type] = timerlist_new(type, notify_cb, NULL); } bool qemu_clock_use_for_deadline(QEMUClockType type) { return !(icount_enabled() && (type == QEMU_CLOCK_VIRTUAL)); } void qemu_clock_notify(QEMUClockType type) { QEMUTimerList *timer_list; QEMUClock *clock = qemu_clock_ptr(type); QLIST_FOREACH(timer_list, &clock->timerlists, list) { timerlist_notify(timer_list); } } /* 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); } } } bool timerlist_has_timers(QEMUTimerList *timer_list) { return !!qatomic_read(&timer_list->active_timers); } bool qemu_clock_has_timers(QEMUClockType type) { return timerlist_has_timers( main_loop_tlg.tl[type]); } bool timerlist_expired(QEMUTimerList *timer_list) { int64_t expire_time = 0; if (!qatomic_read(&timer_list->active_timers)) { 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; } return expire_time <= qemu_clock_get_ns(timer_list->clock->type); } bool qemu_clock_expired(QEMUClockType type) { return timerlist_expired( main_loop_tlg.tl[type]); } /* * As above, but return -1 for no deadline, and do not cap to 2^32 * as we know the result is always positive. */ int64_t timerlist_deadline_ns(QEMUTimerList *timer_list) { int64_t delta; int64_t expire_time = 0; if (!qatomic_read(&timer_list->active_timers)) { return -1; } if (!timer_list->clock->enabled) { return -1; } /* The active timers list may be modified before the caller uses our return * value but ->notify_cb() is called when the deadline changes. Therefore * the caller should notice the change and there is no race condition. */ WITH_QEMU_LOCK_GUARD(&timer_list->active_timers_lock) { if (!timer_list->active_timers) { return -1; } expire_time = timer_list->active_timers->expire_time; } delta = expire_time - qemu_clock_get_ns(timer_list->clock->type); if (delta <= 0) { return 0; } return delta; } /* Calculate the soonest deadline across all timerlists attached * to the clock. This is used for the icount timeout so we * ignore whether or not the clock should be used in deadline * calculations. */ 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) { 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; } void timerlist_notify(QEMUTimerList *timer_list) { if (timer_list->notify_cb) { timer_list->notify_cb(timer_list->notify_opaque, timer_list->clock->type); } else { qemu_notify_event(); } } /* Transition function to convert a nanosecond timeout to ms * This is used where a system does not support ppoll */ int qemu_timeout_ns_to_ms(int64_t ns) { int64_t ms; if (ns < 0) { return -1; } if (!ns) { return 0; } /* Always round up, because it's better to wait too long than to wait too * little and effectively busy-wait */ ms = DIV_ROUND_UP(ns, SCALE_MS); /* To avoid overflow problems, limit this to 2^31, i.e. approx 25 days */ return MIN(ms, INT32_MAX); } /* qemu implementation of g_poll which uses a nanosecond timeout but is * otherwise identical to g_poll */ int qemu_poll_ns(GPollFD *fds, guint nfds, int64_t timeout) { #ifdef CONFIG_PPOLL if (timeout < 0) { return ppoll((struct pollfd *)fds, nfds, NULL, NULL); } else { struct timespec ts; int64_t tvsec = timeout / 1000000000LL; /* Avoid possibly overflowing and specifying a negative number of * seconds, which would turn a very long timeout into a busy-wait. */ if (tvsec > (int64_t)INT32_MAX) { tvsec = INT32_MAX; } ts.tv_sec = tvsec; ts.tv_nsec = timeout % 1000000000LL; return ppoll((struct pollfd *)fds, nfds, &ts, NULL); } #else return g_poll(fds, nfds, qemu_timeout_ns_to_ms(timeout)); #endif } void timer_init_full(QEMUTimer *ts, QEMUTimerListGroup *timer_list_group, QEMUClockType type, int scale, int attributes, QEMUTimerCB *cb, void *opaque) { if (!timer_list_group) { timer_list_group = &main_loop_tlg; } ts->timer_list = timer_list_group->tl[type]; ts->cb = cb; ts->opaque = opaque; ts->scale = scale; ts->attributes = attributes; ts->expire_time = -1; } void timer_deinit(QEMUTimer *ts) { assert(ts->expire_time == -1); ts->timer_list = NULL; } static void timer_del_locked(QEMUTimerList *timer_list, QEMUTimer *ts) { QEMUTimer **pt, *t; ts->expire_time = -1; pt = &timer_list->active_timers; for(;;) { t = *pt; if (!t) break; if (t == ts) { qatomic_set(pt, t->next); break; } pt = &t->next; } } 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; qatomic_set(pt, ts); return pt == &timer_list->active_timers; } static void timerlist_rearm(QEMUTimerList *timer_list) { /* Interrupt execution to force deadline recalculation. */ if (icount_enabled() && timer_list->clock->type == QEMU_CLOCK_VIRTUAL) { icount_start_warp_timer(); } timerlist_notify(timer_list); } /* stop a timer, but do not dealloc it */ void timer_del(QEMUTimer *ts) { QEMUTimerList *timer_list = ts->timer_list; if (timer_list) { qemu_mutex_lock(&timer_list->active_timers_lock); timer_del_locked(timer_list, ts); qemu_mutex_unlock(&timer_list->active_timers_lock); } } /* modify the current timer so that it will be fired when current_time >= expire_time. The corresponding callback will be called. */ void timer_mod_ns(QEMUTimer *ts, int64_t expire_time) { QEMUTimerList *timer_list = ts->timer_list; 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); 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 = false; 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; } } if (rearm) { timerlist_rearm(timer_list); } } 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; } bool timer_expired(QEMUTimer *timer_head, int64_t current_time) { return timer_expired_ns(timer_head, current_time * timer_head->scale); } bool timerlist_run_timers(QEMUTimerList *timer_list) { QEMUTimer *ts; int64_t current_time; bool progress = false; QEMUTimerCB *cb; void *opaque; if (!qatomic_read(&timer_list->active_timers)) { return false; } qemu_event_reset(&timer_list->timers_done_ev); if (!timer_list->clock->enabled) { goto out; } switch (timer_list->clock->type) { case QEMU_CLOCK_REALTIME: break; default: case QEMU_CLOCK_VIRTUAL: break; case QEMU_CLOCK_HOST: if (!replay_checkpoint(CHECKPOINT_CLOCK_HOST)) { goto out; } break; case QEMU_CLOCK_VIRTUAL_RT: if (!replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL_RT)) { goto out; } break; } /* * 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, * and that must only be done once since the clock value stays the same. Because * non-EXTERNAL timers may appear in the timers list while it being processed, * the checkpoint can be issued at a time until no timers are left and we are * done". */ current_time = qemu_clock_get_ns(timer_list->clock->type); qemu_mutex_lock(&timer_list->active_timers_lock); while ((ts = timer_list->active_timers)) { if (!timer_expired_ns(ts, current_time)) { /* No expired timers left. The checkpoint can be skipped * if no timers fired or they were all external. */ break; } /* 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); goto out; } /* remove timer from the list before calling the callback */ timer_list->active_timers = ts->next; ts->next = NULL; ts->expire_time = -1; cb = ts->cb; opaque = ts->opaque; /* run the callback (the timer list can be modified) */ qemu_mutex_unlock(&timer_list->active_timers_lock); cb(opaque); qemu_mutex_lock(&timer_list->active_timers_lock); progress = true; } qemu_mutex_unlock(&timer_list->active_timers_lock); out: qemu_event_set(&timer_list->timers_done_ev); return progress; } bool qemu_clock_run_timers(QEMUClockType type) { return timerlist_run_timers(main_loop_tlg.tl[type]); } void timerlistgroup_init(QEMUTimerListGroup *tlg, QEMUTimerListNotifyCB *cb, void *opaque) { QEMUClockType type; for (type = 0; type < QEMU_CLOCK_MAX; type++) { tlg->tl[type] = timerlist_new(type, cb, opaque); } } void timerlistgroup_deinit(QEMUTimerListGroup *tlg) { QEMUClockType type; for (type = 0; type < QEMU_CLOCK_MAX; type++) { timerlist_free(tlg->tl[type]); } } bool timerlistgroup_run_timers(QEMUTimerListGroup *tlg) { QEMUClockType type; bool progress = false; for (type = 0; type < QEMU_CLOCK_MAX; type++) { progress |= timerlist_run_timers(tlg->tl[type]); } return progress; } int64_t timerlistgroup_deadline_ns(QEMUTimerListGroup *tlg) { int64_t deadline = -1; QEMUClockType type; for (type = 0; type < QEMU_CLOCK_MAX; type++) { if (qemu_clock_use_for_deadline(type)) { deadline = qemu_soonest_timeout(deadline, timerlist_deadline_ns(tlg->tl[type])); } } return deadline; } int64_t qemu_clock_get_ns(QEMUClockType type) { switch (type) { case QEMU_CLOCK_REALTIME: return get_clock(); default: case QEMU_CLOCK_VIRTUAL: return cpus_get_virtual_clock(); case QEMU_CLOCK_HOST: return REPLAY_CLOCK(REPLAY_CLOCK_HOST, get_clock_realtime()); case QEMU_CLOCK_VIRTUAL_RT: return REPLAY_CLOCK(REPLAY_CLOCK_VIRTUAL_RT, cpu_get_clock()); } } static void qemu_virtual_clock_set_ns(int64_t time) { return cpus_set_virtual_clock(time); } void init_clocks(QEMUTimerListNotifyCB *notify_cb) { QEMUClockType type; for (type = 0; type < QEMU_CLOCK_MAX; type++) { qemu_clock_init(type, notify_cb); } #ifdef CONFIG_PRCTL_PR_SET_TIMERSLACK prctl(PR_SET_TIMERSLACK, 1, 0, 0, 0); #endif } uint64_t timer_expire_time_ns(QEMUTimer *ts) { return timer_pending(ts) ? ts->expire_time : -1; } bool qemu_clock_run_all_timers(void) { bool progress = false; QEMUClockType type; for (type = 0; type < QEMU_CLOCK_MAX; type++) { if (qemu_clock_use_for_deadline(type)) { progress |= qemu_clock_run_timers(type); } } return progress; } int64_t qemu_clock_advance_virtual_time(int64_t dest) { int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); AioContext *aio_context; int64_t deadline; aio_context = qemu_get_aio_context(); deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, QEMU_TIMER_ATTR_ALL); /* * A deadline of < 0 indicates this timer is not enabled, so we * won't get far trying to run it forward. */ while (deadline >= 0 && clock < dest) { int64_t warp = qemu_soonest_timeout(dest - clock, deadline); qemu_virtual_clock_set_ns(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + warp); qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL); timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]); clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, QEMU_TIMER_ATTR_ALL); } qemu_clock_notify(QEMU_CLOCK_VIRTUAL); return clock; }