Removed event overflow handling
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 28 Jul 2011 23:48:13 +0000 (16:48 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:36:05 +0000 (17:36 -0700)
You can still request a bit for a message, but we no longer deal with
true overflow, nor any related system-recovery.  The Documentation still
talks about overflow a bit, in case we need to revisit these issues.

Documentation/async_events.txt
kern/include/ros/event.h
tests/mhello.c
tests/syscall.c
user/parlib/event.c
user/parlib/include/event.h
user/pthread/pthread.c
user/pthread/pthread.h

index 976a8a6..0b44161 100644 (file)
@@ -154,32 +154,34 @@ block in the kernel.
 
 If there is a 2LS, the current thread will yield, and call out to the 2LS's
 blockon_sysc() method, which needs a way to stop the thread and be able to
-restart it when the syscall completes.  Specifically, the pthread 2LS puts the
-thread on a "syscall pending" list and registers the syscall to respond to an
-event (described in detail elsewhere in this doc).  When the event comes in,
-meaning the syscall is complete, the thread is put on the runnable list.
+restart it when the syscall completes.  Specifically, the pthread 2LS registers
+the syscall to respond to an event (described in detail elsewhere in this doc).
+When the event comes in, meaning the syscall is complete, the thread is put on
+the runnable list.
 
 Details:
-- A pointer to the struct pthread is stored in the syscall's void*.  You need
-  to be on the pending list before registering for the event (due to how we
-  handle event overflow recovery).  When the syscall is done, we normally get
-  a message from the kernel, and the payload tells us the syscall is done,
-  which tells us which thread to unblock. 
+- A pointer to the struct pthread is stored in the syscall's void*.  When the
+  syscall is done, we normally get a message from the kernel, and the payload
+  tells us the syscall is done, which tells us which thread to unblock. 
 - The pthread code also always asks for an IPI and event message for every
   syscall that completes.  This is far from ideal.  Still, the basics are the
   same for any threading library.  Once you know a thread is done, you need to
   do something about it.
-- The pthread code does syscall blocking/ pending lists and event notification
-  on a per-core basis.
+- The pthread code does syscall blocking and event notification on a per-core
+  basis.  Using the default (VCPD) ev_mbox for this is a bad idea (which we did
+  at some point).
 - There's a race between the 2LS trying to sign up for events and the kernel
   finishing the event.  We handle this in uthread code, so use the helper to
   register_evq(), which does the the right thing (atomics, careful ordering
   with writes, etc).
-- If as syscall has an ev_q*, it is on the pending list.  It can be on the
-  pending list without an ev_q.
 
 2.2.1 Recovering from Event Overflow
 ---------------
+Event overflow recovery is unnecessary, since syscall ev_qs use UCQs now.  this
+section is kept around for some useful tidbits, such as details about
+deregistering ev_qs for a syscall:
+
+---------------------------
 The pthread code expects to receive an event somehow to unblock a thread
 once its syscall is done.  One limitation to our messaging systems is that you
 can't send an infinite amount of event messages.  (By messages, I mean a chunk
@@ -257,17 +259,16 @@ is dependent on how a 2LS does its thread handling/blocking.
 Eventually, we'll want a way to detect and handle excessive overflow, since
 it's probably quite expensive.  Perhaps turn it off and periodically poll the
 syscalls for completion (but don't bother turning on the ev_q).
+---------------------------
 
 3. Event Delivery / Notification
 ====================
 3.1 Basics
 ----------------------------------------------
-The mbox (mailbox) is where the actual messages go, or the overflow of a
-message is tracked.
+The mbox (mailbox) is where the actual messages go.
 
        struct ev_mbox {
                bcq of notif_events     /* bounded buffer, multi-consumer/producer */
-               overflow_count
                msg_bitmap
        }
        struct ev_queue {                       /* aka, event_q, ev_q, etc. */
@@ -423,6 +424,11 @@ know about an event for which a handler is already running.
 
 3.5 Overflowed/Missed Messages in the VCPD 
 ---------------------------------------
+This too is no longer necessary.  It's useful in that it shows what we don't
+have to put up with.  Missing messages requires potentially painful
+infrastructure to handle it:
+
+-----------------------------
 All event_q's requesting IPIs ought to register with the 2LS.  This is for
 recovering in case the vcpd's mbox overflowed, and the vcore knows it missed a
 NE_EVENT type message.  At that point, it would have to check all of its
@@ -444,6 +450,7 @@ message overflow, it would have the check the event_q (which was registered
 before) anyway, and could potentially page fault there.  Also the kernel would
 have faulted on it (and read it in) back when it tried to read those values.
 It's somewhat moot, since we're going to have an allocator that pins event_qs.
+-----------------------------
 
 3.6 Round-Robin or Other IPI-delivery styles
 ---------------------------------------
index c708c5b..11616f5 100644 (file)
@@ -60,8 +60,6 @@ struct event_msg {
 struct event_mbox {
        struct ucq                                      ev_msgs;
        uint8_t                                         ev_bitmap[(MAX_NR_EVENT - 1) / 8 + 1];
-       /* this will go away in a commit or two: TODO */
-       unsigned int                            ev_overflows;
 };
 
 /* The kernel sends messages to this structure, which describes how and where
index be1be29..0b0e147 100644 (file)
@@ -23,8 +23,7 @@ __thread int temp;
 void *core0_tls = 0;
 
 struct event_queue *indirect_q;
-static void handle_generic(struct event_msg *ev_msg, unsigned int ev_type,
-                           bool overflow);
+static void handle_generic(struct event_msg *ev_msg, unsigned int ev_type);
 
 void ghetto_vcore_entry(void);
 struct uthread *ghetto_init(void)
@@ -137,11 +136,9 @@ int main(int argc, char** argv)
        return 0;
 }
 
-static void handle_generic(struct event_msg *ev_msg, unsigned int ev_type,
-                           bool overflow)
+static void handle_generic(struct event_msg *ev_msg, unsigned int ev_type)
 {
-       printf("Got event type %d on vcore %d, with%s overflow\n",
-              ev_type, vcore_id(), overflow ? "" : "out");
+       printf("Got event type %d on vcore %d\n", ev_type, vcore_id());
 }
 
 void ghetto_vcore_entry(void)
index fa939b8..f81348c 100644 (file)
@@ -7,8 +7,7 @@
 #include <ros/bcq.h>
 #include <uthread.h>
 
-static void handle_syscall(struct event_msg *ev_msg, unsigned int ev_type,
-                           bool overflow);
+static void handle_syscall(struct event_msg *ev_msg, unsigned int ev_type);
 struct syscall sysc = {0};
 struct event_queue *ev_q;
 void *core0_tls = 0;
@@ -93,8 +92,7 @@ int main(int argc, char** argv)
        return 0;
 }
 
-static void handle_syscall(struct event_msg *ev_msg, unsigned int ev_type,
-                           bool overflow)
+static void handle_syscall(struct event_msg *ev_msg, unsigned int ev_type)
 {
        struct syscall *my_sysc;
        if (!ev_msg)
index 477811a..9c1d5db 100644 (file)
@@ -105,18 +105,10 @@ void disable_kevent(unsigned int ev_type)
 }
 
 /********* Event Handling / Reception ***********/
-/* Clears the overflows, returning the number of overflows cleared. */
-unsigned int event_clear_overflows(struct event_queue *ev_q)
-{
-       unsigned int retval = ev_q->ev_mbox->ev_overflows;
-       ev_q->ev_mbox->ev_overflows = 0;
-       return retval;
-}
-
 /* Somewhat ghetto helper, for the lazy.  If all you care about is an event
  * number, this will see if the event happened or not.  It will try for a
  * message, but if there is none, it will go for a bit.  Note that multiple
- * messages that overflowed could turn into just one bit. */
+ * bit messages will turn into just one bit. */
 unsigned int get_event_type(struct event_mbox *ev_mbox)
 {
        struct event_msg local_msg = {0};
@@ -138,13 +130,11 @@ unsigned int get_event_type(struct event_mbox *ev_mbox)
 /* Actual Event Handling */
 
 /* List of handlers, process-wide, that the 2LS should fill in.  They all must
- * return (don't context switch to a u_thread), and need to handle ev_msg being
- * 0. */
+ * return (don't context switch to a u_thread) */
 handle_event_t ev_handlers[MAX_NR_EVENT] = {[EV_EVENT] handle_ev_ev, 0};
 
-/* Handles all the messages in the mbox, but not the single bits.  Currently
- * this doesn't tell the handler about overflow, since you should be handling
- * that because of the bits.  Returns the number handled. */
+/* Handles all the messages in the mbox, but not the single bits.  Returns the
+ * number handled. */
 int handle_mbox_msgs(struct event_mbox *ev_mbox)
 {
        int retval = 0;
@@ -156,7 +146,7 @@ int handle_mbox_msgs(struct event_mbox *ev_mbox)
                ev_type = local_msg.ev_type;
                printd("UCQ: ev_type: %d\n", ev_type);
                if (ev_handlers[ev_type])
-                       ev_handlers[ev_type](&local_msg, ev_type, FALSE);       /* no overflow*/
+                       ev_handlers[ev_type](&local_msg, ev_type);
                check_preempt_pending(vcoreid);
                retval++;
        }
@@ -168,61 +158,43 @@ int handle_mbox_msgs(struct event_mbox *ev_mbox)
  * check for preemptions between each event handler. */
 static int handle_mbox(struct event_mbox *ev_mbox, unsigned int flags)
 {
-       bool overflow = FALSE;
        int retval = 0;
        uint32_t vcoreid = vcore_id();
 
-       /* Handle full messages.  Will deal with overflow and bits later. */
+       /* Handle full messages.  Will deal with bits later. */
        retval = handle_mbox_msgs(ev_mbox);
 
-       /* Race here with another core clearing overflows/bits.  Don't have more
-        * than one vcore work on an mbox without being more careful of overflows
-        * (as in, assume any overflow means all bits must be checked, since someone
-        * might have not told a handler of an overflow).
+       /* Process all bits, if they requested NOMSG.  o/w, we'll skip the bitmask
+        * scan.
         *
-        * The purpose of this is to let everyone know we are dealing with
-        * overflows, mostly for preventing code from freaking out about having too
-        * many overflows.  Also slightly important to not have wraparound (though
-        * in theory it is still possible). */
-       if (ev_mbox->ev_overflows) {
-               ev_mbox->ev_overflows = 0;
-               overflow = TRUE;
-       }
-       /* Process all bits.  As far as I've seen, using overflow like this is
-        * thread safe (tested on some code in mhello, asm looks like it knows to
-        * have the function use addresses relative to the frame pointer). */
+        * TODO: if they have a flag saying "it's okay to overflow", then we'll want
+        * to check the bits regardless */
        void bit_handler(unsigned int bit) {
                printd("Bit: ev_type: %d\n", bit);
                cmb();
                if (ev_handlers[bit])
-                       ev_handlers[bit](0, bit, overflow || ev_mbox->ev_overflows);
+                       ev_handlers[bit](0, bit);
                retval++;
                check_preempt_pending(vcoreid);
                /* Consider checking the queue for incoming messages while we're here */
        }
-       BITMASK_FOREACH_SET(ev_mbox->ev_bitmap, MAX_NR_EVENT, bit_handler, TRUE);
-       /* If you ever have bugs where bits are set without overflow (for
-        * non-EVENT_NOMSG handlers, like the syscall, check here for the bit being
-        * set AND there is no overflow.  You must read the bit before checking
-        * the overflows, since the kernel writes them in the other order (kernel
-        * sets overflow, then sets the bit). */
+       if (flags & EVENT_NOMSG)
+               BITMASK_FOREACH_SET(ev_mbox->ev_bitmap, MAX_NR_EVENT, bit_handler,
+                                   TRUE);
        return retval;
 }
 
-/* The EV_EVENT handler - extract the ev_q from the message.  If you want this
- * to catch overflows, you'll need to register your event_queues (TODO).  Might
- * be issues with per-core handling (register globally, or just per vcore). */
-void handle_ev_ev(struct event_msg *ev_msg, unsigned int ev_type, bool overflow)
+/* The EV_EVENT handler - extract the ev_q from the message. */
+void handle_ev_ev(struct event_msg *ev_msg, unsigned int ev_type)
 {
        struct event_queue *ev_q;
-       /* TODO: handle overflow (register, etc) */
-       if (overflow)
-               printf("Ignoring overflow!  Deal with me!\n");
-       if (!ev_msg)
-               return;
+       /* EV_EVENT can't handle not having a message / being a bit.  If we got a
+        * bit message, it's a bug somewhere */
+       assert(ev_msg);
        ev_q = ev_msg->ev_arg3;
-       if (ev_q)
-               handle_event_q(ev_q);
+       /* Same deal, a null ev_q is probably a bug, or someone being a jackass */
+       assert(ev_q);
+       handle_event_q(ev_q);
 }
 
 /* 2LS will probably call this in vcore_entry and places where it wants to check
index 7dc164e..316bb26 100644 (file)
@@ -23,16 +23,13 @@ void enable_kevent(unsigned int ev_type, uint32_t vcoreid, int ev_flags);
 void disable_kevent(unsigned int ev_type);
 
 /********* Event Handling / Reception ***********/
-unsigned int event_clear_overflows(struct event_queue *ev_q);
 unsigned int get_event_type(struct event_mbox *ev_mbox);
 
 /* List of handlers, process-wide, that the 2LS should fill in.  They all must
  * return (don't context switch to a u_thread) */
-typedef void (*handle_event_t)(struct event_msg *ev_msg, unsigned int ev_type,
-                               bool overflow);
+typedef void (*handle_event_t)(struct event_msg *ev_msg, unsigned int ev_type);
 extern handle_event_t ev_handlers[];
-void handle_ev_ev(struct event_msg *ev_msg, unsigned int ev_type,
-                  bool overflow);
+void handle_ev_ev(struct event_msg *ev_msg, unsigned int ev_type);
 int handle_events(uint32_t vcoreid);
 void handle_event_q(struct event_queue *ev_q);
 int handle_mbox_msgs(struct event_mbox *ev_mbox);
index 172aaa7..224cee9 100644 (file)
@@ -43,8 +43,7 @@ void pth_spawn_thread(uintptr_t pc_start, void *data);
 void pth_blockon_sysc(struct syscall *sysc);
 
 /* Event Handlers */
-static void pth_handle_syscall(struct event_msg *ev_msg, unsigned int ev_type,
-                               bool overflow);
+static void pth_handle_syscall(struct event_msg *ev_msg, unsigned int ev_type);
 
 struct schedule_ops pthread_sched_ops = {
        pth_init,
@@ -90,9 +89,6 @@ struct uthread *pth_init(void)
                sysc_mgmt[i].ev_q.ev_mbox =  &__procdata.vcore_preempt_data[i].ev_mbox;
                sysc_mgmt[i].ev_q.ev_flags = EVENT_IPI;         /* totally up to you */
                sysc_mgmt[i].ev_q.ev_vcore = i;
-               /* Init the list and other data */
-               TAILQ_INIT(&sysc_mgmt[i].pending_syscs);
-               sysc_mgmt[i].handling_overflow = FALSE;
        }
        /* Create a pthread_tcb for the main thread */
        pthread_t t = (pthread_t)calloc(1, sizeof(struct pthread_tcb));
@@ -250,84 +246,27 @@ void pth_spawn_thread(uintptr_t pc_start, void *data)
 }
 
 /* Restarts a uthread hanging off a syscall.  For the simple pthread case, we
- * just make it runnable and let the main scheduler code handle it.
- *
- * The pthread code relies on syscall handling being done per-vcore.  Don't try
- * and restart a thread on a different vcore, since you'll get screwed.  We have
- * a little test to catch that. */
+ * just make it runnable and let the main scheduler code handle it. */
 static void restart_thread(struct syscall *sysc)
 {
-       uint32_t vcoreid = vcore_id();
-       /* Using two vars to make the code simpler.  It's the same thread. */
        struct uthread *ut_restartee = (struct uthread*)sysc->u_data;
-       struct pthread_tcb *pt_restartee = (struct pthread_tcb*)sysc->u_data;
        /* uthread stuff here: */
        assert(ut_restartee);
        assert(ut_restartee->state == UT_BLOCKED);
        assert(ut_restartee->sysc == sysc);
        ut_restartee->sysc = 0; /* so we don't 'reblock' on this later */
-       /* pthread stuff here: */
-       /* Rip it from pending syscall list. */
-       assert(pt_restartee->vcoreid == vcoreid);
-       TAILQ_REMOVE(&sysc_mgmt[vcoreid].pending_syscs, pt_restartee, next);
        uthread_runnable(ut_restartee);
 }
 
 /* This handler is usually run in vcore context, though I can imagine it being
  * called by a uthread in some other threading library. */
-static void pth_handle_syscall(struct event_msg *ev_msg, unsigned int ev_type,
-                               bool overflow)
+static void pth_handle_syscall(struct event_msg *ev_msg, unsigned int ev_type)
 {
-       uint32_t vcoreid = vcore_id();
-       struct sysc_mgmt *vc_sysc_mgmt = &sysc_mgmt[vcoreid];
        struct syscall *sysc;
-       struct pthread_tcb *i, *temp;
        assert(in_vcore_context());
-       /* Handle overflow: (if we haven't started handling it yet): */
-       if (overflow && !vc_sysc_mgmt->handling_overflow) {
-               vc_sysc_mgmt->handling_overflow = TRUE;
-               printd("[pthread] handling syscall overflow on vcore %d\n", vcoreid);
-               /* Turn off event handling for all syscs on our list.  Note they remain
-                * on the pending_sysc list. */
-               TAILQ_FOREACH(i, &sysc_mgmt[vcoreid].pending_syscs, next) {
-                       sysc = ((struct uthread*)i)->sysc;
-                       deregister_evq(sysc);
-               }
-               /* Handle event msgs, to get any syscs that sent messages.  We don't
-                * care about bits, since we're dealing with overflow already.  Note
-                * that pthreads currently uses an ev_q shared by a bunch of message
-                * types, so other things could also run (careful with them!). */  
-               handle_mbox_msgs(vc_sysc_mgmt->ev_q.ev_mbox);
-               /* Try to manually handle all syscs, turning on the ev_q if they are not
-                * done, and handling them if they are done.  This deals with the same
-                * issues we dealt with in pth_blockon_sysc().
-                *
-                * This might end up sucking, since we could get more overflow because
-                * of the early turning-on of events.  Alternatively, we could loop
-                * through and simply check for completion (and handle), and then do
-                * this loop. */
-               TAILQ_FOREACH_SAFE(i, &sysc_mgmt[vcoreid].pending_syscs, next, temp) {
-                       sysc = ((struct uthread*)i)->sysc;
-                       if (!register_evq(sysc, &vc_sysc_mgmt->ev_q)) {
-                               /* They are already done, can't sign up for events, just like
-                                * when we blocked on them the first time. */
-                               restart_thread(sysc);
-                       }
-               }
-               /* Done dealing with overflow */
-               vc_sysc_mgmt->handling_overflow = FALSE;
-               /* The original sysc in an ev_msg, if any, has already been done. */
-               return;
-       }
-       /* It's a bug if we don't have a msg (we are handling a syscall bit-event)
-        * while still dealing with overflow.  The only bit events should be for the
-        * first (or subsequent) overflow. */
-       if (!ev_msg) {
-               printf("[pthread] crap, no ev_msg, overflow: %d, handling: %d!!\n",
-                      overflow, vc_sysc_mgmt->handling_overflow);
-               return;
-       }
-       /* Normal path: get the sysc from the message and just restart it */
+       /* It's a bug if we don't have a msg (we're handling a syscall bit-event) */
+       assert(ev_msg);
+       /* Get the sysc from the message and just restart it */
        sysc = ev_msg->ev_arg3;
        assert(sysc);
        restart_thread(sysc);
@@ -354,14 +293,7 @@ void pth_blockon_sysc(struct syscall *sysc)
 
        /* Set things up so we can wake this thread up later */
        sysc->u_data = current_uthread;
-       /* Put the uthread on the pending list.  Note the ordering.  We must be on
-        * the list before we register the ev_q.  All sysc's must be tracked before
-        * we tell the kernel to signal us. */
-       TAILQ_INSERT_TAIL(&sysc_mgmt[vcoreid].pending_syscs, pthread, next);
-       /* Safety: later we'll make sure we restart on the core we slept on */
-       pthread->vcoreid = vcoreid;
-       /* Register our vcore's syscall ev_q to hear about this syscall.  Keep this
-        * in sync with how we handle overflowed syscalls later. */
+       /* Register our vcore's syscall ev_q to hear about this syscall. */
        if (!register_evq(sysc, &sysc_mgmt[vcoreid].ev_q)) {
                /* Lost the race with the call being done.  The kernel won't send the
                 * event.  Just restart him. */
index 490918e..d8295db 100644 (file)
@@ -23,20 +23,15 @@ struct pthread_tcb {
        void *arg;
        void *stacktop;
        void *retval;
-       uint32_t vcoreid;
 };
 typedef struct pthread_tcb* pthread_t;
 TAILQ_HEAD(pthread_queue, pthread_tcb);
 
 /* Per-vcore data structures to manage syscalls.  The ev_q is where we tell the
- * kernel to signal us.  The tailq is for handling overflow of syscall events.
- * The current pthread code handles syscall events (ev_qs, overflow, etc) on a
- * per-vcore basis).  We don't need a lock since this is per-vcore and accessed
- * in vcore context. */
+ * kernel to signal us.  We don't need a lock since this is per-vcore and
+ * accessed in vcore context. */
 struct sysc_mgmt {
        struct event_queue                      ev_q;
-       struct pthread_queue            pending_syscs;
-       bool                                            handling_overflow;
 };
 
 #define PTHREAD_ONCE_INIT 0