proc_yield() will return if you have an event
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 14 Sep 2011 01:49:11 +0000 (18:49 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:36:07 +0000 (17:36 -0700)
INDIRs will now set notif_pending, whose meaning changes slightly to be
"check your shit".  It's actually always been that, but that was before
we split up INDIRs and IPIs.  Without this, we could miss INDIRs while
trying to leave vcore context.

The intuition here is that processes should not leave vcore context
while notif_pending is set.  There are two ways out, one of which has
been sorted for a while: pop_ros_tf() (note the other uses of
clear_notif_pending()).  The other way is via proc_yield(), and the
kernel will help if we lose those races (racing with send_event()).

When yielding, if we lost the race we bail out.  This is a little
different than the usages of clear_notif_pending() in u/p/uthread.c.  In
those cases, we'll go ahead and run the thread, and the events don't get
a chance to change the mind of the 2LS.  (We can change this in the
future).  When vcore_yield() fails, we go back into the main 2LS loop
(in pthread.c).

Note some of this stuff will be more useful in future patches.  The
details of FALLBACK will change shortly.

Documentation/async_events.txt
kern/src/event.c
kern/src/process.c
user/parlib/include/vcore.h
user/parlib/vcore.c

index e1b04ce..e163077 100644 (file)
@@ -410,6 +410,12 @@ other useful thing is being able to handle spurious events.  Vcore code can
 handle extra IPIs and INDIRs to non-VCPD ev_qs.  Any vcore can handle an ev_q
 that is "non-VCPD business".
 
+Worth mentioning is the difference between 'notif_pending' and 'can_rcv_msg'.
+'can_rcv_msg' is the process saying it will check for messages.  'notif_pending'
+is when the kernel says it *has* sent a message.  'notif_pending' is also used
+by the kernel in proc_yield() and the 2LS in pop_ros_tf() to make sure the sent
+message is not missed.
+
 Also, in case this comes up, there's a slight race on changing the mbox* and the
 vcore number within the event_q.  The message could have gone to the wrong (old)
 vcore, but not the IPI.  Not a big deal - IPIs can be spurious, and the other
index cfb9c24..f49aa1f 100644 (file)
@@ -71,13 +71,18 @@ uint32_t find_alertable_vcore(struct proc *p, uint32_t start_loc)
        return 0;       /* vcore 0 is the most likely to come back online */
 }
 
-/* Helper to send an indir, called from a couple places */
+/* Helper to send an indir, called from a couple places.  Note this uses a
+ * userspace address for the VCPD (though not a user's pointer). */
 static void send_indir_to_vcore(struct event_queue *ev_q, uint32_t vcoreid)
 {
+       struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
        struct event_msg local_msg = {0};
        local_msg.ev_type = EV_EVENT;
        local_msg.ev_arg3 = ev_q;
        post_ev_msg(get_proc_ev_mbox(vcoreid), &local_msg, 0);
+       /* Set notif pending, so userspace doesn't miss the INDIR while yielding */
+       wmb();
+       vcpd->notif_pending = TRUE;
 }
 
 /* Helper that alerts a vcore, by IPI and/or INDIR, that it needs to check the
index 751f935..3521949 100644 (file)
@@ -781,6 +781,7 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
 {
        uint32_t vcoreid = get_vcoreid(p, core_id());
        struct vcore *vc = vcoreid2vcore(p, vcoreid);
+       struct preempt_data *vcpd = &p->procdata->vcore_preempt_data[vcoreid];
 
        /* no reason to be nice, return */
        if (being_nice && !vc->preempt_pending)
@@ -798,7 +799,17 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
        /* no need to preempt later, since we are yielding (nice or otherwise) */
        if (vc->preempt_pending)
                vc->preempt_pending = 0;
-
+       /* Don't let them yield if they are missing a notification.  Userspace must
+        * not leave vcore context without dealing with notif_pending.  pop_ros_tf()
+        * handles leaving via uthread context.  This handles leaving via a yield.
+        *
+        * This early check is an optimization.  The real check is below when it
+        * works with the online_vcs list (syncing with event.c and INDIR/IPI
+        * posting). */
+       if (vcpd->notif_pending) {
+               spin_unlock(&p->proc_lock);
+               return;
+       }
        switch (p->state) {
                case (PROC_RUNNING_S):
                        __proc_yield_s(p, current_tf);  /* current_tf 0'd in abandon core */
@@ -812,11 +823,23 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
                                spin_unlock(&p->proc_lock);
                                return;
                        }
