a73477c333aec1410a5cb48101fe38d3feb0dfd6
[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 /* Could move this, along with start_routine and arg, into the 2LSs */
261 static void __uthread_run(void)
262 {
263         struct uthread *me = current_thread;
264         uthread_exit(me->start_routine(me->arg));
265 }
266
267 /* Creates a uthread.  Will pass udata to sched_ops's thread_create.  For now,
268  * the vcore/default 2ls code handles start routines and args.  Mostly because
269  * this is used when initing a utf, which is vcore specific for now. */
270 struct uthread *uthread_create(void *(*start_routine)(void *), void *arg,
271                                void *udata)
272 {
273         /* First time through, init the vcore code (which makes a uthread out of
274          * thread0 / the current code.  Could move this to a ctor. */
275         static bool first = TRUE;
276         if (first) {
277                 if (vcore_init())               /* could make this uthread_init */
278                         printf("Vcore init failed!\n");
279                 first = FALSE;
280         }
281         assert(!in_vcore_context());
282         assert(sched_ops->thread_create);
283         struct uthread *new_thread = sched_ops->thread_create(udata);
284         assert(new_thread->stacktop);
285         new_thread->start_routine = start_routine;
286         new_thread->arg = arg;
287         /* Set the u_tf to start up in __pthread_run, which will call the real
288          * start_routine and pass it the arg. */
289         init_user_tf(&new_thread->utf, (uint32_t)__uthread_run, 
290                  (uint32_t)(new_thread->stacktop));
291         /* Get a TLS */
292         assert(!__uthread_allocate_tls(new_thread));
293         /* Switch into the new guys TLS and let it know who it is */
294         struct uthread *caller = current_thread;
295         assert(caller);
296         /* Don't migrate this thread to another vcore, since it depends on being on
297          * the same vcore throughout. */
298         caller->dont_migrate = TRUE;
299         wmb();
300         /* Note the first time we call this, we technically aren't on a vcore */
301         uint32_t vcoreid = vcore_id();
302         /* Save the new_thread to the new uthread in that uthread's TLS */
303         set_tls_desc(new_thread->tls_desc, vcoreid);
304         current_thread = new_thread;
305         /* Switch back to the caller */
306         set_tls_desc(caller->tls_desc, vcoreid);
307         /* Okay to migrate now. */
308         wmb();
309         caller->dont_migrate = FALSE;
310         /* Allow the 2LS to make the thread runnable, and do whatever. */
311         assert(sched_ops->thread_runnable);
312         sched_ops->thread_runnable(new_thread);
313         /* This is where we'll call out to a smarter 2LS function to see if we want
314          * to get more cores (and how many). */
315         /* Need to get some vcores.  If this is the first time, we'd like to get
316          * two: one for the main thread (aka thread0), and another for the pthread
317          * we are creating.  Can rework this if we get another vcore interface that
318          * deals with absolute core counts.
319          *
320          * Need to get at least one core to put us in _M mode so we can run the 2LS,
321          * etc, so for now we'll just spin until we get at least one (might be none
322          * available).
323          *
324          * TODO: do something smarter regarding asking for cores (paired with
325          * yielding), and block or something until one core is available (will need
326          * kernel support). */
327         static bool first_time = TRUE;
328         if (first_time) {
329                 first_time = FALSE;
330                 /* Try for two, don't settle for less than 1 */
331                 while (num_vcores() < 1) {
332                         vcore_request(2);
333                         cpu_relax();
334                 }
335         } else {        /* common case */
336                 /* Try to get another for the new thread, but doesn't matter if we get
337                  * one or not, so long as we still have at least 1. */
338                 vcore_request(1);
339         }
340         return new_thread;
341 }
342
343 /* Need to have this as a separate, non-inlined function since we clobber the
344  * stack pointer before calling it, and don't want the compiler to play games
345  * with my hart.
346  *
347  * TODO: combine this 2-step logic with uthread_exit() */
348 static void __attribute__((noinline, noreturn)) 
349 __uthread_yield(struct uthread *uthread)
350 {
351         assert(in_vcore_context());
352         /* TODO: want to set this to FALSE once we no longer depend on being on this
353          * vcore.  Though if we are using TLS, we are depending on the vcore.  Since
354          * notifs are disabled and we are in a transition context, we probably
355          * shouldn't be moved anyway.  It does mean that a pthread could get jammed.
356          * If we do this after putting it on the active list, we'll have a race on
357          * dont_migrate. */
358         uthread->dont_migrate = FALSE;
359         assert(sched_ops->thread_yield);
360         /* 2LS will save the thread somewhere for restarting.  Later on, we'll
361          * probably have a generic function for all sorts of waiting. */
362         sched_ops->thread_yield(uthread);
363         /* Leave the current vcore completely */
364         current_thread = NULL; // this might be okay, even with a migration
365         /* Go back to the entry point, where we can handle notifications or
366          * reschedule someone. */
367         vcore_entry();
368 }
369
370 /* Calling thread yields.  TODO: combine similar code with uthread_exit() (done
371  * like this to ease the transition to the 2LS-ops */
372 void uthread_yield(void)
373 {
374         struct uthread *uthread = current_thread;
375         volatile bool yielding = TRUE; /* signal to short circuit when restarting */
376         /* TODO: (HSS) Save silly state */
377         // save_fp_state(&t->as);
378         assert(!in_vcore_context());
379         /* Don't migrate this thread to another vcore, since it depends on being on
380          * the same vcore throughout (once it disables notifs). */
381         uthread->dont_migrate = TRUE;
382         wmb();
383         uint32_t vcoreid = vcore_id();
384         printd("[U] Uthread %08p is yielding on vcore %d\n", uthread, vcoreid);
385         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
386         /* once we do this, we might miss a notif_pending, so we need to enter vcore
387          * entry later.  Need to disable notifs so we don't get in weird loops with
388          * save_ros_tf() and pop_ros_tf(). */
389         disable_notifs(vcoreid);
390         /* take the current state and save it into t->utf when this pthread
391          * restarts, it will continue from right after this, see yielding is false,
392          * and short ciruit the function. */
393         save_ros_tf(&uthread->utf);
394         if (!yielding)
395                 goto yield_return_path;
396         yielding = FALSE; /* for when it starts back up */
397         /* Change to the transition context (both TLS and stack). */
398         extern void** vcore_thread_control_blocks;
399         set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
400         assert(current_thread == uthread);      
401         assert(in_vcore_context());     /* technically, we aren't fully in vcore context */
402         /* After this, make sure you don't use local variables.  Note the warning in
403          * pthread_exit() */
404         set_stack_pointer((void*)vcpd->transition_stack);
405         /* Finish exiting in another function. */
406         __uthread_yield(current_thread);
407         /* Should never get here */
408         assert(0);
409         /* Will jump here when the pthread's trapframe is restarted/popped. */
410 yield_return_path:
411         printd("[U] Uthread %08p returning from a yield!\n", uthread);
412 }
413
414 /* Need to have this as a separate, non-inlined function since we clobber the
415  * stack pointer before calling it, and don't want the compiler to play games
416  * with my hart. */
417 static void __attribute__((noinline, noreturn)) 
418 __uthread_exit(struct uthread *uthread)
419 {
420         assert(in_vcore_context());
421         /* we alloc and manage the TLS, so lets get rid of it */
422         __uthread_free_tls(uthread);
423         /* 2LS specific cleanup */
424         assert(sched_ops->thread_exit);
425         sched_ops->thread_exit(uthread);
426         current_thread = NULL;
427         /* Go back to the entry point, where we can handle notifications or
428          * reschedule someone. */
429         vcore_entry();
430 }
431
432 /* Exits from the uthread */
433 void uthread_exit(void *retval)
434 {
435         assert(!in_vcore_context());
436         struct uthread *uthread = current_thread;
437         uthread->retval = retval;
438         /* Don't migrate this thread to anothe vcore, since it depends on being on
439          * the same vcore throughout. */
440         uthread->dont_migrate = TRUE; // won't set to false later, since he is dying
441         wmb();
442         uint32_t vcoreid = vcore_id();
443         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
444         printd("[U] Uthread %08p is exiting on vcore %d\n", uthread, vcoreid);
445         /* once we do this, we might miss a notif_pending, so we need to enter vcore
446          * entry later. */
447         disable_notifs(vcoreid);
448         /* Change to the transition context (both TLS and stack). */
449         extern void** vcore_thread_control_blocks;
450         set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
451         assert(current_thread == uthread);      
452         /* After this, make sure you don't use local variables.  Also, make sure the
453          * compiler doesn't use them without telling you (TODO).
454          *
455          * In each arch's set_stack_pointer, make sure you subtract off as much room
456          * as you need to any local vars that might be pushed before calling the
457          * next function, or for whatever other reason the compiler/hardware might
458          * walk up the stack a bit when calling a noreturn function. */
459         set_stack_pointer((void*)vcpd->transition_stack);
460         /* Finish exiting in another function.  Ugh. */
461         __uthread_exit(current_thread);
462 }
463
464 /* Runs whatever thread is vcore's current_thread */
465 void run_current_uthread(void)
466 {
467         uint32_t vcoreid = vcore_id();
468         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
469         assert(current_thread);
470         printd("[U] Vcore %d is restarting uthread %d\n", vcoreid, uthread->id);
471         clear_notif_pending(vcoreid);
472         set_tls_desc(current_thread->tls_desc, vcoreid);
473         /* Pop the user trap frame */
474         pop_ros_tf(&vcpd->notif_tf, vcoreid);
475         assert(0);
476 }
477
478 /* Launches the uthread on the vcore */
479 void run_uthread(struct uthread *uthread)
480 {
481         /* Save a ptr to the pthread running in the transition context's TLS */
482         uint32_t vcoreid = vcore_id();
483         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
484         printd("[U] Vcore %d is starting uthread %d\n", vcoreid, uthread->id);
485         current_thread = uthread;
486         clear_notif_pending(vcoreid);
487         set_tls_desc(uthread->tls_desc, vcoreid);
488         /* Load silly state (Floating point) too.  For real */
489         /* TODO: (HSS) */
490         /* Pop the user trap frame */
491         pop_ros_tf(&uthread->utf, vcoreid);
492         assert(0);
493 }
494
495 /* TLS helpers */
496 static int __uthread_allocate_tls(struct uthread *uthread)
497 {
498         assert(!uthread->tls_desc);
499         uthread->tls_desc = allocate_tls();
500         if (!uthread->tls_desc) {
501                 errno = ENOMEM;
502                 return -1;
503         }
504         return 0;
505 }
506
507 /* TODO: probably don't want to dealloc.  Considering caching */
508 static void __uthread_free_tls(struct uthread *uthread)
509 {
510         extern void _dl_deallocate_tls (void *tcb, bool dealloc_tcb) internal_function;
511
512         assert(uthread->tls_desc);
513         _dl_deallocate_tls(uthread->tls_desc, TRUE);
514         uthread->tls_desc = NULL;
515 }