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