2LS op for handling reflected faults
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 18 Feb 2014 02:00:42 +0000 (18:00 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 18 Feb 2014 03:49:30 +0000 (19:49 -0800)
The pthread one knows how to handle page faults on file-backed virtual
addresses.

I'm using the existing pthread syscall handling, so this sits in
pthread.c for now.  Most other 2LSs will do the same thing.  In the
future, we could have a uthread helper for it, but only if we redo some
of the other syscall stuff.  Due to the ev_q and how we track syscalls,
which is a 2LS decision, most of this will probably stay in the specific
2LS.

user/parlib/include/uthread.h
user/parlib/uthread.c
user/pthread/pthread.c

index 639090a..8bb9155 100644 (file)
@@ -25,8 +25,9 @@ struct uthread {
        struct ancillary_state as;
        void *tls_desc;
        int flags;
-       struct syscall *sysc;   /* syscall we're blocking on, if any */
        int state;
+       struct syscall *sysc;   /* syscall we're blocking on, if any */
+       struct syscall local_sysc;      /* for when we don't want to use the stack */
        void (*yield_func)(struct uthread*, void*);
        void *yield_arg;
        int err_no;
@@ -35,7 +36,7 @@ struct uthread {
 typedef struct uthread uthread_t;
 extern __thread struct uthread *current_uthread;
 
-/* 2L-Scheduler operations.  Can be 0.  Examples in pthread.c. */
+/* 2L-Scheduler operations.  Examples in pthread.c. */
 struct schedule_ops {
        /* Functions supporting thread ops */
        void (*sched_entry)(void);
@@ -43,6 +44,8 @@ struct schedule_ops {
        void (*thread_paused)(struct uthread *);
        void (*thread_blockon_sysc)(struct uthread *, void *);
        void (*thread_has_blocked)(struct uthread *, int);
+       void (*thread_refl_fault)(struct uthread *, unsigned int, unsigned int,
+                                                         unsigned long);
        /* Functions event handling wants */
        void (*preempt_pending)(void);
        void (*spawn_thread)(uintptr_t pc_start, void *data);   /* don't run yet */
index aaa3231..438655e 100644 (file)
@@ -461,16 +461,9 @@ static void set_uthread_tls(struct uthread *uthread, uint32_t vcoreid)
 /* Attempts to handle a fault for uth, etc */
 static void handle_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);
-
-       printf("detected faulted uthread, nr %d, err %08x, aux %p\n", trap_nr, err,
-              aux);
-       if (err & PF_VMR_BACKED)
-               printf("and it's VMR backed\n");
-       /* TODO: call 2LS op, which needs to handle list membership and resched */
-       exit(-1);
+       sched_ops->thread_refl_fault(uth, __arch_refl_get_nr(ctx),
+                                    __arch_refl_get_err(ctx),
+                                    __arch_refl_get_aux(ctx));
 }
 
 /* Run the thread that was current_uthread, from a previous run.  Should be
@@ -498,7 +491,7 @@ void run_current_uthread(void)
                current_uthread = 0;
                uth->u_ctx = vcpd->uthread_ctx;
                save_fp_state(&uth->as);
-               uth->state == UT_NOT_RUNNING;
+               uth->state = UT_NOT_RUNNING;
                uth->flags |= UTHREAD_SAVED | UTHREAD_FPSAVED;
                handle_refl_fault(uth, &vcpd->uthread_ctx);
                /* we abort no matter what.  up to the 2LS to reschedule the thread */
index af378f9..a1eafb2 100644 (file)
@@ -39,6 +39,8 @@ void pth_thread_runnable(struct uthread *uthread);
 void pth_thread_paused(struct uthread *uthread);
 void pth_thread_blockon_sysc(struct uthread *uthread, void *sysc);
 void pth_thread_has_blocked(struct uthread *uthread, int flags);
+void pth_thread_refl_fault(struct uthread *uthread, unsigned int trap_nr,
+                           unsigned int err, unsigned long aux);
 void pth_preempt_pending(void);
 void pth_spawn_thread(uintptr_t pc_start, void *data);
 
@@ -51,6 +53,7 @@ struct schedule_ops pthread_sched_ops = {
        pth_thread_paused,
        pth_thread_blockon_sysc,
        pth_thread_has_blocked,
+       pth_thread_refl_fault,
        0, /* pth_preempt_pending, */
        0, /* pth_spawn_thread, */
 };
@@ -223,7 +226,6 @@ void pth_thread_blockon_sysc(struct uthread *uthread, void *syscall)
 {
        struct syscall *sysc = (struct syscall*)syscall;
        int old_flags;
-       bool need_to_restart = FALSE;
        uint32_t vcoreid = vcore_id();
        /* rip from the active queue */
        struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
@@ -256,6 +258,44 @@ void pth_thread_has_blocked(struct uthread *uthread, int flags)
                printf("For great justice!\n");
 }
 
+void pth_thread_refl_fault(struct uthread *uthread, unsigned int trap_nr,
+                           unsigned int err, unsigned long aux)
+{
+       struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
+       pthread->state = PTH_BLK_SYSC;
+       mcs_pdr_lock(&queue_lock);
+       threads_active--;
+       TAILQ_REMOVE(&active_queue, pthread, next);
+       mcs_pdr_unlock(&queue_lock);
+
+       if (trap_nr != 14) {
+               printf("Pthread has unhandled fault\n");
+               print_user_context(&uthread->u_ctx);
+               exit(-1);
+       }
+
+       if (!(err & PF_VMR_BACKED)) {
+               /* TODO: put your SIGSEGV handling here */
+               printf("Pthread page faulted outside a VMR\n");
+               print_user_context(&uthread->u_ctx);
+               exit(-1);
+       }
+       /* stitching for the event handler.  sysc -> uth, uth -> sysc */
+       uthread->local_sysc.u_data = uthread;
+       uthread->sysc = &uthread->local_sysc;
+       pthread->state = PTH_BLK_SYSC;
+       /* one downside is that we'll never check the return val of the syscall.  if
+        * we errored out, we wouldn't know til we PF'd again, and inspected the old
+        * retval/err and other sysc fields (make sure the PF is on the same addr,
+        * etc).  could run into this issue on truncated files too. */
+       syscall_async(&uthread->local_sysc, SYS_populate_va, aux, 1);
+       if (!register_evq(&uthread->local_sysc, sysc_mgmt[vcore_id()].ev_q)) {
+               /* Lost the race with the call being done.  The kernel won't send the
+                * event.  Just restart him. */
+               restart_thread(&uthread->local_sysc);
+       }
+}
+
 void pth_preempt_pending(void)
 {
 }