Split QEMUClock into QEMUClock and QEMUTimerList so that we can
have more than one QEMUTimerList associated with the same clock.

Introduce a default_timerlist concept and make existing
qemu_clock_* calls that actually should operate on a QEMUTimerList
call the relevant QEMUTimerList implementations, using the clock's
default timerlist. This vastly reduces the invasiveness of this
change and means the API stays constant for existing users.

Introduce a list of QEMUTimerLists associated with each clock
so that reenabling the clock can cause all the notifiers
to be called. Note the code to do the notifications is added
in a later patch.

Switch QEMUClockType to an enum. Remove global variables vm_clock,
host_clock and rt_clock and add compatibility defines. Do not
fix qemu_next_alarm_deadline as it's going to be deleted.

Signed-off-by: Alex Bligh <a...@alex.org.uk>
---
 include/qemu/timer.h |   91 +++++++++++++++++++++++--
 qemu-timer.c         |  185 ++++++++++++++++++++++++++++++++++++++------------
 2 files changed, 224 insertions(+), 52 deletions(-)

diff --git a/include/qemu/timer.h b/include/qemu/timer.h
index a1f2ac8..4bf9a9c 100644
--- a/include/qemu/timer.h
+++ b/include/qemu/timer.h
@@ -11,38 +11,65 @@
 #define SCALE_US 1000
 #define SCALE_NS 1
 
-#define QEMU_CLOCK_REALTIME 0
-#define QEMU_CLOCK_VIRTUAL  1
-#define QEMU_CLOCK_HOST     2
+typedef enum {
+    QEMU_CLOCK_REALTIME = 0,
+    QEMU_CLOCK_VIRTUAL = 1,
+    QEMU_CLOCK_HOST = 2,
+    QEMU_CLOCK_MAX
+} QEMUClockType;
 
 typedef struct QEMUClock QEMUClock;
+typedef struct QEMUTimerList QEMUTimerList;
 typedef void QEMUTimerCB(void *opaque);
 
+extern QEMUClock *qemu_clocks[QEMU_CLOCK_MAX];
+
+static inline QEMUClock *qemu_get_clock(QEMUClockType type)
+{
+    return qemu_clocks[type];
+}
+
+/* These three clocks are maintained here with separate variable
+   names for compatibility only.
+*/
+
 /* The real time clock should be used only for stuff which does not
    change the virtual machine state, as it is run even if the virtual
    machine is stopped. The real time clock has a frequency of 1000
    Hz. */
-extern QEMUClock *rt_clock;
+#define rt_clock (qemu_get_clock(QEMU_CLOCK_REALTIME))
 
 /* The virtual clock is only run during the emulation. It is stopped
    when the virtual machine is stopped. Virtual timers use a high
    precision clock, usually cpu cycles (use ticks_per_sec). */
-extern QEMUClock *vm_clock;
+#define vm_clock (qemu_get_clock(QEMU_CLOCK_VIRTUAL))
 
 /* The host clock should be use for device models that emulate accurate
    real time sources. It will continue to run when the virtual machine
    is suspended, and it will reflect system time changes the host may
    undergo (e.g. due to NTP). The host clock has the same precision as
    the virtual clock. */
-extern QEMUClock *host_clock;
+#define host_clock (qemu_get_clock(QEMU_CLOCK_HOST))
 
-QEMUClock *qemu_new_clock(int type);
+QEMUClock *qemu_new_clock(QEMUClockType 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);
+bool qemu_clock_use_for_deadline(QEMUClock *clock);
+QEMUTimerList *qemu_clock_get_default_timerlist(QEMUClock *clock);
+
+QEMUTimerList *timerlist_new(QEMUClockType type);
+void timerlist_free(QEMUTimerList *tl);
+int64_t timerlist_has_timers(QEMUTimerList *tl);
+int64_t timerlist_expired(QEMUTimerList *tl);
+int64_t timerlist_deadline(QEMUTimerList *tl);
+int64_t timerlist_deadline_ns(QEMUTimerList *tl);
+QEMUClock *timerlist_get_clock(QEMUTimerList *tl);
+bool timerlist_run_timers(QEMUTimerList *tl);
+
 int qemu_timeout_ns_to_ms(int64_t ns);
 int qemu_poll_ns(GPollFD *fds, uint nfds, int64_t timeout);
 void qemu_clock_enable(QEMUClock *clock, bool enabled);
