Initialize all vcores in SCP mode
[akaros.git] / user / benchutil / vcore_tick.c
1 /* Copyright (c) 2016 Google Inc.
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * Vcore timer ticks. */
6
7 #include <parlib/vcore.h>
8 #include <parlib/assert.h>
9 #include <parlib/tsc-compat.h>
10 #include <parlib/arch/bitmask.h>
11 #include <benchutil/alarm.h>
12 #include <benchutil/vcore_tick.h>
13
14 /* TODO: if we use some other form of per-vcore memory, we can also have a
15  * per-vcore init function that we run before the VC spools up, removing the
16  * need to check for PREINIT. */
17
18 enum {
19         VC_TICK_PREINIT = 0,
20         VC_TICK_ENABLED = 1,
21         VC_TICK_DISABLED = 2,
22 };
23
24 struct vcore_tick {
25         int                                                     state;
26         int                                                     ctl_fd;
27         int                                                     timer_fd;
28         uint64_t                                        next_deadline;
29         uint64_t                                        period_ticks;
30         struct event_queue                      *ev_q;
31 };
32
33 static __thread struct vcore_tick __vc_tick;
34
35 static void vcore_tick_init(struct vcore_tick *vc_tick)
36 {
37         int ret;
38
39         ret = devalarm_get_fds(&vc_tick->ctl_fd, &vc_tick->timer_fd, 0);
40         assert(!ret);
41         /* We want an IPI and a bit set in the bitmask.  But no wakeups, in case
42          * we're offline. */
43         vc_tick->ev_q = get_eventq(EV_MBOX_BITMAP);
44         assert(vc_tick->ev_q);
45         vc_tick->ev_q->ev_flags = EVENT_IPI;
46         vc_tick->ev_q->ev_vcore = vcore_id();
47         ret = devalarm_set_evq(vc_tick->timer_fd, vc_tick->ev_q, 0);
48         assert(!ret);
49         vc_tick->state = VC_TICK_DISABLED;
50 }
51
52 static void __vcore_tick_start(struct vcore_tick *vc_tick, uint64_t from_now)
53 {
54         int ret;
55
56         ret = devalarm_set_time(vc_tick->timer_fd, read_tsc() + from_now);
57         assert(!ret);
58 }
59
60 /* Starts a timer tick for this vcore, in virtual time (time the vcore is
61  * actually online).  You can call this repeatedly, even if the timer is already
62  * on.  You also can update the period of an already-running tick. */
63 void vcore_tick_enable(uint64_t period_usec)
64 {
65         struct vcore_tick *vc_tick = &__vc_tick;
66
67         if (vc_tick->state == VC_TICK_PREINIT)
68                 vcore_tick_init(vc_tick);
69
70         vc_tick->period_ticks = usec2tsc(period_usec);
71         if (vc_tick->state == VC_TICK_DISABLED) {
72                 vc_tick->next_deadline = vcore_account_uptime_ticks(vcore_id()) +
73                                          vc_tick->period_ticks;
74                 __vcore_tick_start(vc_tick, vc_tick->period_ticks);
75                 vc_tick->state = VC_TICK_ENABLED;
76         }
77 }
78
79 /* Disables the timer tick.  You can call this repeatedly.  It is possible that
80  * you will still have a timer tick pending after this returns. */
81 void vcore_tick_disable(void)
82 {
83         struct vcore_tick *vc_tick = &__vc_tick;
84         int ret;
85
86         if (vc_tick->state == VC_TICK_PREINIT)
87                 vcore_tick_init(vc_tick);
88
89         if (vc_tick->state == VC_TICK_ENABLED) {
90                 ret = devalarm_disable(vc_tick->timer_fd);
91                 assert(!ret);
92                 vc_tick->state = VC_TICK_DISABLED;
93         }
94 }
95
96 /* Polls the vcore timer tick.  Returns the number of times it has expired, 0
97  * for not yet otherwise.  Either way, it will ensure that the underlying alarm
98  * is still turned on. */
99 int vcore_tick_poll(void)
100 {
101         struct vcore_tick *vc_tick = &__vc_tick;
102         struct evbitmap *evbm;
103         int ret = 0;
104         uint64_t from_now, virtual_now;
105
106         if (vc_tick->state == VC_TICK_PREINIT)
107                 vcore_tick_init(vc_tick);
108
109         evbm = &vc_tick->ev_q->ev_mbox->evbm;
110         if (!GET_BITMASK_BIT(evbm->bitmap, EV_ALARM)) {
111                 /* It might be possible that the virtual time has passed, but the alarm
112                  * hasn't arrived yet.
113                  *
114                  * We assume that if the bit is not set and the tick is enabled that
115                  * the kernel still has an alarm set for us.  It is possible for the bit
116                  * to be set more than expected (disable an alarm, but fail to cancel
117                  * the alarm before it goes off, then enable it, and then we'll have the
118                  * bit set before the alarm expired).  However, it is not possible that
119                  * the bit is clear and there is no alarm pending at this point.  This
120                  * is because the only time we clear the bit is below, and then right
121                  * after that we set an alarm. (The bit is also clear at init time, and
122                  * we start the alarm when we enable the tick).
123                  *
124                  * Anyway, the alarm should be arriving shortly.  In this case, as in
125                  * the case where the bit gets set right after we check, we missed
126                  * polling for the event.  The kernel will still __notify us, setting
127                  * notif_pending, and we'll notice the next time we attempt to leave
128                  * vcore context. */
129                 return 0;
130         }
131         /* Don't care about clobbering neighboring bits (non-atomic op) */
132         CLR_BITMASK_BIT(evbm->bitmap, EV_ALARM);
133         /* As mentioned above, it is possible to still have an active alarm in the
134          * kernel.  We can still set a new time for the alarm, and it will just
135          * update the kernel's awaiter.  And if that alarm has fired, then we'll
136          * just have a spurious setting of the bit.  This does not affect our return
137          * value, which is based on virtual time, not alarm resets. */
138         virtual_now = vcore_account_uptime_ticks(vcore_id());
139         /* It's possible that we've fallen multiple ticks behind virtual now.
140          * In that case, we'll just jump ahead a bit */
141         while (vc_tick->next_deadline <= virtual_now) {
142                 ret++;
143                 vc_tick->next_deadline += vc_tick->period_ticks;
144         }
145         /* There's a slight chance we miss an alarm if the period is very small.
146          * virtual_now is a little old.  If the period is so small that this is a
147          * problem and if we updated virtual now in the while loop, then we'd also
148          * get caught in the while loop forever. */
149         from_now = vc_tick->next_deadline - virtual_now;
150         __vcore_tick_start(vc_tick, from_now);
151         return ret;
152 }