Allow thread0 uthreads to block
[akaros.git] / user / parlib / thread0_sched.c
1 /* Copyright (c) 2015 Google, Inc.
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * thread0_sched: a basic scheduler for thread0, used by SCPs without a
6  * multithreaded 2LS linked in.
7  *
8  * This is closely coupled with uthread.c */
9
10 #include <ros/arch/membar.h>
11 #include <parlib/arch/atomic.h>
12 #include <parlib/parlib.h>
13 #include <parlib/vcore.h>
14 #include <parlib/uthread.h>
15 #include <parlib/event.h>
16 #include <stdlib.h>
17
18 static void thread0_sched_entry(void);
19 static void thread0_thread_blockon_sysc(struct uthread *uthread, void *sysc);
20 static void thread0_thread_refl_fault(struct uthread *uthread,
21                                       unsigned int trap_nr, unsigned int err,
22                                       unsigned long aux);
23 static void thread0_thread_runnable(struct uthread *uth);
24 static void thread0_thread_has_blocked(struct uthread *uth, int flags);
25
26 /* externed into uthread.c */
27 struct schedule_ops thread0_2ls_ops = {
28         .sched_entry = thread0_sched_entry,
29         .thread_blockon_sysc = thread0_thread_blockon_sysc,
30         .thread_refl_fault = thread0_thread_refl_fault,
31         .thread_runnable = thread0_thread_runnable,
32         .thread_has_blocked = thread0_thread_has_blocked,
33 };
34
35 /* externed into uthread.c */
36 struct uthread *thread0_uth;
37
38 /* Our thread0 is actually allocated in uthread as just a struct uthread, so we
39  * don't actually attach this mgmt info to it.  But since we just have one
40  * thread, it doesn't matter. */
41 struct thread0_info {
42         bool                                            is_blocked;
43 };
44 static struct thread0_info thread0_info;
45
46 void thread0_lib_init(void)
47 {
48         memset(&thread0_info, 0, sizeof(thread0_info));
49 }
50
51 /* Thread0 scheduler ops (for processes that haven't linked in a full 2LS) */
52 static void thread0_sched_entry(void)
53 {
54         /* TODO: support signal handling whenever we run a uthread */
55         if (current_uthread) {
56                 run_current_uthread();
57                 assert(0);
58         }
59         while (1) {
60                 if (!thread0_info.is_blocked) {
61                         run_uthread(thread0_uth);
62                         assert(0);
63                 }
64                 sys_yield(FALSE);
65                 handle_events(0);
66         }
67 }
68
69 static void thread0_thread_blockon_sysc(struct uthread *uthread, void *arg)
70 {
71         struct syscall *sysc = (struct syscall*)arg;
72         /* We're in vcore context.  Regardless of what we do here, we'll pop back in
73          * to vcore entry, just like with any uthread_yield.  We don't have a 2LS,
74          * but we always have one uthread: the SCP's thread0.  Note that at this
75          * point, current_uthread is still set, but will be cleared as soon as the
76          * callback returns (and before we start over in vcore_entry).
77          *
78          * If notif_pending is already set (due to a concurrent signal), we'll fail
79          * to yield.  Once in VC ctx, we'll handle any other signals/events that
80          * arrived, then restart the uthread that issued the syscall, which if the
81          * syscall isn't done yet, will just blockon again.
82          *
83          * The one trick is that we don't want to register the evq twice.  The way
84          * register_evq currently works, if a SC completed (SC_DONE) while we were
85          * registering, we could end up clearing sysc->ev_q before the kernel sees
86          * it.  We'll use u_data to track whether we registered or not. */
87         #define U_DATA_BLOB ((void*)0x55555555)
88         if ((sysc->u_data == U_DATA_BLOB)
89             || register_evq(sysc, &__ros_scp_simple_evq)) {
90                 sysc->u_data = U_DATA_BLOB;
91                 /* Sending false for now - we want to signal proc code that we want to
92                  * wait (piggybacking on the MCP meaning of this variable).  If
93                  * notif_pending is set, the kernel will immediately return us. */
94                 __ros_syscall_noerrno(SYS_yield, FALSE, 0, 0, 0, 0, 0);
95         }
96 }
97
98 static void thread0_thread_refl_fault(struct uthread *uthread,
99                                       unsigned int trap_nr, unsigned int err,
100                                       unsigned long aux)
101 {
102         printf("SCP has unhandled fault: %d, err: %d, aux: %p\n", trap_nr, err,
103                aux);
104         print_user_context(&uthread->u_ctx);
105         printf("Turn on printx to spew unhandled, malignant trap info\n");
106         exit(-1);
107 }
108
109 static void thread0_thread_runnable(struct uthread *uth)
110 {
111         thread0_info.is_blocked = FALSE;
112 }
113
114 static void thread0_thread_has_blocked(struct uthread *uth, int flags)
115 {
116         thread0_info.is_blocked = TRUE;
117 }