Allows apps to implement vcore_entry() (XCC)
[akaros.git] / user / parlib / uthread.c
1 #include <ros/arch/membar.h>
2 #include <parlib.h>
3 #include <vcore.h>
4 #include <uthread.h>
5 #include <event.h>
6
7 /* Which operations we'll call for the 2LS.  Will change a bit with Lithe.  For
8  * now, there are no defaults.  2LSs can override sched_ops. */
9 struct schedule_ops default_2ls_ops = {0};
10 struct schedule_ops *sched_ops __attribute__((weak)) = &default_2ls_ops;
11
12 __thread struct uthread *current_uthread = 0;
13
14 /* static helpers: */
15 static int __uthread_allocate_tls(struct uthread *uthread);
16 static void __uthread_free_tls(struct uthread *uthread);
17
18 /* Gets called once out of uthread_create().  Can also do this in a ctor. */
19 static int uthread_init(void)
20 {
21         /* Init the vcore system */
22         assert(!vcore_init());
23         /* Bug if vcore init was called with no 2LS */
24         assert(sched_ops->sched_init);
25         /* Get thread 0's thread struct (2LS allocs it) */
26         struct uthread *uthread = sched_ops->sched_init();
27         /* Save a pointer to thread0's tls region (the glibc one) into its tcb */
28         uthread->tls_desc = get_tls_desc(0);
29         /* Save a pointer to the uthread in its own TLS */
30         current_uthread = uthread;
31         /* Change temporarily to vcore0s tls region so we can save the newly created
32          * tcb into its current_uthread variable and then restore it.  One minor
33          * issue is that vcore0's transition-TLS isn't TLS_INITed yet.  Until it is
34          * (right before vcore_entry(), don't try and take the address of any of
35          * its TLS vars. */
36         extern void** vcore_thread_control_blocks;
37         set_tls_desc(vcore_thread_control_blocks[0], 0);
38         current_uthread = uthread;
39         set_tls_desc(uthread->tls_desc, 0);
40         assert(!in_vcore_context());
41         /* don't forget to enable notifs on vcore0.  if you don't, the kernel will
42          * restart your _S with notifs disabled, which is a path to confusion. */
43         enable_notifs(0);
44         return 0;
45 }
46
47 /* 2LSs shouldn't call uthread_vcore_entry directly */
48 void __attribute__((noreturn)) uthread_vcore_entry(void)
49 {
50         uint32_t vcoreid = vcore_id();
51
52         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
53
54         /* Should always have notifications disabled when coming in here. */
55         assert(vcpd->notif_enabled == FALSE);
56         assert(in_vcore_context());
57
58         check_preempt_pending(vcoreid);
59         handle_events(vcoreid);
60         assert(in_vcore_context());     /* double check, in case and event changed it */
61         assert(sched_ops->sched_entry);
62         sched_ops->sched_entry();
63         /* If we get here, the 2LS entry returned.  We can call out to the 2LS for
64          * guidance about whether or not to yield, etc.  Or the 2LS can do it and
65          * just not return.  Whatever we do, it ought to parallel what we do for
66          * requesting more cores in uthread_create(). */
67         printd("Vcore %d is yielding\n", vcoreid);
68         sys_yield(0);
69         assert(0);
70 }
71
72 /* Creates a uthread.  Will pass udata to sched_ops's thread_create.  For now,
73  * the vcore/default 2ls code handles start routines and args.  Mostly because
74  * this is used when initing a utf, which is vcore specific for now. */
75 struct uthread *uthread_create(void (*func)(void), void *udata)
76 {
77         /* First time through, init the uthread code (which makes a uthread out of
78          * thread0 / the current code.  Could move this to a ctor. */
79         static bool first = TRUE;
80         if (first) {
81                 assert(!uthread_init());
82                 first = FALSE;
83         }
84         assert(!in_vcore_context());
85         assert(sched_ops->thread_create);
86         struct uthread *new_thread = sched_ops->thread_create(func, udata);
87         /* Get a TLS */
88         assert(!__uthread_allocate_tls(new_thread));
89         /* Switch into the new guys TLS and let it know who it is */
90         struct uthread *caller = current_uthread;
91         assert(caller);
92         /* Don't migrate this thread to another vcore, since it depends on being on
93          * the same vcore throughout. */
94         caller->dont_migrate = TRUE;
95         wmb();
96         /* Note the first time we call this, we technically aren't on a vcore */
97         uint32_t vcoreid = vcore_id();
98         /* Save the new_thread to the new uthread in that uthread's TLS */
99         set_tls_desc(new_thread->tls_desc, vcoreid);
100         current_uthread = new_thread;
101         /* Switch back to the caller */
102         set_tls_desc(caller->tls_desc, vcoreid);
103         /* Okay to migrate now. */
104         wmb();
105         caller->dont_migrate = FALSE;
106         return new_thread;
107 }
108
109 void uthread_runnable(struct uthread *uthread)
110 {
111         /* Allow the 2LS to make the thread runnable, and do whatever. */
112         assert(sched_ops->thread_runnable);
113         sched_ops->thread_runnable(uthread);
114         /* This is where we'll call out to a smarter 2LS function to see if we want
115          * to get more cores (and how many). */
116         /* Need to get some vcores.  If this is the first time, we'd like to get
117          * two: one for the main thread (aka thread0), and another for the pthread
118          * we are creating.  Can rework this if we get another vcore interface that
119          * deals with absolute core counts.
120          *
121          * Need to get at least one core to put us in _M mode so we can run the 2LS,
122          * etc, so for now we'll just spin until we get at least one (might be none
123          * available).
124          *
125          * TODO: do something smarter regarding asking for cores (paired with
126          * yielding), and block or something until one core is available (will need
127          * kernel support). */
128         static bool first_time = TRUE;
129         if (first_time) {
130                 first_time = FALSE;
131                 /* Try for two, don't settle for less than 1 */
132                 while (num_vcores() < 1) {
133                         vcore_request(2);
134                         cpu_relax();
135                 }
136         } else {        /* common case */
137                 /* Try to get another for the new thread, but doesn't matter if we get
138                  * one or not, so long as we still have at least 1. */
139                 vcore_request(1);
140         }
141 }
142
143 /* Need to have this as a separate, non-inlined function since we clobber the
144  * stack pointer before calling it, and don't want the compiler to play games
145  * with my hart.
146  *
147  * TODO: combine this 2-step logic with uthread_exit() */
148 static void __attribute__((noinline, noreturn)) 
149 __uthread_yield(struct uthread *uthread)
150 {
151         assert(in_vcore_context());
152         /* TODO: want to set this to FALSE once we no longer depend on being on this
153          * vcore.  Though if we are using TLS, we are depending on the vcore.  Since
154          * notifs are disabled and we are in a transition context, we probably
155          * shouldn't be moved anyway.  It does mean that a pthread could get jammed.
156          * If we do this after putting it on the active list, we'll have a race on
157          * dont_migrate. */
158         uthread->dont_migrate = FALSE;
159         assert(sched_ops->thread_yield);
160         /* 2LS will save the thread somewhere for restarting.  Later on, we'll
161          * probably have a generic function for all sorts of waiting. */
162         sched_ops->thread_yield(uthread);
163         /* Leave the current vcore completely */
164         current_uthread = NULL; // this might be okay, even with a migration
165         /* Go back to the entry point, where we can handle notifications or
166          * reschedule someone. */
167         uthread_vcore_entry();
168 }
169
170 /* Calling thread yields.  TODO: combine similar code with uthread_exit() (done
171  * like this to ease the transition to the 2LS-ops */
172 void uthread_yield(void)
173 {
174         struct uthread *uthread = current_uthread;
175         volatile bool yielding = TRUE; /* signal to short circuit when restarting */
176         /* TODO: (HSS) Save silly state */
177         // save_fp_state(&t->as);
178         assert(!in_vcore_context());
179         /* Don't migrate this thread to another vcore, since it depends on being on
180          * the same vcore throughout (once it disables notifs). */
181         uthread->dont_migrate = TRUE;
182         wmb();
183         uint32_t vcoreid = vcore_id();
184         printd("[U] Uthread %08p is yielding on vcore %d\n", uthread, vcoreid);
185         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
186         /* once we do this, we might miss a notif_pending, so we need to enter vcore
187          * entry later.  Need to disable notifs so we don't get in weird loops with
188          * save_ros_tf() and pop_ros_tf(). */
189         disable_notifs(vcoreid);
190         /* take the current state and save it into t->utf when this pthread
191          * restarts, it will continue from right after this, see yielding is false,
192          * and short ciruit the function. */
193         save_ros_tf(&uthread->utf);
194         if (!yielding)
195                 goto yield_return_path;
196         yielding = FALSE; /* for when it starts back up */
197         /* Change to the transition context (both TLS and stack). */
198         extern void** vcore_thread_control_blocks;
199         set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
200         assert(current_uthread == uthread);     
201         assert(in_vcore_context());     /* technically, we aren't fully in vcore context */
202         /* After this, make sure you don't use local variables.  Note the warning in
203          * pthread_exit() */
204         set_stack_pointer((void*)vcpd->transition_stack);
205         /* Finish exiting in another function. */
206         __uthread_yield(current_uthread);
207         /* Should never get here */
208         assert(0);
209         /* Will jump here when the pthread's trapframe is restarted/popped. */
210 yield_return_path:
211         printd("[U] Uthread %08p returning from a yield!\n", uthread);
212 }
213
214 /* Need to have this as a separate, non-inlined function since we clobber the
215  * stack pointer before calling it, and don't want the compiler to play games
216  * with my hart. */
217 static void __attribute__((noinline, noreturn)) 
218 __uthread_exit(struct uthread *uthread)
219 {
220         assert(in_vcore_context());
221         /* we alloc and manage the TLS, so lets get rid of it */
222         __uthread_free_tls(uthread);
223         /* 2LS specific cleanup */
224         assert(sched_ops->thread_exit);
225         sched_ops->thread_exit(uthread);
226         current_uthread = NULL;
227         /* Go back to the entry point, where we can handle notifications or
228          * reschedule someone. */
229         uthread_vcore_entry();
230 }
231
232 /* Exits from the uthread */
233 void uthread_exit(void)
234 {
235         assert(!in_vcore_context());
236         struct uthread *uthread = current_uthread;
237         /* Don't migrate this thread to anothe vcore, since it depends on being on
238          * the same vcore throughout. */
239         uthread->dont_migrate = TRUE; // won't set to false later, since he is dying
240         wmb();
241         uint32_t vcoreid = vcore_id();
242         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
243         printd("[U] Uthread %08p is exiting on vcore %d\n", uthread, vcoreid);
244         /* once we do this, we might miss a notif_pending, so we need to enter vcore
245          * entry later. */
246         disable_notifs(vcoreid);
247         /* Change to the transition context (both TLS and stack). */
248         extern void** vcore_thread_control_blocks;
249         set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
250         assert(current_uthread == uthread);     
251         /* After this, make sure you don't use local variables.  Also, make sure the
252          * compiler doesn't use them without telling you (TODO).
253          *
254          * In each arch's set_stack_pointer, make sure you subtract off as much room
255          * as you need to any local vars that might be pushed before calling the
256          * next function, or for whatever other reason the compiler/hardware might
257          * walk up the stack a bit when calling a noreturn function. */
258         set_stack_pointer((void*)vcpd->transition_stack);
259         /* Finish exiting in another function.  Ugh. */
260         __uthread_exit(current_uthread);
261 }
262
263 /* Runs whatever thread is vcore's current_uthread */
264 void run_current_uthread(void)
265 {
266         uint32_t vcoreid = vcore_id();
267         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
268         assert(current_uthread);
269         printd("[U] Vcore %d is restarting uthread %d\n", vcoreid, uthread->id);
270         clear_notif_pending(vcoreid);
271         set_tls_desc(current_uthread->tls_desc, vcoreid);
272         /* Pop the user trap frame */
273         pop_ros_tf(&vcpd->notif_tf, vcoreid);
274         assert(0);
275 }
276
277 /* Launches the uthread on the vcore */
278 void run_uthread(struct uthread *uthread)
279 {
280         /* Save a ptr to the pthread running in the transition context's TLS */
281         uint32_t vcoreid = vcore_id();
282         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
283         printd("[U] Vcore %d is starting uthread %d\n", vcoreid, uthread->id);
284         current_uthread = uthread;
285         clear_notif_pending(vcoreid);
286         set_tls_desc(uthread->tls_desc, vcoreid);
287         /* Load silly state (Floating point) too.  For real */
288         /* TODO: (HSS) */
289         /* Pop the user trap frame */
290         pop_ros_tf(&uthread->utf, vcoreid);
291         assert(0);
292 }
293
294 /* Deals with a pending preemption (checks, responds).  If the 2LS registered a
295  * function, it will get run.  Returns true if you got preempted.  Called
296  * 'check' instead of 'handle', since this isn't an event handler.  It's the "Oh
297  * shit a preempt is on its way ASAP".  While it is isn't too involved with
298  * uthreads, it is tied in to sched_ops. */
299 bool check_preempt_pending(uint32_t vcoreid)
300 {
301         bool retval = FALSE;
302         if (__procinfo.vcoremap[vcoreid].preempt_pending) {
303                 retval = TRUE;
304                 if (sched_ops->preempt_pending)
305                         sched_ops->preempt_pending();
306                 /* this tries to yield, but will pop back up if this was a spurious
307                  * preempt_pending. */
308                 sys_yield(TRUE);
309         }
310         return retval;
311 }
312
313 /* TLS helpers */
314 static int __uthread_allocate_tls(struct uthread *uthread)
315 {
316         assert(!uthread->tls_desc);
317         uthread->tls_desc = allocate_tls();
318         if (!uthread->tls_desc) {
319                 errno = ENOMEM;
320                 return -1;
321         }
322         return 0;
323 }
324
325 static void __uthread_free_tls(struct uthread *uthread)
326 {
327         free_tls(uthread->tls_desc);
328         uthread->tls_desc = NULL;
329 }