Preemption recovery handler
[akaros.git] / user / parlib / vcore.c
1 #include <arch/arch.h>
2 #include <stdbool.h>
3 #include <errno.h>
4 #include <vcore.h>
5 #include <mcs.h>
6 #include <sys/param.h>
7 #include <parlib.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <sys/mman.h>
11 #include <stdio.h>
12 #include <glibc-tls.h>
13 #include <event.h>
14 #include <uthread.h>
15 #include <ucq.h>
16 #include <ros/arch/membar.h>
17
18 /* starting with 1 since we alloc vcore0's stacks and TLS in vcore_init(). */
19 static size_t _max_vcores_ever_wanted = 1;
20 atomic_t nr_new_vcores_wanted;
21 atomic_t vc_req_being_handled;
22
23 extern void** vcore_thread_control_blocks;
24
25 /* Get a TLS, returns 0 on failure.  Vcores have their own TLS, and any thread
26  * created by a user-level scheduler needs to create a TLS as well. */
27 void *allocate_tls(void)
28 {
29         extern void *_dl_allocate_tls(void *mem) internal_function;
30         void *tcb = _dl_allocate_tls(NULL);
31         if (!tcb)
32                 return 0;
33         /* Make sure the TLS is set up properly - its tcb pointer points to itself.
34          * Keep this in sync with sysdeps/ros/XXX/tls.h.  For whatever reason,
35          * dynamically linked programs do not need this to be redone, but statics
36          * do. */
37         tcbhead_t *head = (tcbhead_t*)tcb;
38         head->tcb = tcb;
39         head->self = tcb;
40         return tcb;
41 }
42
43 /* Free a previously allocated TLS region */
44 void free_tls(void *tcb)
45 {
46         extern void _dl_deallocate_tls (void *tcb, bool dealloc_tcb) internal_function;
47         assert(tcb);
48         _dl_deallocate_tls(tcb, TRUE);
49 }
50
51 /* Reinitialize / reset / refresh a TLS to its initial values.  This doesn't do
52  * it properly yet, it merely frees and re-allocates the TLS, which is why we're
53  * slightly ghetto and return the pointer you should use for the TCB. */
54 void *reinit_tls(void *tcb)
55 {
56         /* TODO: keep this in sync with the methods used in
57          * allocate_transition_tls() */
58         free_tls(tcb);
59         return allocate_tls();
60 }
61
62 /* TODO: probably don't want to dealloc.  Considering caching */
63 static void free_transition_tls(int id)
64 {
65         if(vcore_thread_control_blocks[id])
66         {
67                 free_tls(vcore_thread_control_blocks[id]);
68                 vcore_thread_control_blocks[id] = NULL;
69         }
70 }
71
72 static int allocate_transition_tls(int id)
73 {
74         /* We want to free and then reallocate the tls rather than simply 
75          * reinitializing it because its size may have changed.  TODO: not sure if
76          * this is right.  0-ing is one thing, but freeing and reallocating can be
77          * expensive, esp if syscalls are involved.  Check out glibc's
78          * allocatestack.c for what might work. */
79         free_transition_tls(id);
80
81         void *tcb = allocate_tls();
82
83         if ((vcore_thread_control_blocks[id] = tcb) == NULL) {
84                 errno = ENOMEM;
85                 return -1;
86         }
87         return 0;
88 }
89
90 static void free_transition_stack(int id)
91 {
92         // don't actually free stacks
93 }
94
95 static int allocate_transition_stack(int id)
96 {
97         struct preempt_data *vcpd = vcpd_of(id);
98         if (vcpd->transition_stack)
99                 return 0; // reuse old stack
100
101         void* stackbot = mmap(0, TRANSITION_STACK_SIZE,
102                               PROT_READ|PROT_WRITE|PROT_EXEC,
103                               MAP_POPULATE|MAP_ANONYMOUS, -1, 0);
104
105         if(stackbot == MAP_FAILED)
106                 return -1; // errno set by mmap
107
108         vcpd->transition_stack = (uintptr_t)stackbot + TRANSITION_STACK_SIZE;
109
110         return 0;
111 }
112
113 int vcore_init()
114 {
115         static int initialized = 0;
116         uintptr_t mmap_block;
117         /* Note this is racy, but okay.  The only time it'll be 0 is the first time
118          * through, when we are _S */
119         if(initialized)
120                 return 0;
121
122         vcore_thread_control_blocks = (void**)calloc(max_vcores(),sizeof(void*));
123
124         if(!vcore_thread_control_blocks)
125                 goto vcore_init_fail;
126
127         /* Need to alloc vcore0's transition stuff here (technically, just the TLS)
128          * so that schedulers can use vcore0's transition TLS before it comes up in
129          * vcore_entry() */
130         if(allocate_transition_stack(0) || allocate_transition_tls(0))
131                 goto vcore_init_tls_fail;
132
133         /* Initialize our VCPD event queues' ucqs, two pages per ucq, 4 per vcore */
134         mmap_block = (uintptr_t)mmap(0, PGSIZE * 4 * max_vcores(),
135                                      PROT_WRITE | PROT_READ,
136                                      MAP_POPULATE | MAP_ANONYMOUS, -1, 0);
137         /* Yeah, this doesn't fit in the error-handling scheme, but this whole
138          * system doesn't really handle failure, and needs a rewrite involving less
139          * mmaps/munmaps. */
140         assert(mmap_block);
141         /* Note we may end up doing vcore 0's elsewhere, for _Ss, or else have a
142          * separate ev_q for that. */
143         for (int i = 0; i < max_vcores(); i++) {
144                 /* four pages total for both ucqs from the big block (2 pages each) */
145                 ucq_init_raw(&vcpd_of(i)->ev_mbox_public.ev_msgs,
146                              mmap_block + (4 * i    ) * PGSIZE,
147                              mmap_block + (4 * i + 1) * PGSIZE);
148                 ucq_init_raw(&vcpd_of(i)->ev_mbox_private.ev_msgs,
149                              mmap_block + (4 * i + 2) * PGSIZE,
150                              mmap_block + (4 * i + 3) * PGSIZE);
151         }
152         atomic_init(&vc_req_being_handled, 0);
153         assert(!in_vcore_context());
154         initialized = 1;
155         /* no longer need to enable notifs on vcore 0, it is set like that by
156          * default (so you drop into vcore context immediately on transtioning to
157          * _M) */
158         return 0;
159 vcore_init_tls_fail:
160         free(vcore_thread_control_blocks);
161 vcore_init_fail:
162         errno = ENOMEM;
163         return -1;
164 }
165
166 /* Returns -1 with errno set on error, or 0 on success.  This does not return
167  * the number of cores actually granted (though some parts of the kernel do
168  * internally).
169  *
170  * Note the doesn't block or anything (despite the min number requested is
171  * 1), since the kernel won't block the call.
172  *
173  * There are a few concurrency concerns.  We have _max_vcores_ever_wanted,
174  * initialization of new vcore stacks/TLSs, making sure we don't ask for too
175  * many (minor point), and most importantly not asking the kernel for too much
176  * or otherwise miscommunicating our desires to the kernel.  Remember, the
177  * kernel wants just one answer from the process about what it wants, and it is
178  * up to the process to figure that out.
179  *
180  * So we basically have one thread do the submitting/prepping/bookkeeping, and
181  * other threads come in just update the number wanted and make sure someone
182  * is sorting things out.  This will perform a bit better too, since only one
183  * vcore makes syscalls (which hammer the proc_lock).  This essentially has
184  * cores submit work, and one core does the work (like Eric's old delta
185  * functions).
186  *
187  * There's a slight semantic change: this will return 0 (success) for the
188  * non-submitters, and 0 if we submitted.  -1 only if the submitter had some
189  * non-kernel failure.
190  *
191  * Also, beware that this (like the old version) doesn't protect with races on
192  * num_vcores().  num_vcores() is how many you have now or very soon (accounting
193  * for messages in flight that will take your cores), not how many you told the
194  * kernel you want. */
195 int vcore_request(long nr_new_vcores)
196 {
197         long nr_to_prep_now, nr_vcores_wanted;
198
199         if (vcore_init() < 0)
200                 return -1;      /* consider ERRNO */
201         /* Early sanity checks */
202         if ((nr_new_vcores < 0) || (nr_new_vcores + num_vcores() > max_vcores()))
203                 return -1;      /* consider ERRNO */
204         /* Post our desires (ROS atomic_add() conflicts with glibc) */
205         atomic_fetch_and_add(&nr_new_vcores_wanted, nr_new_vcores);
206 try_handle_it:
207         cmb();  /* inc before swap.  the atomic is a CPU mb() */
208         if (atomic_swap(&vc_req_being_handled, 1)) {
209                 /* We got a 1 back, so someone else is already working on it */
210                 return 0;
211         }
212 handle_it:
213         /* So now we're the ones supposed to handle things.  Figure out how many we
214          * have, though this is racy.  Yields/preempts/grants will change this over
215          * time, and we may end up asking for less than we had. */
216         nr_vcores_wanted = num_vcores();
217         cmb();  /* force a reread of num_vcores() later */
218         /* Pull all of the vcores wanted into our local variable, where we'll deal
219          * with prepping/requesting that many vcores.  Keep doing this til we think
220          * no more are wanted. */
221         while ((nr_to_prep_now = atomic_swap(&nr_new_vcores_wanted, 0))) {
222                 nr_vcores_wanted += nr_to_prep_now;
223                 /* Don't bother prepping or asking for more than we can ever get */
224                 nr_vcores_wanted = MIN(nr_vcores_wanted, max_vcores());
225                 /* Make sure all we might ask for are prepped */
226                 for(long i = _max_vcores_ever_wanted; i < nr_vcores_wanted; i++) {
227                         if (allocate_transition_stack(i) || allocate_transition_tls(i)) {
228                                 atomic_set(&vc_req_being_handled, 0);   /* unlock and bail out*/
229                                 return -1;
230                         }
231                         _max_vcores_ever_wanted++;      /* done in the loop to handle failures*/
232                 }
233         }
234         /* Got all the ones we can get, let's submit it to the kernel.  We check
235          * against num_vcores() one last time, though we still have some races... */
236         if (nr_vcores_wanted > num_vcores()) {
237                 sys_resource_req(RES_CORES, nr_vcores_wanted, 1, 0);
238                 cmb();  /* force a reread of num_vcores() at handle_it: */
239                 goto handle_it;
240         }
241         /* Here, we believe there are none left to do */
242         atomic_set(&vc_req_being_handled, 0);   /* unlock, to allow others to try */
243         /* Double check for any that might have come in while we were out */
244         if (atomic_read(&nr_new_vcores_wanted))
245                 goto try_handle_it;
246         return 0;
247 }
248
249 /* This can return, if you failed to yield due to a concurrent event. */
250 void vcore_yield(bool preempt_pending)
251 {
252         uint32_t vcoreid = vcore_id();
253         struct preempt_data *vcpd = vcpd_of(vcoreid);
254         vcpd->can_rcv_msg = FALSE;
255         /* no wrmb() necessary, clear_notif() has an mb() */
256         /* Clears notif pending.  If we had an event outstanding, this will handle
257          * it and return TRUE, at which point we want to unwind and return to the
258          * 2LS loop (where we may not want to yield anymore).  Note that the kernel
259          * only cares about can_rcv_msg for the desired vcore, not for a FALLBACK.
260          * We need to deal with this notif_pending business regardless of
261          * can_rcv_msg.  We just want to avoid a yield syscall if possible.  It is
262          * important that clear_notif_pending will handle_events().  That is
263          * necessary to do/check after setting can_rcv_msg to FALSE. */
264         if (clear_notif_pending(vcoreid)) {
265                 vcpd->can_rcv_msg = TRUE;
266                 return;
267         }
268         /* We can probably yield.  This may pop back up if notif_pending became set
269          * by the kernel after we cleared it and we lost the race. */
270         sys_yield(preempt_pending);
271         vcpd->can_rcv_msg = TRUE;
272 }
273
274 /* Clear pending, and try to handle events that came in between a previous call
275  * to handle_events() and the clearing of pending.  While it's not a big deal,
276  * we'll loop in case we catch any.  Will break out of this once there are no
277  * events, and we will have send pending to 0. 
278  *
279  * Note that this won't catch every race/case of an incoming event.  Future
280  * events will get caught in pop_ros_tf() or proc_yield().
281  *
282  * Also note that this handles events, which may change your current uthread or
283  * might not return!  Be careful calling this.  Check run_uthread for an example
284  * of how to use this. */
285 bool clear_notif_pending(uint32_t vcoreid)
286 {
287         bool handled_event = FALSE;
288         do {
289                 vcpd_of(vcoreid)->notif_pending = 0;
290                 /* need a full mb(), since handle events might be just a read or might
291                  * be a write, either way, it needs to happen after notif_pending */
292                 mb();
293                 handled_event = handle_events(vcoreid);
294         } while (handled_event);
295         return handled_event;
296 }
297
298 /* Enables notifs, and deals with missed notifs by self notifying.  This should
299  * be rare, so the syscall overhead isn't a big deal.  The other alternative
300  * would be to uthread_yield(), which would require us to revert some uthread
301  * interface changes. */
302 void enable_notifs(uint32_t vcoreid)
303 {
304         __enable_notifs(vcoreid);
305         wrmb(); /* need to read after the write that enabled notifs */
306         /* Note we could get migrated before executing this.  If that happens, our
307          * vcore had gone into vcore context (which is what we wanted), and this
308          * self_notify to our old vcore is spurious and harmless. */
309         if (vcpd_of(vcoreid)->notif_pending)
310                 sys_self_notify(vcoreid, EV_NONE, 0, TRUE);
311 }
312
313 /* Helper to disable notifs.  It simply checks to make sure we disabled uthread
314  * migration, which is a common mistake. */
315 void disable_notifs(uint32_t vcoreid)
316 {
317         if (!in_vcore_context() && current_uthread)
318                 assert(current_uthread->flags & UTHREAD_DONT_MIGRATE);
319         __disable_notifs(vcoreid);
320 }
321
322 /* Like smp_idle(), this will put the core in a state that it can only be woken
323  * up by an IPI.  In the future, we may halt or something. */
324 void __attribute__((noreturn)) vcore_idle(void)
325 {
326         uint32_t vcoreid = vcore_id();
327         clear_notif_pending(vcoreid);
328         enable_notifs(vcoreid);
329         while (1) {
330                 cpu_relax();
331         }
332 }