Split uthread code into its own source file
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 4 Mar 2011 00:48:20 +0000 (16:48 -0800)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:35:59 +0000 (17:35 -0700)
Some things were a bit uneven, mostly due to event code calling some
functions that think there is a 2LS / sched_ops.  That sort of stuff
goes into uthread.  If you want to not use the uthread code, then you
get to write your own event handling helpers too.  Have fun working with
the raw kernel interface!

16 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/msr_nice_while.c
tests/syscall.c
user/parlib/event.c
user/parlib/include/uthread.h [new file with mode: 0644]
user/parlib/include/vcore.h
user/parlib/uthread.c [new file with mode: 0644]
user/parlib/vcore.c
user/pthread/pthread.h

index 266ac9c..8186aab 100644 (file)
@@ -18,6 +18,7 @@
 #include <timing.h>
 #include <assert.h>
 #include <event.h>
+#include <uthread.h>
 
 void *core0_tls = 0;
 void *in_buf, *out_buf;
index 5ee3c8a..d3edb4b 100644 (file)
@@ -3,6 +3,7 @@
 #include <vcore.h>
 #include <parlib.h>
 #include <mcs.h>
+#include <uthread.h>
 
 mcs_barrier_t b;
 
index 029805e..d4481b9 100644 (file)
@@ -11,6 +11,7 @@
 #include <timing.h>
 #include <rassert.h>
 #include <event.h>
+#include <uthread.h>
 
 #ifdef __sparc_v8__
 # define udelay(x) udelay((x)/2000)
index a8e5e24..cf4b810 100644 (file)
@@ -7,6 +7,7 @@
 #include <ros/resource.h>
 #include <rstdio.h>
 #include <timing.h>
+#include <uthread.h>
 
 #ifdef __sparc_v8__
 # define udelay(x) udelay((x)/2000)
index 5f5bd9e..6fd37d2 100644 (file)
@@ -10,6 +10,7 @@
 #include <vcore.h>
 #include <timing.h>
 #include <mcs.h>
+#include <uthread.h>
 
 #ifdef __sparc_v8__
 # define udelay(x) udelay((x)/2000)
index b05706f..b27ba67 100644 (file)
@@ -7,6 +7,7 @@
 #include <vcore.h>
 #include <arch/arch.h>
 #include <event.h>