@@ -54,6 +81,8 @@ void qemu_unregister_clock_reset_notifier(QEMUClock *clock,
 
 QEMUTimer *qemu_new_timer(QEMUClock *clock, int scale,
                           QEMUTimerCB *cb, void *opaque);
+QEMUTimer *timer_new(QEMUTimerList *tl, int scale,
+                     QEMUTimerCB *cb, void *opaque);
 void qemu_free_timer(QEMUTimer *ts);
 void qemu_del_timer(QEMUTimer *ts);
 void qemu_mod_timer_ns(QEMUTimer *ts, int64_t expire_time);
@@ -62,6 +91,42 @@ bool qemu_timer_pending(QEMUTimer *ts);
 bool qemu_timer_expired(QEMUTimer *timer_head, int64_t current_time);
 uint64_t qemu_timer_expire_time_ns(QEMUTimer *ts);
 
+/* New format calling conventions for timers */
+static inline void timer_free(QEMUTimer *ts)
+{
+    qemu_free_timer(ts);
+}
+
+static inline void timer_del(QEMUTimer *ts)
+{
+    qemu_del_timer(ts);
+}
+
+static inline void timer_mod_ns(QEMUTimer *ts, int64_t expire_time)
+{
+    qemu_mod_timer_ns(ts, expire_time);
+}
+
+static inline void timer_mod(QEMUTimer *ts, int64_t expire_timer)
+{
+    qemu_mod_timer(ts, expire_timer);
+}
+
+static inline bool timer_pending(QEMUTimer *ts)
+{
+    return qemu_timer_pending(ts);
+}
+
+static inline bool timer_expired(QEMUTimer *timer_head, int64_t current_time)
+{
+    return qemu_timer_expired(timer_head, current_time);
+}
+
+static inline uint64_t timer_expire_time_ns(QEMUTimer *ts)
+{
+    return qemu_timer_expire_time_ns(ts);
+}
+
 bool qemu_run_timers(QEMUClock *clock);
 bool qemu_run_all_timers(void);
 void configure_alarms(char const *opt);
@@ -87,12 +152,24 @@ static inline QEMUTimer *qemu_new_timer_ns(QEMUClock 
*clock, QEMUTimerCB *cb,
     return qemu_new_timer(clock, SCALE_NS, cb, opaque);
 }
 
+static inline QEMUTimer *timer_new_ns(QEMUTimerList *tl, QEMUTimerCB *cb,
+                                      void *opaque)
+{
+    return timer_new(tl, SCALE_NS, cb, opaque);
+}
+
 static inline QEMUTimer *qemu_new_timer_ms(QEMUClock *clock, QEMUTimerCB *cb,
                                            void *opaque)
 {
     return qemu_new_timer(clock, SCALE_MS, cb, opaque);
 }
 
+static inline QEMUTimer *timer_new_ms(QEMUTimerList *tl, QEMUTimerCB *cb,
+                                      void *opaque)
+{
+    return timer_new(tl, SCALE_MS, cb, opaque);
+}
+
 static inline int64_t qemu_get_clock_ms(QEMUClock *clock)
 {
     return qemu_get_clock_ns(clock) / SCALE_MS;
diff --git a/qemu-timer.c b/qemu-timer.c
index 714bc92..b2d18e4 100644
--- a/qemu-timer.c
+++ b/qemu-timer.c
@@ -49,18 +49,34 @@
 /* timers */
 
 struct QEMUClock {
-    QEMUTimer *active_timers;
+    QEMUTimerList *default_timerlist;
+    QLIST_HEAD(, QEMUTimerList) timerlists;
 
     NotifierList reset_notifiers;
     int64_t last;
 
-    int type;
+    QEMUClockType type;
     bool enabled;
 };
 
+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;
+    QEMUTimer *active_timers;
+    QLIST_ENTRY(QEMUTimerList) list;
+};
+
 struct QEMUTimer {
     int64_t expire_time;       /* in nanoseconds */
-    QEMUClock *clock;
+    QEMUTimerList *tl;
     QEMUTimerCB *cb;
     void *opaque;
     QEMUTimer *next;
@@ -93,21 +109,25 @@ static int64_t qemu_next_alarm_deadline(void)
 {
     int64_t delta = INT64_MAX;
     int64_t rtdelta;
+    int64_t hdelta;
 
-    if (!use_icount && vm_clock->enabled && vm_clock->active_timers) {
-        delta = vm_clock->active_timers->expire_time -
-                     qemu_get_clock_ns(vm_clock);
+    if (!use_icount && vm_clock->enabled &&
+        vm_clock->default_timerlist->active_timers) {
+        delta = vm_clock->default_timerlist->active_timers->expire_time -
+            qemu_get_clock_ns(vm_clock);
     }
-    if (host_clock->enabled && host_clock->active_timers) {
-        int64_t hdelta = host_clock->active_timers->expire_time -
-                 qemu_get_clock_ns(host_clock);
+    if (host_clock->enabled &&
+        host_clock->default_timerlist->active_timers) {
+        hdelta = host_clock->default_timerlist->active_timers->expire_time -
+            qemu_get_clock_ns(host_clock);
         if (hdelta < delta) {
             delta = hdelta;
         }
     }
-    if (rt_clock->enabled && rt_clock->active_timers) {
-        rtdelta = (rt_clock->active_timers->expire_time -
-                 qemu_get_clock_ns(rt_clock));
+    if (rt_clock->enabled &&
+        rt_clock->default_timerlist->active_timers) {
+        rtdelta = (rt_clock->default_timerlist->active_timers->expire_time -
+                   qemu_get_clock_ns(rt_clock));
         if (rtdelta < delta) {
             delta = rtdelta;
         }
@@ -231,11 +251,33 @@ next:
     }
 }
 
-QEMUClock *rt_clock;
-QEMUClock *vm_clock;
-QEMUClock *host_clock;
+static QEMUTimerList *timerlist_new_from_clock(QEMUClock *clock)
+{
+    QEMUTimerList *tl;
+
+    tl = g_malloc0(sizeof(QEMUTimerList));
+    tl->clock = clock;
+    QLIST_INSERT_HEAD(&clock->timerlists, tl, list);
+    return tl;
+}
+
+QEMUTimerList *timerlist_new(QEMUClockType type)
+{
+    return timerlist_new_from_clock(qemu_get_clock(type));
+}
 
-QEMUClock *qemu_new_clock(int type)
+void timerlist_free(QEMUTimerList *tl)
+{
+    if (tl->clock) {
+        QLIST_REMOVE(tl, list);
+        if (tl->clock->default_timerlist == tl) {
+            tl->clock->default_timerlist = NULL;
+        }
+    }
+    g_free(tl);
+}
+
+QEMUClock *qemu_new_clock(QEMUClockType type)
 {
     QEMUClock *clock;
 
@@ -244,14 +286,21 @@ QEMUClock *qemu_new_clock(int type)
     clock->enabled = true;
     clock->last = INT64_MIN;
     notifier_list_init(&clock->reset_notifiers);
+    clock->default_timerlist = timerlist_new_from_clock(clock);
     return clock;
 }
 
 void qemu_free_clock(QEMUClock *clock)
 {
+    timerlist_free(clock->default_timerlist);
     g_free(clock);
 }
 
+bool qemu_clock_use_for_deadline(QEMUClock *clock)
+{
+    return !(use_icount && (clock->type = QEMU_CLOCK_VIRTUAL));
+}
+
 void qemu_clock_enable(QEMUClock *clock, bool enabled)
 {
     bool old = clock->enabled;
@@ -261,24 +310,34 @@ void qemu_clock_enable(QEMUClock *clock, bool enabled)
     }
 }
 
+int64_t timerlist_has_timers(QEMUTimerList *tl)
+{
+    return !!tl->active_timers;
+}
+
 int64_t qemu_clock_has_timers(QEMUClock *clock)
 {
-    return !!clock->active_timers;
+    return timerlist_has_timers(clock->default_timerlist);
+}
+
+int64_t timerlist_expired(QEMUTimerList *tl)
+{
+    return (tl->active_timers &&
+            tl->active_timers->expire_time < qemu_get_clock_ns(tl->clock));
 }
 
 int64_t qemu_clock_expired(QEMUClock *clock)
 {
-    return (clock->active_timers &&
-            clock->active_timers->expire_time < qemu_get_clock_ns(clock));
+    return timerlist_expired(clock->default_timerlist);
 }
 
-int64_t qemu_clock_deadline(QEMUClock *clock)
+int64_t timerlist_deadline(QEMUTimerList *tl)
 {
     /* To avoid problems with overflow limit this to 2^32.  */
     int64_t delta = INT32_MAX;
 
-    if (clock->enabled && clock->active_timers) {
-        delta = clock->active_timers->expire_time - qemu_get_clock_ns(clock);
+    if (tl->clock->enabled && tl->active_timers) {
+        delta = tl->active_timers->expire_time - qemu_get_clock_ns(tl->clock);
     }
     if (delta < 0) {
         delta = 0;
@@ -286,20 +345,25 @@ int64_t qemu_clock_deadline(QEMUClock *clock)
     return delta;
 }
 
+int64_t qemu_clock_deadline(QEMUClock *clock)
+{
+    return timerlist_deadline(clock->default_timerlist);
+}
+
 /*
  * 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 timerlist_deadline_ns(QEMUTimerList *tl)
 {
     int64_t delta;
 
-    if (!clock->enabled || !clock->active_timers) {
+    if (!tl->clock->enabled || !tl->active_timers) {
         return -1;
     }
 
-    delta = clock->active_timers->expire_time - qemu_get_clock_ns(clock);
+    delta = tl->active_timers->expire_time - qemu_get_clock_ns(tl->clock);
 
     if (delta <= 0) {
         return 0;
@@ -308,6 +372,21 @@ int64_t qemu_clock_deadline_ns(QEMUClock *clock)
     return delta;
 }
 
+int64_t qemu_clock_deadline_ns(QEMUClock *clock)
+{
+    return timerlist_deadline_ns(clock->default_timerlist);
+}
+
+QEMUClock *timerlist_get_clock(QEMUTimerList *tl)
+{
+    return tl->clock;
+}
+
+QEMUTimerList *qemu_clock_get_default_timerlist(QEMUClock *clock)
+{
+    return clock->default_timerlist;
+}
+
 /* Transition function to convert a nanosecond timeout to ms
  * This is used where a system does not support ppoll
  */
@@ -356,19 +435,26 @@ int qemu_poll_ns(GPollFD *fds, uint nfds, int64_t timeout)
 }
 
 
-QEMUTimer *qemu_new_timer(QEMUClock *clock, int scale,
-                          QEMUTimerCB *cb, void *opaque)
+QEMUTimer *timer_new(QEMUTimerList *tl, int scale,
+                     QEMUTimerCB *cb, void *opaque)
 {
     QEMUTimer *ts;
 
     ts = g_malloc0(sizeof(QEMUTimer));
-    ts->clock = clock;
+    ts->tl = tl;
     ts->cb = cb;
     ts->opaque = opaque;
     ts->scale = scale;
     return ts;
 }
 
+QEMUTimer *qemu_new_timer(QEMUClock *clock, int scale,
+                          QEMUTimerCB *cb, void *opaque)
+{
+    return timer_new(clock->default_timerlist,
+                     scale, cb, opaque);
+}
+
 void qemu_free_timer(QEMUTimer *ts)
 {
     g_free(ts);
@@ -381,7 +467,7 @@ void qemu_del_timer(QEMUTimer *ts)
 
     /* NOTE: this code must be signal safe because
        qemu_timer_expired() can be called from a signal. */
-    pt = &ts->clock->active_timers;
+    pt = &ts->tl->active_timers;
     for(;;) {
         t = *pt;
         if (!t)
@@ -405,7 +491,7 @@ void qemu_mod_timer_ns(QEMUTimer *ts, int64_t expire_time)
     /* add the timer in the sorted list */
     /* NOTE: this code must be signal safe because
        qemu_timer_expired() can be called from a signal. */
-    pt = &ts->clock->active_timers;
+    pt = &ts->tl->active_timers;
     for(;;) {
         t = *pt;
         if (!qemu_timer_expired_ns(t, expire_time)) {
@@ -418,12 +504,12 @@ void qemu_mod_timer_ns(QEMUTimer *ts, int64_t expire_time)
     *pt = ts;
 
     /* Rearm if necessary  */
-    if (pt == &ts->clock->active_timers) {
+    if (pt == &ts->tl->active_timers) {
         if (!alarm_timer->pending) {
             qemu_rearm_alarm_timer(alarm_timer);
         }
         /* Interrupt execution to force deadline recalculation.  */
-        qemu_clock_warp(ts->clock);
+        qemu_clock_warp(ts->tl->clock);
         if (use_icount) {
             qemu_notify_event();
         }
@@ -438,7 +524,7 @@ void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time)
 bool qemu_timer_pending(QEMUTimer *ts)
 {
     QEMUTimer *t;
-    for (t = ts->clock->active_timers; t != NULL; t = t->next) {
+    for (t = ts->tl->active_timers; t != NULL; t = t->next) {
         if (t == ts) {
             return true;
         }
@@ -451,23 +537,24 @@ bool qemu_timer_expired(QEMUTimer *timer_head, int64_t 
current_time)
     return qemu_timer_expired_ns(timer_head, current_time * timer_head->scale);
 }
 
-bool qemu_run_timers(QEMUClock *clock)
+bool timerlist_run_timers(QEMUTimerList *tl)
 {
     QEMUTimer *ts;
     int64_t current_time;
     bool progress = false;
    
-    if (!clock->enabled)
+    if (!tl->clock->enabled) {
         return progress;
+    }
 
-    current_time = qemu_get_clock_ns(clock);
+    current_time = qemu_get_clock_ns(tl->clock);
     for(;;) {
-        ts = clock->active_timers;
+        ts = tl->active_timers;
         if (!qemu_timer_expired_ns(ts, current_time)) {
             break;
         }
         /* remove timer from the list before calling the callback */
-        clock->active_timers = ts->next;
+        tl->active_timers = ts->next;
         ts->next = NULL;
 
         /* run the callback (the timer list can be modified) */
@@ -477,6 +564,11 @@ bool qemu_run_timers(QEMUClock *clock)
     return progress;
 }
 
+bool qemu_run_timers(QEMUClock *clock)
+{
+    return timerlist_run_timers(clock->default_timerlist);
+}
+
 int64_t qemu_get_clock_ns(QEMUClock *clock)
 {
     int64_t now, last;
@@ -514,11 +606,13 @@ void qemu_unregister_clock_reset_notifier(QEMUClock 
*clock, Notifier *notifier)
 
 void init_clocks(void)
 {
-    if (!rt_clock) {
-        rt_clock = qemu_new_clock(QEMU_CLOCK_REALTIME);
-        vm_clock = qemu_new_clock(QEMU_CLOCK_VIRTUAL);
-        host_clock = qemu_new_clock(QEMU_CLOCK_HOST);
+    QEMUClockType type;
+    for (type = 0; type < QEMU_CLOCK_MAX; type++) {
+        if (!qemu_clocks[type]) {
+            qemu_clocks[type] = qemu_new_clock(type);
+        }
     }
+
 #ifdef CONFIG_PRCTL_PR_SET_TIMERSLACK
     prctl(PR_SET_TIMERSLACK, 1, 0, 0, 0);
 #endif
@@ -535,9 +629,10 @@ bool qemu_run_all_timers(void)
     alarm_timer->pending = false;
 
     /* vm time timers */
-    progress |= qemu_run_timers(vm_clock);
-    progress |= qemu_run_timers(rt_clock);
-    progress |= qemu_run_timers(host_clock);
+    QEMUClockType type;
+    for (type = 0; type < QEMU_CLOCK_MAX; type++) {
+        progress |= qemu_run_timers(qemu_get_clock(type));
+    }
 
     /* rearm timer, if not periodic */
     if (alarm_timer->expired) {
-- 
1.7.9.5


Reply via email to