uth: add got_posix_signal() to the 2LS ops
[akaros.git] / user / parlib / thread0_sched.c
index f5c838d..62a7f6f 100644 (file)
 #include <parlib/vcore.h>
 #include <parlib/uthread.h>
 #include <parlib/event.h>
+#include <parlib/arch/trap.h>
+#include <parlib/ros_debug.h>
 #include <stdlib.h>
+#include <sys/fork_cb.h>
 
+static void thread0_sched_init(void);
 static void thread0_sched_entry(void);
+static void thread0_thread_blockon_sysc(struct uthread *uthread, void *sysc);
+static void thread0_thread_refl_fault(struct uthread *uth,
+                                      struct user_context *ctx);
+static void thread0_thread_runnable(struct uthread *uth);
+static void thread0_thread_has_blocked(struct uthread *uth, int flags);
+static void thread0_thread_exited(struct uthread *uth);
+static struct uthread *thread0_thread_create(void *(*func)(void *), void *arg);
+static void thread0_got_posix_signal(int sig_nr, struct siginfo *info);
+static void thread0_sync_init(uth_sync_t *s);
+static void thread0_sync_destroy(uth_sync_t *s);
+static void thread0_sync_enqueue(struct uthread *uth, uth_sync_t *s);
+static struct uthread *thread0_sync_get_next(uth_sync_t *s);
+static bool thread0_sync_get_uth(uth_sync_t *s, struct uthread *uth);
+static void thread0_sync_swap(uth_sync_t *a, uth_sync_t *b);
+static bool thread0_sync_is_empty(uth_sync_t *s);
 
 /* externed into uthread.c */
 struct schedule_ops thread0_2ls_ops = {
+       .sched_init = thread0_sched_init,
        .sched_entry = thread0_sched_entry,
+       .thread_blockon_sysc = thread0_thread_blockon_sysc,
+       .thread_refl_fault = thread0_thread_refl_fault,
+       .thread_runnable = thread0_thread_runnable,
+       .thread_paused = thread0_thread_runnable,
+       .thread_has_blocked = thread0_thread_has_blocked,
+       .thread_exited = thread0_thread_exited,
+       .thread_create = thread0_thread_create,
+       .got_posix_signal = thread0_got_posix_signal,
+       .sync_init = thread0_sync_init,
+       .sync_destroy = thread0_sync_destroy,
+       .sync_enqueue = thread0_sync_enqueue,
+       .sync_get_next = thread0_sync_get_next,
+       .sync_get_uth = thread0_sync_get_uth,
+       .sync_swap = thread0_sync_swap,
+       .sync_is_empty = thread0_sync_is_empty,
 };
 
+struct schedule_ops *sched_ops __attribute__((weak)) = &thread0_2ls_ops;
+
 /* externed into uthread.c */
 struct uthread *thread0_uth;
 
