proc_wakeup() - spammable and starts in the ksched
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 25 Apr 2012 00:16:33 +0000 (17:16 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 25 Apr 2012 00:16:33 +0000 (17:16 -0700)
Need to have the ksched lock held while waking up so that we atomically
wake up and alert the ksched (via the callbacks).  If we grab the ksched
lock after the proc lock, we can deadlock.  If we unlock the proc lock
before telling the ksched, we could have another core try a
proc_destroy(), and the ksched would destroy it after the old
__proc_wakeup, but before the ksched finds out about the wakeup.  So we
enter the call via the ksched, and grab its lock first, like with
destroy().

Note that excessive spamming of proc_wakeup would hurt the ksched a bit
(coarser lock than all the proc locks), but all cases expect it to be a
necessary call.  For instance, the event code all peaks at the state of
p, making sure it was WAITING before bothering the ksched.  We'd have
excessive calls only if two or more cores tried that at the same time
and all saw WAITING before the ksched called __proc_wakeup to change it
to RUNNABLE.

kern/include/schedule.h
kern/src/event.c
kern/src/process.c
kern/src/schedule.c

index 2465fe4..04418c8 100644 (file)
@@ -23,13 +23,17 @@ struct sched_proc_data {
 
 void schedule_init(void);
 
-/************** Process registration **************/
+/************** Process management **************/
 /* Tell the ksched about the process, which it will track cradle-to-grave */
 void register_proc(struct proc *p);
 
 /* _S is now runnable, tell the ksched to try to run it. */
 void schedule_scp(struct proc *p);
 
+/* Makes sure p is runnable.  Callers include event delivery, SCP yield, and new
+ * SCPs.  Will trigger the __sched_.cp_wakeup() callbacks. */
+void proc_wakeup(struct proc *p);
+
 /* The ksched starts the death process (lock ordering issue), which calls back
  * to proc.c's __proc_destroy while holding the locks (or whatever) */
 void proc_destroy(struct proc *p);
@@ -47,10 +51,9 @@ void schedule(void);
  * be careful of abuse. */
 void poke_ksched(struct proc *p, int res_type);
 
-/* Proc p just woke up (due to an event).  This is a more specific case than
- * poke_ksched(), in case kscheds want to do some accounting or something more
- * than just giving it cores. */
-void ksched_proc_unblocked(struct proc *p);
+/* Callbacks triggered from proc_wakeup() */
+void __sched_mcp_wakeup(struct proc *p);
+void __sched_scp_wakeup(struct proc *p);
 
 /* The calling cpu/core has nothing to do and plans to idle/halt.  This is an
  * opportunity to pick the nature of that halting (low power state, etc), or
index 4fd8cc7..ab1d1e4 100644 (file)
@@ -239,12 +239,8 @@ static void spam_public_msg(struct proc *p, struct event_msg *ev_msg,
                         * right as another vcore was yielding, and the preempted
                         * message was sent after the last vcore yielded (which caused
                         * us to be WAITING */
-                       if (p->state == PROC_WAITING) {
-                               spin_lock(&p->proc_lock);
-                               __proc_wakeup(p);       /* internally, this double-checks WAITING */
-                               spin_unlock(&p->proc_lock);
-                               ksched_proc_unblocked(p);
-                       }
+                       if (p->state == PROC_WAITING)
+                               proc_wakeup(p); /* internally, this double-checks WAITING */
                        return;
                }
        }
@@ -288,10 +284,9 @@ ultimate_fallback:
        /* The first event to catch the process with no online/bp vcores will need
         * to wake it up.  (We could be RUNNABLE_M here if another event already woke
         * us.) and we didn't get lucky with the penultimate fallback.
-        * __proc_wakeup() will check for WAITING. */
-       __proc_wakeup(p);
+        * proc_wakeup (and __proc_wakeup()) will check for WAITING. */
        spin_unlock(&p->proc_lock);
-       ksched_proc_unblocked(p);
+       proc_wakeup(p);
        return;
 }
 
