Pulled code specific to all 2LS out of pthread.c
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 3 Mar 2011 01:06:36 +0000 (17:06 -0800)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:35:59 +0000 (17:35 -0700)
In general, I tried to keep things the same as before.  Things got
moved, but there should be very little new behavior.  For instance,
uthread_yield is the old pthread_yield, but with a call out to the 2LS
to do its thing.  Its thing is the same pthread-specific thing as
before.

The test programs that had a vcore_entry() needed to be changed.  Don't
trust any of them, though mhello and syscall still work well.  The
others are so ancient it isn't worth keeping them up, esp since they do
stuff they shouldn't.

c3po doesn't build, mostly due to issues with current_thread.  That
specific __thread belongs to the vcore.  c3po can either use vcores,
like the pthread code does or have another that it uses.  I didn't want
to mess with this too much, so you'll get compile errors.

13 files changed:
tests/eth_audio.c
tests/manycore_test.c
tests/mhello.c
tests/mproctests.c
tests/msr_cycling_vcores.c
tests/msr_dumb_while.c
tests/msr_get_cores.c
tests/msr_get_singlecore.c
tests/syscall.c
user/parlib/include/vcore.h
user/parlib/vcore.c
user/pthread/pthread.c
user/pthread/pthread.h

index 25ac8a1..266ac9c 100644 (file)
 void *core0_tls = 0;
 void *in_buf, *out_buf;
 
+void ghetto_vcore_entry(void);
+struct schedule_ops ghetto_sched_ops = {
+       0, /* init, */
+       ghetto_vcore_entry,
+       0, /* thread_create, */
+       0, /* thread_runnable, */
+       0, /* thread_yield, */
+       0, /* thread_exit, */
+       0, /* preempt_pending, */
+       0, /* spawn_thread, */
+};
+struct schedule_ops *sched_ops = &ghetto_sched_ops;
 /* Test program for the audio device.  mmap()s the stuff, sets up a notif
  * handler, and switches to multi_mode.
  *
@@ -93,11 +105,12 @@ void process_packet(void)
        //printf("contents of out_buf %s\n", out_buf);
 }
 
-void vcore_entry(void)
+void ghetto_vcore_entry(void)
 {
        uint32_t vcoreid = vcore_id();
        static bool first_time = TRUE;
 
+       printf("GIANT WARNING: this is ancient shit\n");
 /* begin: stuff userspace needs to do to handle events/notifications */
 
        struct vcore *vc = &__procinfo.vcoremap[vcoreid];
index b286e41..5ee3c8a 100644 (file)
@@ -6,6 +6,19 @@
 
 mcs_barrier_t b;
 
+void ghetto_vcore_entry(void);
+struct schedule_ops ghetto_sched_ops = {
+       0, /* init, */
+       ghetto_vcore_entry,
+       0, /* thread_create, */
+       0, /* thread_runnable, */
+       0, /* thread_yield, */
+       0, /* thread_exit, */
+       0, /* preempt_pending, */
+       0, /* spawn_thread, */
+};
+struct schedule_ops *sched_ops = &ghetto_sched_ops;
+
 void do_work_son(int vcoreid)
 {
        int cpuid = sys_getcpuid();
@@ -14,7 +27,7 @@ void do_work_son(int vcoreid)
        mcs_barrier_wait(&b,vcoreid);
 }
 
