Glibc syscalls now block properly (XCC)
[akaros.git] / user / parlib / event.c
index c7cdb1a..8e4acf6 100644 (file)
@@ -7,7 +7,7 @@
 
 #include <ros/event.h>
 #include <ros/procdata.h>
-#include <ros/bcq.h>
+#include <ucq.h>
 #include <bitmask.h>
 #include <vcore.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <parlib.h>
 #include <event.h>
+#include <uthread.h>
+
+/* For remote VCPD mbox event handling */
+__thread bool __vc_handle_an_mbox = FALSE;
+__thread uint32_t __vc_rem_vcoreid;
 
 /********* Event_q Setup / Registration  ***********/
 
 /* Get event_qs via these interfaces, since eventually we'll want to either
- * allocate from pinned memory or use some form of a slab allocator.  Also, this
- * stitches up the big_q so its ev_mbox points to its internal mbox.  Never
- * access the internal mbox directly. */
-struct event_queue *get_big_event_q(void)
+ * allocate from pinned memory or use some form of a slab allocator.  Also,
+ * these stitch up the big_q so its ev_mbox points to its internal mbox.  Never
+ * access the internal mbox directly.
+ *
+ * Raw ones need to have their UCQs initialized.  If you're making a lot of
+ * these, you can do one big mmap and init the ucqs on your own, which ought to
+ * perform better.
+ *
+ * Use the 'regular' one for big_qs if you don't want to worry about the ucq
+ * initalization */
+struct event_queue *get_big_event_q_raw(void)
 {
        /* TODO: (PIN) should be pinned memory */
        struct event_queue_big *big_q = malloc(sizeof(struct event_queue_big));
@@ -32,8 +44,17 @@ struct event_queue *get_big_event_q(void)
        return (struct event_queue*)big_q;
 }
 
-/* Give it up */
-void put_big_event_q(struct event_queue *ev_q)
+struct event_queue *get_big_event_q(void)
+{
+       struct event_queue *big_q = get_big_event_q_raw();
+       /* uses the simpler, internally mmapping ucq_init() */
+       ucq_init(&big_q->ev_mbox->ev_msgs);
+       return big_q;
+}
+
+/* Give it up.  I don't recommend calling these unless you're sure the queues
+ * aren't in use (unregistered, etc). (TODO: consider some checks for this) */
+void put_big_event_q_raw(struct event_queue *ev_q)
 {
        /* if we use something other than malloc, we'll need to be aware that ev_q
         * is actually an event_queue_big.  One option is to use the flags, though
@@ -41,6 +62,12 @@ void put_big_event_q(struct event_queue *ev_q)
        free(ev_q);
 }
 
+void put_big_event_q(struct event_queue *ev_q)
+{
+       ucq_free_pgs(&ev_q->ev_mbox->ev_msgs);
+       put_big_event_q_raw(ev_q);
+}
+
 /* Need to point this event_q to an mbox - usually to a vcpd */
 struct event_queue *get_event_q(void)
 {
@@ -50,18 +77,23 @@ struct event_queue *get_event_q(void)
        return ev_q;
 }
 
-/* Gets a small ev_q, with ev_mbox pointing to the vcpd mbox of vcoreid */
-struct event_queue *get_event_q_vcpd(uint32_t vcoreid)
+/* Gets a small ev_q, with ev_mbox pointing to the vcpd mbox of vcoreid.  If
+ * ev_flags has EVENT_VCORE_PRIVATE set, it'll give you the private mbox.  o/w,
+ * you'll get the public one. */
+struct event_queue *get_event_q_vcpd(uint32_t vcoreid, int ev_flags)
 {
        struct event_queue *ev_q = get_event_q();
-       ev_q->ev_mbox = &__procdata.vcore_preempt_data[vcoreid].ev_mbox;
+       if (ev_flags & EVENT_VCORE_PRIVATE)
+               ev_q->ev_mbox = &vcpd_of(vcoreid)->ev_mbox_private;
+       else
+               ev_q->ev_mbox = &vcpd_of(vcoreid)->ev_mbox_public;
        return ev_q;
 }
 
 void put_event_q(struct event_queue *ev_q)
 {
        /* if we use something other than malloc, we'll need to be aware that ev_q
-        * is actually an event_queue_big. */
+        * is not an event_queue_big. */
        free(ev_q);
 }
 
@@ -81,62 +113,42 @@ struct event_queue *clear_kevent_q(unsigned int ev_type)
 }
 
 /* Enables an IPI/event combo for ev_type sent to vcoreid's default mbox.  IPI
- * if you want one or not.  This is the simplest thing applications may want,
- * and shows how you can put the other event functions together to get similar
- * things done. */
+ * if you want one or not.  If you want the event to go to the vcore private
+ * mbox (meaning no other core should ever handle it), send in
+ * EVENT_VCORE_PRIVATE with ev_flags.
+ *
+ * This is the simplest thing applications may want, and shows how you can put
+ * the other event functions together to get similar things done. */
 void enable_kevent(unsigned int ev_type, uint32_t vcoreid, int ev_flags)
 {
-       struct event_queue *ev_q = get_event_q_vcpd(vcoreid);
+       struct event_queue *ev_q = get_event_q_vcpd(vcoreid, ev_flags);
        ev_q->ev_flags = ev_flags;
        ev_q->ev_vcore = vcoreid;
        ev_q->ev_handler = 0;
+       wmb();  /* make sure ev_q is filled out before registering */
        register_kevent_q(ev_q, ev_type);
 }
 
