Ksched preempts SCPs on schedule() calls
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 30 Mar 2012 21:24:15 +0000 (14:24 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 30 Mar 2012 21:24:15 +0000 (14:24 -0700)
Also will check for SCPs to run when the core has nothing to do
(cpu_bored()), as opposed to waiting til the next timer tick.

kern/src/kthread.c
kern/src/process.c
kern/src/schedule.c

index 0f0883c..0427704 100644 (file)
@@ -239,9 +239,10 @@ void __launch_kthread(struct trapframe *tf, uint32_t srcid, long a0, long a1,
                 * ROUTINE (which it is right now). */
                if (pcpui->owning_proc->state == PROC_RUNNING_S) {
                        spin_lock(&pcpui->owning_proc->proc_lock);
-                       /* Wrap up / yield the _S proc, which calls schedule_proc */
+                       /* Wrap up / yield the _S proc */
                        __proc_yield_s(pcpui->owning_proc, pcpui->cur_tf);
                        spin_unlock(&pcpui->owning_proc->proc_lock);
+                       schedule_scp(p);
                        abandon_core();
                        /* prob need to clear the owning proc?  this is some old shit, so
                         * don't just uncomment it. */
index e8b8a63..4f6dcac 100644 (file)
@@ -634,6 +634,8 @@ void proc_restartcore(void)
 {
        struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
        assert(!pcpui->cur_sysc);
+       /* TODO: can probably remove this enable_irq.  it was an optimization for
+        * RKMs */
        /* Try and get any interrupts before we pop back to userspace.  If we didn't
         * do this, we'd just get them in userspace, but this might save us some
         * effort/overhead. */
@@ -883,7 +885,6 @@ void __proc_yield_s(struct proc *p, struct trapframe *tf)
        assert(p->state == PROC_RUNNING_S);
        __proc_set_state(p, PROC_RUNNABLE_S);
        __proc_save_context_s(p, tf);
-       schedule_scp(p);
 }
 
 /* Yields the calling core.  Must be called locally (not async) for now.
@@ -962,6 +963,7 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
                        } else {
                                /* yielding to allow other processes to run */
                                __proc_yield_s(p, current_tf);
+                               schedule_scp(p);
                        }
                        spin_unlock(&p->proc_lock);     /* note that irqs are not enabled yet */
                        goto out_yield_core;
index c0084bd..371fb7e 100644 (file)
@@ -153,6 +153,55 @@ void register_mcp(struct proc *p)
        //poke_ksched(p, RES_CORES);
 }
 
+/* mgmt/LL cores should call this to schedule the calling core and give it to an
+ * SCP.  will also prune the dead SCPs from the list.  hold the lock before
+ * calling.  returns TRUE if it scheduled a proc. */
+static bool __schedule_scp(void)
+{
+       struct proc *p;
+       uint32_t pcoreid = core_id();
+       struct per_cpu_info *pcpui = &per_cpu_info[pcoreid];
+       int8_t state = 0;
+       /* prune any dying SCPs at the head of the queue and maybe sched our core
+        * (let all the cores do this, whoever happens to be running schedule()). */
+       while ((p = TAILQ_FIRST(&runnable_scps))) {
+               if (p->state == PROC_DYING) {
+                       TAILQ_REMOVE(&runnable_scps, p, proc_link);
+                       proc_decref(p);
+               } else {
+                       /* protect owning proc, cur_tf, etc.  note this nests with the
+                        * calls in proc_yield_s */
+                       disable_irqsave(&state);
+                       /* someone is currently running, dequeue them */
+                       if (pcpui->owning_proc) {
+                               printd("Descheduled %d in favor of %d\n",
+                                      pcpui->owning_proc->pid, p->pid);
+                               __proc_yield_s(pcpui->owning_proc, pcpui->cur_tf);
+                               /* round-robin the SCPs */
+                               TAILQ_INSERT_TAIL(&runnable_scps, pcpui->owning_proc,
+                                                 proc_link);
+                               /* could optimize the refcnting if we cared */
+                               proc_incref(pcpui->owning_proc, 1);
+                               clear_owning_proc(pcoreid);
+                               /* Note we abandon core.  It's not strictly necessary.  If
+                                * we didn't, the TLB would still be loaded with the old
+                                * one, til we proc_run_s, and the various paths in
+                                * proc_run_s would pick it up.  This way is a bit safer for
+                                * future changes, but has an extra (empty) TLB flush.  */
+                               abandon_core();
+                       } 
+                       /* Run the new 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);
+                       enable_irqsave(&state);
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+
 /* Something has changed, and for whatever reason the scheduler should
  * reevaluate things. 
  *
@@ -160,7 +209,6 @@ void register_mcp(struct proc *p)
 void schedule(void)
 {
        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 */
@@ -184,22 +232,8 @@ void schedule(void)
                        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 {
-                       /* check our core to see if we can give it out to an SCP */
-                       if (management_core() && (!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;
-               }
-       }
+       if (management_core())
+               __schedule_scp();
        spin_unlock(&sched_lock);
 }
 
@@ -240,10 +274,20 @@ void ksched_proc_unblocked(struct proc *p)
  * disabled, and if you return, the core will cpu_halt(). */
 void cpu_bored(void)
 {
+       bool new_proc = FALSE;
        if (!management_core())
                return;
-       /* TODO run a process, and if none exist at all and we're core 0, bring up
-        * the monitor/manager */
+       spin_lock(&sched_lock);
+       new_proc = __schedule_scp();
+       spin_unlock(&sched_lock);
+       /* if we just scheduled a proc, we need to manually restart it, instead of
+        * returning.  if we return, the core will halt. */
+       if (new_proc) {
+               proc_restartcore();
+               assert(0);
+       }
+       /* Could drop into the monitor if there are no processes at all.  For now,
+        * the 'call of the giraffe' suffices. */
 }
 
 /* Helper function to return a core to the idlemap.  It causes some more lock