Fixes bug in cpu_relax_vc()
[akaros.git] / user / parlib / event.c
index 9bcf094..6407591 100644 (file)
@@ -17,6 +17,7 @@
 #include <parlib.h>
 #include <event.h>
 #include <uthread.h>
+#include <spinlock.h>
 
 /* For remote VCPD mbox event handling */
 __thread bool __vc_handle_an_mbox = FALSE;
@@ -164,50 +165,79 @@ 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) */
-handle_event_t ev_handlers[MAX_NR_EVENT] = {[EV_EVENT] handle_ev_ev,
-                                            [EV_CHECK_MSGS] handle_check_msgs,
-                                            0};
+/* List of handler lists, process-wide.  They all must return (don't context
+ * switch to a u_thread) */
+struct ev_handler *ev_handlers[MAX_NR_EVENT] = {0};
+spinpdrlock_t ev_h_wlock = SPINPDR_INITIALIZER;
 
-/* Handles all the messages in the mbox, but not the single bits.  Returns the
- * number handled. */
-static int handle_mbox_msgs(struct event_mbox *ev_mbox)
+int register_ev_handler(unsigned int ev_type, handle_event_t handler,
+                        void *data)
+{
+       struct ev_handler *new_h = malloc(sizeof(struct ev_handler));
+       if (!new_h)
+               return -1;
+       new_h->func = handler;
+       new_h->data = data;
+       spin_pdr_lock(&ev_h_wlock);
+       new_h->next = ev_handlers[ev_type];
+       wmb();  /* make sure new_h is done before publishing to readers */
+       ev_handlers[ev_type] = new_h;
+       spin_pdr_unlock(&ev_h_wlock);
+       return 0;
+}
+
+int deregister_ev_handler(unsigned int ev_type, handle_event_t handler,
+                          void *data)
+{
+       /* TODO: User-level RCU */
+       printf("Failed to dereg handler, not supported yet!\n");
+}
+
+static void run_ev_handlers(unsigned int ev_type, struct event_msg *ev_msg)
+{
+       struct ev_handler *handler;
+       /* TODO: RCU read lock */
+       handler = ev_handlers[ev_type];
+       while (handler) {
+               handler->func(ev_msg, ev_type, handler->data);
+               handler = handler->next;
+       }
+}
+
+/* Attempts to handle a message.  Returns 1 if we dequeued a msg, 0 o/w. */
+int handle_one_mbox_msg(struct event_mbox *ev_mbox)
 {
-       int retval = 0;
        struct event_msg local_msg;
        unsigned int ev_type;
-       uint32_t vcoreid = vcore_id();
-       /* Some stack-smashing bugs cause this to fail */
-       assert(ev_mbox);
-       /* Try to dequeue, dispatch whatever you get. */
-       while (!get_ucq_msg(&ev_mbox->ev_msgs, &local_msg)) {
-               ev_type = local_msg.ev_type;
-               printd("[event] UCQ (mbox %08p), ev_type: %d\n", ev_mbox, ev_type);
-               if (ev_handlers[ev_type])
-                       ev_handlers[ev_type](&local_msg, ev_type);
-               retval++;
-       }
-       return retval;
+       /* get_ucq returns 0 on success, -1 on empty */
+       if (get_ucq_msg(&ev_mbox->ev_msgs, &local_msg) == -1)
+               return 0;
+       ev_type = local_msg.ev_type;
+       assert(ev_type < MAX_NR_EVENT);
+       printd("[event] UCQ (mbox %08p), ev_type: %d\n", ev_mbox, ev_type);
+       run_ev_handlers(ev_type, &local_msg);
+       return 1;
 }
 
 /* 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.  For now, we
- * check for preemptions between each event handler. */
+ * takes an ev_mbox, since the vcpd mbox isn't a regular ev_q.  Returns 1 if we
+ * handled something, 0 o/w. */
 int handle_mbox(struct event_mbox *ev_mbox)
 {
        int retval = 0;
        uint32_t vcoreid = vcore_id();
        void bit_handler(unsigned int bit) {
                printd("[event] Bit: ev_type: %d\n", bit);
-               if (ev_handlers[bit])
-                       ev_handlers[bit](0, bit);
-               retval++;
+               run_ev_handlers(bit, 0);
+               retval = 1;
                /* Consider checking the queue for incoming messages while we're here */
        }
        printd("[event] handling ev_mbox %08p on vcore %d\n", ev_mbox, vcore_id());
-       /* Handle full messages. */
-       retval = handle_mbox_msgs(ev_mbox);
+       /* Some stack-smashing bugs cause this to fail */
+       assert(ev_mbox);
+       /* Handle all full messages, tracking if we do at least one. */
+       while (handle_one_mbox_msg(ev_mbox))
+               retval = 1;
        /* Process all bits, if the kernel tells us any bit is set.  We don't clear
         * the flag til after we check everything, in case one of the handlers
         * doesn't return.  After we clear it, we recheck. */
@@ -231,7 +261,7 @@ bool mbox_is_empty(struct event_mbox *ev_mbox)
 }
 
 /* The EV_EVENT handler - extract the ev_q from the message. */
