Ksched tracks MCPs for their entire lifetime
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 28 Feb 2012 21:23:41 +0000 (13:23 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 29 Feb 2012 22:52:52 +0000 (14:52 -0800)
_Ss still work like traditional schedulers - we tell the ksched about
them when they are runnable.  _Ms are always tracked, regardless of
running or not.

There's some minor races with proc state that give_cores can handle,
though I'd like to change that part a bit.

Also note that schedule() grabs proclocks.  Don't call this when holding
a proclock or from interrupt context.

kern/include/schedule.h
kern/src/monitor.c
kern/src/process.c
kern/src/resource.c
kern/src/schedule.c
kern/src/syscall.c
kern/src/testing.c

index d7676f1..2d1e7a2 100644 (file)
 
 void schedule_init(void);
 
-/*
- * Add a process to the runnable list.  If you do this, do not manually env_run
- * the process.  It must be selected with schedule().  This also applies to
- * smp_call_function to indirectly call env_run.  Currently, this is always
- * called with changing the state to RUNNABLE_S, but in the future, it might
- * need a RUNNABLE_M instead - but one or the other should be done before
- * calling this.
- */
-void schedule_proc(struct proc *p);
-
-/* Rip a process out of the runnable list */
-void deschedule_proc(struct proc *p);
+/* _S is runnable, tell the ksched to try to run it. */
+void schedule_scp(struct proc *p);
+/* _M exists.  Tell the ksched about it. */
+void register_mcp(struct proc *p);
+/* to remove from these lists, simply proc_destroy - the ksched will notice */
 
-/* Pick and run a process.  Note that this can return. */
 void schedule(void);
 
 /* Take a look at proc's resource (temp interface) */
index 92b892c..9ec6c36 100644 (file)
@@ -301,7 +301,7 @@ int mon_bin_run(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf)
 
        spin_lock(&p->proc_lock);
        __proc_set_state(p, PROC_RUNNABLE_S);
-       schedule_proc(p);
+       schedule_scp(p);
        spin_unlock(&p->proc_lock);
        proc_decref(p); /* let go of the reference created in proc_create() */
        kref_put(&program->f_kref);
index 148fc6c..4049a29 100644 (file)
@@ -536,6 +536,7 @@ void __proc_run_m(struct proc *p)
                         *   it may not get the message for a while... */
                        return;
                case (PROC_RUNNING_M):
+               case (PROC_WAITING):
                        return;
                default:
                        /* unlock just so the monitor can call something that might lock*/
@@ -823,7 +824,7 @@ void __proc_yield_s(struct proc *p, struct trapframe *tf)
        env_push_ancillary_state(p);                    /* TODO: (HSS) */
        __unmap_vcore(p, 0);    /* VC# keep in sync with proc_run_s */
        __proc_set_state(p, PROC_RUNNABLE_S);
-       schedule_proc(p);
+       schedule_scp(p);
 }
 
 /* Yields the calling core.  Must be called locally (not async) for now.
@@ -998,11 +999,13 @@ void __proc_wakeup(struct proc *p)
 {
        if (p->state != PROC_WAITING)
                return;
-       if (__proc_is_mcp(p))
+       if (__proc_is_mcp(p)) {
+               /* TODO: adjust amt_wanted here, instead of in yield */
                __proc_set_state(p, PROC_RUNNABLE_M);
-       else
+       } else {
                __proc_set_state(p, PROC_RUNNABLE_S);
-       schedule_proc(p);
+               schedule_scp(p);
+       }
 }
 
 /* Is the process in multi_mode / is an MCP or not?  */
@@ -1107,7 +1110,6 @@ void proc_preempt_core(struct proc *p, uint32_t pcoreid, uint64_t usec)
        }
        if (!p->procinfo->num_vcores) {
                __proc_set_state(p, PROC_RUNNABLE_M);
-               schedule_proc(p);
        }
        spin_unlock(&p->proc_lock);
 }
@@ -1129,7 +1131,6 @@ void proc_preempt_all(struct proc *p, uint64_t usec)
        __proc_preempt_all(p);
        assert(!p->procinfo->num_vcores);
        __proc_set_state(p, PROC_RUNNABLE_M);
-       schedule_proc(p);
        spin_unlock(&p->proc_lock);
 }
 
@@ -1268,7 +1269,8 @@ void __proc_give_cores(struct proc *p, uint32_t *pc_arr, uint32_t num)
                        panic("Don't give cores to a process in a *_S state!\n");
                        break;
                case (PROC_DYING):
-                       /* We're dying, give the cores back to the ksched and return */
+               case (PROC_WAITING):
+                       /* can't accept, give the cores back to the ksched and return */
                        for (int i = 0; i < num; i++)
                                put_idle_core(pc_arr[i]);
                        return;
