Removes the alarm_dispatcher
[akaros.git] / user / benchutil / 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.h>
11 #include <vcore.h>
12 #include <event.h>
13 #include <spinlock.h>
14 #include <arch/atomic.h>
15 #include <arch/bitmask.h>
16 #include <sys/queue.h>
17 #include <fcntl.h>
18 #include <pvcalarm.h>
19
20 /* Different states for enabling/disabling the per-vcore alarms. */
21 enum {
22         S_ENABLING,
23         S_ENABLED,
24         S_DISABLING,
25         S_DISABLED,
26 };
27
28 /* The data associated with each per-vcore alarm that needs to be tracked by
29  * each vcore. It is ultimately stored in an __thread variable. */
30 struct pvcalarm_data {
31         int ctlfd;
32         int timerfd;
33         int alarmid;
34         uint64_t start_uptime;
35 };
36
37 /* The global state of the pvcalarm service itself */
38 struct pvcalarm {
39         uint64_t interval;
40         void (*callback) (void);
41
42         atomic_t state;
43         int busy_count;
44         handle_event_t handler;
45         struct pvcalarm_data *data;
46 };
47
48 /* The only state we need to make sure is set for the global alarm service is
49  * to make sure it s in the disabled state at bootup */
50 static struct pvcalarm global_pvcalarm = { .state = (void*)S_DISABLED };
51
52 /* Helper functions */
53 static void init_pvcalarm(struct pvcalarm_data *pvcalarm_data, int vcoreid);
54 static void handle_pvcalarm(struct event_msg *ev_msg, unsigned int ev_type,
55                             void *data);
56 static void handle_alarm_real(struct event_msg *ev_msg, unsigned int ev_type,
57                               void *data);
58 static void handle_alarm_prof(struct event_msg *ev_msg, unsigned int ev_type,
59                               void *data);
60
61 /* Initialize the pvcalarm service. Only call this function once */
62 static int init_global_pvcalarm()
63 {
64         global_pvcalarm.interval = 0;
65         global_pvcalarm.callback = NULL;
66         global_pvcalarm.busy_count = 0;
67         global_pvcalarm.handler = NULL;
68
69         /* Preemptively setup timers for all possible vcores */
70         global_pvcalarm.data = malloc(max_vcores() * sizeof(struct pvcalarm_data));
71         for (int i=0; i<max_vcores(); i++) {
72                 init_pvcalarm(&global_pvcalarm.data[i], i);
73         }
74 }
75
76 /* Run the pvc alarm associated with pvcalarm_data for the given amount of
77  * time */
78 static void run_pvcalarm(struct pvcalarm_data *pvcalarm_data, uint64_t offset)
79 {
80         int ret;
81         char buf[20];
82         ret = snprintf(buf, sizeof(buf), "%llx", read_tsc() + offset);
83         ret = write(pvcalarm_data->timerfd, buf, ret);
84         if (ret <= 0) {
85                 perror("Useralarm: Failed to set timer");
86                 return;
87         }
88 }
89
90 /* Run the pvc alarm associated with pvcalarm_data for the given amount of
91  * time. Also mark the start time of the alarm so we can use it for accounting
92  * later. */
93 static void start_pvcalarm(struct pvcalarm_data *pvcalarm_data, uint64_t offset)
94 {
95         pvcalarm_data->start_uptime = vcore_account_uptime_ticks(vcore_id());
96         run_pvcalarm(pvcalarm_data, offset);
97 }
98
99 /* Stop the pvc alarm associated with pvcalarm_data */
100 static void stop_pvcalarm(struct pvcalarm_data *pvcalarm_data)
101 {
102         int ret;
103         ret = write(pvcalarm_data->ctlfd, "cancel", sizeof("cancel"));
104         if (ret <= 0) {
105                 printf("Useralarm: unable to disarm alarm!\n");
106                 return;
107         }
108 }
109
110 /* Enable the per-vcore alarm service according to one of the policies listed
111  * above.  Every interval usecs the provided callback will be called on each
112  * active vcore according to that policy. */
113 int enable_pvcalarms(int method, uint64_t interval, void (*callback) (void))
114 {
115         assert(!in_vcore_context());
116         if (method != PVCALARM_REAL && method != PVCALARM_PROF)
117                 return EINVAL;
118
119         if (atomic_cas(&global_pvcalarm.state, S_ENABLED, S_ENABLED))
120                 return EALREADY;
121
122         if (!atomic_cas(&global_pvcalarm.state, S_DISABLED, S_ENABLING))
123                 return EBUSY;
124
125         run_once_racy(init_global_pvcalarm());
126
127         global_pvcalarm.interval = usec2tsc(interval);
128         global_pvcalarm.callback = callback;
129         global_pvcalarm.busy_count = 0;
130         switch (method) {
131                 case PVCALARM_REAL:
132                         global_pvcalarm.handler = handle_alarm_real;
133                         break;
134                 case PVCALARM_PROF:
135                         global_pvcalarm.handler = handle_alarm_prof;
136                         break;
137         }
138
139         /* Start the timer on all vcores to go off after interval usecs */
140         for (int i=0; i<max_vcores(); i++) {
141                 start_pvcalarm(&global_pvcalarm.data[i], 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 }
175
176 /* Initialize a specific pvcalarm.  This happens once per vcore as it comes
177  * online and the pvcalarm service is active */
178 static void init_pvcalarm(struct pvcalarm_data *pvcalarm_data, int vcoreid)
179 {
180         int ctlfd, timerfd, alarmid, ev_flags, ret;
181         char buf[20];
182         char path[32];
183         struct event_queue *ev_q;
184
185         ctlfd = open("#A/clone", O_RDWR | O_CLOEXEC);
186         if (ctlfd < 0) {
187                 perror("Pvcalarm: Can't clone an alarm");
188                 return;
189         }
190         ret = read(ctlfd, buf, sizeof(buf) - 1);
191         if (ret <= 0) {
192                 if (!ret)
193                         printf("Pvcalarm: Got early EOF from ctl\n");
194                 else
195                         perror("Pvcalarm: Can't read ctl");
196                 return;
197         }
198         buf[ret] = 0;
199         alarmid = atoi(buf);
200         snprintf(path, sizeof(path), "#A/a%s/timer", buf);
201         timerfd = open(path, O_RDWR | O_CLOEXEC);
202         if (timerfd < 0) {
203                 perror("Pvcalarm: Can't open timer");
204                 return;
205         }
206         register_ev_handler(EV_ALARM, handle_pvcalarm, 0);
207         ev_flags = EVENT_IPI | EVENT_VCORE_PRIVATE;
208         ev_q = get_event_q_vcpd(vcoreid, ev_flags);
209         if (!ev_q) {
210                 perror("Pvcalarm: Failed ev_q");
211                 return;
212         }
213         ev_q->ev_vcore = vcoreid;
214         ev_q->ev_flags = ev_flags;
215         ret = snprintf(path, sizeof(path), "evq %llx", ev_q);
216         ret = write(ctlfd, path, ret);
217         if (ret <= 0) {
218                 perror("Pvcalarm: Failed to write ev_q");
219                 return;
220         }
221         /* now the alarm is all set, just need to write the timer whenever we want
222          * it to go off. */
223         pvcalarm_data->alarmid = alarmid;
224         pvcalarm_data->ctlfd = ctlfd;
225         pvcalarm_data->timerfd = timerfd;
226 }
227
228 /* TODO: implement a way to completely remove each per-vcore alarm and
229  * deregister it from the #A device */
230
231 /* A preamble function to run anytime we are about to do anything on behalf of
232  * the pvcalarms while in vcore context.  This preamble is necessary to ensure
233  * we maintain proper invariants when enabling and disabling the pvcalarm
234  * service in a running application. */
235 static inline bool __vcore_preamble()
236 {
237         assert(in_vcore_context());
238         __sync_fetch_and_add(&global_pvcalarm.busy_count, 1);
239         if (atomic_cas(&global_pvcalarm.state, S_DISABLED, S_DISABLED))
240                 goto disabled;
241         if (atomic_cas(&global_pvcalarm.state, S_DISABLING, S_DISABLING))
242                 goto disabled;
243         return true;
244 disabled:
245         __sync_fetch_and_add(&global_pvcalarm.busy_count, -1);
246         return false;
247 }
248
249 /* The counterpart to the __vcore_preamble() function */
250 static inline void __vcore_postamble()
251 {
252         __sync_fetch_and_add(&global_pvcalarm.busy_count, -1);
253 }
254
255 /* The global handler function.  It simply calls the proper underlying handler
256  * function depending on whether the service is set for the REAL or PERF
257  * policy. */
258 static void handle_pvcalarm(struct event_msg *ev_msg, unsigned int ev_type,
259                             void *data)
260 {
261         struct pvcalarm_data *pvcalarm_data = &global_pvcalarm.data[vcore_id()];
262         if (!ev_msg || (ev_msg->ev_arg2 != pvcalarm_data->alarmid))
263                 return;
264         if (!__vcore_preamble()) return;
265         global_pvcalarm.handler(ev_msg, ev_type, data);
266         __vcore_postamble();
267 }
268
269 /* The pvcalarm handler for the REAL policy.  Simply call the registered
270  * callback and restart the interval alarm. */
271 static void handle_alarm_real(struct event_msg *ev_msg, unsigned int ev_type,
272                               void *data)
273 {
274         global_pvcalarm.callback();
275         start_pvcalarm(&global_pvcalarm.data[vcore_id()], global_pvcalarm.interval);
276 }
277
278 /* The pvcalarm handler for the PROF policy.  Account for any time the vcore
279  * has been offline.  Only when the uptime since the last interval is equal to
280  * the interval time do we run the callback function.  Otherwise we restart the
281  * alarm to make up the difference. */
282 static void handle_alarm_prof(struct event_msg *ev_msg, unsigned int ev_type,
283                               void *data)
284
285         int vcoreid = vcore_id();
286         struct pvcalarm_data *pvcalarm_data = &global_pvcalarm.data[vcoreid];
287         uint32_t uptime = vcore_account_uptime_ticks(vcoreid);
288         uint64_t diff = uptime - pvcalarm_data->start_uptime;
289
290         if (diff < global_pvcalarm.interval) {
291                 uint64_t remaining = global_pvcalarm.interval - diff;
292                 run_pvcalarm(pvcalarm_data, remaining);
293         } else {
294                 global_pvcalarm.callback();
295                 start_pvcalarm(pvcalarm_data, global_pvcalarm.interval);
296         }
297 }
298