Moves retvals, start_routine, and args to the 2LSs
[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 <rstdio.h>
12 #include <glibc-tls.h>
13 #include <event.h>
14 #include <ros/arch/membar.h>
15
16 /* starting with 1 since we alloc vcore0's stacks and TLS in vcore_init(). */
17 static size_t _max_vcores_ever_wanted = 1;
18 static mcs_lock_t _vcore_lock = MCS_LOCK_INIT;
19
20 /* Which operations we'll call for the 2LS.  Will change a bit with Lithe.  For
21  * now, there are no defaults.  2LSs can override sched_ops. */
22 struct schedule_ops default_2ls_ops = {0};
23 struct schedule_ops *sched_ops __attribute__((weak)) = &default_2ls_ops;
24
25 extern void** vcore_thread_control_blocks;
26
27 __thread struct uthread *current_thread = 0;
28
29 /* Get a TLS, returns 0 on failure.  Vcores have their own TLS, and any thread
30  * created by a user-level scheduler needs to create a TLS as well. */
31 void *allocate_tls(void)
32 {
33         extern void *_dl_allocate_tls(void *mem) internal_function;
34         void *tcb = _dl_allocate_tls(NULL);
35         if (!tcb)
36                 return 0;
37         /* Make sure the TLS is set up properly - its tcb pointer points to itself.
38          * Keep this in sync with sysdeps/ros/XXX/tls.h.  For whatever reason,
39          * dynamically linked programs do not need this to be redone, but statics
40          * do. */
41         tcbhead_t *head = (tcbhead_t*)tcb;
42         head->tcb = tcb;
43         head->self = tcb;
44         return tcb;
45 }
46
47 /* TODO: probably don't want to dealloc.  Considering caching */
48 static void free_transition_tls(int id)
49 {
50         extern void _dl_deallocate_tls (void *tcb, bool dealloc_tcb) internal_function;
51         if(vcore_thread_control_blocks[id])
52         {
53                 _dl_deallocate_tls(vcore_thread_control_blocks[id],true);
54                 vcore_thread_control_blocks[id] = NULL;
55         }
56 }
57
58 static int allocate_transition_tls(int id)
59 {
60         /* We want to free and then reallocate the tls rather than simply 
61          * reinitializing it because its size may have changed.  TODO: not sure if
62          * this is right.  0-ing is one thing, but freeing and reallocating can be
63          * expensive, esp if syscalls are involved.  Check out glibc's
64          * allocatestack.c for what might work. */
65         free_transition_tls(id);
66
67         void *tcb = allocate_tls();
68
69         if ((vcore_thread_control_blocks[id] = tcb) == NULL) {
70                 errno = ENOMEM;
71                 return -1;
72         }
73         return 0;
74 }
75
76 static void free_transition_stack(int id)
77 {
78         // don't actually free stacks
79 }
80
81 static int allocate_transition_stack(int id)
82 {
83         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[id];
84         if (vcpd->transition_stack)
85                 return 0; // reuse old stack
86
87         void* stackbot = mmap(0, TRANSITION_STACK_SIZE,
88                               PROT_READ|PROT_WRITE|PROT_EXEC,
89                               MAP_POPULATE|MAP_ANONYMOUS, -1, 0);
90
91         if(stackbot == MAP_FAILED)
92                 return -1; // errno set by mmap
93
94         vcpd->transition_stack = (uintptr_t)stackbot + TRANSITION_STACK_SIZE;
95
96         return 0;
97 }
98
99 int vcore_init()
100 {
101         static int initialized = 0;
102         if(initialized)
103                 return 0;
104
105         vcore_thread_control_blocks = (void**)calloc(max_vcores(),sizeof(void*));
106
107         if(!vcore_thread_control_blocks)
108                 goto vcore_init_fail;
109
110         /* Need to alloc vcore0's transition stuff here (technically, just the TLS)
111          * so that schedulers can use vcore0's transition TLS before it comes up in
112          * vcore_entry() */
113         if(allocate_transition_stack(0) || allocate_transition_tls(0))
114                 goto vcore_init_tls_fail;
115
116         assert(!in_vcore_context());
117
118         /* Bug if vcore init was called with no 2LS */
119         assert(sched_ops->sched_init);
120         /* Get thread 0's thread struct (2LS allocs it) */
121         struct uthread *uthread = sched_ops->sched_init();
122         
123         /* Save a pointer to thread0's tls region (the glibc one) into its tcb */
124         uthread->tls_desc = get_tls_desc(0);
125         /* Save a pointer to the uthread in its own TLS */
126         current_thread = uthread;
127
128         /* Change temporarily to vcore0s tls region so we can save the newly created
129          * tcb into its current_thread variable and then restore it.  One minor
130          * issue is that vcore0's transition-TLS isn't TLS_INITed yet.  Until it is
131          * (right before vcore_entry(), don't try and take the address of any of
132          * its TLS vars. */
133         extern void** vcore_thread_control_blocks;
134         set_tls_desc(vcore_thread_control_blocks[0], 0);
135         current_thread = uthread;
136         set_tls_desc(uthread->tls_desc, 0);
137         assert(!in_vcore_context());
138
139         /* don't forget to enable notifs on vcore0.  if you don't, the kernel will
140          * restart your _S with notifs disabled, which is a path to confusion. */
141         enable_notifs(0);
142
143         initialized = 1;
144         return 0;
145
146 vcore_init_tls_fail:
147         free(vcore_thread_control_blocks);
148 vcore_init_fail:
149         errno = ENOMEM;
150         return -1;
151 }
152
153 /* Returns -1 with errno set on error, or 0 on success.  This does not return
154  * the number of cores actually granted (though some parts of the kernel do
155  * internally).
156  *
157  * Note the doesn't block or anything (despite the min number requested is
158  * 1), since the kernel won't block the call. */
159 int vcore_request(size_t k)
160 {
161         int ret = -1;
162         size_t i,j;
163
164         if(vcore_init() < 0)
165                 return -1;
166
167         // TODO: could do this function without a lock once we 
168         // have atomic fetch and add in user space
169         mcs_lock_lock(&_vcore_lock);
170
171         size_t vcores_wanted = num_vcores() + k;
172         if(k < 0 || vcores_wanted > max_vcores())
173         {
174                 errno = EAGAIN;
175                 goto fail;
176         }
177
178         for(i = _max_vcores_ever_wanted; i < vcores_wanted; i++)
179         {
180                 if(allocate_transition_stack(i) || allocate_transition_tls(i))
181                         goto fail; // errno set by the call that failed
182                 _max_vcores_ever_wanted++;
183         }
184         ret = sys_resource_req(RES_CORES, vcores_wanted, 1, 0);
185
186 fail:
187         mcs_lock_unlock(&_vcore_lock);
188         return ret;
189 }
190
191 void vcore_yield()
192 {
193         sys_yield(0);
194 }
195
196 /* Deals with a pending preemption (checks, responds).  If the 2LS registered a
197  * function, it will get run.  Returns true if you got preempted.  Called
198  * 'check' instead of 'handle', since this isn't an event handler.  It's the "Oh
199  * shit a preempt is on its way ASAP". */
200 bool check_preempt_pending(uint32_t vcoreid)
201 {
202         bool retval = FALSE;
203         if (__procinfo.vcoremap[vcoreid].preempt_pending) {
204                 retval = TRUE;
205                 if (sched_ops->preempt_pending)
206                         sched_ops->preempt_pending();
207                 /* this tries to yield, but will pop back up if this was a spurious
208                  * preempt_pending. */
209                 sys_yield(TRUE);
210         }
211         return retval;
212 }
213
214 /* Clear pending, and try to handle events that came in between a previous call
215  * to handle_events() and the clearing of pending.  While it's not a big deal,
216  * we'll loop in case we catch any.  Will break out of this once there are no
217  * events, and we will have send pending to 0. 
218  *
219  * Note that this won't catch every race/case of an incoming event.  Future
220  * events will get caught in pop_ros_tf() */
221 void clear_notif_pending(uint32_t vcoreid)
222 {
223         do {
224                 cmb();
225                 __procdata.vcore_preempt_data[vcoreid].notif_pending = 0;
226         } while (handle_events(vcoreid));
227 }
228
229 /****************** uthread *******************/
230 /* static helpers: */
231 static int __uthread_allocate_tls(struct uthread *uthread);
232 static void __uthread_free_tls(struct uthread *uthread);
233
234 /* 2LSs shouldn't call vcore_entry() directly */
235 // XXX this is going to break testing apps like mhello and syscall
236 void __attribute__((noreturn)) vcore_entry()
237 {
238         uint32_t vcoreid = vcore_id();
239
240         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
241
242         /* Should always have notifications disabled when coming in here. */
243         assert(vcpd->notif_enabled == FALSE);
244         assert(in_vcore_context());
245
246         check_preempt_pending(vcoreid);
247         handle_events(vcoreid);
248         assert(in_vcore_context());     /* double check, in case and event changed it */
249         assert(sched_ops->sched_entry);
250         sched_ops->sched_entry();
251         /* If we get here, the 2LS entry returned.  We can call out to the 2LS for
252          * guidance about whether or not to yield, etc.  Or the 2LS can do it and
253          * just not return.  Whatever we do, it ought to parallel what we do for
254          * requesting more cores in uthread_create(). */
255         printd("Vcore %d is yielding\n", vcoreid);
256         sys_yield(0);
257         assert(0);
258 }
259
260 /* Creates a uthread.  Will pass udata to sched_ops's thread_create.  For now,
261  * the vcore/default 2ls code handles start routines and args.  Mostly because
262  * this is used when initing a utf, which is vcore specific for now. */
263 struct uthread *uthread_create(void (*func)(void), void *udata)
264 {
265         /* First time through, init the vcore code (which makes a uthread out of
266          * thread0 / the current code.  Could move this to a ctor. */
267         static bool first = TRUE;
268         if (first) {
269                 if (vcore_init())               /* could make this uthread_init */
270                         printf("Vcore init failed!\n");
271                 first = FALSE;
272         }
273         assert(!in_vcore_context());
274         assert(sched_ops->thread_create);
275         struct uthread *new_thread = sched_ops->thread_create(func, udata);
276         /* Get a TLS */
277         assert(!__uthread_allocate_tls(new_thread));
278         /* Switch into the new guys TLS and let it know who it is */
279         struct uthread *caller = current_thread;
280         assert(caller);
281         /* Don't migrate this thread to another vcore, since it depends on being on
282          * the same vcore throughout. */
283         caller->dont_migrate = TRUE;
284         wmb();
285         /* Note the first time we call this, we technically aren't on a vcore */
286         uint32_t vcoreid = vcore_id();
287         /* Save the new_thread to the new uthread in that uthread's TLS */
288         set_tls_desc(new_thread->tls_desc, vcoreid);
289         current_thread = new_thread;
290         /* Switch back to the caller */
291         set_tls_desc(caller->tls_desc, vcoreid);
292         /* Okay to migrate now. */
293         wmb();
294         caller->dont_migrate = FALSE;
295         return new_thread;
296 }
297
298 void uthread_runnable(struct uthread *uthread)
299 {
300         /* Allow the 2LS to make the thread runnable, and do whatever. */
301         assert(sched_ops->thread_runnable);
302         sched_ops->thread_runnable(uthread);
303         /* This is where we'll call out to a smarter 2LS function to see if we want
304          * to get more cores (and how many). */
305         /* Need to get some vcores.  If this is the first time, we'd like to get
306          * two: one for the main thread (aka thread0), and another for the pthread
307          * we are creating.  Can rework this if we get another vcore interface that
308          * deals with absolute core counts.
309          *
310          * Need to get at least one core to put us in _M mode so we can run the 2LS,
311          * etc, so for now we'll just spin until we get at least one (might be none
312          * available).
313          *
314          * TODO: do something smarter regarding asking for cores (paired with
315          * yielding), and block or something until one core is available (will need
316          * kernel support). */
317         static bool first_time = TRUE;
318         if (first_time) {
319                 first_time = FALSE;
320                 /* Try for two, don't settle for less than 1 */
321                 while (num_vcores() < 1) {
322                         vcore_request(2);
323                         cpu_relax();
324                 }
325         } else {        /* common case */
326                 /* Try to get another for the new thread, but doesn't matter if we get
327                  * one or not, so long as we still have at least 1. */
328                 vcore_request(1);
329         }
330 }
331
332 /* Need to have this as a separate, non-inlined function since we clobber the
333  * stack pointer before calling it, and don't want the compiler to play games
334  * with my hart.
335  *
336  * TODO: combine this 2-step logic with uthread_exit() */
337 static void __attribute__((noinline, noreturn)) 
338 __uthread_yield(struct uthread *uthread)
339 {
340         assert(in_vcore_context());
341         /* TODO: want to set this to FALSE once we no longer depend on being on this
342          * vcore.  Though if we are using TLS, we are depending on the vcore.  Since
343          * notifs are disabled and we are in a transition context, we probably
344          * shouldn't be moved anyway.  It does mean that a pthread could get jammed.
345          * If we do this after putting it on the active list, we'll have a race on
346          * dont_migrate. */
347         uthread->dont_migrate = FALSE;
348         assert(sched_ops->thread_yield);
349         /* 2LS will save the thread somewhere for restarting.  Later on, we'll
350          * probably have a generic function for all sorts of waiting. */
351         sched_ops->thread_yield(uthread);
352         /* Leave the current vcore completely */
353         current_thread = NULL; // this might be okay, even with a migration
354         /* Go back to the entry point, where we can handle notifications or
355          * reschedule someone. */
356         vcore_entry();
357 }
358
359 /* Calling thread yields.  TODO: combine similar code with uthread_exit() (done
360  * like this to ease the transition to the 2LS-ops */
361 void uthread_yield(void)
362 {
363         struct uthread *uthread = current_thread;
364         volatile bool yielding = TRUE; /* signal to short circuit when restarting */
365         /* TODO: (HSS) Save silly state */
366         // save_fp_state(&t->as);
367         assert(!in_vcore_context());
368         /* Don't migrate this thread to another vcore, since it depends on being on
369          * the same vcore throughout (once it disables notifs). */
370         uthread->dont_migrate = TRUE;
371         wmb();
372         uint32_t vcoreid = vcore_id();
373         printd("[U] Uthread %08p is yielding on vcore %d\n", uthread, vcoreid);
374         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
375         /* once we do this, we might miss a notif_pending, so we need to enter vcore
376          * entry later.  Need to disable notifs so we don't get in weird loops with
377          * save_ros_tf() and pop_ros_tf(). */
378         disable_notifs(vcoreid);
379         /* take the current state and save it into t->utf when this pthread
380          * restarts, it will continue from right after this, see yielding is false,
381          * and short ciruit the function. */
382         save_ros_tf(&uthread->utf);
383         if (!yielding)
384                 goto yield_return_path;
385         yielding = FALSE; /* for when it starts back up */
386         /* Change to the transition context (both TLS and stack). */
387         extern void** vcore_thread_control_blocks;
388         set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
389         assert(current_thread == uthread);      
390         assert(in_vcore_context());     /* technically, we aren't fully in vcore context */
391         /* After this, make sure you don't use local variables.  Note the warning in
392          * pthread_exit() */
393         set_stack_pointer((void*)vcpd->transition_stack);
394         /* Finish exiting in another function. */
395         __uthread_yield(current_thread);
396         /* Should never get here */
397         assert(0);
398         /* Will jump here when the pthread's trapframe is restarted/popped. */
399 yield_return_path:
400         printd("[U] Uthread %08p returning from a yield!\n", uthread);
401 }
402
403 /* Need to have this as a separate, non-inlined function since we clobber the
404  * stack pointer before calling it, and don't want the compiler to play games
405  * with my hart. */
406 static void __attribute__((noinline, noreturn)) 
407 __uthread_exit(struct uthread *uthread)
408 {
409         assert(in_vcore_context());
410         /* we alloc and manage the TLS, so lets get rid of it */
411         __uthread_free_tls(uthread);
412         /* 2LS specific cleanup */
413         assert(sched_ops->thread_exit);
414         sched_ops->thread_exit(uthread);
415         current_thread = NULL;
416         /* Go back to the entry point, where we can handle notifications or
417          * reschedule someone. */
418         vcore_entry();
419 }
420
421 /* Exits from the uthread */
422 void uthread_exit(void)
423 {
424         assert(!in_vcore_context());
425         struct uthread *uthread = current_thread;
426         /* Don't migrate this thread to anothe vcore, since it depends on being on
427          * the same vcore throughout. */
428         uthread->dont_migrate = TRUE; // won't set to false later, since he is dying
429         wmb();
430         uint32_t vcoreid = vcore_id();
431         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
432         printd("[U] Uthread %08p is exiting on vcore %d\n", uthread, vcoreid);
433         /* once we do this, we might miss a notif_pending, so we need to enter vcore
434          * entry later. */
435         disable_notifs(vcoreid);
436         /* Change to the transition context (both TLS and stack). */
437         extern void** vcore_thread_control_blocks;
438         set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
439         assert(current_thread == uthread);      
440         /* After this, make sure you don't use local variables.  Also, make sure the
441          * compiler doesn't use them without telling you (TODO).
442          *
443          * In each arch's set_stack_pointer, make sure you subtract off as much room
444          * as you need to any local vars that might be pushed before calling the
445          * next function, or for whatever other reason the compiler/hardware might
446          * walk up the stack a bit when calling a noreturn function. */
447         set_stack_pointer((void*)vcpd->transition_stack);
448         /* Finish exiting in another function.  Ugh. */
449         __uthread_exit(current_thread);
450 }
451
452 /* Runs whatever thread is vcore's current_thread */
453 void run_current_uthread(void)
454 {
455         uint32_t vcoreid = vcore_id();
456         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
457         assert(current_thread);
458         printd("[U] Vcore %d is restarting uthread %d\n", vcoreid, uthread->id);
459         clear_notif_pending(vcoreid);
460         set_tls_desc(current_thread->tls_desc, vcoreid);
461         /* Pop the user trap frame */
462         pop_ros_tf(&vcpd->notif_tf, vcoreid);
463         assert(0);
464 }
465
466 /* Launches the uthread on the vcore */
467 void run_uthread(struct uthread *uthread)
468 {
469         /* Save a ptr to the pthread running in the transition context's TLS */
470         uint32_t vcoreid = vcore_id();
471         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
472         printd("[U] Vcore %d is starting uthread %d\n", vcoreid, uthread->id);
473         current_thread = uthread;
474         clear_notif_pending(vcoreid);
475         set_tls_desc(uthread->tls_desc, vcoreid);
476         /* Load silly state (Floating point) too.  For real */
477         /* TODO: (HSS) */
478         /* Pop the user trap frame */
479         pop_ros_tf(&uthread->utf, vcoreid);
480         assert(0);
481 }
482
483 /* TLS helpers */
484 static int __uthread_allocate_tls(struct uthread *uthread)
485 {
486         assert(!uthread->tls_desc);
487         uthread->tls_desc = allocate_tls();
488         if (!uthread->tls_desc) {
489                 errno = ENOMEM;
490                 return -1;
491         }
492         return 0;
493 }
494
495 /* TODO: probably don't want to dealloc.  Considering caching */
496 static void __uthread_free_tls(struct uthread *uthread)
497 {
498         extern void _dl_deallocate_tls (void *tcb, bool dealloc_tcb) internal_function;
499
500         assert(uthread->tls_desc);
501         _dl_deallocate_tls(uthread->tls_desc, TRUE);
502         uthread->tls_desc = NULL;
503 }