index d676cba..fec001d 100644 (file)
@@ -114,7 +114,8 @@ error_t resource_req(struct proc *p, int type, size_t amt_wanted,
                        spin_lock(&p->proc_lock);
                        if (p->state == PROC_RUNNING_S) {
                                __proc_switch_to_m(p);  /* will later be a separate syscall */
-                               schedule_proc(p);
+                               /* tell the ksched about us */
+                               register_mcp(p);
                                spin_unlock(&p->proc_lock);
                        } else {
                                /* _M */
index a464058..bd89a6a 100644 (file)
@@ -19,8 +19,9 @@
 #include <sys/queue.h>
 
 /* Process Lists */
-struct proc_list proc_runnablelist = TAILQ_HEAD_INITIALIZER(proc_runnablelist);
-spinlock_t runnablelist_lock = SPINLOCK_INITIALIZER;
+struct proc_list runnable_scps = TAILQ_HEAD_INITIALIZER(runnable_scps);
+struct proc_list all_mcps = TAILQ_HEAD_INITIALIZER(all_mcps);
+spinlock_t sched_lock = SPINLOCK_INITIALIZER;
 
 // This could be useful for making scheduling decisions.  
 /* Physical coremap: each index is a physical core id, with a proc ptr for
@@ -37,7 +38,8 @@ uint32_t num_mgmtcores = 1;
 
 void schedule_init(void)
 {
-       TAILQ_INIT(&proc_runnablelist);
+       TAILQ_INIT(&runnable_scps);
+       TAILQ_INIT(&all_mcps);
 
        /* Ghetto old idle core init */
        /* Init idle cores. Core 0 is the management core. */
@@ -84,69 +86,75 @@ void schedule_init(void)
        return;
 }
 
-void schedule_proc(struct proc *p)
+/* _S procs are scheduled like in traditional systems */
+void schedule_scp(struct proc *p)
 {
        /* up the refcnt since we are storing the reference */
        proc_incref(p, 1);
-       spin_lock_irqsave(&runnablelist_lock);
+       spin_lock(&sched_lock);
        printd("Scheduling PID: %d\n", p->pid);
-       TAILQ_INSERT_TAIL(&proc_runnablelist, p, proc_link);
-       spin_unlock_irqsave(&runnablelist_lock);
-       return;
+       TAILQ_INSERT_TAIL(&runnable_scps, p, proc_link);
+       spin_unlock(&sched_lock);
+}
+
+/* important to only call this on RUNNING_S, for now */
+void register_mcp(struct proc *p)
+{
+       proc_incref(p, 1);
+       spin_lock(&sched_lock);
+       TAILQ_INSERT_TAIL(&all_mcps, p, proc_link);
+       spin_unlock(&sched_lock);
+       //poke_ksched(p, RES_CORES);
 }
 
 /* Something has changed, and for whatever reason the scheduler should
- * reevaluate things.  Currently, this assumes the calling core is free (since
- * an _S can be proc_run()'d), and it only attempts to run one process at a time
- * (which will suck, longer term, since the manager will just spin this func).
+ * reevaluate things. 
  *
- * Using irqsave spinlocks for now, since this could be called from a timer
- * interrupt handler (though ought to be in a bottom half or something).
- *     - TODO: the functions we call aren't irqsafe (proc_run, for instance), so
- *     we'll have to not call this directly from interrupt context.
- */
+ * Don't call this from interrupt context (grabs proclocks). */
 void schedule(void)
 {
-       struct proc *p;
-       
-       spin_lock_irqsave(&runnablelist_lock);
-       /* TODO at the very least, we should do a TAILQ_FOREACH_SAFE, trying to sort
-        * out everyone, but with the highest priority one first (want a priority
-        * queue or something), so people don't have to loop calling schedule().
-        *
-        * Also, we don't want a 'runnable list'.  that's so _S. */
-       p = TAILQ_FIRST(&proc_runnablelist);
-       if (p) {
-               TAILQ_REMOVE(&proc_runnablelist, p, proc_link);
-               spin_unlock_irqsave(&runnablelist_lock);
-               /* TODO: consider the process's state, or push that into
-                * give_cores/core_request */
-               /* lockless reference check, safe since we hold a ref and it's dying.
-                * Note that a process can still be killed right after we do this
-                * check, but give_cores and proc_run can handle that race. */
+       struct proc *p, *temp;
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       spin_lock(&sched_lock);
+       /* trivially try to handle the needs of all our MCPS.  smarter schedulers
+        * would do something other than FCFS */
+       TAILQ_FOREACH_SAFE(p, &all_mcps, proc_link, temp) {
+               printd("Ksched has MCP %08p (%d)\n", p, p->pid);
+               /* If they are dying, abort.  There's a bit of a race here.  If they
+                * start dying right after the check, core_request/give_cores would
+                * start dealing with a DYING proc.  The code can handle it, but this
+                * will probably change. */
                if (p->state == PROC_DYING) {
+                       TAILQ_REMOVE(&all_mcps, p, proc_link);
                        proc_decref(p);
-                       return;
+                       continue;
                }
-               /* TODO: ^^^^^ this is shit i'd like to hide from pluggable kscheds */
-               printd("PID of proc i'm running: %d\n", p->pid);
-               /* We can safely read is_mcp without locking (i think). */
-               if (__proc_is_mcp(p)) {
-                       /* _Ms need to get some cores, which will call proc_run() internally
-                        * (for now) */
-                       /* TODO: this interface sucks, change it */
-                       if (!core_request(p))
-                               schedule_proc(p);       /* can't run, put it back on the queue */
+               if (!num_idlecores)
+                       break;
+               /* TODO: might use amt_wanted as a proxy.  right now, they have
+                * amt_wanted == 1, even though they are waiting.
+                * TODO: this is RACY too - just like with DYING. */
+               if (p->state == PROC_WAITING)
+                       continue;
+               core_request(p);
+       }
+       /* prune any dying SCPs at the head of the queue and maybe sched our core */
+       while ((p = TAILQ_FIRST(&runnable_scps))) {
+               if (p->state == PROC_DYING) {
+                       TAILQ_REMOVE(&runnable_scps, p, proc_link);
+                       proc_decref(p);
                } else {
-                       /* _S proc, just run it */
-                       proc_run_s(p);
+                       /* check our core to see if we can give it out to an SCP */
+                       if (!pcpui->owning_proc) {
+                               TAILQ_REMOVE(&runnable_scps, p, proc_link);
+                               printd("PID of the SCP i'm running: %d\n", p->pid);
+                               proc_run_s(p);  /* gives it core we're running on */
+                               proc_decref(p);
+                       }
+                       break;
                }
-               /* decref the ref from the TAILQ */
-               proc_decref(p);
-       } else {
-               spin_unlock_irqsave(&runnablelist_lock);
        }
-       return;
+       spin_unlock(&sched_lock);
 }
 
 /* A process is asking the ksched to look at its resource desires.  The
@@ -214,9 +222,10 @@ uint32_t proc_wants_cores(struct proc *p, uint32_t *pc_arr, uint32_t amt_new)
 void sched_diag(void)
 {
        struct proc *p;
-       /* just print the runnables for now */
-       TAILQ_FOREACH(p, &proc_runnablelist, proc_link)
-               printk("PID: %d\n", p->pid);
+       TAILQ_FOREACH(p, &runnable_scps, proc_link)
+               printk("_S PID: %d\n", p->pid);
+       TAILQ_FOREACH(p, &all_mcps, proc_link)
+               printk("MCP PID: %d\n", p->pid);
        return;
 }
 
index fae2b19..0386bf9 100644 (file)
@@ -324,7 +324,7 @@ static error_t sys_proc_run(struct proc *p, unsigned pid)
                retval = -EINVAL;
        } else {
                __proc_set_state(target, PROC_RUNNABLE_S);
-               schedule_proc(target);
+               schedule_scp(target);
        }
        spin_unlock(&p->proc_lock);
        proc_decref(target);
@@ -447,7 +447,7 @@ static ssize_t sys_fork(env_t* e)
        clone_files(&e->open_files, &env->open_files);
        __proc_ready(env);
        __proc_set_state(env, PROC_RUNNABLE_S);
-       schedule_proc(env);
+       schedule_scp(env);
 
        // don't decref the new process.
        // that will happen when the parent waits for it.
@@ -549,7 +549,7 @@ success:
        spin_lock(&p->proc_lock);
        __unmap_vcore(p, 0);    /* VC# keep in sync with proc_run_s */
        __proc_set_state(p, PROC_RUNNABLE_S);
-       schedule_proc(p);
+       schedule_scp(p);
        spin_unlock(&p->proc_lock);
 all_out:
        /* we can't return, since we'd write retvals to the old location of the
index b7183c5..3be6a78 100644 (file)
@@ -1035,7 +1035,7 @@ void test_ucq(void)
        struct proc *p = proc_create(program, 0, p_envp);
        spin_lock(&p->proc_lock);
        __proc_set_state(p, PROC_RUNNABLE_S);
-       schedule_proc(p);
+       schedule_scp(p);
        spin_unlock(&p->proc_lock);
        /* instead of getting rid of the reference created in proc_create, we'll put
         * it in the awaiter */