uthread yield and exit code merged
[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         /* Get ourselves into _M mode */
45         while (num_vcores() < 1) {
46                 vcore_request(1);
47                 /* TODO: consider blocking */
48                 cpu_relax();
49         }
50         return 0;
51 }
52
53 /* 2LSs shouldn't call uthread_vcore_entry directly */
54 void __attribute__((noreturn)) uthread_vcore_entry(void)
55 {
56         uint32_t vcoreid = vcore_id();
57
58         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
59
60         /* Should always have notifications disabled when coming in here. */
61         assert(vcpd->notif_enabled == FALSE);
62         assert(in_vcore_context());
63
64         check_preempt_pending(vcoreid);
65         handle_events(vcoreid);
66         assert(in_vcore_context());     /* double check, in case and event changed it */
67         assert(sched_ops->sched_entry);
68         sched_ops->sched_entry();
69         /* 2LS sched_entry should never return */
70         assert(0);
71 }
72
73 /* Creates a uthread.  Will pass udata to sched_ops's thread_create.  For now,
74  * the vcore/default 2ls code handles start routines and args.  Mostly because
75  * this is used when initing a utf, which is vcore specific for now. */
76 struct uthread *uthread_create(void (*func)(void), void *udata)
77 {
78         /* First time through, init the uthread code (which makes a uthread out of
79          * thread0 / the current code.  Could move this to a ctor. */
80         static bool first = TRUE;
81         if (first) {
82                 assert(!uthread_init());
83                 first = FALSE;
84         }
85         assert(!in_vcore_context());
86         assert(sched_ops->thread_create);
87         struct uthread *new_thread = sched_ops->thread_create(func, udata);
88         /* Get a TLS */
89         assert(!__uthread_allocate_tls(new_thread));
90         /* Switch into the new guys TLS and let it know who it is */
91         struct uthread *caller = current_uthread;
92         assert(caller);
93         /* Don't migrate this thread to another vcore, since it depends on being on
94          * the same vcore throughout. */
95         caller->flags |= UTHREAD_DONT_MIGRATE;
96         wmb();
97         /* Note the first time we call this, we technically aren't on a vcore */
98         uint32_t vcoreid = vcore_id();
99         /* Save the new_thread to the new uthread in that uthread's TLS */
100         set_tls_desc(new_thread->tls_desc, vcoreid);
101         current_uthread = new_thread;
102         /* Switch back to the caller */
103         set_tls_desc(caller->tls_desc, vcoreid);
104         /* Okay to migrate now. */
105         wmb();
106         caller->flags &= ~UTHREAD_DONT_MIGRATE;
107         return new_thread;
108 }
109
110 void uthread_runnable(struct uthread *uthread)
111 {
112         /* Allow the 2LS to make the thread runnable, and do whatever. */
113         assert(sched_ops->thread_runnable);
114         sched_ops->thread_runnable(uthread);
115         /* Ask the 2LS how many vcores it wants, and put in the request. */
116         assert(sched_ops->vcores_wanted);
117         vcore_request(sched_ops->vcores_wanted());
118 }
119
120 /* Need to have this as a separate, non-inlined function since we clobber the
121  * stack pointer before calling it, and don't want the compiler to play games
122  * with my hart. */
123 static void __attribute__((noinline, noreturn)) 
124 __uthread_yield(struct uthread *uthread)
125 {
126         assert(in_vcore_context());
127         /* Do slightly different things depending on whether or not we're exiting */
128         if (!(uthread->flags & UTHREAD_DYING)) {
129                 uthread->flags &= ~UTHREAD_DONT_MIGRATE;
130                 assert(sched_ops->thread_yield);
131                 /* 2LS will save the thread somewhere for restarting.  Later on, we'll
132                  * probably have a generic function for all sorts of waiting. */
133                 sched_ops->thread_yield(uthread);
134         } else { /* DYING */
135                 /* we alloc and manage the TLS, so lets get rid of it */
136                 __uthread_free_tls(uthread);
137                 /* 2LS specific cleanup */
138                 assert(sched_ops->thread_exit);
139                 sched_ops->thread_exit(uthread);
140         }
141         /* Leave the current vcore completely */
142         current_uthread = NULL;
143         /* Go back to the entry point, where we can handle notifications or
144          * reschedule someone. */
145         uthread_vcore_entry();
146 }
147
148 /* Calling thread yields.  Both exiting and yielding calls this, the difference
149  * is the thread's state (in the flags). */
150 void uthread_yield(void)
151 {
152         struct uthread *uthread = current_uthread;
153         volatile bool yielding = TRUE; /* signal to short circuit when restarting */
154         /* TODO: (HSS) Save silly state */
155         // if (!(uthread->flags & UTHREAD_DYING))
156         //      save_fp_state(&t->as);
157         assert(!in_vcore_context());
158         /* Don't migrate this thread to another vcore, since it depends on being on
159          * the same vcore throughout (once it disables notifs).  The race is that we
160          * read vcoreid, then get interrupted / migrated before disabling notifs. */
161         uthread->flags |= UTHREAD_DONT_MIGRATE;
162         wmb();
163         uint32_t vcoreid = vcore_id();
164         printd("[U] Uthread %08p is yielding on vcore %d\n", uthread, vcoreid);
165         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
166         /* once we do this, we might miss a notif_pending, so we need to enter vcore
167          * entry later.  Need to disable notifs so we don't get in weird loops with
168          * save_ros_tf() and pop_ros_tf(). */
169         disable_notifs(vcoreid);
170         /* take the current state and save it into t->utf when this pthread
171          * restarts, it will continue from right after this, see yielding is false,
172          * and short ciruit the function.  Don't do this if we're dying. */
173         if (!(uthread->flags & UTHREAD_DYING))
174                 save_ros_tf(&uthread->utf);
175         /* Restart path doesn't matter if we're dying */
176         if (!yielding)
177                 goto yield_return_path;
178         yielding = FALSE; /* for when it starts back up */
179         /* Change to the transition context (both TLS and stack). */
180         extern void** vcore_thread_control_blocks;
181         set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
182         assert(current_uthread == uthread);     
183         assert(in_vcore_context());     /* technically, we aren't fully in vcore context */
184         /* After this, make sure you don't use local variables.  Also, make sure the
185          * compiler doesn't use them without telling you (TODO).
186          *
187          * In each arch's set_stack_pointer, make sure you subtract off as much room
188          * as you need to any local vars that might be pushed before calling the
189          * next function, or for whatever other reason the compiler/hardware might
190          * walk up the stack a bit when calling a noreturn function. */
191         set_stack_pointer((void*)vcpd->transition_stack);
192         /* Finish exiting in another function. */
193         __uthread_yield(current_uthread);
194         /* Should never get here */
195         assert(0);
196         /* Will jump here when the pthread's trapframe is restarted/popped. */
197 yield_return_path:
198         printd("[U] Uthread %08p returning from a yield!\n", uthread);
199 }
200
201 /* Exits from the uthread.  Tempting to get rid of this function, but we need to
202  * manage the flags so we know to clean up the TLS and stuff later. */
203 void uthread_exit(void)
204 {
205         current_uthread->flags |= UTHREAD_DYING;
206         uthread_yield();
207 }
208
209 /* Attempts to block on sysc, returning when it is done or progress has been
210  * made. */
211 void ros_syscall_blockon(struct syscall *sysc)
212 {
213         if (in_vcore_context()) {
214                 /* vcore's don't know what to do yet, so do the default (spin) */
215                 __ros_syscall_blockon(sysc);
216                 return;
217         }
218         if (!sched_ops->thread_blockon_sysc || !current_uthread) {
219                 /* There isn't a 2LS op for blocking.  Spin for now. */
220                 __ros_syscall_blockon(sysc);
221                 return;
222         }
223         /* double check before doing all this crap */
224         if (sysc->flags & (SC_DONE | SC_PROGRESS))
225                 return;
226         /* TODO: switch to vcore context, then do shit */
227         sched_ops->thread_blockon_sysc(sysc);
228 }
229
230 /* Runs whatever thread is vcore's current_uthread */
231 void run_current_uthread(void)
232 {
233         uint32_t vcoreid = vcore_id();
234         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
235         assert(current_uthread);
236         printd("[U] Vcore %d is restarting uthread %d\n", vcoreid, uthread->id);
237         clear_notif_pending(vcoreid);
238         set_tls_desc(current_uthread->tls_desc, vcoreid);
239         /* Pop the user trap frame */
240         pop_ros_tf(&vcpd->notif_tf, vcoreid);
241         assert(0);
242 }
243
244 /* Launches the uthread on the vcore.  Don't call this on current_uthread. */
245 void run_uthread(struct uthread *uthread)
246 {
247         assert(uthread != current_uthread);
248         /* Save a ptr to the pthread running in the transition context's TLS */
249         uint32_t vcoreid = vcore_id();
250         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
251         printd("[U] Vcore %d is starting uthread %d\n", vcoreid, uthread->id);
252         current_uthread = uthread;
253         clear_notif_pending(vcoreid);
254         set_tls_desc(uthread->tls_desc, vcoreid);
255         /* Load silly state (Floating point) too.  For real */
256         /* TODO: (HSS) */
257         /* Pop the user trap frame */
258         pop_ros_tf(&uthread->utf, vcoreid);
259         assert(0);
260 }
261
262 /* Deals with a pending preemption (checks, responds).  If the 2LS registered a
263  * function, it will get run.  Returns true if you got preempted.  Called
264  * 'check' instead of 'handle', since this isn't an event handler.  It's the "Oh
265  * shit a preempt is on its way ASAP".  While it is isn't too involved with
266  * uthreads, it is tied in to sched_ops. */
267 bool check_preempt_pending(uint32_t vcoreid)
268 {
269         bool retval = FALSE;
270         if (__procinfo.vcoremap[vcoreid].preempt_pending) {
271                 retval = TRUE;
272                 if (sched_ops->preempt_pending)
273                         sched_ops->preempt_pending();
274                 /* this tries to yield, but will pop back up if this was a spurious
275                  * preempt_pending. */
276                 sys_yield(TRUE);
277         }
278         return retval;
279 }
280
281 /* TLS helpers */
282 static int __uthread_allocate_tls(struct uthread *uthread)
283 {
284         assert(!uthread->tls_desc);
285         uthread->tls_desc = allocate_tls();
286         if (!uthread->tls_desc) {
287                 errno = ENOMEM;
288                 return -1;
289         }
290         return 0;
291 }
292
293 static void __uthread_free_tls(struct uthread *uthread)
294 {
295         free_tls(uthread->tls_desc);
296         uthread->tls_desc = NULL;
297 }