Default event handling infrastructure
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 23 Feb 2011 22:41:45 +0000 (14:41 -0800)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:35:58 +0000 (17:35 -0700)
2LSs/whoever can register handlers in ev_handlers to deal with specific
events.  For now, use the default ev_ev handler for EV_EVENT.
handle_events() works for a vcore's default mbox, and handle_event_q()
works for generic ev_qs.

tests/mhello.c
tests/syscall.c
user/parlib/event.c
user/parlib/include/event.h
user/pthread/pthread.c

index eefb5b1..49f07e5 100644 (file)
@@ -22,6 +22,7 @@ __thread int temp;
 void *core0_tls = 0;
 
 struct event_queue *indirect_q;
+static void handle_generic(struct event_msg *ev_msg, bool overflow);
 
 int main(int argc, char** argv)
 {
@@ -40,9 +41,17 @@ int main(int argc, char** argv)
        register_kevent_q(indirect_q, EV_FREE_APPLE_PIE);
 
 /* begin: stuff userspace needs to do before switching to multi-mode */
+       /* handle events: just want to print out what we get.  This is just a
+        * quick set of handlers, not a registration for a kevent. */
+       for (int i = 0; i < MAX_NR_EVENT; i++)
+               ev_handlers[i] = handle_generic;
+       /* Want to use the default ev_ev (which we just overwrote) */
+       ev_handlers[EV_EVENT] = handle_ev_ev;
        /* vcore_init() done in vcore_request() now. */
        /* Set up event reception.  For example, this will allow us to receive an
-        * event and IPI for USER_IPIs on vcore 0.  Check event.c for more stuff. */
+        * event and IPI for USER_IPIs on vcore 0.  Check event.c for more stuff.
+        * Note you don't have to register for USER_IPIs to receive ones you send
+        * yourself with sys_self_notify(). */
        enable_kevent(EV_USER_IPI, 0, TRUE);
 
        /* Need to save this somewhere that you can find it again when restarting
@@ -100,6 +109,12 @@ int main(int argc, char** argv)
        return 0;
 }
 
+static void handle_generic(struct event_msg *ev_msg, bool overflow)
+{
+       printf("Got event type %d on vcore %d, with%s overflow\n", ev_msg->ev_type,
+              vcore_id(), overflow ? "" : "out");
+}
+
 void vcore_entry(void)
 {
        uint32_t vcoreid = vcore_id();
@@ -113,29 +128,8 @@ void vcore_entry(void)
        vcpd = &__procdata.vcore_preempt_data[vcoreid];
        
        /* here is how you receive an event */
-       struct event_msg ev_msg = {0};
-       if (event_activity(&vcpd->ev_mbox, 0)) {
-               /* Ought to while loop/dequeue, processing as they come in. */
-               bcq_dequeue(&vcpd->ev_mbox.ev_msgs, &ev_msg, NR_BCQ_EVENTS);
-               printf("the queue is on vcore %d and has a ne with type %d\n", vcoreid,
-                      ev_msg.ev_type);
-               printf("Number of event overflows: %d\n", vcpd->ev_mbox.ev_overflows);
-       }
-       /* it might be in bitmask form too: */
-       //printf("and the bitmask looks like: ");
-       //PRINT_BITMASK(__procdata.vcore_preempt_data[vcoreid].notif_bmask, MAX_NR_NOTIF);
-
-       /* How we handle indirection events: */
-       struct event_queue_big *ev_q;
-       struct event_msg indir_msg = {0};
-       if (ev_msg.ev_type == EV_EVENT) {
-               ev_q = ev_msg.ev_arg3;  /* convention */
-               printf("Detected EV_EVENT, ev_q is %08p (%08p)\n", ev_q, indirect_q);
-               /* Ought to loop/dequeue, processing as they come in. */
-               bcq_dequeue(&ev_q->ev_mbox->ev_msgs, &indir_msg, NR_BCQ_EVENTS);
-               printf("Message of type: %d (%d)\n", indir_msg.ev_type,
-                      EV_FREE_APPLE_PIE);
-       }
+       handle_events(vcoreid);
+
        /* how we tell a preemption is pending (regardless of notif/events) */
        if (vc->preempt_pending) {
                printf("Oh crap, vcore %d is being preempted!  Yielding\n", vcoreid);
index 9c312bd..c0d4ee5 100644 (file)
@@ -6,6 +6,7 @@
 #include <rassert.h>
 #include <ros/bcq.h>
 
+static void handle_syscall(struct event_msg *ev_msg, bool overflow);
 struct syscall sysc = {0};
 struct event_queue *ev_q;
 void *core0_tls = 0;
@@ -14,6 +15,10 @@ int main(int argc, char** argv)
 {
        int num_started, retval;
        unsigned int ev_type;
+
+       /* register our syscall handler (2LS does this) */
+       ev_handlers[EV_SYSCALL] = handle_syscall;
+
        printf("Trying to block\n");
        /* Not doing anything else to it: no EVENT_IPI yet, etc. */
        ev_q = get_big_event_q();
@@ -29,23 +34,20 @@ int main(int argc, char** argv)
        while (!(sysc.flags & SC_DONE))
                cpu_relax();
        #endif
-       /* But let's check on events... */
-       while (!event_activity(ev_q->ev_mbox, ev_q->ev_flags))
+       /* But let's check on events...  Handle event_q returns how many events it
+        * handled.  Want to spin in this testing code. */
+       while(!handle_event_q(ev_q))
                cpu_relax();
-       ev_type = get_event_type(ev_q->ev_mbox);
-       if (ev_type = EV_SYSCALL) {
-               /* our syscall should be done (we ought to check the msg pointer) */
-               if (sysc.flags & SC_DONE) 
-                       printf("Syscall is done, retval: %d\n", sysc.retval);
-               else
-                       printf("BUG! Syscall wasn't done!\n");
-       } else {
-               printf("Whoa, got an unexpected event type %d!\n", ev_type);
-       }
-
+       /* by now, we should have run our handler */
+       /********************************************************/
        /* Start MCP / IPI test */
        printf("Switching to _M mode and testing an IPI-d ev_q\n");
+       printf("Our indirect ev_q is %08p\n", ev_q);
+
 /* begin: stuff userspace needs to do before switching to multi-mode */
+       /* Note we don't need to set up event reception for any particular kevent.
+        * The ev_q in the syscall said to send an IPI to vcore 0 which means an
+        * EV_EVENT will be sent straight to vcore0. */
        /* Need to save this somewhere that you can find it again when restarting
         * core0 */
        core0_tls = get_tls_desc(0);
@@ -61,7 +63,7 @@ int main(int argc, char** argv)
        ev_q->ev_flags = EVENT_IPI;
        ev_q->ev_vcore = 0;
        sysc.u_data = (void*)1; /* using this to loop on */
-       /* issue the diagnostic block syscall */
+       /* issue the diagnostic blocking syscall */
        sysc.num = SYS_block;
        sysc.ev_q = ev_q;
        num_started = __ros_arch_syscall((long)&sysc, 1);
@@ -79,6 +81,22 @@ int main(int argc, char** argv)
        return 0;
 }
 
+static void handle_syscall(struct event_msg *ev_msg, bool overflow)
+{
+       struct syscall *my_sysc;
+       my_sysc = ev_msg->ev_arg3;
+       printf("Handling syscall event for sysc %08p (%08p)\n",
+              my_sysc, &sysc);
+       /* our syscall should be done (we ought to check the msg pointer) */
+       if (sysc.flags & SC_DONE) 
+               printf("Syscall is done, retval: %d\n", sysc.retval);
+       else
+               printf("BUG! Syscall wasn't done!\n");
+       /* signal to thread 0 that the sysc is done, just to show this
+        * is getting done in vcore context. */
+       my_sysc->u_data = 0;
+}
+
 void vcore_entry(void)
 {
        uint32_t vcoreid = vcore_id();
@@ -90,35 +108,9 @@ void vcore_entry(void)
        struct preempt_data *vcpd;
        vcpd = &__procdata.vcore_preempt_data[vcoreid];
        
-       /* here is how you receive an event */
-       struct event_msg ev_msg = {0};
-       struct event_queue_big *indir_q;
-       struct syscall *my_sysc;
-       if (event_activity(&vcpd->ev_mbox, 0)) {
-               /* Ought to while loop/dequeue, processing as they come in. */
-               bcq_dequeue(&vcpd->ev_mbox.ev_msgs, &ev_msg, NR_BCQ_EVENTS);
-               if (vcpd->ev_mbox.ev_overflows)
-                       printf("Had an overflow...\n");
-               /* should do generic handling.  this is customized for the syscalls */
-               if (ev_msg.ev_type == EV_EVENT) {
-                       indir_q = ev_msg.ev_arg3;       /* convention */
-                       printf("Detected EV_EVENT, ev_q is %08p (%08p)\n", indir_q, ev_q);
-                       assert(indir_q);
-                       /* Ought to loop/dequeue, processing as they come in. */
-                       bcq_dequeue(&indir_q->ev_mbox->ev_msgs, &ev_msg, NR_BCQ_EVENTS);
-                       /* should have received a syscall off the indirect ev_q */
-                       if (ev_msg.ev_type == EV_SYSCALL) {
-                               my_sysc = ev_msg.ev_arg3;
-                               printf("Handling syscall event for sysc %08p (%08p)\n",
-                                      my_sysc, &sysc);
-                               /* signal to thread 0 that the sysc is done, just to show this
-                                * is getting done in vcore context. */
-                               my_sysc->u_data = 0;
-                       } else {
-                               printf("Got a different event, type %d\n", ev_msg.ev_type);
-                       }
-               }
-       }
+       /* here is how you receive an event (remember to register the syscall
+        * handler, and whatever other handlers you want). */
+       handle_events(vcoreid);
 
        /* how we tell a preemption is pending (regardless of notif/events) */
        if (vc->preempt_pending) {
index 95e5864..919e16e 100644 (file)
@@ -149,3 +149,89 @@ unsigned int get_event_type(struct event_mbox *ev_mbox)
        }
        return EV_NONE;
 }
+
+/* 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. */
+handle_event_t ev_handlers[MAX_NR_EVENT] = {[EV_EVENT] handle_ev_ev, 0};
+
+/* Handle an mbox.  This is the receive-side processing of an event_queue.  It
+ * takes an ev_mbox, since the vcpd mbox isn't a regular ev_q. */
+static int handle_mbox(struct event_mbox *ev_mbox, unsigned int flags)
+{
+       struct event_msg local_msg;
+       unsigned int ev_type;
+       bool overflow = FALSE;
+       int retval = 0;
+       if (!event_activity(ev_mbox, flags))
+               return retval;
+       /* Try to dequeue, dispatch whatever you get.  TODO consider checking for
+        * overflow first */
+       while (!bcq_dequeue(&ev_mbox->ev_msgs, &local_msg, NR_BCQ_EVENTS)) {
+               ev_type = local_msg.ev_type;
+               if (ev_handlers[ev_type])
+                       ev_handlers[ev_type](&local_msg, overflow);
+               /* TODO: check for preemption here */
+               retval++;
+       }
+       /* 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). */
+       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). */
+       void bit_handler(unsigned int bit) {
+               if (ev_handlers[bit])
+                       ev_handlers[bit](0, overflow);
+               retval++;
+               /* TODO: check for preemption here */
+               /* Consider checking the queue for incoming messages while we're here */
+       }
+       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, bool overflow)
+{
+       struct event_queue *ev_q;
+       /* TODO: handle overflow (register, etc) */
+       if (overflow)
+               printf("Ignoring overflow!  Deal with me!\n");
+       if (!ev_msg)
+               return;
+       ev_q = ev_msg->ev_arg3;
+       if (!ev_q)
+               return;
+       handle_mbox(ev_q->ev_mbox, ev_q->ev_flags);
+}
+
+/* 2LS will probably call this in vcore_entry and places where it wants to check
+ * for / handle events.  This will process all the events for the given vcore.
+ * Note, it probably should be the calling vcore you do this to...  Returns the
+ * number of events handled. */
+int handle_events(uint32_t vcoreid)
+{
+       struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
+       /* TODO: EVENT_NOMSG checks could be painful.  we could either keep track of
+        * whether or not the 2LS has a NOMSG ev_q pointing to its vcpd, or have the
+        * kernel set another flag for "bits" */
+       return handle_mbox(&vcpd->ev_mbox, EVENT_NOMSG);
+
+}
+
+/* Handles the events on ev_q IAW the event_handlers[].  Returns the number of
+ * events handled. */
+int handle_event_q(struct event_queue *ev_q)
+{
+       return handle_mbox(ev_q->ev_mbox, ev_q->ev_flags);
+}
index 74867cd..273b161 100644 (file)
@@ -27,4 +27,12 @@ bool event_activity(struct event_mbox *ev_mbox, int flags);
 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, bool overflow);
+extern handle_event_t ev_handlers[];
+void handle_ev_ev(struct event_msg *ev_msg, bool overflow);
+int handle_events(uint32_t vcoreid);
+int handle_event_q(struct event_queue *ev_q);
+
 #endif /* _EVENT_H */
index 4a99248..3480775 100644 (file)
@@ -91,7 +91,7 @@ void __attribute__((noreturn)) vcore_entry()
 
        /* Put this in the loop that deals with notifications.  It will return if
         * there is no preempt pending. */ 
-       // TODO: prob make a handle_notif() function
+       handle_events(vcoreid);
        if (vc->preempt_pending)
                sys_yield(TRUE);
        // TODO: consider making this restart path work for restarting as well as