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