Posix signals in uthread context not vcore context
authorKevin Klues <klueska@cs.berkeley.edu>
Fri, 22 Aug 2014 18:12:45 +0000 (11:12 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Fri, 22 Aug 2014 18:12:45 +0000 (11:12 -0700)
This patch set makes is so that posix signals are run from uthread
context instead of vcore context when triggered from the pthread
library.

Whenever a pthread has a signal pending and it is about to be restarted,
a temprary signal context and stack is swapped in to run the signal
handlers for those pending signals, and the pthread is restored into
that context.  When the signal handlers have compelted, the original
context and stack are restored, and the pthread is rsumed.

A new 'sigdata' struct has been introduced to encapsulate the data
structures necessary to handle all this swapping back and forth.  These
'sigdata' structs are served as part of the parlib signal library, and
reused across different pthreads whenever they have a signal handler to
run. The rest of the changes live entirely in the pthread library,
though we may be able to move parts of it out to parlib once we
generalize it a bit more.

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

index abfc4d4..397b67f 100644 (file)
@@ -60,8 +60,18 @@ int         sys_abort_sysc_fd(int fd);
 
 long           syscall_async(struct syscall *sysc, unsigned long num, ...);
 
-void           init_posix_signals(void);       /* in signal.c */
-void           trigger_posix_signal(int sig_nr, struct siginfo *info, void *aux);
+/* Posix signal related stuff */
+struct sigdata {
+       struct user_context u_ctx;
+       struct ancillary_state as;
+       struct siginfo info;
+       void *stack;
+};
+void init_posix_signals(void);
+struct sigdata *alloc_sigdata();
+void free_sigdata(struct sigdata *sigdata);
+void trigger_posix_signal(int sig_nr, struct siginfo *info, void *aux);
+
 #ifdef __cplusplus
 }
 #endif
index 979427c..dacc1c3 100644 (file)
 #include <signal.h>
 #include <stdio.h>
 
+#include <parlib.h>
 #include <event.h>
 #include <errno.h>
 #include <assert.h>
 #include <ros/procinfo.h>
 #include <ros/syscall.h>
+#include <sys/mman.h>
 #include <vcore.h> /* for print_user_context() */
+#include <waitfreelist.h>
 
 /* This is list of sigactions associated with each posix signal. */
 static struct sigaction sigactions[_NSIG - 1];
 
+/* This is a wait-free-list used to hold the data necessary to execute signal
+ * handlers inside a 2LS. We are able to store them in a wfl because all
+ * sigdata structs are created equal, and reuse is encouraged as uthreads
+ * ask for them on demand. */
+static struct wfl sigdata_list;
+#define SIGNAL_STACK_SIZE (2*PGSIZE + sizeof(struct sigdata))
+
 /* These are the default handlers for each posix signal.  They are listed in
  * SIGNAL(7) of the Linux Programmer's Manual.  We run them as default
  * sigactions, instead of the older handlers, so that we have access to the
@@ -116,6 +126,31 @@ static __sigacthandler_t default_handlers[] = {
        [SIGSYS]    = default_core_handler
 };
 
+/* This function allocates a sigdata struct for use when running signal
+ * handlers inside a 2LS. The sigdata struct returned is pre-initialized with
+ * the 'stack' field pointing to a valid stack.  Space is allocated for both
+ * the sigdata struct and the stack in a single mmap call.  The sigdata struct
+ * just sits at the bottom of the stack, and its 'stack' field points just
+ * above it.  */
+struct sigdata *alloc_sigdata()
+{
+       struct sigdata *data = wfl_remove(&sigdata_list);
+       if (data == NULL) {
+               void *stack = mmap(0, SIGNAL_STACK_SIZE,
+                                  PROT_READ|PROT_WRITE|PROT_EXEC,
+                                  MAP_POPULATE|MAP_ANONYMOUS, -1, 0);
+               assert(stack != MAP_FAILED);
+               data = stack + SIGNAL_STACK_SIZE - sizeof(struct sigdata);
+               data->stack = data;
+       }
+       return data;
+}
+
+/* This function frees a previously allocated sigdata struct. */
+void free_sigdata(struct sigdata *sigdata)
+{
+       wfl_insert(&sigdata_list, sigdata);
+}
 
 /* This is the akaros posix signal trigger.  Signals are dispatched from
  * this function to their proper posix signal handler */
