Provide vcore timer ticks for 2LSs
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 23 Feb 2016 20:34:21 +0000 (15:34 -0500)
committerBarret Rhoden <brho@cs.berkeley.edu>
Mon, 2 May 2016 21:11:15 +0000 (17:11 -0400)
2LSs can now run their own timer ticks, which run in virtual time (the time
a vcore is online).  Each vcore can have its own independent tick.  The 2LS
needs to poll the tick in its sched_entry() 2LS op.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
user/benchutil/include/benchutil/vcore_tick.h [new file with mode: 0644]
user/benchutil/vcore_tick.c [new file with mode: 0644]

diff --git a/user/benchutil/include/benchutil/vcore_tick.h b/user/benchutil/include/benchutil/vcore_tick.h
new file mode 100644 (file)
index 0000000..50781bd
--- /dev/null
@@ -0,0 +1,43 @@
+/* Copyright (c) 2016 Google Inc.
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Vcore timer ticks.
+ *
+ * Each vcore can independently operate timer ticks, based in virtual time (the
+ * time a vcore is actually online).  When the tick expires, the vcore will
+ * receive an IPI/notification, not any event via handle_events().  These ticks
+ * need to be polled by the 2LS.
+ *
+ * E.g. to set and use a 10ms tick:
+ *
+ *             vcore_tick_enable(10000);
+ *
+ * In the 2LS sched_entry():
+ *
+ *             if (vcore_tick_poll())
+ *                     time_is_up_do_something();
+ *
+ * You can change the period on the fly, and it will kick in after the next
+ * tick.  You can also attempt to enable or disable the alarm as many times as
+ * you want.  For instance:
+ *
+ *             vcore_tick_enable(10000);
+ *             vcore_tick_enable(10000);
+ *             vcore_tick_enable(10000);
+ *
+ * will only set one alarm.  The latter two calls just set the period to 10000
+ * usec. */
+
+#pragma once
+
+#include <ros/event.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+void vcore_tick_enable(uint64_t period_usec);
+void vcore_tick_disable(void);
+int vcore_tick_poll(void);
+
+__END_DECLS
diff --git a/user/benchutil/vcore_tick.c b/user/benchutil/vcore_tick.c
new file mode 100644 (file)
index 0000000..71529bb
--- /dev/null
@@ -0,0 +1,152 @@
+/* Copyright (c) 2016 Google Inc.
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Vcore timer ticks. */
+
+#include <parlib/vcore.h>
+#include <parlib/assert.h>
+#include <parlib/tsc-compat.h>
+#include <parlib/arch/bitmask.h>
+#include <benchutil/alarm.h>
+#include <benchutil/vcore_tick.h>
+
+/* TODO: if we use some other form of per-vcore memory, we can also have a
+ * per-vcore init function that we run before the VC spools up, removing the
+ * need to check for PREINIT. */
+
+enum {
+       VC_TICK_PREINIT = 0,
+       VC_TICK_ENABLED = 1,
+       VC_TICK_DISABLED = 2,
+};
+
+struct vcore_tick {
+       int                                                     state;
+       int                                                     ctl_fd;
+       int                                                     timer_fd;
+       uint64_t                                        next_deadline;
+       uint64_t                                        period_ticks;
+       struct event_queue                      *ev_q;
+};
+
+static __thread struct vcore_tick __vc_tick;
+
+static void vcore_tick_init(struct vcore_tick *vc_tick)
+{
+       int ret;
+
+       ret = devalarm_get_fds(&vc_tick->ctl_fd, &vc_tick->timer_fd, 0);
+       assert(!ret);
+       /* We want an IPI and a bit set in the bitmask.  But no wakeups, in case
+        * we're offline. */
+       vc_tick->ev_q = get_eventq(EV_MBOX_BITMAP);
+       assert(vc_tick->ev_q);
+       vc_tick->ev_q->ev_flags = EVENT_IPI;
+       vc_tick->ev_q->ev_vcore = vcore_id();
+       ret = devalarm_set_evq(vc_tick->timer_fd, vc_tick->ev_q, 0);
+       assert(!ret);
+       vc_tick->state = VC_TICK_DISABLED;
+}
+
+static void __vcore_tick_start(struct vcore_tick *vc_tick, uint64_t from_now)
+{
+       int ret;
+
+       ret = devalarm_set_time(vc_tick->timer_fd, read_tsc() + from_now);
+       assert(!ret);
+}
+
+/* Starts a timer tick for this vcore, in virtual time (time the vcore is
+ * actually online).  You can call this repeatedly, even if the timer is already
+ * on.  You also can update the period of an already-running tick. */
+void vcore_tick_enable(uint64_t period_usec)
+{
+       struct vcore_tick *vc_tick = &__vc_tick;
+
+       if (vc_tick->state == VC_TICK_PREINIT)
+               vcore_tick_init(vc_tick);
+
+       vc_tick->period_ticks = usec2tsc(period_usec);
+       if (vc_tick->state == VC_TICK_DISABLED) {
+               vc_tick->next_deadline = vcore_account_uptime_ticks(vcore_id()) +
+                                        vc_tick->period_ticks;
+               __vcore_tick_start(vc_tick, vc_tick->period_ticks);
+               vc_tick->state = VC_TICK_ENABLED;
+       }
+}
+
+/* Disables the timer tick.  You can call this repeatedly.  It is possible that
+ * you will still have a timer tick pending after this returns. */
+void vcore_tick_disable(void)
+{
+       struct vcore_tick *vc_tick = &__vc_tick;
+       int ret;
+
+       if (vc_tick->state == VC_TICK_PREINIT)
+               vcore_tick_init(vc_tick);
+
+       if (vc_tick->state == VC_TICK_ENABLED) {
+               ret = devalarm_disable(vc_tick->timer_fd);
+               assert(!ret);
+               vc_tick->state = VC_TICK_DISABLED;
+       }
+}
+
+/* Polls the vcore timer tick.  Returns the number of times it has expired, 0
+ * for not yet otherwise.  Either way, it will ensure that the underlying alarm
+ * is still turned on. */
+int vcore_tick_poll(void)
+{
+       struct vcore_tick *vc_tick = &__vc_tick;
+       struct evbitmap *evbm;
+       int ret = 0;
+       uint64_t from_now, virtual_now;
+
+       if (vc_tick->state == VC_TICK_PREINIT)
+               vcore_tick_init(vc_tick);
+
+       evbm = &vc_tick->ev_q->ev_mbox->evbm;
+       if (!GET_BITMASK_BIT(evbm->bitmap, EV_ALARM)) {
+               /* It might be possible that the virtual time has passed, but the alarm
+                * hasn't arrived yet.
+                *
+                * We assume that if the bit is not set and the tick is enabled that
+                * the kernel still has an alarm set for us.  It is possible for the bit
+                * to be set more than expected (disable an alarm, but fail to cancel
+                * the alarm before it goes off, then enable it, and then we'll have the
+                * bit set before the alarm expired).  However, it is not possible that
+                * the bit is clear and there is no alarm pending at this point.  This
+                * is because the only time we clear the bit is below, and then right
+                * after that we set an alarm. (The bit is also clear at init time, and
+                * we start the alarm when we enable the tick).
+                *
+                * Anyway, the alarm should be arriving shortly.  In this case, as in
+                * the case where the bit gets set right after we check, we missed
+                * polling for the event.  The kernel will still __notify us, setting
+                * notif_pending, and we'll notice the next time we attempt to leave
+                * vcore context. */
+               return 0;
+       }
+       /* Don't care about clobbering neighboring bits (non-atomic op) */
+       CLR_BITMASK_BIT(evbm->bitmap, EV_ALARM);
+       /* As mentioned above, it is possible to still have an active alarm in the
+        * kernel.  We can still set a new time for the alarm, and it will just
+        * update the kernel's awaiter.  And if that alarm has fired, then we'll
+        * just have a spurious setting of the bit.  This does not affect our return
+        * value, which is based on virtual time, not alarm resets. */
+       virtual_now = vcore_account_uptime_ticks(vcore_id());
+       /* It's possible that we've fallen multiple ticks behind virtual now.
+        * In that case, we'll just jump ahead a bit */
+       while (vc_tick->next_deadline <= virtual_now) {
+               ret++;
+               vc_tick->next_deadline += vc_tick->period_ticks;
+       }
+       /* There's a slight chance we miss an alarm if the period is very small.
+        * virtual_now is a little old.  If the period is so small that this is a
+        * problem and if we updated virtual now in the while loop, then we'd also
+        * get caught in the while loop forever. */
+       from_now = vc_tick->next_deadline - virtual_now;
+       __vcore_tick_start(vc_tick, from_now);
+       return ret;
+}