-void vcore_entry()
+void ghetto_vcore_entry()
 {
        assert(vcore_id() > 0);
        do_work_son(vcore_id());
index fbe1ed5..3c9b5dc 100644 (file)
@@ -25,6 +25,24 @@ struct event_queue *indirect_q;
 static void handle_generic(struct event_msg *ev_msg, unsigned int ev_type,
                            bool overflow);
 
+void ghetto_vcore_entry(void);
+struct uthread *ghetto_init(void)
+{
+       return malloc(sizeof(struct uthread));
+}
+
+struct schedule_ops ghetto_sched_ops = {
+       ghetto_init,
+       ghetto_vcore_entry,
+       0, /* thread_create, */
+       0, /* thread_runnable, */
+       0, /* thread_yield, */
+       0, /* thread_exit, */
+       0, /* preempt_pending, */
+       0, /* spawn_thread, */
+};
+struct schedule_ops *sched_ops = &ghetto_sched_ops;
+
 int main(int argc, char** argv)
 {
        uint32_t vcoreid;
@@ -126,7 +144,7 @@ static void handle_generic(struct event_msg *ev_msg, unsigned int ev_type,
               ev_type, vcore_id(), overflow ? "" : "out");
 }
 
-void vcore_entry(void)
+void ghetto_vcore_entry(void)
 {
        uint32_t vcoreid = vcore_id();
        static bool first_time = TRUE;
@@ -135,39 +153,10 @@ void vcore_entry(void)
        /* vcore_context test (don't need to do this anywhere) */
        assert(in_vcore_context());
 
-/* begin: stuff userspace needs to do to handle notifications */
-
-       struct preempt_data *vcpd;
-       vcpd = &__procdata.vcore_preempt_data[vcoreid];
-
-       /* checks if a preempt is pending, yields if so */
-       check_preempt_pending(vcoreid);
-
-       /* here is how you receive an event */
-       handle_events(vcoreid);
-
-       /* Lets try to restart vcore0's context.  Note this doesn't do anything to
-        * set the appropriate TLS.  On x86, this will involve changing the LDT
-        * entry for this vcore to point to the TCB of the new user-thread. */
-       if (vcoreid == 0) {
-               /* // test for preempting a notif_handler.  do it from the monitor
-               int ctr = 0;
-               while(ctr < 3) {
-                       printf("Vcore %d Spinning (%d), temp = %08x!\n", vcoreid, ctr++, temp);
-                       udelay(5000000);
-               } */
-               printf("restarting vcore0 from userspace\n");
-               clear_notif_pending(vcoreid);
-               /* // testing for missing a notif
-               if (first_time) {
-                       first_time = FALSE;
-                       printf("setting pending, trying to renotify etc\n");
-                       vcpd->notif_pending = 1;
-               } */
-               set_tls_desc(core0_tls, 0);
-               /* Load silly state (Floating point) too */
-               pop_ros_tf(&vcpd->notif_tf, vcoreid);
-               panic("should never see me!");
+       /* old logic was moved to parlib code */
+       if (current_thread) {
+               assert(vcoreid == 0);
+               run_current_uthread();
        }
        /* unmask notifications once you can let go of the notif_tf and it is okay
         * to clobber the transition stack.
index b3c94d7..a8e5e24 100644 (file)
@@ -27,6 +27,19 @@ int test = TEST_SWITCH_TO_RUNNABLE_S;
 
 static void global_tests(uint32_t vcoreid);
 
+void ghetto_vcore_entry(void);
+struct schedule_ops ghetto_sched_ops = {
+       0, /* init, */
+       ghetto_vcore_entry,
+       0, /* thread_create, */
+       0, /* thread_runnable, */
+       0, /* thread_yield, */
+       0, /* thread_exit, */
+       0, /* preempt_pending, */
+       0, /* spawn_thread, */
+};
+struct schedule_ops *sched_ops = &ghetto_sched_ops;
+
 int main(int argc, char** argv)
 {
        uint32_t vcoreid;
@@ -98,7 +111,7 @@ int main(int argc, char** argv)
        return 0;
 }
 
-void vcore_entry(void)
+void ghetto_vcore_entry(void)
 {
        uint32_t vcoreid;
        static int first_time = 1; // used by vcore2
index 40a71d4..5f5bd9e 100644 (file)
 mcs_barrier_t b;
 uint64_t begin = 0, end = 0;
 
+void ghetto_vcore_entry(void);
+struct schedule_ops ghetto_sched_ops = {
+       0, /* init, */
+       ghetto_vcore_entry,
+       0, /* thread_create, */
+       0, /* thread_runnable, */
+       0, /* thread_yield, */
+       0, /* thread_exit, */
+       0, /* preempt_pending, */
+       0, /* spawn_thread, */
+};
+struct schedule_ops *sched_ops = &ghetto_sched_ops;
+
 int main(int argc, char** argv)
 {
 
@@ -37,7 +50,7 @@ int main(int argc, char** argv)
        return -1;
 }
 
-void vcore_entry(void)
+void ghetto_vcore_entry(void)
 {
        uint32_t vcoreid = vcore_id();
 
index aff755c..b05706f 100644 (file)
@@ -8,6 +8,19 @@
 #include <arch/arch.h>
 #include <event.h>
 
+void ghetto_vcore_entry(void);
+struct schedule_ops ghetto_sched_ops = {
+       0, /* init, */
+       ghetto_vcore_entry,
+       0, /* thread_create, */
+       0, /* thread_runnable, */
+       0, /* thread_yield, */
+       0, /* thread_exit, */
+       0, /* preempt_pending, */
+       0, /* spawn_thread, */
+};
+struct schedule_ops *sched_ops = &ghetto_sched_ops;
+
 int main(int argc, char** argv)
 {
 
@@ -25,7 +38,7 @@ int main(int argc, char** argv)
        return -1;
 }
 
-void vcore_entry(void)
+void ghetto_vcore_entry(void)
 {
        struct preempt_data *vcpd = &__procdata.vcore_preempt_data[0];
        vcpd->notif_enabled = TRUE;
index bf6c499..f05df88 100644 (file)
@@ -32,6 +32,19 @@ mcs_barrier_t b;
 void *core0_tls = 0;
 uint64_t begin = 0, end = 0;
 
+void ghetto_vcore_entry(void);
+struct schedule_ops ghetto_sched_ops = {
+       0, /* init, */
+       ghetto_vcore_entry,
+       0, /* thread_create, */
+       0, /* thread_runnable, */
+       0, /* thread_yield, */
+       0, /* thread_exit, */
+       0, /* preempt_pending, */
+       0, /* spawn_thread, */
+};
+struct schedule_ops *sched_ops = &ghetto_sched_ops;
+
 int main(int argc, char** argv)
 {
        uint32_t vcoreid = vcore_id();
@@ -88,7 +101,7 @@ int main(int argc, char** argv)
        return 0;
 }
 
-void vcore_entry(void)
+void ghetto_vcore_entry(void)
 {
        uint32_t vcoreid = vcore_id();
 
index 2023ed8..3bfba72 100644 (file)
@@ -25,6 +25,19 @@ void *core0_tls = 0;
 uint64_t begin = 0, end = 0;
 volatile bool core1_up = FALSE;
 
+void ghetto_vcore_entry(void);
+struct schedule_ops ghetto_sched_ops = {
+       0, /* init, */
+       ghetto_vcore_entry,
+       0, /* thread_create, */
+       0, /* thread_runnable, */
+       0, /* thread_yield, */
+       0, /* thread_exit, */
+       0, /* preempt_pending, */
+       0, /* spawn_thread, */
+};
+struct schedule_ops *sched_ops = &ghetto_sched_ops;
+
 int main(int argc, char** argv)
 {
        uint32_t vcoreid = vcore_id();
@@ -89,7 +102,7 @@ int main(int argc, char** argv)
        return 0;
 }
 
-void vcore_entry(void)
+void ghetto_vcore_entry(void)
 {
        uint32_t vcoreid = vcore_id();
 
index 6a12009..4c98f5d 100644 (file)
@@ -12,6 +12,24 @@ struct syscall sysc = {0};
 struct event_queue *ev_q;
 void *core0_tls = 0;
 
+void ghetto_vcore_entry(void);
+struct uthread *ghetto_init(void)
+{
+       return malloc(sizeof(struct uthread));
+}
+
+struct schedule_ops ghetto_sched_ops = {
+       ghetto_init,
+       ghetto_vcore_entry,
+       0, /* thread_create, */
+       0, /* thread_runnable, */
+       0, /* thread_yield, */
+       0, /* thread_exit, */
+       0, /* preempt_pending, */
+       0, /* spawn_thread, */
+};
+struct schedule_ops *sched_ops = &ghetto_sched_ops;
+
 int main(int argc, char** argv)
 {
        int num_started, retval;
@@ -102,35 +120,24 @@ static void handle_syscall(struct event_msg *ev_msg, unsigned int ev_type,
        my_sysc->u_data = 0;
 }
 
-void vcore_entry(void)
+void ghetto_vcore_entry(void)
 {
        uint32_t vcoreid = vcore_id();
        static bool first_time = TRUE;
 
 /* begin: stuff userspace needs to do to handle notifications */
 
-       struct preempt_data *vcpd;
-       vcpd = &__procdata.vcore_preempt_data[vcoreid];
-       
-       /* checks if a preempt is pending, yields if so */
-       check_preempt_pending(vcoreid);
-               
-       /* here is how you receive an event (remember to register the syscall
-        * handler, and whatever other handlers you want). */
-       handle_events(vcoreid);
-
        /* Restart vcore0's context. */
        if (vcoreid == 0) {
-               clear_notif_pending(vcoreid);
-               set_tls_desc(core0_tls, 0);
-               /* Load silly state (Floating point) too */
-               pop_ros_tf(&vcpd->notif_tf, vcoreid);
+               run_current_uthread();
                panic("should never see me!");
        }       
        /* unmask notifications once you can let go of the notif_tf and it is okay
         * to clobber the transition stack.
         * Check Documentation/processes.txt: 4.2.4.  In real code, you should be
         * popping the tf of whatever user process you want (get off the x-stack) */
+       struct preempt_data *vcpd;
+       vcpd = &__procdata.vcore_preempt_data[vcoreid];
        vcpd->notif_enabled = TRUE;
        
 /* end: stuff userspace needs to do to handle notifications */
index c34d235..97da920 100644 (file)
@@ -10,7 +10,7 @@ extern "C" {
 #include <string.h>
 
 /*****************************************************************************/
-/* TODO: This is a complete hack, but necessary for vcore stuff toork for now
+/* TODO: This is a complete hack, but necessary for vcore stuff to work for now
  * The issue is that exit sometimes calls sys_yield(), and we can't recover from
  * that properly under our vcore model (we shouldn't though).  We really need to
  * rethink what sys_yield 'should' do when in multicore mode, or else come up 
@@ -27,13 +27,6 @@ extern "C" {
 #define TRANSITION_STACK_PAGES 2
 #define TRANSITION_STACK_SIZE (TRANSITION_STACK_PAGES*PGSIZE)
 
-/* 2L-Scheduler operations.  Can be 0. */
-struct schedule_ops {
-       void (*preempt_pending)(void);
-       void (*spawn_thread)(uintptr_t pc_start, void *data);   /* don't run yet */
-};
-extern struct schedule_ops *sched_ops;
-
 /* Defined by glibc; Must be implemented by a user level threading library */
 extern void vcore_entry();
 /* Declared in glibc's start.c */
@@ -55,7 +48,56 @@ void vcore_yield(void);
 bool check_preempt_pending(uint32_t vcoreid);
 void clear_notif_pending(uint32_t vcoreid);
 
-/* Inlines */
+/* Bare necessities of a user thread.  2LSs should allocate a bigger struct and
+ * cast their threads to uthreads when talking with vcore code.  Vcore/default
+ * 2LS code won't touch udata or beyond. */
+struct uthread {
+       struct user_trapframe utf;
+       struct ancillary_state as;
+       void *tls_desc;
+       /* these four could be put in the 2LSs, but since their main use is by
+        * init_user_tf, which mucks with utf, we'll keep it in vcore code for now*/
+       void *(*start_routine)(void*);
+       void *arg;
+       void *stacktop;
+       void *retval;   /* tied to join, which isn't as clean as i'd like */
+       /* whether or not the scheduler can migrate you from your vcore */
+       bool dont_migrate;
+};
+extern __thread struct uthread *current_thread;
+
+/* 2L-Scheduler operations.  Can be 0.  Examples in pthread.c. */
+struct schedule_ops {
+       /* Functions supporting thread ops */
+       struct uthread *(*sched_init)(void);
+       void (*sched_entry)(void);
+       struct uthread *(*thread_create)(void *);
+       void (*thread_runnable)(struct uthread *);
+       void (*thread_yield)(struct uthread *);
+       void (*thread_exit)(struct uthread *);
+       /* Functions event handling wants */
+       void (*preempt_pending)(void);
+       void (*spawn_thread)(uintptr_t pc_start, void *data);   /* don't run yet */
+};
+extern struct schedule_ops *sched_ops;
+
+/* Functions to make/manage uthreads.  Can be called by functions such as
+ * pthread_create(), which can wrap these with their own stuff (like attrs,
+ * retvals, etc). */
+
+/* Creates a uthread.  Will pass udata to sched_ops's thread_create.  For now,
+ * the vcore/default 2ls code handles start routines and args.  Mostly because
+ * this is used when initing a utf, which is vcore specific for now. */
+struct uthread *uthread_create(void *(*start_routine)(void *), void *arg,
+                               void *udata);
+void uthread_yield(void);
+void uthread_exit(void *retval);
+
+/* Helpers, which sched_entry() can call */
+void run_current_uthread(void);
+void run_uthread(struct uthread *uthread);
+
+/* Static inlines */
 static inline size_t max_vcores(void)
 {
        return MIN(__procinfo.max_vcores, MAX_VCORES);
index 7e02b6c..a73477c 100644 (file)
@@ -18,12 +18,14 @@ static size_t _max_vcores_ever_wanted = 1;
 static mcs_lock_t _vcore_lock = MCS_LOCK_INIT;
 
 /* Which operations we'll call for the 2LS.  Will change a bit with Lithe.  For
- * now, there are no defaults. */
+ * now, there are no defaults.  2LSs can override sched_ops. */
 struct schedule_ops default_2ls_ops = {0};
-struct schedule_ops *sched_ops = &default_2ls_ops;
+struct schedule_ops *sched_ops __attribute__((weak)) = &default_2ls_ops;
 
 extern void** vcore_thread_control_blocks;
 
+__thread struct uthread *current_thread = 0;
+
 /* Get a TLS, returns 0 on failure.  Vcores have their own TLS, and any thread
  * created by a user-level scheduler needs to create a TLS as well. */
 void *allocate_tls(void)
@@ -111,6 +113,33 @@ int vcore_init()
        if(allocate_transition_stack(0) || allocate_transition_tls(0))
                goto vcore_init_tls_fail;
 
+       assert(!in_vcore_context());
+
+       /* Bug if vcore init was called with no 2LS */
+       assert(sched_ops->sched_init);
+       /* Get thread 0's thread struct (2LS allocs it) */
+       struct uthread *uthread = sched_ops->sched_init();
+       
+       /* Save a pointer to thread0's tls region (the glibc one) into its tcb */
+       uthread->tls_desc = get_tls_desc(0);
+       /* Save a pointer to the uthread in its own TLS */
+       current_thread = uthread;
+
+       /* Change temporarily to vcore0s tls region so we can save the newly created
+        * tcb into its current_thread variable and then restore it.  One minor
+        * issue is that vcore0's transition-TLS isn't TLS_INITed yet.  Until it is
+        * (right before vcore_entry(), don't try and take the address of any of
+        * its TLS vars. */
+       extern void** vcore_thread_control_blocks;
+       set_tls_desc(vcore_thread_control_blocks[0], 0);
+       current_thread = uthread;
+       set_tls_desc(uthread->tls_desc, 0);
+       assert(!in_vcore_context());
+
+       /* don't forget to enable notifs on vcore0.  if you don't, the kernel will
+        * restart your _S with notifs disabled, which is a path to confusion. */
+       enable_notifs(0);
+
        initialized = 1;
        return 0;
 
@@ -164,7 +193,6 @@ void vcore_yield()
        sys_yield(0);
 }
 
-
 /* Deals with a pending preemption (checks, responds).  If the 2LS registered a
  * function, it will get run.  Returns true if you got preempted.  Called
  * 'check' instead of 'handle', since this isn't an event handler.  It's the "Oh
@@ -197,3 +225,291 @@ void clear_notif_pending(uint32_t vcoreid)
                __procdata.vcore_preempt_data[vcoreid].notif_pending = 0;
        } while (handle_events(vcoreid));
 }
+
+/****************** uthread *******************/
+/* static helpers: */
+static int __uthread_allocate_tls(struct uthread *uthread);
+static void __uthread_free_tls(struct uthread *uthread);
+
+/* 2LSs shouldn't call vcore_entry() directly */
+// XXX this is going to break testing apps like mhello and syscall
+void __attribute__((noreturn)) vcore_entry()
+{
+       uint32_t vcoreid = vcore_id();
+
+       struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
+
+       /* Should always have notifications disabled when coming in here. */
+       assert(vcpd->notif_enabled == FALSE);
+       assert(in_vcore_context());
+
+       check_preempt_pending(vcoreid);
+       handle_events(vcoreid);
+       assert(in_vcore_context());     /* double check, in case and event changed it */
+       assert(sched_ops->sched_entry);
+       sched_ops->sched_entry();
+       /* If we get here, the 2LS entry returned.  We can call out to the 2LS for
+        * guidance about whether or not to yield, etc.  Or the 2LS can do it and
+        * just not return.  Whatever we do, it ought to parallel what we do for
+        * requesting more cores in uthread_create(). */
+       printd("Vcore %d is yielding\n", vcoreid);
+       sys_yield(0);
+       assert(0);
+}
+
+/* Could move this, along with start_routine and arg, into the 2LSs */
+static void __uthread_run(void)
+{
+       struct uthread *me = current_thread;
+       uthread_exit(me->start_routine(me->arg));
+}
+
+/* Creates a uthread.  Will pass udata to sched_ops's thread_create.  For now,
+ * the vcore/default 2ls code handles start routines and args.  Mostly because
+ * this is used when initing a utf, which is vcore specific for now. */
+struct uthread *uthread_create(void *(*start_routine)(void *), void *arg,
+                               void *udata)
+{
+       /* First time through, init the vcore code (which makes a uthread out of
+        * thread0 / the current code.  Could move this to a ctor. */
+       static bool first = TRUE;
+       if (first) {
+               if (vcore_init())               /* could make this uthread_init */
+                       printf("Vcore init failed!\n");
+               first = FALSE;
+       }
+       assert(!in_vcore_context());
+       assert(sched_ops->thread_create);
+       struct uthread *new_thread = sched_ops->thread_create(udata);
+       assert(new_thread->stacktop);
+       new_thread->start_routine = start_routine;
+       new_thread->arg = arg;
+       /* Set the u_tf to start up in __pthread_run, which will call the real
+        * start_routine and pass it the arg. */
+       init_user_tf(&new_thread->utf, (uint32_t)__uthread_run, 
+                 (uint32_t)(new_thread->stacktop));
+       /* Get a TLS */
+       assert(!__uthread_allocate_tls(new_thread));
+       /* Switch into the new guys TLS and let it know who it is */
+       struct uthread *caller = current_thread;
+       assert(caller);
+       /* Don't migrate this thread to another vcore, since it depends on being on
+        * the same vcore throughout. */
+       caller->dont_migrate = TRUE;
+       wmb();
+       /* Note the first time we call this, we technically aren't on a vcore */
+       uint32_t vcoreid = vcore_id();
+       /* Save the new_thread to the new uthread in that uthread's TLS */
+       set_tls_desc(new_thread->tls_desc, vcoreid);
+       current_thread = new_thread;
+       /* Switch back to the caller */
+       set_tls_desc(caller->tls_desc, vcoreid);
+       /* Okay to migrate now. */
+       wmb();
+       caller->dont_migrate = FALSE;
+       /* Allow the 2LS to make the thread runnable, and do whatever. */
+       assert(sched_ops->thread_runnable);
+       sched_ops->thread_runnable(new_thread);
+       /* This is where we'll call out to a smarter 2LS function to see if we want
+        * to get more cores (and how many). */
+       /* Need to get some vcores.  If this is the first time, we'd like to get
+        * two: one for the main thread (aka thread0), and another for the pthread
+        * we are creating.  Can rework this if we get another vcore interface that
+        * deals with absolute core counts.
+        *
+        * Need to get at least one core to put us in _M mode so we can run the 2LS,
+        * etc, so for now we'll just spin until we get at least one (might be none
+        * available).
+        *
+        * TODO: do something smarter regarding asking for cores (paired with
+        * yielding), and block or something until one core is available (will need
+        * kernel support). */
+       static bool first_time = TRUE;
+       if (first_time) {
+               first_time = FALSE;
+               /* Try for two, don't settle for less than 1 */
+               while (num_vcores() < 1) {
+                       vcore_request(2);
+                       cpu_relax();
+               }
+       } else {        /* common case */
+               /* Try to get another for the new thread, but doesn't matter if we get
+                * one or not, so long as we still have at least 1. */
+               vcore_request(1);
+       }
+       return new_thread;
+}
+
+/* Need to have this as a separate, non-inlined function since we clobber the
+ * stack pointer before calling it, and don't want the compiler to play games
+ * with my hart.
+ *
+ * TODO: combine this 2-step logic with uthread_exit() */
+static void __attribute__((noinline, noreturn)) 
+__uthread_yield(struct uthread *uthread)
+{
+       assert(in_vcore_context());
+       /* TODO: want to set this to FALSE once we no longer depend on being on this
+        * vcore.  Though if we are using TLS, we are depending on the vcore.  Since
+        * notifs are disabled and we are in a transition context, we probably
+        * shouldn't be moved anyway.  It does mean that a pthread could get jammed.
+        * If we do this after putting it on the active list, we'll have a race on
+        * dont_migrate. */
+       uthread->dont_migrate = FALSE;
+       assert(sched_ops->thread_yield);
+       /* 2LS will save the thread somewhere for restarting.  Later on, we'll
+        * probably have a generic function for all sorts of waiting. */
+       sched_ops->thread_yield(uthread);
+       /* Leave the current vcore completely */
+       current_thread = NULL; // this might be okay, even with a migration
+       /* Go back to the entry point, where we can handle notifications or
+        * reschedule someone. */
+       vcore_entry();
+}
+
+/* Calling thread yields.  TODO: combine similar code with uthread_exit() (done
+ * like this to ease the transition to the 2LS-ops */
+void uthread_yield(void)
+{
+       struct uthread *uthread = current_thread;
+       volatile bool yielding = TRUE; /* signal to short circuit when restarting */
+       /* TODO: (HSS) Save silly state */
+       // save_fp_state(&t->as);
+       assert(!in_vcore_context());
+       /* Don't migrate this thread to another vcore, since it depends on being on
+        * the same vcore throughout (once it disables notifs). */
+       uthread->dont_migrate = TRUE;
+       wmb();
+       uint32_t vcoreid = vcore_id();
+       printd("[U] Uthread %08p is yielding on vcore %d\n", uthread, vcoreid);
+       struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
+       /* once we do this, we might miss a notif_pending, so we need to enter vcore
+        * entry later.  Need to disable notifs so we don't get in weird loops with
+        * save_ros_tf() and pop_ros_tf(). */
+       disable_notifs(vcoreid);
+       /* take the current state and save it into t->utf when this pthread
+        * restarts, it will continue from right after this, see yielding is false,
+        * and short ciruit the function. */
+       save_ros_tf(&uthread->utf);
+       if (!yielding)
+               goto yield_return_path;
+       yielding = FALSE; /* for when it starts back up */
+       /* Change to the transition context (both TLS and stack). */
+       extern void** vcore_thread_control_blocks;
+       set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
+       assert(current_thread == uthread);      
+       assert(in_vcore_context());     /* technically, we aren't fully in vcore context */
+       /* After this, make sure you don't use local variables.  Note the warning in
+        * pthread_exit() */
+       set_stack_pointer((void*)vcpd->transition_stack);
+       /* Finish exiting in another function. */
+       __uthread_yield(current_thread);
+       /* Should never get here */
+       assert(0);
+       /* Will jump here when the pthread's trapframe is restarted/popped. */
+yield_return_path:
+       printd("[U] Uthread %08p returning from a yield!\n", uthread);
+}
+
+/* Need to have this as a separate, non-inlined function since we clobber the
+ * stack pointer before calling it, and don't want the compiler to play games
+ * with my hart. */
+static void __attribute__((noinline, noreturn)) 
+__uthread_exit(struct uthread *uthread)
+{
+       assert(in_vcore_context());
+       /* we alloc and manage the TLS, so lets get rid of it */
+       __uthread_free_tls(uthread);
+       /* 2LS specific cleanup */
+       assert(sched_ops->thread_exit);
+       sched_ops->thread_exit(uthread);
+       current_thread = NULL;
+       /* Go back to the entry point, where we can handle notifications or
+        * reschedule someone. */
+       vcore_entry();
+}
+
+/* Exits from the uthread */
+void uthread_exit(void *retval)
+{
+       assert(!in_vcore_context());
+       struct uthread *uthread = current_thread;
+       uthread->retval = retval;
+       /* Don't migrate this thread to anothe vcore, since it depends on being on
+        * the same vcore throughout. */
+       uthread->dont_migrate = TRUE; // won't set to false later, since he is dying
+       wmb();
+       uint32_t vcoreid = vcore_id();
+       struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
+       printd("[U] Uthread %08p is exiting on vcore %d\n", uthread, vcoreid);
+       /* once we do this, we might miss a notif_pending, so we need to enter vcore
+        * entry later. */
+       disable_notifs(vcoreid);
+       /* Change to the transition context (both TLS and stack). */
+       extern void** vcore_thread_control_blocks;
+       set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
+       assert(current_thread == uthread);      
+       /* After this, make sure you don't use local variables.  Also, make sure the
+        * compiler doesn't use them without telling you (TODO).
+        *
+        * In each arch's set_stack_pointer, make sure you subtract off as much room
+        * as you need to any local vars that might be pushed before calling the
+        * next function, or for whatever other reason the compiler/hardware might
+        * walk up the stack a bit when calling a noreturn function. */
+       set_stack_pointer((void*)vcpd->transition_stack);
+       /* Finish exiting in another function.  Ugh. */
+       __uthread_exit(current_thread);
+}
+
+/* Runs whatever thread is vcore's current_thread */
+void run_current_uthread(void)
+{
+       uint32_t vcoreid = vcore_id();
+       struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
+       assert(current_thread);
+       printd("[U] Vcore %d is restarting uthread %d\n", vcoreid, uthread->id);
+       clear_notif_pending(vcoreid);
+       set_tls_desc(current_thread->tls_desc, vcoreid);
+       /* Pop the user trap frame */
+       pop_ros_tf(&vcpd->notif_tf, vcoreid);
+       assert(0);
+}
+
+/* Launches the uthread on the vcore */
+void run_uthread(struct uthread *uthread)
+{
+       /* Save a ptr to the pthread running in the transition context's TLS */
+       uint32_t vcoreid = vcore_id();
+       struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
+       printd("[U] Vcore %d is starting uthread %d\n", vcoreid, uthread->id);
+       current_thread = uthread;
+       clear_notif_pending(vcoreid);
+       set_tls_desc(uthread->tls_desc, vcoreid);
+       /* Load silly state (Floating point) too.  For real */
+       /* TODO: (HSS) */
+       /* Pop the user trap frame */
+       pop_ros_tf(&uthread->utf, vcoreid);
+       assert(0);
+}
+
+/* TLS helpers */
+static int __uthread_allocate_tls(struct uthread *uthread)
+{
+       assert(!uthread->tls_desc);
+       uthread->tls_desc = allocate_tls();
+       if (!uthread->tls_desc) {
+               errno = ENOMEM;
+               return -1;
+       }
+       return 0;
+}
+
+/* TODO: probably don't want to dealloc.  Considering caching */
+static void __uthread_free_tls(struct uthread *uthread)
+{
+       extern void _dl_deallocate_tls (void *tcb, bool dealloc_tcb) internal_function;
+
+       assert(uthread->tls_desc);
+       _dl_deallocate_tls(uthread->tls_desc, TRUE);
+       uthread->tls_desc = NULL;
+}
index 9cafe70..fc705d9 100644 (file)
@@ -27,16 +27,38 @@ int threads_active = 0;
 static int get_next_pid(void);
 static inline void spin_to_sleep(unsigned int spins, unsigned int *spun);
 
-__thread struct pthread_tcb *current_thread = 0;
-
-void _pthread_init()
+/* Pthread 2LS operations */
+struct uthread *pth_init(void);
+void pth_sched_entry(void);
+struct uthread *pth_thread_create(void *udata);
+void pth_thread_runnable(struct uthread *uthread);
+void pth_thread_yield(struct uthread *uthread);
+void pth_thread_exit(struct uthread *uthread);
+void pth_preempt_pending(void);
+void pth_spawn_thread(uintptr_t pc_start, void *data);
+
+struct schedule_ops pthread_sched_ops = {
+       pth_init,
+       pth_sched_entry,
+       pth_thread_create,
+       pth_thread_runnable,
+       pth_thread_yield,
+       pth_thread_exit,
+       0, /* pth_preempt_pending, */
+       0, /* pth_spawn_thread, */
+};
+
+/* Publish our sched_ops, overriding the weak defaults */
+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);
+
+/* Do whatever init you want.  Return a uthread representing thread0 (int
+ * main()) */
+struct uthread *pth_init(void)
 {
-       if (vcore_init())
-               printf("vcore_init() failed, we're fucked!\n");
-       
-       assert(vcore_id() == 0);
-       assert(!in_vcore_context());
-
        /* Tell the kernel where and how we want to receive events.  This is just an
         * example of what to do to have a notification turned on.  We're turning on
         * USER_IPIs, posting events to vcore 0's vcpd, and telling the kernel to
@@ -45,60 +67,28 @@ void _pthread_init()
         * to use parts of event.c to do what you want. */
        enable_kevent(EV_USER_IPI, 0, EVENT_IPI);
 
-       /* don't forget to enable notifs on vcore0.  if you don't, the kernel will
-        * restart your _S with notifs disabled, which is a path to confusion. */
-       enable_notifs(0);
-
        /* Create a pthread_tcb for the main thread */
        pthread_t t = (pthread_t)calloc(1, sizeof(struct pthread_tcb));
        t->id = get_next_pid();
        assert(t->id == 0);
+
        /* Put the new pthread on the active queue */
        mcs_lock_lock(&queue_lock);
        threads_active++;
        TAILQ_INSERT_TAIL(&active_queue, t, next);
        mcs_lock_unlock(&queue_lock);
-       
-       /* Save a pointer to the newly created threads tls region into its tcb */
-       t->tls_desc = get_tls_desc(0);
-       /* Save a pointer to the pthread in its own TLS */
-       current_thread = t;
-
-       /* Change temporarily to vcore0s tls region so we can save the newly created
-        * tcb into its current_thread variable and then restore it.  One minor
-        * issue is that vcore0's transition-TLS isn't TLS_INITed yet.  Until it is
-        * (right before vcore_entry(), don't try and take the address of any of
-        * its TLS vars. */
-       extern void** vcore_thread_control_blocks;
-       set_tls_desc(vcore_thread_control_blocks[0], 0);
-       current_thread = t;
-       set_tls_desc(t->tls_desc, 0);
-       assert(!in_vcore_context());
-}
-
-void __attribute__((noreturn)) vcore_entry()
-{
-       uint32_t vcoreid = vcore_id();
-
-       struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
-
-       /* Should always have notifications disabled when coming in here. */
-       assert(vcpd->notif_enabled == FALSE);
-       assert(in_vcore_context());
-
-       check_preempt_pending(vcoreid);
-       handle_events(vcoreid);
-       assert(in_vcore_context());     /* double check, in case and event changed it */
-       // TODO: consider making this restart path work for restarting as well as
-       // freshly starting
+       return (struct uthread*)t;
+}
+
+/* Called from vcore entry.  Options usually include restarting whoever was
+ * running there before or running a new thread.  Events are handled out of
+ * event.c (table of function pointers, stuff like that). */
+void pth_sched_entry(void)
+{
        if (current_thread) {
-               clear_notif_pending(vcoreid);
-               set_tls_desc(current_thread->tls_desc, vcoreid);
-               /* Pop the user trap frame */
-               pop_ros_tf(&vcpd->notif_tf, vcoreid);
+               run_current_uthread();
                assert(0);
        }
-
        /* no one currently running, so lets get someone from the ready queue */
        struct pthread_tcb *new_thread = NULL;
        mcs_lock_lock(&queue_lock);
@@ -110,63 +100,112 @@ void __attribute__((noreturn)) vcore_entry()
                threads_ready--;
        }
        mcs_lock_unlock(&queue_lock);
+       /* For now, this dumb logic is done here */
        if (!new_thread) {
                /* TODO: consider doing something more intelligent here */
-               printd("[P] No threads, vcore %d is yielding\n", vcoreid);
+               printd("[P] No threads, vcore %d is yielding\n", vcore_id());
                sys_yield(0);
+               assert(0);
        }
-       /* Save a ptr to the pthread running in the transition context's TLS */
-       current_thread = new_thread;
-       printd("[P] Vcore %d is starting pthread %d\n", vcoreid, new_thread->id);
+       run_uthread((struct uthread*)new_thread);
+       assert(0);
+}
 
-       clear_notif_pending(vcoreid);
-       set_tls_desc(new_thread->tls_desc, vcoreid);
+struct uthread *pth_thread_create(void *udata)
+{
+       struct pthread_tcb *pthread;
+       pthread_attr_t *attr = (pthread_attr_t*)udata;
+       pthread = (pthread_t)calloc(1, sizeof(struct pthread_tcb));
+       pthread->stacksize = PTHREAD_STACK_SIZE;        /* default */
+       pthread->id = get_next_pid();
+       pthread->detached = FALSE;                              /* default */
+       /* Respect the attributes */
+       if (attr) {
+               if (attr->stacksize)                                    /* don't set a 0 stacksize */
+                       pthread->stacksize = attr->stacksize;
+               if (attr->detachstate == PTHREAD_CREATE_DETACHED)
+                       pthread->detached = TRUE;
+       }
+       /* allocate a stack */
+       if (__pthread_allocate_stack(pthread))
+               printf("We're fucked\n");
+       return (struct uthread*)pthread;
+}
 
-       /* Load silly state (Floating point) too.  For real */
-       // TODO: (HSS)
+void pth_thread_runnable(struct uthread *uthread)
+{
+       struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
+       /* Insert the newly created thread into the ready queue of threads.
+        * It will be removed from this queue later when vcore_entry() comes up */
+       mcs_lock_lock(&queue_lock);
+       TAILQ_INSERT_TAIL(&ready_queue, pthread, next);
+       threads_ready++;
+       mcs_lock_unlock(&queue_lock);
+}
 
-       /* Pop the user trap frame */
-       pop_ros_tf(&new_thread->utf, vcoreid);
-       assert(0);
+/* The calling thread is yielding.  Do what you need to do to restart (like put
+ * yourself on a runqueue), or do some accounting.  Eventually, this might be a
+ * little more generic than just yield. */
+void pth_thread_yield(struct uthread *uthread)
+{
+       struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
+       /* Take from the active list, and put on the ready list (tail).  Don't do
+        * this until we are done completely with the thread, since it can be
+        * restarted somewhere else. */
+       mcs_lock_lock(&queue_lock);
+       threads_active--;
+       TAILQ_REMOVE(&active_queue, pthread, next);
+       threads_ready++;
+       TAILQ_INSERT_TAIL(&ready_queue, pthread, next);
+       mcs_lock_unlock(&queue_lock);
 }
 
-int pthread_attr_init(pthread_attr_t *a)
+/* Thread is exiting, do your 2LS specific stuff.  You're in vcore context.
+ * Don't use the thread's TLS or stack or anything. */
+void pth_thread_exit(struct uthread *uthread)
 {
-       a->stacksize = PTHREAD_STACK_SIZE;
-       a->detachstate = PTHREAD_CREATE_JOINABLE;
-       return 0;
+       struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
+       /* Remove from the active runqueue */
+       mcs_lock_lock(&queue_lock);
+       threads_active--;
+       TAILQ_REMOVE(&active_queue, pthread, next);
+       mcs_lock_unlock(&queue_lock);
+       /* Cleanup, mirroring pth_thread_create() */
+       __pthread_free_stack(pthread);
+       /* TODO: race on detach state */
+       if (pthread->detached)
+               free(pthread);
+       /* Once we do this, our joiner can free us.  He won't free us if we're
+        * detached, but there is still a potential race there (since he's accessing
+        * someone who is freed. */
+       pthread->finished = 1;
 }
 
-int pthread_attr_destroy(pthread_attr_t *a)
+void pth_preempt_pending(void)
 {
-       return 0;
 }
 
-/* TODO: probably don't want to dealloc.  Considering caching */
-static void __pthread_free_tls(struct pthread_tcb *pt)
+void pth_spawn_thread(uintptr_t pc_start, void *data)
 {
-       extern void _dl_deallocate_tls (void *tcb, bool dealloc_tcb) internal_function;
+}
+
+/* Pthread interface stuff and helpers */
 
-       assert(pt->tls_desc);
-       _dl_deallocate_tls(pt->tls_desc, TRUE);
-       pt->tls_desc = NULL;
+int pthread_attr_init(pthread_attr_t *a)
+{
+       a->stacksize = PTHREAD_STACK_SIZE;
+       a->detachstate = PTHREAD_CREATE_JOINABLE;
+       return 0;
 }
 
-static int __pthread_allocate_tls(struct pthread_tcb *pt)
+int pthread_attr_destroy(pthread_attr_t *a)
 {
-       assert(!pt->tls_desc);
-       pt->tls_desc = allocate_tls();
-       if (!pt->tls_desc) {
-               errno = ENOMEM;
-               return -1;
-       }
        return 0;
 }
 
-
 static void __pthread_free_stack(struct pthread_tcb *pt)
 {
-       assert(!munmap(pt->stacktop - PTHREAD_STACK_SIZE, PTHREAD_STACK_SIZE));
+       assert(!munmap(pt->uthread.stacktop - PTHREAD_STACK_SIZE, PTHREAD_STACK_SIZE));
 }
 
 static int __pthread_allocate_stack(struct pthread_tcb *pt)
@@ -177,16 +216,10 @@ static int __pthread_allocate_stack(struct pthread_tcb *pt)
                              MAP_POPULATE|MAP_ANONYMOUS, -1, 0);
        if (stackbot == MAP_FAILED)
                return -1; // errno set by mmap
-       pt->stacktop = stackbot + pt->stacksize;
+       pt->uthread.stacktop = stackbot + pt->stacksize;
        return 0;
 }
 
-void __pthread_run(void)
-{
-       struct pthread_tcb *me = current_thread;
-       pthread_exit(me->start_routine(me->arg));
-}
-
 // Warning, this will reuse numbers eventually
 static int get_next_pid(void)
 {
@@ -194,7 +227,6 @@ static int get_next_pid(void)
        return next_pid++;
 }
 
-
 int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize)
 {
        attr->stacksize = stacksize;
@@ -209,78 +241,10 @@ int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize)
 int pthread_create(pthread_t* thread, const pthread_attr_t* attr,
                    void *(*start_routine)(void *), void* arg)
 {
-       /* After this init, we are an MCP and the caller is a pthread */
-       pthread_once(&init_once, &_pthread_init);
-
-       struct pthread_tcb *t = pthread_self();
-       assert(t);
-       assert(!in_vcore_context());
-       /* Don't migrate this thread to another vcore, since it depends on being on
-        * the same vcore throughout. */
-       t->dont_migrate = TRUE;
-       wmb();
-       /* Note the first time we call this, we technically aren't on a vcore */
-       uint32_t vcoreid = vcore_id();
-       *thread = (pthread_t)calloc(1, sizeof(struct pthread_tcb));
-       (*thread)->start_routine = start_routine;
-       (*thread)->arg = arg;
-       (*thread)->stacksize = PTHREAD_STACK_SIZE;      /* default */
-       (*thread)->id = get_next_pid();
-       (*thread)->detached = FALSE;                            /* default */
-       /* Respect the attributes*/
-       if (attr) {
-               if (attr->stacksize)                                    /* don't set a 0 stacksize */
-                       (*thread)->stacksize = attr->stacksize;
-               if (attr->detachstate == PTHREAD_CREATE_DETACHED)
-                       (*thread)->detached = TRUE;
-       }
-       if (__pthread_allocate_stack(*thread) ||  __pthread_allocate_tls(*thread))
-               printf("We're fucked\n");
-       /* Save the ptr to the new pthread in that pthread's TLS */
-       set_tls_desc((*thread)->tls_desc, vcoreid);
-       current_thread = *thread;
-       set_tls_desc(t->tls_desc, vcoreid);
-       /* Set the u_tf to start up in __pthread_run, which will call the real
-        * start_routine and pass it the arg. */
-       init_user_tf(&(*thread)->utf, (uint32_t)__pthread_run, 
-                 (uint32_t)((*thread)->stacktop));
-       /* Insert the newly created thread into the ready queue of threads.
-        * It will be removed from this queue later when vcore_entry() comes up */
-       mcs_lock_lock(&queue_lock);
-       TAILQ_INSERT_TAIL(&ready_queue, *thread, next);
-       threads_ready++;
-       mcs_lock_unlock(&queue_lock);
-       /* Okay to migrate now. */
-       wmb();
-       t->dont_migrate = FALSE;
-
-       /* Need to get some vcores.  If this is the first time, we'd like to get
-        * two: one for the main thread (aka thread0), and another for the pthread
-        * we are creating.  Can rework this if we get another vcore interface that
-        * deals with absolute core counts.
-        *
-        * Need to get at least one core to put us in _M mode so we can run the 2LS,
-        * etc, so for now we'll just spin until we get at least one (might be none
-        * available).
-        *
-        * TODO: do something smarter regarding asking for cores (paired with
-        * yielding), and block or something until one core is available (will need
-        * kernel support). */
-       static bool first_time = TRUE;
-       if (first_time) {
-               first_time = FALSE;
-               /* Try for two, don't settle for less than 1 */
-               while (num_vcores() < 1) {
-                       vcore_request(2);
-                       cpu_relax();
-               }
-       } else {        /* common case */
-               /* Try to get another for the new thread, but doesn't matter if we get
-                * one or not, so long as we still have at least 1. */
-               vcore_request(1);
-       }
-       assert(num_vcores() > 0);
-       assert(!in_vcore_context());    /* still are the calling u_thread */
+       struct uthread *uthread = uthread_create(start_routine, arg, (void*)attr);
+       if (!uthread)
+               return -1;
+       *thread = (struct pthread_tcb*)uthread;
        return 0;
 }
 
@@ -296,80 +260,14 @@ int pthread_join(pthread_t thread, void** retval)
        while (!thread->finished)
                pthread_yield();
        if (retval)
-               *retval = thread->retval;
+               *retval = thread->uthread.retval;
        free(thread);
        return 0;
 }
 
-static void __attribute__((noinline, noreturn)) 
-__pthread_yield(struct pthread_tcb *t)
-{
-       assert(in_vcore_context());
-       /* TODO: want to set this to FALSE once we no longer depend on being on this
-        * vcore.  Though if we are using TLS, we are depending on the vcore.  Since
-        * notifs are disabled and we are in a transition context, we probably
-        * shouldn't be moved anyway.  It does mean that a pthread could get jammed.
-        * If we do this after putting it on the active list, we'll have a race on
-        * dont_migrate. */
-       t->dont_migrate = FALSE;
-       /* Take from the active list, and put on the ready list (tail).  Don't do
-        * this until we are done completely with the thread, since it can be
-        * restarted somewhere else. */
-       mcs_lock_lock(&queue_lock);
-       threads_active--;
-       TAILQ_REMOVE(&active_queue, t, next);
-       threads_ready++;
-       TAILQ_INSERT_TAIL(&ready_queue, t, next);
-       mcs_lock_unlock(&queue_lock);
-       /* Leave the current vcore completely */
-       current_thread = NULL; // this might be okay, even with a migration
-       /* Go back to the entry point, where we can handle notifications or
-        * reschedule someone. */
-       vcore_entry();
-}
-
 int pthread_yield(void)
 {
-       struct pthread_tcb *t = pthread_self();
-       volatile bool yielding = TRUE; /* signal to short circuit when restarting */
-
-       /* TODO: (HSS) Save silly state */
-       // save_fp_state(&t->as);
-
-       assert(!in_vcore_context());
-       /* Don't migrate this thread to another vcore, since it depends on being on
-        * the same vcore throughout (once it disables notifs). */
-       t->dont_migrate = TRUE;
-       wmb();
-       uint32_t vcoreid = vcore_id();
-       printd("[P] Pthread id %d is yielding on vcore %d\n", t->id, vcoreid);
-       struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
-       /* once we do this, we might miss a notif_pending, so we need to enter vcore
-        * entry later.  Need to disable notifs so we don't get in weird loops with
-        * save_ros_tf() and pop_ros_tf(). */
-       disable_notifs(vcoreid);
-       /* take the current state and save it into t->utf when this pthread
-        * restarts, it will continue from right after this, see yielding is false,
-        * and short ciruit the function. */
-       save_ros_tf(&t->utf);
-       if (!yielding)
-               goto yield_return_path;
-       yielding = FALSE; /* for when it starts back up */
-       /* Change to the transition context (both TLS and stack). */
-       extern void** vcore_thread_control_blocks;
-       set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
-       assert(current_thread == t);    
-       assert(in_vcore_context());     /* technically, we aren't fully in vcore context */
-       /* After this, make sure you don't use local variables.  Note the warning in
-        * pthread_exit() */
-       set_stack_pointer((void*)vcpd->transition_stack);
-       /* Finish exiting in another function. */
-       __pthread_yield(current_thread);
-       /* Should never get here */
-       assert(0);
-       /* Will jump here when the pthread's trapframe is restarted/popped. */
-yield_return_path:
-       printd("[P] pthread %d returning from a yield!\n", t->id);
+       uthread_yield();
        return 0;
 }
 
@@ -384,7 +282,6 @@ int pthread_mutexattr_destroy(pthread_mutexattr_t* attr)
   return 0;
 }
 
-
 int pthread_attr_setdetachstate(pthread_attr_t *__attr, int __detachstate)
 {
        __attr->detachstate = __detachstate;
@@ -535,7 +432,7 @@ int pthread_condattr_getpshared(pthread_condattr_t *a, int *s)
 
 pthread_t pthread_self()
 {
-  return current_thread;
+  return (struct pthread_tcb*)current_thread;
 }
 
 int pthread_equal(pthread_t t1, pthread_t t2)
@@ -543,68 +440,11 @@ int pthread_equal(pthread_t t1, pthread_t t2)
   return t1 == t2;
 }
 
-/* Need to have this as a separate, non-inlined function since we clobber the
- * stack pointer before calling it, and don't want the compiler to play games
- * with my hart. */
-static void __attribute__((noinline, noreturn)) 
-__pthread_exit(struct pthread_tcb *t)
-{
-       assert(in_vcore_context());
-       __pthread_free_tls(t);
-       __pthread_free_stack(t);
-       /* TODO: race on detach state */
-       if (t->detached)
-               free(t);
-       /* Once we do this, our joiner can free us.  He won't free us if we're
-        * detached, but there is still a potential race there (since he's accessing
-        * someone who is freed. */
-       t->finished = 1;
-       current_thread = NULL;
-       /* Go back to the entry point, where we can handle notifications or
-        * reschedule someone. */
-       vcore_entry();
-}
-
 /* This function cannot be migrated to a different vcore by the userspace
- * scheduler.  Will need to sort that shit out.  */
-void pthread_exit(void* ret)
-{
-       assert(!in_vcore_context());
-       struct pthread_tcb *t = pthread_self();
-       /* Don't migrate this thread to anothe vcore, since it depends on being on
-        * the same vcore throughout. */
-       t->dont_migrate = TRUE; // won't set this to false later, since he is dying
-       wmb();
-
-       uint32_t vcoreid = vcore_id();
-       struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
-
-       t->retval = ret;
-       mcs_lock_lock(&queue_lock);
-       threads_active--;
-       TAILQ_REMOVE(&active_queue, t, next);
-       mcs_lock_unlock(&queue_lock);
-
-       printd("[P] Pthread id %d is exiting on vcore %d\n", t->id, vcoreid);
-       
-       /* once we do this, we might miss a notif_pending, so we need to enter vcore
-        * entry later. */
-       disable_notifs(vcoreid);
-
-       /* Change to the transition context (both TLS and stack). */
-       extern void** vcore_thread_control_blocks;
-       set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
-       assert(current_thread == t);    
-       /* After this, make sure you don't use local variables.  Also, make sure the
-        * compiler doesn't use them without telling you (TODO).
-        *
-        * In each arch's set_stack_pointer, make sure you subtract off as much room
-        * as you need to any local vars that might be pushed before calling the
-        * next function, or for whatever other reason the compiler/hardware might
-        * walk up the stack a bit when calling a noreturn function. */
-       set_stack_pointer((void*)vcpd->transition_stack);
-       /* Finish exiting in another function.  Ugh. */
-       __pthread_exit(current_thread);
+ * scheduler.  Will need to sort that shit out. */
+void pthread_exit(void *ret)
+{
+       uthread_exit(ret);
 }
 
 int pthread_once(pthread_once_t* once_control, void (*init_routine)(void))
index 8d430ad..9bc93a8 100644 (file)
@@ -9,26 +9,15 @@
   extern "C" {
 #endif
 
-/* Our internal types for pthread management */
+/* Pthread struct.  First has to be the uthread struct, which the vcore code
+ * will access directly (as if pthread_tcb is a struct uthread). */
 struct pthread_tcb {
+       struct uthread uthread;
        TAILQ_ENTRY(pthread_tcb) next;
-       void* (*start_routine)(void*);
-       void* arg;
-
-       struct user_trapframe utf;
-       struct ancillary_state as;
-       void *tls_desc;
-       void *stacktop;
-       uint32_t stacksize;
-       uint32_t id;
-
        int finished;
-       void *retval;
        bool detached;
-       // whether or not the scheduler can migrate you from your vcore
-       // will be slightly difficult to see this when given just a vcoreid and
-       // notif_tf ptr
-       bool dont_migrate;
+       uint32_t id;
+       uint32_t stacksize;
 };
 typedef struct pthread_tcb* pthread_t;
 TAILQ_HEAD(pthread_queue, pthread_tcb);