36091345048cba1992ec708f95c23ceb720c9499
[akaros.git] / user / parlib / pvcalarm.c
1 /* Copyright (c) 2014 The Regents of the University of California
2  * Kevin Klues <klueska@cs.berkeley.edu>
3  * See LICENSE for details. */
4
5 #include <stdint.h>
6 #include <stdbool.h>
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <errno.h>
10 #include <parlib/parlib.h>
11 #include <parlib/vcore.h>
12 #include <parlib/event.h>
13 #include <parlib/spinlock.h>
14 #include <parlib/arch/atomic.h>
15 #include <parlib/arch/bitmask.h>
16 #include <sys/queue.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <parlib/pvcalarm.h>
20 #include <parlib/alarm.h>
21
22 /* Different states for enabling/disabling the per-vcore alarms. */
23 enum {
24         S_ENABLING,
25         S_ENABLED,
26         S_DISABLING,
27         S_DISABLED,
28 };
29
30 /* The data associated with each per-vcore alarm that needs to be tracked by
31  * each vcore. It is ultimately stored in an __thread variable. */
32 struct pvcalarm_data {
33         int ctlfd;
34         int timerfd;
35         int alarmid;
36         uint64_t start_uptime;
37 };
38
39 /* The global state of the pvcalarm service itself */
40 struct pvcalarm {
41         uint64_t interval;
42         void (*callback) (void);
43
44         atomic_t state;
45         int busy_count;
46         handle_event_t handler;
47         struct pvcalarm_data *data;
48 };
49
50 /* The only state we need to make sure is set for the global alarm service is
51  * to make sure it s in the disabled state at bootup */
52 static struct pvcalarm global_pvcalarm = { .state = (void*)S_DISABLED };
53
54 /* Helper functions */
55 static void init_pvcalarm(struct pvcalarm_data *pvcalarm_data, int vcoreid);
56 static void handle_pvcalarm(struct event_msg *ev_msg, unsigned int ev_type,
57                             void *data);
58 static void handle_alarm_real(struct event_msg *ev_msg, unsigned int ev_type,
59                               void *data);
60 static void handle_alarm_prof(struct event_msg *ev_msg, unsigned int ev_type,
61                               void *data);
62
63 /* Initialize the pvcalarm service. Only call this function once */
64 static void init_global_pvcalarm(void *arg)
65 {
66         global_pvcalarm.interval = 0;
67         global_pvcalarm.callback = NULL;
68         global_pvcalarm.busy_count = 0;
69         global_pvcalarm.handler = NULL;
70
71         /* Preemptively setup timers for all possible vcores */
72         global_pvcalarm.data = malloc(max_vcores() *
73                                       sizeof(struct pvcalarm_data));
74         for (int i=0; i<max_vcores(); i++) {
75                 init_pvcalarm(&global_pvcalarm.data[i], i);
76         }
77 }
78
79 /* Run the pvc alarm associated with pvcalarm_data for the given amount of
80  * time */
81 static void run_pvcalarm(struct pvcalarm_data *pvcalarm_data, uint64_t offset)
82 {
83         if (devalarm_set_time(pvcalarm_data->timerfd, read_tsc() + offset)) {
84                 perror("Useralarm: Failed to set timer");
85                 return;
86         }
87 }
88
89 /* Run the pvc alarm associated with pvcalarm_data for the given amount of
90  * time. Also mark the start time of the alarm so we can use it for accounting
91  * later. */
92 static void start_pvcalarm(struct pvcalarm_data *pvcalarm_data, uint64_t offset)
93 {
94         pvcalarm_data->start_uptime = vcore_account_uptime_ticks(vcore_id());
95         run_pvcalarm(pvcalarm_data, offset);
96 }
97
98 /* Stop the pvc alarm associated with pvcalarm_data */
99 static void stop_pvcalarm(struct pvcalarm_data *pvcalarm_data)
100 {
101         if (devalarm_disable(pvcalarm_data->timerfd)) {
102                 printf("Useralarm: unable to disarm alarm!\n");
103                 return;
104         }
105 }
106
107 /* Enable the per-vcore alarm service according to one of the policies listed
108  * above.  Every interval usecs the provided callback will be called on each
109  * active vcore according to that policy. */
110 int enable_pvcalarms(int method, uint64_t interval, void (*callback) (void))
111 {
112         static parlib_once_t once = PARLIB_ONCE_INIT;
113
114         assert(!in_vcore_context());
115         if (method != PVCALARM_REAL && method != PVCALARM_PROF)
116                 return EINVAL;
117
118         if (atomic_cas(&global_pvcalarm.state, S_ENABLED, S_ENABLED))
119                 return EALREADY;
120
121         if (!atomic_cas(&global_pvcalarm.state, S_DISABLED, S_ENABLING))
122                 return EBUSY;
123
124         parlib_run_once(&once, init_global_pvcalarm, NULL);
125
126         global_pvcalarm.interval = usec2tsc(interval);
127         global_pvcalarm.callback = callback;
128         global_pvcalarm.busy_count = 0;
129         switch (method) {
130                 case PVCALARM_REAL:
131                         global_pvcalarm.handler = handle_alarm_real;
132                         break;
133                 case PVCALARM_PROF:
134                         global_pvcalarm.handler = handle_alarm_prof;
135                         break;
136         }
137
138         /* Start the timer on all vcores to go off after interval usecs */
139         for (int i=0; i<max_vcores(); i++) {
140                 start_pvcalarm(&global_pvcalarm.data[i],
141                                global_pvcalarm.interval);
142         }
143
144         atomic_set(&global_pvcalarm.state, S_ENABLED);
145         return 0;
146 }
147
148 /* Disable the currently active per-vcore alarm service */
149 int disable_pvcalarms()
150 {
151         assert(!in_vcore_context());
152         if (atomic_cas(&global_pvcalarm.state, S_DISABLED, S_DISABLED))
153                 return EALREADY;
154
155         if (!atomic_cas(&global_pvcalarm.state, S_ENABLED, S_DISABLING))
156                 return EBUSY;
157
158         /* We loop here to let any vcores currently running code associated with
159          * the pvcalarms to finish what they are doing before we disable the
160          * pvcalarm service.  Since we ensure that this function is only called
161          * from non-vcore context, this is OK. */
162         while(global_pvcalarm.busy_count != 0)
163                 cpu_relax();
164
165         global_pvcalarm.interval = 0;
166         global_pvcalarm.callback = NULL;
167         global_pvcalarm.handler = NULL;
168
169         /* Stop the timer on all vcores */
170         for (int i=0; i<max_vcores(); i++)
171                 stop_pvcalarm(&global_pvcalarm.data[i]);
172
173         atomic_set(&global_pvcalarm.state, S_DISABLED);
174         return 0;
175 }
176
177 /* Initialize a specific pvcalarm.  This happens once per vcore as it comes
178  * online and the pvcalarm service is active */
179 static void init_pvcalarm(struct pvcalarm_data *pvcalarm_data, int vcoreid)
180 {
181         int ctlfd, timerfd, alarmid, ev_flags;
182         struct event_queue *ev_q;
183
184         if (devalarm_get_fds(&ctlfd, &timerfd, &alarmid)) {
185                 perror("Pvcalarm: alarm setup");
186                 return;
187         }
188         register_ev_handler(EV_ALARM, handle_pvcalarm, 0);
189         ev_flags = EVENT_IPI | EVENT_VCORE_PRIVATE;
190         ev_q = get_eventq_vcpd(vcoreid, ev_flags);
191         if (!ev_q) {
192                 perror("Pvcalarm: Failed ev_q");
193                 return;
194         }
195         ev_q->ev_vcore = vcoreid;
196         ev_q->ev_flags = ev_flags;
197         if (devalarm_set_evq(timerfd, ev_q, alarmid)) {
198                 perror("Pvcalarm: Failed to set evq");
199                 return;
200         }
201         /* now the alarm is all set, just need to write the timer whenever we
202          * want it to go off. */
203         pvcalarm_data->alarmid = alarmid;
204         pvcalarm_data->ctlfd = ctlfd;
205         pvcalarm_data->timerfd = timerfd;
206 }
207
208 /* TODO: implement a way to completely remove each per-vcore alarm and
209  * deregister it from the #alarm device */
210
211 /* A preamble function to run anytime we are about to do anything on behalf of
212  * the pvcalarms while in vcore context.  This preamble is necessary to ensure
213  * we maintain proper invariants when enabling and disabling the pvcalarm
214  * service in a running application. */
215 static inline bool __vcore_preamble()
216 {
217         int state;
218         assert(in_vcore_context());
219         __sync_fetch_and_add(&global_pvcalarm.busy_count, 1);
220         /* order the state read after the incref.  __sync provides cpu mb */
221         cmb();
222         state = atomic_read(&global_pvcalarm.state);
223         if (state == S_DISABLED || state == S_DISABLING)
224                 goto disabled;
225         return true;
226 disabled:
227         __sync_fetch_and_add(&global_pvcalarm.busy_count, -1);
228         return false;
229 }
230
231 /* The counterpart to the __vcore_preamble() function */
232 static inline void __vcore_postamble()
233 {
234         __sync_fetch_and_add(&global_pvcalarm.busy_count, -1);
235 }
236
237 /* The global handler function.  It simply calls the proper underlying handler
238  * function depending on whether the service is set for the REAL or PERF
239  * policy. */
240 static void handle_pvcalarm(struct event_msg *ev_msg, unsigned int ev_type,
241                             void *data)
242 {
243         struct pvcalarm_data *pvcalarm_data = &global_pvcalarm.data[vcore_id()];
244
245         if (devalarm_get_id(ev_msg) != pvcalarm_data->alarmid)
246                 return;
247         if (!__vcore_preamble()) return;
248         global_pvcalarm.handler(ev_msg, ev_type, data);
249         __vcore_postamble();
250 }
251
252 /* The pvcalarm handler for the REAL policy.  Simply call the registered
253  * callback and restart the interval alarm. */
254 static void handle_alarm_real(struct event_msg *ev_msg, unsigned int ev_type,
255                               void *data)
256 {
257         global_pvcalarm.callback();
258         start_pvcalarm(&global_pvcalarm.data[vcore_id()],
259                        global_pvcalarm.interval);
260 }
261
262 /* The pvcalarm handler for the PROF policy.  Account for any time the vcore
263  * has been offline.  Only when the uptime since the last interval is equal to
264  * the interval time do we run the callback function.  Otherwise we restart the
265  * alarm to make up the difference. */
266 static void handle_alarm_prof(struct event_msg *ev_msg, unsigned int ev_type,
267                               void *data)
268 {
269         int vcoreid = vcore_id();
270         struct pvcalarm_data *pvcalarm_data = &global_pvcalarm.data[vcoreid];
271         uint64_t uptime = vcore_account_uptime_ticks(vcoreid);
272         uint64_t diff = uptime - pvcalarm_data->start_uptime;
273
274         if (diff < global_pvcalarm.interval) {
275                 uint64_t remaining = global_pvcalarm.interval - diff;
276                 run_pvcalarm(pvcalarm_data, remaining);
277         } else {
278                 global_pvcalarm.callback();
279                 start_pvcalarm(pvcalarm_data, global_pvcalarm.interval);
280         }
281 }