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