-/* Stop receiving the events (one could be on the way) */
-void disable_kevent(unsigned int ev_type)
+/* Stop receiving the events (one could be on the way).  Caller needs to be
+ * careful, since the kernel might be sending an event to the ev_q.  Depending
+ * on the ev_q, it may be hard to know when it is done (for instance, if all
+ * syscalls you ever registered with the ev_q are done, then it would be okay).
+ * o/w, don't free it. */
+struct event_queue *disable_kevent(unsigned int ev_type)
 {
-       struct event_queue *ev_q = clear_kevent_q(ev_type);
-       if (ev_q)
-               put_event_q(ev_q);
-       else
-               printf("Tried to disable but no event_q loaded on ev_type %d", ev_type);
+       return clear_kevent_q(ev_type);
 }
 
 /********* Event Handling / Reception ***********/
-/* Tests the ev_q to see if anything has happened on it.  Up to the caller to do
- * something with the info, such as try and dequeue or handle an overflow.
- * Flags is for the ev_q's flags (if you know it), which is to check the NO_MSG
- * style ev_qs. */
-bool event_activity(struct event_mbox *ev_mbox, int flags)
-{
-       if (!bcq_empty(&ev_mbox->ev_msgs))
-               return TRUE;
-       /* Only need to check the bitmask for activity if we've had overflows or if
-        * we are a NO_MSG.  This means the client can clear its overflows. */
-       if (ev_mbox->ev_overflows || (flags & EVENT_NOMSG)) {
-               if (!BITMASK_IS_CLEAR(&ev_mbox->ev_bitmap, MAX_NR_EVENT))
-                       return TRUE;
-       }
-       return FALSE;
-}
-
-/* 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};
-       if (bcq_dequeue(&ev_mbox->ev_msgs, &local_msg, NR_BCQ_EVENTS)) {
+       /* UCQ returns 0 on success, so this will dequeue and return the type. */
+       if (!get_ucq_msg(&ev_mbox->ev_msgs, &local_msg)) {
                return local_msg.ev_type;
        }
        if (BITMASK_IS_CLEAR(&ev_mbox->ev_bitmap, MAX_NR_EVENT))
@@ -153,71 +165,107 @@ 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. */
-handle_event_t ev_handlers[MAX_NR_EVENT] = {[EV_EVENT] handle_ev_ev, 0};
+ * 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};
 
-/* 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. */
-static int handle_mbox(struct event_mbox *ev_mbox, unsigned int flags)
+/* 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 retval = 0;
        struct event_msg local_msg;
        unsigned int ev_type;
-       bool overflow = FALSE;
-       int retval = 0;
        uint32_t vcoreid = vcore_id();
-
-       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)) {
+       /* Try to dequeue, dispatch whatever you get. */
+       while (!get_ucq_msg(&ev_mbox->ev_msgs, &local_msg)) {
                ev_type = local_msg.ev_type;
-               printd("BCQ: ev_type: %d\n", 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, overflow);
-               check_preempt_pending(vcoreid);
+                       ev_handlers[ev_type](&local_msg, ev_type);
                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). */
