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