-void handle_ev_ev(struct event_msg *ev_msg, unsigned int ev_type)
+void handle_ev_ev(struct event_msg *ev_msg, unsigned int ev_type, void *data)
 {
        struct event_queue *ev_q;
        /* EV_EVENT can't handle not having a message / being a bit.  If we got a
@@ -251,35 +281,24 @@ void handle_ev_ev(struct event_msg *ev_msg, unsigned int ev_type)
        handle_event_q(ev_q);
 }
 
-/* This handler tells us to check the public message box of a vcore. */
-void handle_check_msgs(struct event_msg *ev_msg, unsigned int ev_type)
-{
-       uint32_t rem_vcoreid;
-       assert(ev_msg);
-       rem_vcoreid = ev_msg->ev_arg2;
-       printd("[event] handle check msgs for VC %d on VC %d\n", rem_vcoreid,
-              vcore_id());
-       /* if it is a message for ourselves, then we can abort.  Vcores will check
-        * their own messages via handle_events() (which either we're doing now, or
-        * will do when we are done dealing with another vcore's mbox). */
-       if (rem_vcoreid == vcore_id())
-               return;
-       /* they should have had their can_rcv turned off at some point, though it is
-        * possible that it was turned back on by now.  we don't really care - our
-        * job is to make sure their messages get checked. */
-       handle_vcpd_mbox(rem_vcoreid);
-}
-
-/* 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. */
+/* Attempts to handle events, if notif_pending.  The kernel always sets
+ * notif_pending after posting a message to either public or private mailbox.
+ * When this returns, as far as we are concerned, notif_pending is FALSE.
+ * However, a concurrent kernel writer could have reset it to true.  This is
+ * fine; whenever we leave VC ctx we double check notif_pending.  Returns 1 or 2
+ * if we actually handled a message, 0 o/w.
+ *
+ * WARNING: this might not return and/or current_uthread may change. */
 int handle_events(uint32_t vcoreid)
 {
        struct preempt_data *vcpd = vcpd_of(vcoreid);
        int retval = 0;
-       retval += handle_mbox(&vcpd->ev_mbox_private);
-       retval += handle_mbox(&vcpd->ev_mbox_public);
+       if (vcpd->notif_pending) {
+               vcpd->notif_pending = FALSE;
+               wrmb(); /* prevent future reads from happening before notif_p write */
+               retval += handle_mbox(&vcpd->ev_mbox_private);
+               retval += handle_mbox(&vcpd->ev_mbox_public);
+       }
        return retval;
 }
 
@@ -426,3 +445,14 @@ void ev_we_returned(bool were_handling_remotes)
        if (were_handling_remotes)
                __vc_handle_an_mbox = TRUE;
 }
+
+/* Debugging */
+void print_ev_msg(struct event_msg *msg)
+{
+       printf("MSG at %08p\n", msg);
+       printf("\ttype: %d\n", msg->ev_type);
+       printf("\targ1 (16): 0x%4x\n", msg->ev_arg1);
+       printf("\targ2 (32): 0x%8x\n", msg->ev_arg2);
+       printf("\targ3 (32): 0x%8x\n", msg->ev_arg3);
+       printf("\targ4 (64): 0x%16x\n", msg->ev_arg4);
+}