+       return retval;
+}
+
+/* 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. */
+int handle_mbox(struct event_mbox *ev_mbox)
+{
+       int retval = 0;
+       uint32_t vcoreid = vcore_id();
        void bit_handler(unsigned int bit) {
-               printd("Bit: ev_type: %d\n", ev_type);
+               printd("[event] Bit: ev_type: %d\n", bit);
                if (ev_handlers[bit])
-                       ev_handlers[bit](0, overflow);
+                       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);
+       printd("[event] handling ev_mbox %08p on vcore %d\n", ev_mbox, vcore_id());
+       /* Handle full messages. */
+       retval = handle_mbox_msgs(ev_mbox);
+       /* 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. */
+       if (ev_mbox->ev_check_bits) {
+               do {
+                       ev_mbox->ev_check_bits = TRUE;  /* in case we don't return */
+                       cmb();
+                       BITMASK_FOREACH_SET(ev_mbox->ev_bitmap, MAX_NR_EVENT, bit_handler,
+                                           TRUE);
+                       ev_mbox->ev_check_bits = FALSE;
+                       wrmb(); /* check_bits written before we check for it being clear */
+               } while (!BITMASK_IS_CLEAR(ev_mbox->ev_bitmap, MAX_NR_EVENT));
+       }
        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)
+/* Empty if the UCQ is empty and the bits don't need checked */
+bool mbox_is_empty(struct event_mbox *ev_mbox)
+{
+       return (ucq_is_empty(&ev_mbox->ev_msgs) && (!ev_mbox->ev_check_bits));
+}
+
+/* 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)
+       /* Same deal, a null ev_q is probably a bug, or someone being a jackass */
+       assert(ev_q);
+       /* Clear pending, so we can start getting INDIRs and IPIs again.  We must
+        * set this before (compared to handle_events, then set it, then handle
+        * again), since there is no guarantee handle_event_q() will return.  If
+        * there is a pending preemption, the vcore quickly yields and will deal
+        * with the remaining events in the future - meaning it won't return to
+        * here. */
+       ev_q->ev_alert_pending = FALSE;
+       wmb();  /* don't let the pending write pass the signaling of an ev recv */
+       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;
-       handle_mbox(ev_q->ev_mbox, ev_q->ev_flags);
+       /* 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
@@ -226,17 +274,151 @@ void handle_ev_ev(struct event_msg *ev_msg, bool overflow)
  * 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);
+       struct preempt_data *vcpd = vcpd_of(vcoreid);
+       int retval = 0;
+       retval += handle_mbox(&vcpd->ev_mbox_private);
+       retval += handle_mbox(&vcpd->ev_mbox_public);
+       return retval;
+}
+
+/* Handles the events on ev_q IAW the event_handlers[].  If the ev_q is
+ * application specific, then this will dispatch/handle based on its flags. */
+void handle_event_q(struct event_queue *ev_q)
+{
+       /* If the program wants to handle the ev_q on its own: */
+       if (ev_q->ev_flags & (EVENT_JUSTHANDLEIT | EVENT_THREAD)) {
+               if (!ev_q->ev_handler) {
+                       printf("No ev_handler installed for ev_q %08p, aborting!\n", ev_q);
+                       return;
+               }
+               if (ev_q->ev_flags & EVENT_JUSTHANDLEIT) {
+                       /* Remember this can't block or page fault */
+                       ev_q->ev_handler(ev_q);
+               } else if (ev_q->ev_flags & EVENT_THREAD) {
+                       /* 2LS sched op.  The 2LS can use an existing thread if it wants,
+                        * but do so inside spawn_thread() */
+                       if (sched_ops->spawn_thread)
+                               sched_ops->spawn_thread((uintptr_t)ev_q->ev_handler, ev_q);
+                       else
+                               printf("2LS can't spawn a thread for ev_q %08p\n", ev_q);
+               }
+               return;
+       }
+       printd("[event] handling ev_q %08p on vcore %d\n", ev_q, vcore_id());
+       handle_mbox(ev_q->ev_mbox);
+}
 