@@ -183,6 +218,7 @@ void init_posix_signals(void)
        assert(posix_sig_ev_q);
        posix_sig_ev_q->ev_flags = EVENT_IPI | EVENT_INDIR | EVENT_FALLBACK;
        register_kevent_q(posix_sig_ev_q, EV_POSIX_SIGNAL);
+       wfl_init(&sigdata_list);
 }
 
 int sigaddset(sigset_t *__set, int __signo)
index aca465c..3fac38d 100644 (file)
@@ -68,40 +68,125 @@ struct schedule_ops *sched_ops = &pthread_sched_ops;
 /* Static helpers */
 static void __pthread_free_stack(struct pthread_tcb *pt);
 static int __pthread_allocate_stack(struct pthread_tcb *pt);
+static void __pth_yield_cb(struct uthread *uthread, void *junk);
+
+/* Swap the contents of two user contexts (not just their pointers). */
+static void swap_user_contexts(struct user_context *c1, struct user_context *c2)
+{
+       struct user_context temp_ctx;
+       temp_ctx = *c1;
+       *c1 = *c2;
+       *c2 = temp_ctx;
+}
+
+/* Prep a pthread to run a signal handler.  The original context of the pthread
+ * is saved, and a new context with a new stack is set up to run the signal
+ * handler the next time the pthread is run. */
+static void __pthread_prep_sighandler(struct pthread_tcb *pthread,
+                                      void (*entry)(void),
+                                      struct siginfo *info)
+{
+       pthread->sigdata = alloc_sigdata();
+       if (info != NULL)
+               pthread->sigdata->info = *info;
+       init_user_ctx(&pthread->sigdata->u_ctx,
+                     (uintptr_t)entry,
+                     (uintptr_t)pthread->sigdata->stack);
+       if (!(pthread->uthread.flags & UTHREAD_SAVED)) {
+               assert(current_uthread == &pthread->uthread);
+               current_uthread = NULL;
+               pthread->uthread.u_ctx = vcpd_of(vcore_id())->uthread_ctx;
+               save_fp_state(&pthread->sigdata->as);
+               pthread->uthread.state = UT_NOT_RUNNING;
+       } else if (pthread->uthread.flags & UTHREAD_FPSAVED) {
+               pthread->sigdata->as = pthread->uthread.as;
+       }
+       pthread->uthread.flags |= UTHREAD_SAVED;
+       pthread->uthread.flags &= ~UTHREAD_FPSAVED;
+       swap_user_contexts(&pthread->uthread.u_ctx, &pthread->sigdata->u_ctx);
+}
 
-/* Trigger a posix signal on a pthread from vcore context */
-static void __pthread_trigger_posix_signal(pthread_t pthread, int signo,
-                                           struct siginfo *info)
+/* Restore the context saved as the result of running a signal handler on a
+ * pthread. This context will execute the next time the pthread is run. */
+static void __pthread_restore_after_sighandler(struct pthread_tcb *pthread)
 {
-       int vcoreid = vcore_id();
-       struct user_context *ctx;
-       struct uthread *uthread = (struct uthread*)pthread;
+       pthread->uthread.u_ctx = pthread->sigdata->u_ctx;
+       pthread->uthread.flags |= UTHREAD_SAVED;
+       if (pthread->uthread.u_ctx.type == ROS_HW_CTX) {
+               pthread->uthread.as = pthread->sigdata->as;
+               pthread->uthread.flags |= UTHREAD_FPSAVED;
+       }
+       free_sigdata(pthread->sigdata);
+       pthread->sigdata = NULL;
+}
 