+#include <uthread.h>
 
 void ghetto_vcore_entry(void);
 struct schedule_ops ghetto_sched_ops = {
index f05df88..845b9d2 100644 (file)
@@ -22,6 +22,7 @@
 #include <mcs.h>
 #include <timing.h>
 #include <rassert.h>
+#include <uthread.h>
 
 #ifdef __sparc_v8__
 # define udelay(x) udelay((x)/2000)
index 3bfba72..4696f28 100644 (file)
@@ -14,6 +14,7 @@
 #include <mcs.h>
 #include <timing.h>
 #include <rassert.h>
+#include <uthread.h>
 
 #ifdef __sparc_v8__
 # define udelay(x) udelay((x)/2000)
index 8ce6ad6..7f76718 100644 (file)
@@ -9,6 +9,7 @@
 #include <pthread.h>
 #include <rassert.h>
 #include <event.h>
+#include <uthread.h>
 
 void *while_thread(void *arg)
 {
index 4c98f5d..8e99f05 100644 (file)
@@ -5,6 +5,7 @@
 #include <vcore.h>
 #include <rassert.h>
 #include <ros/bcq.h>
+#include <uthread.h>
 
 static void handle_syscall(struct event_msg *ev_msg, unsigned int ev_type,
                            bool overflow);
index 47a4444..9f8a06b 100644 (file)
@@ -16,6 +16,7 @@
 #include <errno.h>
 #include <parlib.h>
 #include <event.h>
+#include <uthread.h>
 
 /********* Event_q Setup / Registration  ***********/
 
diff --git a/user/parlib/include/uthread.h b/user/parlib/include/uthread.h
new file mode 100644 (file)
index 0000000..bcc6706
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef _UTHREAD_H
+#define _UTHREAD_H
+
+#include <vcore.h>
+
+/* 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;
+       /* whether or not the scheduler can migrate you from your vcore */
+       bool dont_migrate;
+};
+extern __thread struct uthread *current_uthread;
+
+/* 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 (*func)(void), 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.  Func is
+ * what gets run, and if you want args, wrap it (like pthread) */
+struct uthread *uthread_create(void (*func)(void), void *udata);
+void uthread_runnable(struct uthread *uthread);
+void uthread_yield(void);
+void uthread_exit(void);
+
+/* Utility function.  Event code also calls this. */
+bool check_preempt_pending(uint32_t vcoreid);
+
+/* Helpers, which sched_entry() can call */
+void run_current_uthread(void);
+void run_uthread(struct uthread *uthread);
+
+#endif /* _UTHREAD_H */
index f3e2ff8..0efa970 100644 (file)
@@ -46,51 +46,8 @@ static inline void disable_notifs(uint32_t vcoreid);
 int vcore_init(void);
 int vcore_request(size_t k);
 void vcore_yield(void);
-bool check_preempt_pending(uint32_t vcoreid);
 void clear_notif_pending(uint32_t vcoreid);
 
-/* 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;
-       /* whether or not the scheduler can migrate you from your vcore */
-       bool dont_migrate;
-};
-extern __thread struct uthread *current_uthread;
-
-/* 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 (*func)(void), 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.  Func is
- * what gets run, and if you want args, wrap it (like pthread) */
-struct uthread *uthread_create(void (*func)(void), void *udata);
-void uthread_runnable(struct uthread *uthread);
-void uthread_yield(void);
-void uthread_exit(void);
-
-/* 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)
 {
diff --git a/user/parlib/uthread.c b/user/parlib/uthread.c
new file mode 100644 (file)
index 0000000..20d9685
--- /dev/null
@@ -0,0 +1,329 @@
+#include <ros/arch/membar.h>
+#include <parlib.h>
+#include <vcore.h>
+#include <uthread.h>
+#include <event.h>
+
+/* Which operations we'll call for the 2LS.  Will change a bit with Lithe.  For
+ * now, there are no defaults.  2LSs can override sched_ops. */
+struct schedule_ops default_2ls_ops = {0};
+struct schedule_ops *sched_ops __attribute__((weak)) = &default_2ls_ops;
+
+__thread struct uthread *current_uthread = 0;
+
+/* static helpers: */
+static int __uthread_allocate_tls(struct uthread *uthread);
+static void __uthread_free_tls(struct uthread *uthread);
+
+/* Gets called once out of uthread_create().  Can also do this in a ctor. */
+static int uthread_init(void)
+{
+       /* Init the vcore system */
+       assert(!vcore_init());
+       /* 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_uthread = uthread;
+       /* Change temporarily to vcore0s tls region so we can save the newly created
+        * tcb into its current_uthread 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_uthread = 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);
+       return 0;
+}
+
+/* 2LSs shouldn't call vcore_entry() directly */
+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);
+}
+
+/* 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 (*func)(void), void *udata)
+{
+       /* First time through, init the uthread code (which makes a uthread out of
+        * thread0 / the current code.  Could move this to a ctor. */
+       static bool first = TRUE;
+       if (first) {
+               assert(!uthread_init());
+               first = FALSE;
+       }
+       assert(!in_vcore_context());
+       assert(sched_ops->thread_create);
+       struct uthread *new_thread = sched_ops->thread_create(func, udata);
+       /* 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_uthread;
+       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_uthread = new_thread;
+       /* Switch back to the caller */
+       set_tls_desc(caller->tls_desc, vcoreid);
+       /* Okay to migrate now. */
+       wmb();
+       caller->dont_migrate = FALSE;
+       return new_thread;
+}
+
+void uthread_runnable(struct uthread *uthread)
+{
+       /* Allow the 2LS to make the thread runnable, and do whatever. */
+       assert(sched_ops->thread_runnable);
+       sched_ops->thread_runnable(uthread);
+       /* 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);
+       }
+}
+
+/* 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_uthread = 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_uthread;
+       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_uthread == 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_uthread);
+       /* 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_uthread = 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)
+{
+       assert(!in_vcore_context());
+       struct uthread *uthread = current_uthread;
+       /* 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_uthread == 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_uthread);
+}
+
+/* Runs whatever thread is vcore's current_uthread */
+void run_current_uthread(void)
+{
+       uint32_t vcoreid = vcore_id();
+       struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
+       assert(current_uthread);
+       printd("[U] Vcore %d is restarting uthread %d\n", vcoreid, uthread->id);
+       clear_notif_pending(vcoreid);
+       set_tls_desc(current_uthread->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_uthread = 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);
+}
+
+/* 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
+ * shit a preempt is on its way ASAP".  While it is isn't too involved with
+ * uthreads, it is tied in to sched_ops. */
+bool check_preempt_pending(uint32_t vcoreid)
+{
+       bool retval = FALSE;
+       if (__procinfo.vcoremap[vcoreid].preempt_pending) {
+               retval = TRUE;
+               if (sched_ops->preempt_pending)
+                       sched_ops->preempt_pending();
+               /* this tries to yield, but will pop back up if this was a spurious
+                * preempt_pending. */
+               sys_yield(TRUE);
+       }
+       return retval;
+}
+
+/* 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;
+}
+
+static void __uthread_free_tls(struct uthread *uthread)
+{
+       free_tls(uthread->tls_desc);
+       uthread->tls_desc = NULL;
+}
index 547643c..4da73da 100644 (file)
 #include <rstdio.h>
 #include <glibc-tls.h>
 #include <event.h>
+#include <uthread.h>
 #include <ros/arch/membar.h>
 
 /* starting with 1 since we alloc vcore0's stacks and TLS in vcore_init(). */
 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.  2LSs can override sched_ops. */
-struct schedule_ops default_2ls_ops = {0};
-struct schedule_ops *sched_ops __attribute__((weak)) = &default_2ls_ops;
-
 extern void** vcore_thread_control_blocks;
 
-__thread struct uthread *current_uthread = 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)
@@ -121,35 +115,8 @@ int vcore_init()
                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_uthread = uthread;
