Add utility functions to qemu-timer.c for nanosecond timing. Ensure we keep track of all QEMUClocks on a list.
Add qemu_clock_deadline_ns and qemu_clock_deadline_all_ns to calculate deadlines to nanosecond accuracy. Add utility function qemu_soonest_timeout to calculate soonest deadline. Add qemu_timeout_ns_to_ms to convert a timeout in nanoseconds back to milliseconds for when ppoll is not used. Signed-off-by: Alex Bligh <a...@alex.org.uk> --- include/qemu/timer.h | 19 ++++++++++++ qemu-timer.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 96 insertions(+), 6 deletions(-) diff --git a/include/qemu/timer.h b/include/qemu/timer.h index 8638d36..2f1b609 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -11,6 +11,10 @@ #define SCALE_US 1000 #define SCALE_NS 1 +#define QEMU_CLOCK_REALTIME 0 +#define QEMU_CLOCK_VIRTUAL 1 +#define QEMU_CLOCK_HOST 2 + typedef struct QEMUClock QEMUClock; typedef void QEMUTimerCB(void *opaque); @@ -32,10 +36,16 @@ extern QEMUClock *vm_clock; the virtual clock. */ extern QEMUClock *host_clock; +QEMUClock *qemu_new_clock(int type); +void qemu_free_clock(QEMUClock *clock); int64_t qemu_get_clock_ns(QEMUClock *clock); int64_t qemu_clock_has_timers(QEMUClock *clock); int64_t qemu_clock_expired(QEMUClock *clock); int64_t qemu_clock_deadline(QEMUClock *clock); +int64_t qemu_clock_deadline_ns(QEMUClock *clock); +int64_t qemu_clock_deadline_all_ns(void); +int qemu_timeout_ns_to_ms(int64_t ns); +gint qemu_g_poll_ns(GPollFD *fds, guint nfds, int64_t timeout); void qemu_clock_enable(QEMUClock *clock, bool enabled); void qemu_clock_warp(QEMUClock *clock); @@ -61,6 +71,15 @@ int64_t cpu_get_ticks(void); void cpu_enable_ticks(void); void cpu_disable_ticks(void); +static inline int64_t qemu_soonest_timeout(int64_t timeout1, int64_t timeout2) +{ + /* we can abuse the fact that -1 (which means infinite) is a maximal + * value when cast to unsigned. As this is disgusting, it's kept in + * one inline function. + */ + return ((uint64_t) timeout1 < (uint64_t) timeout2) ? timeout1 : timeout2; +} + static inline QEMUTimer *qemu_new_timer_ns(QEMUClock *clock, QEMUTimerCB *cb, void *opaque) { diff --git a/qemu-timer.c b/qemu-timer.c index 062af38..2150782 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -36,10 +36,6 @@ /***********************************************************/ /* timers */ -#define QEMU_CLOCK_REALTIME 0 -#define QEMU_CLOCK_VIRTUAL 1 -#define QEMU_CLOCK_HOST 2 - struct QEMUClock { QEMUTimer *active_timers; @@ -48,6 +44,8 @@ struct QEMUClock { int type; bool enabled; + + QLIST_ENTRY(QEMUClock) list; }; struct QEMUTimer { @@ -59,6 +57,9 @@ struct QEMUTimer { int scale; }; +static QLIST_HEAD(, QEMUClock) qemu_clocks = + QLIST_HEAD_INITIALIZER(qemu_clocks); + static bool qemu_timer_expired_ns(QEMUTimer *timer_head, int64_t current_time) { return timer_head && (timer_head->expire_time <= current_time); @@ -68,7 +69,7 @@ QEMUClock *rt_clock; QEMUClock *vm_clock; QEMUClock *host_clock; -static QEMUClock *qemu_new_clock(int type) +QEMUClock *qemu_new_clock(int type) { QEMUClock *clock; @@ -77,9 +78,16 @@ static QEMUClock *qemu_new_clock(int type) clock->enabled = true; clock->last = INT64_MIN; notifier_list_init(&clock->reset_notifiers); + QLIST_INSERT_HEAD(&qemu_clocks, clock, list); return clock; } +void qemu_free_clock(QEMUClock *clock) +{ + QLIST_REMOVE(clock, list); + g_free(clock); +} + void qemu_clock_enable(QEMUClock *clock, bool enabled) { clock->enabled = enabled; @@ -101,7 +109,7 @@ int64_t qemu_clock_deadline(QEMUClock *clock) /* To avoid problems with overflow limit this to 2^32. */ int64_t delta = INT32_MAX; - if (clock->active_timers) { + if (clock->enabled && clock->active_timers) { delta = clock->active_timers->expire_time - qemu_get_clock_ns(clock); } if (delta < 0) { @@ -110,6 +118,69 @@ int64_t qemu_clock_deadline(QEMUClock *clock) return delta; } +/* + * 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 qemu_clock_deadline_ns(QEMUClock *clock) +{ + int64_t delta; + + if (!clock->enabled || !clock->active_timers) { + return -1; + } + + delta = clock->active_timers->expire_time - qemu_get_clock_ns(clock); + + if (delta <= 0) { + return 0; + } + + return delta; +} + +/* Return the minimum deadline across all clocks or -1 for no + * deadline + */ +int64_t qemu_clock_deadline_all_ns(void) +{ + QEMUClock *clock; + int64_t ret = -1; + QLIST_FOREACH(clock, &qemu_clocks, list) { + ret = qemu_soonest_timeout(ret, qemu_clock_deadline_ns(clock)); + } + return ret; +} + +/* 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 = (ns + SCALE_MS - 1) / 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; +} + + QEMUTimer *qemu_new_timer(QEMUClock *clock, int scale, QEMUTimerCB *cb, void *opaque) { -- 1.7.9.5