+/* Our thread0 is actually allocated in uthread as just a struct uthread, so we
+ * don't actually attach this mgmt info to it.  But since we just have one
+ * thread, it doesn't matter. */
+struct thread0_info {
+       bool                            is_blocked;
+};
+static struct thread0_info thread0_info;
+static struct event_queue *sysc_evq;
+
+void thread0_handle_syscall(struct event_msg *ev_msg,
+                            unsigned int ev_type, void *data)
+{
+       thread0_info.is_blocked = FALSE;
+}
+
+static void thread0_pre_fork(void)
+{
+}
+
+static void thread0_post_fork(pid_t ret)
+{
+}
+
+void thread0_sched_init(void)
+{
+       int ret;
+
+       ret = posix_memalign((void**)&thread0_uth, __alignof__(struct uthread),
+                            sizeof(struct uthread));
+       assert(!ret);
+       /* aggressively 0 for bugs*/
+       memset(thread0_uth, 0, sizeof(struct uthread));
+       memset(&thread0_info, 0, sizeof(thread0_info));
+       /* we don't care about the message, so don't bother with a UCQ */
+       sysc_evq = get_eventq(EV_MBOX_BITMAP);
+       sysc_evq->ev_flags = EVENT_INDIR | EVENT_WAKEUP;
+       uthread_2ls_init(thread0_uth, thread0_handle_syscall, NULL);
+       pre_fork_2ls = thread0_pre_fork;
+       post_fork_2ls = thread0_post_fork;
+}
+
 /* Thread0 scheduler ops (for processes that haven't linked in a full 2LS) */
 static void thread0_sched_entry(void)
 {
-       if (current_uthread)
+       /* TODO: support signal handling whenever we run a uthread */
+       if (current_uthread) {
+               uthread_prep_pending_signals(current_uthread);
                run_current_uthread();
+               assert(0);
+       }
+       while (1) {
+               if (!thread0_info.is_blocked) {
+                       uthread_prep_pending_signals(thread0_uth);
+                       run_uthread(thread0_uth);
+                       assert(0);
+               }
+               sys_yield(FALSE);
+               handle_events(0);
+       }
+}
+
+static void thread0_thread_blockon_sysc(struct uthread *uthread, void *arg)
+{
+       struct syscall *sysc = (struct syscall*)arg;
+
+       thread0_thread_has_blocked(uthread, 0);
+       if (!register_evq(sysc, sysc_evq))
+               thread0_thread_runnable(uthread);
+}
+
+static void refl_error(struct uthread *uth, unsigned int trap_nr,
+                       unsigned int err, unsigned long aux)
+{
+       printf("Thread has unhandled fault: %d, err: %d, aux: %p\n",
+              trap_nr, err, aux);
+       /* Note that uthread.c already copied out our ctx into the uth
+        * struct */
+       print_user_context(&uth->u_ctx);
+       printf("Turn on printx to spew unhandled, malignant trap info\n");
+       exit(-1);
+}
+
+static bool handle_page_fault(struct uthread *uth, unsigned int err,
+                              unsigned long aux)
+{
+       if (!(err & PF_VMR_BACKED))
+               return FALSE;
+       syscall_async(&uth->local_sysc, SYS_populate_va, aux, 1);
+       __block_uthread_on_async_sysc(uth);
+       return TRUE;
+}
+
+static void thread0_thread_refl_fault(struct uthread *uth,
+                                      struct user_context *ctx)
+{
+       unsigned int trap_nr = __arch_refl_get_nr(ctx);
+       unsigned int err = __arch_refl_get_err(ctx);
+       unsigned long aux = __arch_refl_get_aux(ctx);
+
+       assert(ctx->type == ROS_HW_CTX);
+       switch (trap_nr) {
+       case HW_TRAP_PAGE_FAULT:
+               if (!handle_page_fault(uth, err, aux))
+                       refl_error(uth, trap_nr, err, aux);
+               break;
+       default:
+               refl_error(uth, trap_nr, err, aux);
+       }
+}
+
+static void thread0_thread_runnable(struct uthread *uth)
+{
+       thread0_info.is_blocked = FALSE;
+}
+
+static void thread0_thread_has_blocked(struct uthread *uth, int flags)
+{
+       assert(!thread0_info.is_blocked);
+       thread0_info.is_blocked = TRUE;
+}
+
+/* Actually, a 2LS only needs to implement this if it calls
+ * uth_2ls_thread_exit().  Keep it here to catch bugs. */
+static void thread0_thread_exited(struct uthread *uth)
+{
+       assert(0);
+}
+
+static struct uthread *thread0_thread_create(void *(*func)(void *), void *arg)
+{
+       panic("Thread0 sched asked to create more threads!");
+}
+
+static void thread0_got_posix_signal(int sig_nr, struct siginfo *info)
+{
+       if (current_uthread)
+               trigger_posix_signal(sig_nr, info, get_cur_uth_ctx());
        else
-               run_uthread(thread0_uth);
+               trigger_posix_signal(sig_nr, info, &thread0_uth->u_ctx);
+       /* Legacy single-threaded programs, which often use thread0, expect
+        * signals to interrupt their syscall.  For most 2LSes, we can't match a
+        * process-wide signal to a particular thread; the kernel knows nothing
+        * of threads, we're just receiving an event.  However, thread0 has only
+        * one thread. */
+       if (thread0_uth->sysc)
+               sys_abort_sysc(thread0_uth->sysc);
+}
+
+static void thread0_sync_init(uth_sync_t *s)
+{
+       memset(s, 0x5a, sizeof(uth_sync_t));
+}
+
+static void thread0_sync_destroy(uth_sync_t *s)
+{
+}
+
+static void thread0_sync_enqueue(struct uthread *uth, uth_sync_t *s)
+{
+}
+
+static struct uthread *thread0_sync_get_next(uth_sync_t *s)
+{
+       if (thread0_info.is_blocked) {
+               /* Note we don't clear is_blocked.  Runnable does that, which
+                * should be called before the next get_next (since we have only
+                * one thread). */
+               return thread0_uth;
+       } else {
+               return NULL;
+       }
+}
+
+static bool thread0_sync_get_uth(uth_sync_t *s, struct uthread *uth)
+{
+       assert(uth == thread0_uth);
+       if (thread0_info.is_blocked) {
+               /* Note we don't clear is_blocked.  Runnable does that. */
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static void thread0_sync_swap(uth_sync_t *a, uth_sync_t *b)
+{
+}
+
+static bool thread0_sync_is_empty(uth_sync_t *s)
+{
+       return !thread0_info.is_blocked;
 }