parlib: Add trylock to uthread mutexes
[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 <parlib/arch/trap.h>
17 #include <stdlib.h>
18
19 static void thread0_sched_entry(void);
20 static void thread0_thread_blockon_sysc(struct uthread *uthread, void *sysc);
21 static void thread0_thread_refl_fault(struct uthread *uth,
22                                       struct user_context *ctx);
23 static void thread0_thread_runnable(struct uthread *uth);
24 static void thread0_thread_has_blocked(struct uthread *uth, int flags);
25 static uth_mutex_t thread0_mtx_alloc(void);
26 static void thread0_mtx_free(uth_mutex_t m);
27 static void thread0_mtx_lock(uth_mutex_t m);
28 static bool thread0_mtx_trylock(uth_mutex_t m);
29 static void thread0_mtx_unlock(uth_mutex_t m);
30
31 /* externed into uthread.c */
32 struct schedule_ops thread0_2ls_ops = {
33         .sched_entry = thread0_sched_entry,
34         .thread_blockon_sysc = thread0_thread_blockon_sysc,
35         .thread_refl_fault = thread0_thread_refl_fault,
36         .thread_runnable = thread0_thread_runnable,
37         .thread_paused = thread0_thread_runnable,
38         .thread_has_blocked = thread0_thread_has_blocked,
39         .mutex_alloc = thread0_mtx_alloc,
40         .mutex_free = thread0_mtx_free,
41         .mutex_lock = thread0_mtx_lock,
42         .mutex_trylock = thread0_mtx_trylock,
43         .mutex_unlock = thread0_mtx_unlock,
44 };
45
46 /* externed into uthread.c */
47 struct uthread *thread0_uth;
48
49 /* Our thread0 is actually allocated in uthread as just a struct uthread, so we
50  * don't actually attach this mgmt info to it.  But since we just have one
51  * thread, it doesn't matter. */
52 struct thread0_info {
53         bool                                            is_blocked;
54 };
55 static struct thread0_info thread0_info;
56 static struct event_queue *sysc_evq;
57
58 void thread0_handle_syscall(struct event_msg *ev_msg,
59                             unsigned int ev_type, void *data)
60 {
61         thread0_info.is_blocked = FALSE;
62 }
63
64 void thread0_lib_init(void)
65 {
66         memset(&thread0_info, 0, sizeof(thread0_info));
67         /* we don't care about the message, so don't bother with a UCQ */
68         sysc_evq = get_eventq(EV_MBOX_BITMAP);
69         sysc_evq->ev_flags = EVENT_INDIR | EVENT_WAKEUP;
70 }
71
72 /* Thread0 scheduler ops (for processes that haven't linked in a full 2LS) */
73 static void thread0_sched_entry(void)
74 {
75         /* TODO: support signal handling whenever we run a uthread */
76         if (current_uthread) {
77                 uthread_prep_pending_signals(current_uthread);
78                 run_current_uthread();
79                 assert(0);
80         }
81         while (1) {
82                 if (!thread0_info.is_blocked) {
83                         uthread_prep_pending_signals(thread0_uth);
84                         run_uthread(thread0_uth);
85                         assert(0);
86                 }
87                 sys_yield(FALSE);
88                 handle_events(0);
89         }
90 }
91
92 static void thread0_thread_blockon_sysc(struct uthread *uthread, void *arg)
93 {
94         struct syscall *sysc = (struct syscall*)arg;
95         thread0_thread_has_blocked(uthread, 0);
96         if (!register_evq(sysc, sysc_evq))
97                 thread0_thread_runnable(uthread);
98 }
99
100 static void refl_error(struct uthread *uth, unsigned int trap_nr,
101                        unsigned int err, unsigned long aux)
102 {
103         printf("Thread has unhandled fault: %d, err: %d, aux: %p\n",
104                trap_nr, err, aux);
105         /* Note that uthread.c already copied out our ctx into the uth
106          * struct */
107         print_user_context(&uth->u_ctx);
108         printf("Turn on printx to spew unhandled, malignant trap info\n");
109         exit(-1);
110 }
111
112 static bool handle_page_fault(struct uthread *uth, unsigned int err,
113                               unsigned long aux)
114 {
115         if (!(err & PF_VMR_BACKED))
116                 return FALSE;
117         syscall_async(&uth->local_sysc, SYS_populate_va, aux, 1);
118         __block_uthread_on_async_sysc(uth);
119         return TRUE;
120 }
121
122 static void thread0_thread_refl_fault(struct uthread *uth,
123                                       struct user_context *ctx)
124 {
125         unsigned int trap_nr = __arch_refl_get_nr(ctx);
126         unsigned int err = __arch_refl_get_err(ctx);
127         unsigned long aux = __arch_refl_get_aux(ctx);
128
129         assert(ctx->type == ROS_HW_CTX);
130         switch (trap_nr) {
131         case HW_TRAP_PAGE_FAULT:
132                 if (!handle_page_fault(uth, err, aux))
133                         refl_error(uth, trap_nr, err, aux);
134                 break;
135         default:
136                 refl_error(uth, trap_nr, err, aux);
137         }
138 }
139
140 static void thread0_thread_runnable(struct uthread *uth)
141 {
142         thread0_info.is_blocked = FALSE;
143 }
144
145 static void thread0_thread_has_blocked(struct uthread *uth, int flags)
146 {
147         thread0_info.is_blocked = TRUE;
148 }
149
150 /* We only have one thread, so we don't *need* mutexes.  But we'll use a bool to
151  * catch code that could deadlock itself. */
152 static uth_mutex_t thread0_mtx_alloc(void)
153 {
154         bool *mtx = malloc(sizeof(bool));
155
156         assert(mtx);
157         *mtx = FALSE;
158         return (uth_mutex_t)mtx;
159 }
160
161 static void thread0_mtx_free(uth_mutex_t m)
162 {
163         bool *mtx = (bool*)m;
164
165         assert(*mtx == FALSE);
166         free((void*)m);
167 }
168
169 static void thread0_mtx_lock(uth_mutex_t m)
170 {
171         bool *mtx = (bool*)m;
172
173         assert(*mtx == FALSE);
174         *mtx = TRUE;
175 }
176
177 static bool thread0_mtx_trylock(uth_mutex_t m)
178 {
179         bool *mtx = (bool*)m;
180
181         if (*mtx)
182                 return FALSE;
183         *mtx = TRUE;
184         return TRUE;
185 }
186
187 static void thread0_mtx_unlock(uth_mutex_t m)
188 {
189         bool *mtx = (bool*)m;
190
191         assert(*mtx == TRUE);
192         *mtx = FALSE;
193 }