Separates EVENT_INDIR from EVENT_IPI (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 11 Aug 2011 22:21:15 +0000 (15:21 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:36:06 +0000 (17:36 -0700)
Previously, and IPI implied and INDIR.  Now you can do them
independently, such as if you want INDIR messages that you'll poll out
of the VCPD.  Read the new Documentation.

Recompile / reinstall your kernel headers.

Documentation/async_events.txt
kern/include/ros/event.h
kern/src/event.c
user/pthread/pthread.c

index 0b44161..49a4ef9 100644 (file)
@@ -323,39 +323,104 @@ There are also interfaces in the kernel to put a message in an ev_mbox
 regardless of the process's wishes (post_vcore_event()), and can send an IPI
 at any time (proc_notify()).
 
-3.3 IPIs and Indirection Events
+3.3 IPIs, Indirection Events, and Fallback
 ----------------------------------------------
-When an ev_q calls for an IPI, the kernel finds out what vcore the process
-wants.  The code already sent the message to the ev_q's mbox.  If the vcore's
-vcpd mbox is the same as the ev_q's mbox (pointer check), then just send the
-IPI.  If it is different, we need to put a message in the vcpd's mbox telling
-them "ev_q*", so the vcore knows why it got an IPI.  This level of indirection
-is only necessary when the ev_q requests an IPI and it is not the vcore using
-its vcpd mbox.  The vcore needs to know why it received an IPI.  The IPI
-(active notifcation) is merely a prodding, and the vcore needs a known place
-to look for why it was woken up.  This is a little different when dealing with
-non-specific IPIs (like Round-Robin).
-
-If the vcore gets an indirection message, it will be of type NE_EVENT (or
-whatever), with an ev_q* as the payload.
-
-In the event there are issues with this, we can introduce a flag that says we
-don't need a separate notif_event explaining the IPI: prodding the vcore was
-enough.  Either way, we can deliver event messages directly to the vcore's
-mbox / bcq.
-
-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 vcore will
-eventually get it.  The real way around this is create a new ev_q and change
-the pointer (thus atomically changing the entire ev_q's contents), though this
-can be a bit tricky if you have multiple places pointing to the same ev_q
+An ev_q can ask for an IPI, an indirection event, and a fallback in case a vcore
+is offline.  Or any combination of these.  Note that these have little to do
+with the actual message being sent.  The actual message is dropped in the
+ev_mbox pointed to by the ev_q.
+
+The main use for all of this is for syscalls.  If you want to receive an event
+when a syscall completes or has a change in status, simply allocate an event_q,
+and point the syscall at it.  syscall: ev_q* -> "vcore for IPI, syscall message
+in the ev_q mbox", etc.  You can also point it to an existing ev_q.  Pthread
+code has examples of two ways to do this.  Both have per vcore ev_qs, requesting
+IPIs, INDIRS, and FALLBACK.  One way is to have an ev_mbox per vcore, and
+another is to have a global ev_mbox that all ev_qs point to.  As a side note, if
+you do the latter, you don't need to worry about a vcore's ev_q if it gets
+preempted: just check the global ev_mbox (which is done by checking your own
+vcore's syscall ev_q).
+
+3.3.1: IPIs and INDIRs
+---------------
+An EVENT_IPI simply means we'll send an IPI to the given vcore.  Nothing else.
+This will usually be paired with an Indirection event (EVENT_INDIR).  An INDIR
+is a message of type EV_EVENT with an ev_q* payload.  It means "check this
+ev_q".  Most ev_qs that ask for an IPI will also want an INDIR so that the vcore
+knows why it was IPIed.  You don't have to do this: for instance, your 2LS might
+poll its own ev_q, so you won't need the indirection event.
+
+Additionally, note that IPIs and INDIRs can be spurious.  It's not a big deal to
+receive and IPI and have nothing to do, or to be told to check an empty ev_q.
+All of the event handling code can deal with this.  The only thing you shouldn't
+do is have one vcore handle another's VCPD.  That mbox is meant for
+vcore-business.  Never use a VCPD for messages you might want to receive if that
+vcore is offline.
+
+3.3.2: Fallback
+---------------
+Both IPI and INDIR need an actual vcore.  If that vcore is offline and if
+EVENT_FALLBACK is set, the kernel will pick an online vcore and send the
+messages there.  This allows an ev_q to be set up to handle work when the vcore
+is online, while allowing the program to handle events when that core goes
+offline: perhaps due to a yield or a preemption, without having to reset all of
+its ev_qs to point to "known" online vcores (and avoiding those races).
+
+One question is whether or not 2LSs need a FALLBACK flag for their ev_qs.  The
+main use for FALLBACK is so that vcores can yield.  (Note that fallback won't
+help you *miss* INDIR messages in the event of a preemption; you can always lose
+that race due to it taking too long to process the messages).  An alternative
+would be for vcores to pick another vcore and change all of its ev_qs to that
+vcore.  There are a couple problems with this.  One is that it'll be a pain to
+get those ev_qs back when the vcore comes back online (if ever).  Another issue
+is that other vcores will build up a list of ev_qs that they aren't aware of,
+which will be hard to deal with when *they* yield.  FALLBACK avoids all of those
+problems.
+
+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
+vcore will eventually get it.  The real way around this is create a new ev_q and
+change the pointer (thus atomically changing the entire ev_q's contents), though
+this can be a bit tricky if you have multiple places pointing to the same ev_q
 (can't change them all at once).
 
-If you want to receive an event when a syscall completes or has a change in
-status, simply allocate an event_q, and point the syscall at it.  syscall:
-ev_q* -> "vcore for IPI, syscall message in the ev_q mbox", etc.  You can also
-point it to an existing ev_q.
+3.3.3: Fallback and Preemption
+---------------
+FALLBACK doesn't protect you from preemptions.  A vcore can be preempted and
+have INDIRs in its VCPD.  Dealing with this is part of the 2LSs job.  The 2LS
+must check the ev_mboxes/ev_qs of all ev_qs that could send INDIRS to the
+offline vcore.  There could be INDIRS in the VCPD that are just lying there.
+The 2LS knows which ev_qs these are (such as for completed syscalls), and for
+many things, this will be a common ev_q (such as for 'vcore-x-was-preempted').
+
+A couple things will ameliorate this:
+1) the 2LS can inspect the VCPD of the target.  If it is empty, then we don't
+have to worry about any INDIRs, and we're done worrying about their messages
+(though we still have to worry about them being stuck in vcore context and
+having a uthread).  Also note you only need to check the UCQ, since INDIRs must
+be messages, not bits.
+2) the kernel will initiate FALLBACK if the vcore has a preempt pending, which
+ought to cut down on this happening frequently.
+
+It is tempting to just use sys_change_vcore(), which will change the calling
+vcore to the new one.  This should only be used to "unstick" a vcore.  A vcore
+is stuck when it was preempted while it had notifications disabled.  This is
+usually when it is vcore context, but also in any lock holding code for locks
+shared with vcore context (the userspace equivalent of irqsave locks).  With
+this syscall, you could change to the offline vcore and process its INDIRs.
+
+The problem with that plan is the calling core (that is trying to save the
+other) may have extra messages, and that sys_change_vcore does not return.  We
+need a way to deal with our other messages.  We're back to the same problem we
+had before, just with different vcores.  The only thing we really accomplished
+is that we unstuck the other vcore.  We could tell the restarted vcore (via an
+event) to switch back to us, but by the time it does that, it may have other
+events that got lost.  So we're back to polling the ev_qs that it might have
+received INDIRs about.  Note that we still want to send an event with
+sys_change_vcore().  We want the new vcore to know the old vcore was put
+offline: a preemption (albeit one that it chose to do, and one that isn't stuck
+in vcore context).
 
 3.4 Application-specific Event Handling
 ---------------------------------------
index 11616f5..f53050e 100644 (file)
 /* #include <ros/ucq.h> included below */
 
 /* Event Delivery Flags from the process to the kernel */
-#define EVENT_IPI                              0x001   /* IPI the core */
-#define EVENT_NOMSG                            0x002   /* just send the bit, not the msg */
-#define EVENT_ROUNDROBIN               0x004   /* pick a vcore, RR style */
-#define EVENT_VCORE_APPRO              0x008   /* send to where the kernel wants */
+#define EVENT_IPI                              0x001   /* IPI the vcore (usually with INDIR) */
+#define EVENT_INDIR                            0x002   /* send an indirection event to vcore */
+#define EVENT_FALLBACK                 0x004   /* pick another vcore if it's offline */
+#define EVENT_NOMSG                            0x008   /* just send the bit, not the msg */
+#define EVENT_ROUNDROBIN               0x010   /* pick a vcore, RR style */
+#define EVENT_VCORE_APPRO              0x020   /* send to where the kernel wants */
 /* Flags from the program to the 2LS */
-#define EVENT_JUSTHANDLEIT             0x00c   /* 2LS should handle the ev_q */
-#define EVENT_THREAD                   0x010   /* spawn thread to handle ev_q */
+#define EVENT_JUSTHANDLEIT             0x100   /* 2LS should handle the ev_q */
+#define EVENT_THREAD                   0x200   /* spawn thread to handle ev_q */
 
 /* Event Message Types */
 #define EV_NONE                                         0
index 2565099..14d14d8 100644 (file)
@@ -81,20 +81,24 @@ void send_event(struct proc *p, struct event_queue *ev_q, struct event_msg *msg,
        }
        /* Check on the style, which could affect our mbox selection.  Other styles
         * would go here (or in similar functions we call to).  Important thing is
-        * we come out knowing which vcore to send to in the event of an IPI, and we
-        * know what mbox to post to. */
+        * we come out knowing which vcore to send to in the event of an IPI/INDIR,
+        * and we know what mbox to post to. */
        if (ev_q->ev_flags & EVENT_ROUNDROBIN) {
                /* Pick a vcore, and if we don't have a mbox yet, pick that vcore's
                 * default mbox.  Assuming ev_vcore was the previous one used.  Note
                 * that round-robin overrides the passed-in vcoreid. */
                vcoreid = (ev_q->ev_vcore + 1) % p->procinfo->num_vcores;
                ev_q->ev_vcore = vcoreid;
+               /* Note that the style of not having a specific ev_mbox may go away.  I
+                * can't think of legitimate uses of this for now, since things that are
+                * RR probably are non-vcore-business, and thus inappropriate for a VCPD
+                * ev_mbox. */
                if (!ev_mbox)
                        ev_mbox = get_proc_ev_mbox(vcoreid);
        }
        /* At this point, we ought to have the right mbox to send the msg to, and
         * which vcore to send an IPI to (if we send one).  The mbox could be the
-        * vcore's vcpd ev_mbox. */
+        * vcore's vcpd ev_mbox.  The vcoreid only matters for IPIs and INDIRs. */
        if (!ev_mbox) {
                /* this is a process error */
                warn("[kernel] ought to have an mbox by now!");
@@ -111,21 +115,19 @@ void send_event(struct proc *p, struct event_queue *ev_q, struct event_msg *msg,
         * vehicle for sending the ev_type. */
        assert(msg);
        post_ev_msg(ev_mbox, msg, ev_q->ev_flags);
-       /* Optional IPIs */
-       if (ev_q->ev_flags & EVENT_IPI) {
-               /* if the mbox we sent to isn't the default one, we need to send the
-                * vcore an ev_q indirection event */
+       /* Vcore options: IPIs and INDIRs */
+       if (ev_q->ev_flags & EVENT_INDIR) {
                vcore_mbox = get_proc_ev_mbox(vcoreid);
-               if (ev_mbox != vcore_mbox) {
-                       /* it is tempting to send_kernel_event(), using the ev_q for that
-                        * event, but that is inappropriate here, since we are sending to a
-                        * specific vcore */
-                       local_msg.ev_type = EV_EVENT;
-                       local_msg.ev_arg3 = ev_q;
-                       post_ev_msg(vcore_mbox, &local_msg, 0);
-               }
-               proc_notify(p, vcoreid);
+               /* Help out userspace, since we can detect this bug:*/
+               if (ev_mbox == vcore_mbox)
+                       printk("[kernel] EVENT_INDIR requested for a VCPD mbox!\n");
+               /* Actually post the INDIR */
+               local_msg.ev_type = EV_EVENT;
+               local_msg.ev_arg3 = ev_q;
+               post_ev_msg(vcore_mbox, &local_msg, 0);
        }
+       if (ev_q->ev_flags & EVENT_IPI)
+               proc_notify(p, vcoreid);
        /* Fall through */
 out:
        /* Return to the old address space. */
index 29f29a4..f8ce256 100644 (file)
@@ -95,7 +95,7 @@ struct uthread *pth_init(void)
        for (int i = 0; i < max_vcores(); i++) {
                /* Each vcore needs to point to a non-VCPD ev_q */
                sysc_mgmt[i].ev_q = get_big_event_q_raw();
-               sysc_mgmt[i].ev_q->ev_flags = EVENT_IPI;                /* totally up to you */
+               sysc_mgmt[i].ev_q->ev_flags = EVENT_IPI | EVENT_INDIR;  /* up to you */
                sysc_mgmt[i].ev_q->ev_vcore = i;
                ucq_init_raw(&sysc_mgmt[i].ev_q->ev_mbox->ev_msgs, 
                             mmap_block + (2 * i    ) * PGSIZE, 
@@ -115,7 +115,7 @@ struct uthread *pth_init(void)
        ucq_init_raw(&sysc_mbox->ev_msgs, two_pages, two_pages + PGSIZE);
        for (int i = 0; i < max_vcores(); i++) {
                sysc_mgmt[i].ev_q = get_event_q();
-               sysc_mgmt[i].ev_q->ev_flags = EVENT_IPI;
+               sysc_mgmt[i].ev_q->ev_flags = EVENT_IPI | EVENT_INDIR;
                sysc_mgmt[i].ev_q->ev_vcore = i;
                sysc_mgmt[i].ev_q->ev_mbox = sysc_mbox;
        }