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