2LS op for how many vcores to request
authorBarret Rhoden <brho@cs.berkeley.edu>
Sat, 5 Mar 2011 00:14:19 +0000 (16:14 -0800)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:36:00 +0000 (17:36 -0700)
Called out of uthread_runnable(), for now.  Yielding or whatever is done
by the 2LS sched_entry(), which should never return.

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

index bcc6706..e20e315 100644 (file)
@@ -24,6 +24,7 @@ struct schedule_ops {
        void (*thread_runnable)(struct uthread *);
        void (*thread_yield)(struct uthread *);
        void (*thread_exit)(struct uthread *);
+       unsigned int (*vcores_wanted)(void);
        /* Functions event handling wants */
        void (*preempt_pending)(void);
        void (*spawn_thread)(uintptr_t pc_start, void *data);   /* don't run yet */
index ccebe66..a2b474f 100644 (file)
@@ -41,6 +41,12 @@ static int uthread_init(void)
        /* 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);
+       /* Get ourselves into _M mode */
+       while (num_vcores() < 1) {
+               vcore_request(1);
+               /* TODO: consider blocking */
+               cpu_relax();
+       }
        return 0;
 }
 
@@ -60,12 +66,7 @@ void __attribute__((noreturn)) uthread_vcore_entry(void)
        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);
+       /* 2LS sched_entry should never return */
        assert(0);
 }
 
@@ -111,33 +112,9 @@ 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);
-       }
+       /* Ask the 2LS how many vcores it wants, and put in the request. */
+       assert(sched_ops->vcores_wanted);
+       vcore_request(sched_ops->vcores_wanted());
 }
 
 /* Need to have this as a separate, non-inlined function since we clobber the
@@ -274,9 +251,10 @@ void run_current_uthread(void)
        assert(0);
 }
 
-/* Launches the uthread on the vcore */
+/* Launches the uthread on the vcore.  Don't call this on current_uthread. */
 void run_uthread(struct uthread *uthread)
 {
+       assert(uthread != current_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];
index 0a7fce9..0b782fd 100644 (file)
@@ -34,6 +34,7 @@ struct uthread *pth_thread_create(void (*func)(void), void *udata);
 void pth_thread_runnable(struct uthread *uthread);
 void pth_thread_yield(struct uthread *uthread);
 void pth_thread_exit(struct uthread *uthread);
+unsigned int pth_vcores_wanted(void);
 void pth_preempt_pending(void);
 void pth_spawn_thread(uintptr_t pc_start, void *data);
 
@@ -44,6 +45,7 @@ struct schedule_ops pthread_sched_ops = {
        pth_thread_runnable,
        pth_thread_yield,
        pth_thread_exit,
+       pth_vcores_wanted,
        0, /* pth_preempt_pending, */
        0, /* pth_spawn_thread, */
 };
@@ -84,7 +86,7 @@ struct uthread *pth_init(void)
 /* 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)
+void __attribute__((noreturn)) pth_sched_entry(void)
 {
        if (current_uthread) {
                run_current_uthread();
@@ -102,7 +104,9 @@ void pth_sched_entry(void)
                threads_ready--;
        }
        mcs_lock_unlock(&queue_lock, &local_qn);
-       /* For now, this dumb logic is done here */
+       /* Instead of yielding, you could spin, turn off the core, set an alarm,
+        * whatever.  You want some logic to decide this.  Uthread code wil have
+        * helpers for this (like how we provide run_uthread()) */
        if (!new_thread) {
                /* TODO: consider doing something more intelligent here */
                printd("[P] No threads, vcore %d is yielding\n", vcore_id());
@@ -199,6 +203,13 @@ void pth_thread_exit(struct uthread *uthread)
        pthread->finished = 1;
 }
 
+/* Returns how many *more* vcores we want.  Smarter schedulers should look at
+ * the num_vcores() and how much work is going on to make this decision. */
+unsigned int pth_vcores_wanted(void)
+{
+       return 1;
+}
+
 void pth_preempt_pending(void)
 {
 }