-                       __seq_start_write(&p->procinfo->coremap_seqctr);
                        /* Remove from the online list, add to the yielded list, and unmap
                         * the vcore, which gives up the core. */
                        TAILQ_REMOVE(&p->online_vcs, vc, list);
+                       /* Now that we're off the online list, check to see if an alert made
+                        * it through (event.c sets this) */
+                       cmb();
+                       if (vcpd->notif_pending) {
+                               /* We lost, put it back on the list and abort the yield */
+                               TAILQ_INSERT_TAIL(&p->online_vcs, vc, list); /* could go HEAD */
+                               spin_unlock(&p->proc_lock);
+                               return;
+                       }
+                       /* We won the race with event sending, we can safely yield */
                        TAILQ_INSERT_HEAD(&p->inactive_vcs, vc, list);
+                       /* Note this protects stuff userspace should look at, which doesn't
+                        * include the TAILQs. */
+                       __seq_start_write(&p->procinfo->coremap_seqctr);
                        __unmap_vcore(p, vcoreid);
                        /* Adjust implied resource desires */
                        p->resources[RES_CORES].amt_granted = --(p->procinfo->num_vcores);
@@ -1446,6 +1469,7 @@ void __startcore(struct trapframe *tf, uint32_t srcid, long a0, long a1, long a2
                        proc_secure_trapframe(&local_tf);
                }
        } else { /* not restarting from a preemption, use a fresh vcore */
+               assert(vcpd->transition_stack);
                proc_init_trapframe(&local_tf, vcoreid, p_to_run->env_entry,
                                    vcpd->transition_stack);
                /* Disable/mask active notifications for fresh vcores */
index 4d460f7..5b2be7a 100644 (file)
@@ -48,7 +48,7 @@ static inline bool notif_is_enabled(uint32_t vcoreid);
 int vcore_init(void);
 int vcore_request(size_t k);
 void vcore_yield(bool preempt_pending);
-void clear_notif_pending(uint32_t vcoreid);
+bool clear_notif_pending(uint32_t vcoreid);
 void enable_notifs(uint32_t vcoreid);
 void disable_notifs(uint32_t vcoreid);
 void vcore_idle(void);
index 434774b..1dd7d88 100644 (file)
@@ -202,13 +202,22 @@ void vcore_yield(bool preempt_pending)
        struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
        vcpd->can_rcv_msg = FALSE;
        wmb();
-       if (handle_events(vcoreid)) {
-               /* we handled outstanding events, turn the flag back on and return */
+       /* Clears notif pending.  If we had an event outstanding, this will handle
+        * it and return TRUE, at which point we want to unwind and return to the
+        * 2LS loop (where we may not want to yield anymore).  Note that the kernel
+        * only cares about can_rcv_msg for the desired vcore, not for a FALLBACK.
+        * We need to deal with this notif_pending business regardless of
+        * can_rcv_msg.  We just want to avoid a yield syscall if possible.  It is
+        * important that clear_notif_pending will handle_events().  That is
+        * necessary to do/check after setting can_rcv_msg to FALSE. */
+       if (clear_notif_pending(vcoreid)) {
                vcpd->can_rcv_msg = TRUE;
                return;
        }
-       /* o/w, we can safely yield */
+       /* We can probably yield.  This may pop back up if notif_pending became set
+        * by the kernel after we cleared it and we lost the race. */
        sys_yield(preempt_pending);
+       vcpd->can_rcv_msg = TRUE;
 }
 
 /* Clear pending, and try to handle events that came in between a previous call
@@ -217,13 +226,16 @@ void vcore_yield(bool preempt_pending)
  * events, and we will have send pending to 0. 
  *
  * Note that this won't catch every race/case of an incoming event.  Future
- * events will get caught in pop_ros_tf() */
-void clear_notif_pending(uint32_t vcoreid)
+ * events will get caught in pop_ros_tf() or proc_yield() */
+bool clear_notif_pending(uint32_t vcoreid)
 {
+       bool handled_event = FALSE;
        do {
                cmb();
                __procdata.vcore_preempt_data[vcoreid].notif_pending = 0;
-       } while (handle_events(vcoreid));
+               handled_event = handle_events(vcoreid);
+       } while (handled_event);
+       return handled_event;
 }
 
 /* Enables notifs, and deals with missed notifs by self notifying.  This should