@@ -366,12 +361,8 @@ void send_event(struct proc *p, struct event_queue *ev_q, struct event_msg *msg,
                wrmb(); /* don't let the notif_pending write pass the state read */
                /* using the same pattern as in spam_public (which can have multiple
                 * unblock callbacks */
-               if (p->state == PROC_WAITING) {
-                       spin_lock(&p->proc_lock);
-                       __proc_wakeup(p);
-                       spin_unlock(&p->proc_lock);
-                       ksched_proc_unblocked(p);
-               }
+               if (p->state == PROC_WAITING)
+                       proc_wakeup(p);
                goto out;
        }
        /* Get the vcoreid that we'll message (if appropriate).  For INDIR and
index 9dc7fde..25645a3 100644 (file)
@@ -1095,23 +1095,53 @@ void proc_notify(struct proc *p, uint32_t vcoreid)
        }
 }
 
-/* Hold the lock before calling this.  If the process is WAITING, it will wake
- * it up and schedule it. */
+/* Makes sure p is runnable.  May be spammed, via the ksched.  Called only by
+ * the ksched when it holds the ksched lock (or whatever).  We need to lock both
+ * the ksched and the proc at some point, so we need to start this call in the
+ * ksched (lock ordering).
+ *
+ * Will call back to the ksched via one of the __sched_.cp_wakeup() calls. */
 void __proc_wakeup(struct proc *p)
 {
-       if (p->state != PROC_WAITING)
-               return;
+       spin_lock(&p->proc_lock);
        if (__proc_is_mcp(p)) {
-               /* Need to make sure they want at least 1 vcore, so the ksched gives
-                * them something.  Might do this via short handler later. */
+               /* we only wake up WAITING mcps */
+               if (p->state != PROC_WAITING)
+                       goto out_unlock;
                if (!p->procdata->res_req[RES_CORES].amt_wanted)
                        p->procdata->res_req[RES_CORES].amt_wanted = 1;
                __proc_set_state(p, PROC_RUNNABLE_M);
+               spin_unlock(&p->proc_lock);
+               __sched_mcp_wakeup(p);
+               goto out;
        } else {
+               /* SCPs can wake up for a variety of reasons.  the only times we need
+                * to do something is if it was waiting or just created.  other cases
+                * are either benign (just go out), or potential bugs (_Ms) */
+               switch (p->state) {
+                       case (PROC_CREATED):
+                       case (PROC_WAITING):
+                               __proc_set_state(p, PROC_RUNNABLE_S);
+                               break;
+                       case (PROC_RUNNABLE_S):
+                       case (PROC_RUNNING_S):
+                       case (PROC_DYING):
+                               goto out_unlock;
+                       case (PROC_RUNNABLE_M):
+                       case (PROC_RUNNING_M):
+                               warn("Weird state(%s) in %s()", procstate2str(p->state),
+                                    __FUNCTION__);
+                               goto out_unlock;
+               }
                printd("[kernel] FYI, waking up an _S proc\n"); /* thanks, past brho! */
-               __proc_set_state(p, PROC_RUNNABLE_S);
-               schedule_scp(p);
+               spin_unlock(&p->proc_lock);
+               __sched_scp_wakeup(p);
+               goto out;
        }
+out_unlock:
+       spin_unlock(&p->proc_lock);
+out:
+       return;
 }
 
 /* Is the process in multi_mode / is an MCP or not?  */
index 27d63b6..057acef 100644 (file)
@@ -156,6 +156,12 @@ static void switch_lists(struct proc *p, struct proc_list *old,
        add_to_list(p, new);
 }
 
+static void __remove_from_any_list(struct proc *p)
+{
+       if (p->ksched_data.cur_list)
+               TAILQ_REMOVE(p->ksched_data.cur_list, p, ksched_data.proc_link);
+}
+
 /* Removes from whatever list p is on */
 static void remove_from_any_list(struct proc *p)
 {
@@ -215,6 +221,18 @@ int proc_change_to_m(struct proc *p)
        return retval;
 }
 
+/* Makes sure p is runnable.  Callers may spam this, so it needs to handle
+ * repeated calls for the same event.  Callers include event delivery, SCP
+ * yield, and new SCPs.  Most every scheduler should do something like this -
+ * grab whatever lock you have, then call the proc helper. */
+void proc_wakeup(struct proc *p)
+{
+       spin_lock(&sched_lock);
+       /* will trigger one of the __sched_.cp_wakeup()s */
+       __proc_wakeup(p);
+       spin_unlock(&sched_lock);
+}
+
 /* Destroys the given process.  This may be called from another process, a light
  * kernel thread (no real process context), asynchronously/cross-core, or from
  * the process on its own core.
@@ -334,15 +352,19 @@ void poke_ksched(struct proc *p, int res_type)
        spin_unlock(&sched_lock);
 }
 
-/* Proc p just woke up (due to an event).  Our dumb ksched will just try to deal
- * with its core desires. 
- * TODO: this may get called multiple times per unblock */
-void ksched_proc_unblocked(struct proc *p)
+/* ksched callbacks.  p just woke up, is unlocked, and the ksched lock is held */
+void __sched_mcp_wakeup(struct proc *p)
+{
+       /* the essence of poke_ksched for RES_CORES */
+       __core_request(p);
+}
+
+/* ksched callbacks.  p just woke up, is unlocked, and the ksched lock is held */
+void __sched_scp_wakeup(struct proc *p)
 {
-       /* TODO: this now gets called when an _S unblocks.  schedule_scp() also gets
-        * called, so the process is on the _S runqueue.  Might merge the two in the
-        * future. */
-       poke_ksched(p, RES_CORES);
+       /* might not be on a list if it is new.  o/w, it should be unrunnable */
+       __remove_from_any_list(p);
+       add_to_list(p, &runnable_scps);
 }
 
 /* The calling cpu/core has nothing to do and plans to idle/halt.  This is an