MCS locks have a "notification-safe" variant
[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->dont_migrate = TRUE;
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->dont_migrate = FALSE;
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  *
124  * TODO: combine this 2-step logic with uthread_exit() */
125 static void __attribute__((noinline, noreturn)) 
126 __uthread_yield(struct uthread *uthread)
127 {
128         assert(in_vcore_context());
129         /* TODO: want to set this to FALSE once we no longer depend on being on this
130          * vcore.  Though if we are using TLS, we are depending on the vcore.  Since
131          * notifs are disabled and we are in a transition context, we probably
132          * shouldn't be moved anyway.  It does mean that a pthread could get jammed.
133          * If we do this after putting it on the active list, we'll have a race on
134          * dont_migrate. */
135         uthread->dont_migrate = FALSE;
136         assert(sched_ops->thread_yield);
137         /* 2LS will save the thread somewhere for restarting.  Later on, we'll
138          * probably have a generic function for all sorts of waiting. */
139         sched_ops->thread_yield(uthread);
140         /* Leave the current vcore completely */
141         current_uthread = NULL; // this might be okay, even with a migration
142         /* Go back to the entry point, where we can handle notifications or
143          * reschedule someone. */
144         uthread_vcore_entry();
145 }
146
147 /* Calling thread yields.  TODO: combine similar code with uthread_exit() (done
148  * like this to ease the transition to the 2LS-ops */
149 void uthread_yield(void)
150 {
151         struct uthread *uthread = current_uthread;
152         volatile bool yielding = TRUE; /* signal to short circuit when restarting */
153         /* TODO: (HSS) Save silly state */
154         // save_fp_state(&t->as);
155         assert(!in_vcore_context());
156         /* Don't migrate this thread to another vcore, since it depends on being on
157          * the same vcore throughout (once it disables notifs). */
158         uthread->dont_migrate = TRUE;
159         wmb();
160         uint32_t vcoreid = vcore_id();
161         printd("[U] Uthread %08p is yielding on vcore %d\n", uthread, vcoreid);
162         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
163         /* once we do this, we might miss a notif_pending, so we need to enter vcore
164          * entry later.  Need to disable notifs so we don't get in weird loops with
165          * save_ros_tf() and pop_ros_tf(). */
166         disable_notifs(vcoreid);
167         /* take the current state and save it into t->utf when this pthread
168          * restarts, it will continue from right after this, see yielding is false,
169          * and short ciruit the function. */
170         save_ros_tf(&uthread->utf);
171         if (!yielding)
172                 goto yield_return_path;
173         yielding = FALSE; /* for when it starts back up */
174         /* Change to the transition context (both TLS and stack). */
175         extern void** vcore_thread_control_blocks;
176         set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
177         assert(current_uthread == uthread);     
178         assert(in_vcore_context());     /* technically, we aren't fully in vcore context */
179         /* After this, make sure you don't use local variables.  Note the warning in
180          * pthread_exit() */
181         set_stack_pointer((void*)vcpd->transition_stack);
182         /* Finish exiting in another function. */
183         __uthread_yield(current_uthread);
184         /* Should never get here */
185         assert(0);
186         /* Will jump here when the pthread's trapframe is restarted/popped. */
187 yield_return_path:
188         printd("[U] Uthread %08p returning from a yield!\n", uthread);
189 }
190
191 /* Need to have this as a separate, non-inlined function since we clobber the
192  * stack pointer before calling it, and don't want the compiler to play games
193  * with my hart. */
194 static void __attribute__((noinline, noreturn)) 
195 __uthread_exit(struct uthread *uthread)
196 {
197         assert(in_vcore_context());
198         /* we alloc and manage the TLS, so lets get rid of it */
199         __uthread_free_tls(uthread);
200         /* 2LS specific cleanup */
201         assert(sched_ops->thread_exit);
202         sched_ops->thread_exit(uthread);
203         current_uthread = NULL;
204         /* Go back to the entry point, where we can handle notifications or
205          * reschedule someone. */
206         uthread_vcore_entry();
207 }
208
209 /* Exits from the uthread */
210 void uthread_exit(void)
211 {
212         assert(!in_vcore_context());
213         struct uthread *uthread = current_uthread;
214         /* Don't migrate this thread to anothe vcore, since it depends on being on
215          * the same vcore throughout. */
216         uthread->dont_migrate = TRUE; // won't set to false later, since he is dying
217         wmb();
218         uint32_t vcoreid = vcore_id();
219         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
220         printd("[U] Uthread %08p is exiting on vcore %d\n", uthread, vcoreid);
221         /* once we do this, we might miss a notif_pending, so we need to enter vcore
222          * entry later. */
223         disable_notifs(vcoreid);
224         /* Change to the transition context (both TLS and stack). */
225         extern void** vcore_thread_control_blocks;
226         set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
227         assert(current_uthread == uthread);     
228         /* After this, make sure you don't use local variables.  Also, make sure the
229          * compiler doesn't use them without telling you (TODO).
230          *
231          * In each arch's set_stack_pointer, make sure you subtract off as much room
232          * as you need to any local vars that might be pushed before calling the
233          * next function, or for whatever other reason the compiler/hardware might
234          * walk up the stack a bit when calling a noreturn function. */
235         set_stack_pointer((void*)vcpd->transition_stack);
236         /* Finish exiting in another function.  Ugh. */
237         __uthread_exit(current_uthread);
238 }
239
240 /* Runs whatever thread is vcore's current_uthread */
241 void run_current_uthread(void)
242 {
243         uint32_t vcoreid = vcore_id();
244         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
245         assert(current_uthread);
246         printd("[U] Vcore %d is restarting uthread %d\n", vcoreid, uthread->id);
247         clear_notif_pending(vcoreid);
248         set_tls_desc(current_uthread->tls_desc, vcoreid);
249         /* Pop the user trap frame */
250         pop_ros_tf(&vcpd->notif_tf, vcoreid);
251         assert(0);
252 }
253
254 /* Launches the uthread on the vcore.  Don't call this on current_uthread. */
255 void run_uthread(struct uthread *uthread)
256 {
257         assert(uthread != current_uthread);
258         /* Save a ptr to the pthread running in the transition context's TLS */
259         uint32_t vcoreid = vcore_id();
260         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
261         printd("[U] Vcore %d is starting uthread %d\n", vcoreid, uthread->id);
262         current_uthread = uthread;
263         clear_notif_pending(vcoreid);
264         set_tls_desc(uthread->tls_desc, vcoreid);
265         /* Load silly state (Floating point) too.  For real */
266         /* TODO: (HSS) */
267         /* Pop the user trap frame */
268         pop_ros_tf(&uthread->utf, vcoreid);
269         assert(0);
270 }
271
272 /* Deals with a pending preemption (checks, responds).  If the 2LS registered a
273  * function, it will get run.  Returns true if you got preempted.  Called
274  * 'check' instead of 'handle', since this isn't an event handler.  It's the "Oh
275  * shit a preempt is on its way ASAP".  While it is isn't too involved with
276  * uthreads, it is tied in to sched_ops. */
277 bool check_preempt_pending(uint32_t vcoreid)
278 {
279         bool retval = FALSE;
280         if (__procinfo.vcoremap[vcoreid].preempt_pending) {
281                 retval = TRUE;
282                 if (sched_ops->preempt_pending)
283                         sched_ops->preempt_pending();
284                 /* this tries to yield, but will pop back up if this was a spurious
285                  * preempt_pending. */
286                 sys_yield(TRUE);
287         }
288         return retval;
289 }
290
291 /* TLS helpers */
292 static int __uthread_allocate_tls(struct uthread *uthread)
293 {
294         assert(!uthread->tls_desc);
295         uthread->tls_desc = allocate_tls();
296         if (!uthread->tls_desc) {
297                 errno = ENOMEM;
298                 return -1;
299         }
300         return 0;
301 }
302
303 static void __uthread_free_tls(struct uthread *uthread)
304 {
305         free_tls(uthread->tls_desc);
306         uthread->tls_desc = NULL;
307 }