-
-       /* Change temporarily to vcore0s tls region so we can save the newly created
-        * tcb into its current_uthread 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_uthread = 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;
-
 vcore_init_tls_fail:
        free(vcore_thread_control_blocks);
 vcore_init_fail:
@@ -200,24 +167,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
- * shit a preempt is on its way ASAP". */
-bool check_preempt_pending(uint32_t vcoreid)
-{
-       bool retval = FALSE;
-       if (__procinfo.vcoremap[vcoreid].preempt_pending) {
-               retval = TRUE;
-               if (sched_ops->preempt_pending)
-                       sched_ops->preempt_pending();
-               /* this tries to yield, but will pop back up if this was a spurious
-                * preempt_pending. */
-               sys_yield(TRUE);
-       }
-       return retval;
-}
-
 /* Clear pending, and try to handle events that came in between a previous call
  * to handle_events() and the clearing of pending.  While it's not a big deal,
  * we'll loop in case we catch any.  Will break out of this once there are no
@@ -232,279 +181,3 @@ 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);
-}
-
-/* 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 (*func)(void), 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(func, udata);
-       /* 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_uthread;
-       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_uthread = new_thread;
-       /* Switch back to the caller */
-       set_tls_desc(caller->tls_desc, vcoreid);
-       /* Okay to migrate now. */
-       wmb();
-       caller->dont_migrate = FALSE;
-       return new_thread;
-}
-
-void uthread_runnable(struct uthread *uthread)
-{
-       /* Allow the 2LS to make the thread runnable, and do whatever. */
-       assert(sched_ops->thread_runnable);
-       sched_ops->thread_runnable(uthread);
-       /* 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);
-       }
-}
-
-/* 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_uthread = 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_uthread;
-       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_uthread == 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_uthread);
-       /* 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_uthread = 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)
-{
-       assert(!in_vcore_context());
-       struct uthread *uthread = current_uthread;
-       /* 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_uthread == 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_uthread);
-}
-
-/* Runs whatever thread is vcore's current_uthread */
-void run_current_uthread(void)
-{
-       uint32_t vcoreid = vcore_id();
-       struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
-       assert(current_uthread);
-       printd("[U] Vcore %d is restarting uthread %d\n", vcoreid, uthread->id);
-       clear_notif_pending(vcoreid);
-       set_tls_desc(current_uthread->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_uthread = 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 5fae89d..c21d248 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <sys/queue.h>
 #include <vcore.h>
+#include <uthread.h>
 #include <mcs.h>
 
 #ifdef __cplusplus