parlib: slab: Use the modern ctor/dtor interface
[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() * sizeof(struct pvcalarm_data));
73         for (int i=0; i<max_vcores(); i++) {
74                 init_pvcalarm(&global_pvcalarm.data[i], i);
75         }
76 }
77
78 /* Run the pvc alarm associated with pvcalarm_data for the given amount of
79  * time */
80 static void run_pvcalarm(struct pvcalarm_data *pvcalarm_data, uint64_t offset)
81 {
82         if (devalarm_set_time(pvcalarm_data->timerfd, read_tsc() + offset)) {
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         if (devalarm_disable(pvcalarm_data->timerfd)) {
101                 printf("Useralarm: unable to disarm alarm!\n");
102                 return;
103         }
104 }
105
106 /* Enable the per-vcore alarm service according to one of the policies listed
107  * above.  Every interval usecs the provided callback will be called on each
108  * active vcore according to that policy. */
109 int enable_pvcalarms(int method, uint64_t interval, void (*callback) (void))
110 {
111         static parlib_once_t once = PARLIB_ONCE_INIT;
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         parlib_run_once(&once, init_global_pvcalarm, NULL);
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         return 0;
173 }
174
175 /* Initialize a specific pvcalarm.  This happens once per vcore as it comes
176  * online and the pvcalarm service is active */
177 static void init_pvcalarm(struct pvcalarm_data *pvcalarm_data, int vcoreid)
178 {
179         int ctlfd, timerfd, alarmid, ev_flags;
180         struct event_queue *ev_q;
181
182         if (devalarm_get_fds(&ctlfd, &timerfd, &alarmid)) {
183                 perror("Pvcalarm: alarm setup");
184                 return;
185         }
186         register_ev_handler(EV_ALARM, handle_pvcalarm, 0);
187         ev_flags = EVENT_IPI | EVENT_VCORE_PRIVATE;
188         ev_q = get_eventq_vcpd(vcoreid, ev_flags);
189         if (!ev_q) {
190                 perror("Pvcalarm: Failed ev_q");
191                 return;
192         }
193         ev_q->ev_vcore = vcoreid;
194         ev_q->ev_flags = ev_flags;
195         if (devalarm_set_evq(timerfd, ev_q, alarmid)) {
196                 perror("Pvcalarm: Failed to set evq");
197                 return;
198         }
199         /* now the alarm is all set, just need to write the timer whenever we want
200          * it to go off. */
201         pvcalarm_data->alarmid = alarmid;
202         pvcalarm_data->ctlfd = ctlfd;
203         pvcalarm_data->timerfd = timerfd;
204 }
205
206 /* TODO: implement a way to completely remove each per-vcore alarm and
207  * deregister it from the #alarm device */
208
209 /* A preamble function to run anytime we are about to do anything on behalf of
210  * the pvcalarms while in vcore context.  This preamble is necessary to ensure
211  * we maintain proper invariants when enabling and disabling the pvcalarm
212  * service in a running application. */
213 static inline bool __vcore_preamble()
214 {
215         int state;
216         assert(in_vcore_context());
217         __sync_fetch_and_add(&global_pvcalarm.busy_count, 1);
218         cmb();  /* order the state read after the incref.  __sync provides cpu mb */
219         state = atomic_read(&global_pvcalarm.state);
220         if (state == S_DISABLED || state == S_DISABLING)
221                 goto disabled;
222         return true;
223 disabled:
224         __sync_fetch_and_add(&global_pvcalarm.busy_count, -1);
225         return false;
226 }
227
228 /* The counterpart to the __vcore_preamble() function */
229 static inline void __vcore_postamble()
230 {
231         __sync_fetch_and_add(&global_pvcalarm.busy_count, -1);
232 }
233
234 /* The global handler function.  It simply calls the proper underlying handler
235  * function depending on whether the service is set for the REAL or PERF
236  * policy. */
237 static void handle_pvcalarm(struct event_msg *ev_msg, unsigned int ev_type,
238                             void *data)
239 {
240         struct pvcalarm_data *pvcalarm_data = &global_pvcalarm.data[vcore_id()];
241
242         if (devalarm_get_id(ev_msg) != pvcalarm_data->alarmid)
243                 return;
244         if (!__vcore_preamble()) return;
245         global_pvcalarm.handler(ev_msg, ev_type, data);
246         __vcore_postamble();
247 }
248
249 /* The pvcalarm handler for the REAL policy.  Simply call the registered
250  * callback and restart the interval alarm. */
251 static void handle_alarm_real(struct event_msg *ev_msg, unsigned int ev_type,
252                               void *data)
253 {
254         global_pvcalarm.callback();
255         start_pvcalarm(&global_pvcalarm.data[vcore_id()], global_pvcalarm.interval);
256 }
257
258 /* The pvcalarm handler for the PROF policy.  Account for any time the vcore
259  * has been offline.  Only when the uptime since the last interval is equal to
260  * the interval time do we run the callback function.  Otherwise we restart the
261  * alarm to make up the difference. */
262 static void handle_alarm_prof(struct event_msg *ev_msg, unsigned int ev_type,
263                               void *data)
264 {
265         int vcoreid = vcore_id();
266         struct pvcalarm_data *pvcalarm_data = &global_pvcalarm.data[vcoreid];
267         uint32_t uptime = vcore_account_uptime_ticks(vcoreid);
268         uint64_t diff = uptime - pvcalarm_data->start_uptime;
269
270         if (diff < global_pvcalarm.interval) {
271                 uint64_t remaining = global_pvcalarm.interval - diff;
272                 run_pvcalarm(pvcalarm_data, remaining);
273         } else {
274                 global_pvcalarm.callback();
275                 start_pvcalarm(pvcalarm_data, global_pvcalarm.interval);
276         }
277 }