-       if (uthread->flags & UTHREAD_SAVED) {
-               ctx = &uthread->u_ctx;
-       } else {
-               struct preempt_data *vcpd = vcpd_of(vcoreid);
-               assert(current_uthread == uthread);
-               ctx = &vcpd->uthread_ctx;
+/* Callback when yielding a pthread after upon completion of a sighandler.  We
+ * didn't save the current context on yeild, but that's ok because here we
+ * restore the original saved context of the pthread and then treat this like a
+ * normal voluntary yield. */
+static void __exit_sighandler_cb(struct uthread *uthread, void *junk)
+{
+       struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
+       __pthread_restore_after_sighandler(pthread);
+       __pth_yield_cb(uthread, 0);
+}
+
+/* Run a specific sighandler from the top of the sigdata stack. The 'info'
+ * struct is prepopulated before the call is triggered as the result of a
+ * reflected fault. */
+static void __run_sighandler()
+{
+       struct pthread_tcb *me = pthread_self();
+       __sigdelset(&me->sigpending, me->sigdata->info.si_signo);
+       trigger_posix_signal(me->sigdata->info.si_signo,
+                            &me->sigdata->info,
+                            &me->sigdata->u_ctx);
+       uthread_yield(FALSE, __exit_sighandler_cb, 0);
+}
+
+/* Run through all pending sighandlers and trigger them with a NULL info field.
+ * These handlers are triggered as the result of a pthread_kill(), and thus
+ * don't require individual 'info' structs. */
+static void __run_pending_sighandlers()
+{
+       struct pthread_tcb *me = pthread_self();
+       sigset_t andset = me->sigpending & (~me->sigmask);
+       for (int i = 1; i < _NSIG; i++) {
+               if (__sigismember(&andset, i)) {
+                       __sigdelset(&me->sigpending, i);
+                       trigger_posix_signal(i, NULL, &me->sigdata->u_ctx);
+               }
        }
+       uthread_yield(FALSE, __exit_sighandler_cb, 0);
+}
 
-       void *temp_tls_desc = get_tls_desc(vcoreid);
-       set_tls_desc(uthread->tls_desc, vcoreid);
-       trigger_posix_signal(signo, info, ctx);
-       set_tls_desc(temp_tls_desc, vcoreid);
+/* If the given signal is unmasked, prep the pthread to run it's signal
+ * handler, but don't run it yet. In either case, make the pthread runnable
+ * again. Once the signal handler is complete, the original context will be
+ * restored and restarted. */
+static void __pthread_signal_and_restart(struct pthread_tcb *pthread,
+                                          int signo, int code, void *addr)
+{
+       if (!__sigismember(&pthread->sigmask, signo)) {
+               struct siginfo info = {0};
+               info.si_signo = signo;
+               info.si_code = code;
+               info.si_addr = addr;
+               __pthread_prep_sighandler(pthread, __run_sighandler, &info);
+       }
+       pth_thread_runnable(&pthread->uthread);
 }
 
-static void __pthread_trigger_pending_posix_signals(pthread_t thread)
+/* If there are any pending signals, prep the pthread to run it's signal
+ * handler and then run it. Once the signal handler is complete, the original
+ * context will be restored and restarted. */
+static void __pthread_handle_pending_posix_signals(pthread_t pthread)
 {
-       if (thread->sigpending) {
-               sigset_t andset = thread->sigpending & (~thread->sigmask);
+       if (!pthread->sigdata && pthread->sigpending) {
+               sigset_t andset = pthread->sigpending & (~pthread->sigmask);
                if (!__sigisemptyset(&andset)) {
-                       for (int i = 1; i < _NSIG; i++) {
-                               if (__sigismember(&andset, i)) {
-                                       __sigdelset(&thread->sigpending, i);
-                                       __pthread_trigger_posix_signal(thread, i, NULL);
-                               }
-                       }
+                       __pthread_prep_sighandler(pthread, __run_pending_sighandlers, NULL);
+                       run_uthread(&pthread->uthread);
                }
        }
 }
@@ -114,7 +199,7 @@ void __attribute__((noreturn)) pth_sched_entry(void)
        uint32_t vcoreid = vcore_id();
        if (current_uthread) {
                /* Run any pending posix signal handlers registered via pthread_kill */
-               __pthread_trigger_pending_posix_signals((pthread_t)current_uthread);
+               __pthread_handle_pending_posix_signals((pthread_t)current_uthread);
                run_current_uthread();
                /* Run the thread itself */
                assert(0);
@@ -153,7 +238,7 @@ void __attribute__((noreturn)) pth_sched_entry(void)
        } while (1);
        assert(new_thread->state == PTH_RUNNABLE);
        /* Run any pending posix signal handlers registered via pthread_kill */
-       __pthread_trigger_pending_posix_signals(new_thread);
+       __pthread_handle_pending_posix_signals(new_thread);
        /* Run the thread itself */
        run_uthread((struct uthread*)new_thread);
        assert(0);
@@ -306,31 +391,18 @@ void pth_thread_has_blocked(struct uthread *uthread, int flags)
                printf("For great justice!\n");
 }
 
-static void signal_and_make_runnable(struct pthread_tcb *pthread, int signo,
-                                     int code, void *addr)
-{
-       if (!__sigismember(&pthread->sigmask,  signo)) {
-               struct siginfo info = {0};
-               info.si_signo =  signo;
-               info.si_code = code;
-               info.si_addr = addr;
-               __pthread_trigger_posix_signal(pthread, signo, &info);
-       }
-       pth_thread_runnable((struct uthread*)pthread);
-}
-
 static void handle_div_by_zero(struct uthread *uthread, unsigned int err,
                                unsigned long aux)
 {
        struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
-       signal_and_make_runnable(pthread, SIGFPE, FPE_INTDIV, (void*)aux);
+       __pthread_signal_and_restart(pthread, SIGFPE, FPE_INTDIV, (void*)aux);
 }
 
 static void handle_gp_fault(struct uthread *uthread, unsigned int err,
                             unsigned long aux)
 {
        struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
-       signal_and_make_runnable(pthread, SIGSEGV, SEGV_ACCERR, (void*)aux);
+       __pthread_signal_and_restart(pthread, SIGSEGV, SEGV_ACCERR, (void*)aux);
 }
 
 static void handle_page_fault(struct uthread *uthread, unsigned int err,
@@ -338,7 +410,7 @@ static void handle_page_fault(struct uthread *uthread, unsigned int err,
 {
        struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
        if (!(err & PF_VMR_BACKED)) {
-               signal_and_make_runnable(pthread, SIGSEGV, SEGV_MAPERR, (void*)aux);
+               __pthread_signal_and_restart(pthread, SIGSEGV, SEGV_MAPERR, (void*)aux);
        } else {
                /* stitching for the event handler.  sysc -> uth, uth -> sysc */
                uthread->local_sysc.u_data = uthread;
@@ -573,6 +645,7 @@ int __pthread_create(pthread_t *thread, const pthread_attr_t *attr,
        pthread->joiner = 0;
        pthread->sigmask = ((pthread_t)current_uthread)->sigmask;
        __sigemptyset(&pthread->sigpending);
+       pthread->sigdata = NULL;
        /* Respect the attributes */
        if (attr) {
                if (attr->stacksize)                                    /* don't set a 0 stacksize */
index 89a88b7..2613cac 100644 (file)
@@ -41,6 +41,7 @@ struct pthread_tcb {
        void *retval;
        sigset_t sigmask;
        sigset_t sigpending;
+       struct sigdata *sigdata;
 };
 typedef struct pthread_tcb* pthread_t;
 TAILQ_HEAD(pthread_queue, pthread_tcb);