+/* Sends the calling vcore a message to its public mbox.  This is purposefully
+ * limited to just the calling vcore, since in future versions, we can send via
+ * ucqs directly (in many cases).  That will require the caller to be the
+ * vcoreid, due to some preemption recovery issues (another ucq poller is
+ * waiting on us when we got preempted, and we never up nr_cons). */
+void send_self_vc_msg(struct event_msg *ev_msg)
+{
+       // TODO: try to use UCQs (requires additional support)
+       /* ev_type actually gets ignored currently.  ev_msg is what matters if it is
+        * non-zero.  FALSE means it's going to the public mbox */
+       sys_self_notify(vcore_id(), ev_msg->ev_type, ev_msg, FALSE);
+}
+
+/* Helper: makes the current core handle a remote vcore's VCPD public mbox events.
+ *
+ * Both cases (whether we are handling someone else's already or not) use some
+ * method of telling our future self what to do.  When we aren't already
+ * handling it, we use TLS, and jump to vcore entry.  When we are already
+ * handling, then we send a message to ourself, which we deal with when we
+ * handle our own events (which is later in vcore entry).
+ *
+ * We need to reset the stack and deal with it in vcore entry to avoid recursing
+ * deeply and running off the transition stack.  (handler calling handle event).
+ *
+ * Note that we might not be the one that gets the message we send.  If we pull
+ * a sys_change_to, someone else might be polling our public message box.  All
+ * we're doing is making sure that we don't forget to check rem_vcoreid's mbox.
+ *
+ * Finally, note that this function might not return.  However, it'll handle the
+ * details related to vcpd mboxes, so you don't use the ev_might_not_return()
+ * helpers with this. */
+void handle_vcpd_mbox(uint32_t rem_vcoreid)
+{
+       uint32_t vcoreid = vcore_id();
+       struct preempt_data *vcpd = vcpd_of(vcoreid);
+       struct event_msg local_msg = {0};
+       assert(vcoreid != rem_vcoreid);                 /* this shouldn't happen */
+       /* If they are empty, then we're done */
+       if (mbox_is_empty(&vcpd_of(rem_vcoreid)->ev_mbox_public))
+               return;
+       if (__vc_handle_an_mbox) {
+               /* we might be already handling them, in which case, abort */
+               if (__vc_rem_vcoreid == rem_vcoreid)
+                       return;
+               /* Already handling message for someone, need to send ourselves a
+                * message to check rem_vcoreid, which we'll process later. */
+               local_msg.ev_type = EV_CHECK_MSGS;
+               local_msg.ev_arg2 = rem_vcoreid;        /* 32bit arg */
+               send_self_vc_msg(&local_msg);
+               return;
+       }
+       /* No return after here */
+       /* At this point, we aren't in the process of handling someone else's
+        * messages, so just tell our future self what to do */
+       __vc_handle_an_mbox = TRUE;
+       __vc_rem_vcoreid = rem_vcoreid;
+       /* Reset the stack and start over in vcore context */
+       set_stack_pointer((void*)vcpd->transition_stack);
+       vcore_entry();
+       assert(0);
+}
+
+/* Handle remote vcpd public mboxes, if that's what we want to do.  Call this
+ * from vcore entry, pairs with handle_vcpd_mbox(). */
+void try_handle_remote_mbox(void)
+{
+       if (__vc_handle_an_mbox) {
+               handle_mbox(&vcpd_of(__vc_rem_vcoreid)->ev_mbox_public);
+               /* only clear the flag when we have returned from handling messages.  if
+                * an event handler (like preempt_recover) doesn't return, we'll clear
+                * this flag elsewhere. (it's actually not a big deal if we don't). */
+               cmb();
+               __vc_handle_an_mbox = FALSE;
+       }
+}
+
+/* Event handler helpers */
+
+/* For event handlers that might not return, we need to call this before the
+ * command that might not return.  In the event we were handling a remote
+ * vcore's messages, it'll send ourselves a messages that we (or someone who
+ * polls us) will get so that someone finishes off that vcore's messages).
+ * Doesn't matter who does, so long as someone does.
+ *
+ * This returns whether or not we were handling someone's messages.  Pass the
+ * parameter to ev_we_returned() */
+bool ev_might_not_return(void)
+{
+       struct event_msg local_msg = {0};
+       bool were_handling_remotes = FALSE;
+       if (__vc_handle_an_mbox) {
+               /* slight chance we finished with their mbox (were on the last one) */
+               if (!mbox_is_empty(&vcpd_of(__vc_rem_vcoreid)->ev_mbox_public)) {
+                       /* But we aren't, so we'll need to send a message */
+                       local_msg.ev_type = EV_CHECK_MSGS;
+                       local_msg.ev_arg2 = __vc_rem_vcoreid;   /* 32bit arg */
+                       send_self_vc_msg(&local_msg);
+               }
+               /* Either way, we're not working on this one now.  Note this is more of
+                * an optimization - it'd be harmless (I think) to poll another vcore's
+                * pub mbox once when we pop up in vc_entry in the future */
+               __vc_handle_an_mbox = FALSE;
+               return TRUE;
+       }
+       return FALSE;
 }
 
-/* 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)
+/* Call this when you return, paired up with ev_might_not_return().  If
+ * ev_might_not_return turned off uth_handle, we'll turn it back on. */
+void ev_we_returned(bool were_handling_remotes)
 {
-       return handle_mbox(ev_q->ev_mbox, ev_q->ev_flags);
+       if (were_handling_remotes)
+               __vc_handle_an_mbox = TRUE;
 }