| /* Copyright (c) 2011 The Regents of the University of California |
| * Barret Rhoden <brho@cs.berkeley.edu> |
| * See LICENSE for details. |
| * |
| * Userspace utility functions for receiving events and notifications (IPIs). |
| * Some are higher level than others; just use what you need. */ |
| |
| #include <ros/event.h> |
| #include <ros/procdata.h> |
| #include <ucq.h> |
| #include <bitmask.h> |
| #include <vcore.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <assert.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, |
| * 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)); |
| memset(big_q, 0, sizeof(struct event_queue_big)); |
| big_q->ev_mbox = &big_q->ev_imbox; |
| return (struct event_queue*)big_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 |
| * this could be error prone. */ |
| 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) |
| { |
| /* TODO: (PIN) should be pinned memory */ |
| struct event_queue *ev_q = malloc(sizeof(struct event_queue)); |
| memset(ev_q, 0, sizeof(struct event_queue)); |
| return ev_q; |
| } |
| |
| /* 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(); |
| 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 not an event_queue_big. */ |
| free(ev_q); |
| } |
| |
| /* Sets ev_q to be the receiving end for kernel event ev_type */ |
| void register_kevent_q(struct event_queue *ev_q, unsigned int ev_type) |
| { |
| __procdata.kernel_evts[ev_type] = ev_q; |
| } |
| |
| /* Clears the event, returning an ev_q if there was one there. You'll need to |
| * free it. */ |
| struct event_queue *clear_kevent_q(unsigned int ev_type) |
| { |
| struct event_queue *ev_q = __procdata.kernel_evts[ev_type]; |
| __procdata.kernel_evts[ev_type] = 0; |
| return ev_q; |
| } |
| |
| /* Enables an IPI/event combo for ev_type sent to vcoreid's default mbox. IPI |
| * 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, 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). 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) |
| { |
| return clear_kevent_q(ev_type); |
| } |
| |
| /********* Event Handling / Reception ***********/ |
| /* 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 |
| * bit messages will turn into just one bit. */ |
| unsigned int get_event_type(struct event_mbox *ev_mbox) |
| { |
| struct event_msg local_msg = {0}; |
| /* 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)) |
| return EV_NONE; /* aka, 0 */ |
| for (int i = 0; i < MAX_NR_EVENT; i++) { |
| if (GET_BITMASK_BIT(ev_mbox->ev_bitmap, i)) { |
| CLR_BITMASK_BIT_ATOMIC(ev_mbox->ev_bitmap, i); |
| return i; |
| } |
| } |
| 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) */ |
| handle_event_t ev_handlers[MAX_NR_EVENT] = {[EV_EVENT] handle_ev_ev, |
| 0}; |
| |
| /* 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) |
| { |
| struct event_msg local_msg; |
| unsigned int ev_type; |
| /* 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); |
| if (ev_handlers[ev_type]) |
| ev_handlers[ev_type](&local_msg, ev_type); |
| 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. 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 = 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()); |
| /* 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. */ |
| 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; |
| } |
| |
| /* 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; |
| /* 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; |
| /* 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); |
| } |
| |
| /* 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; |
| 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; |
| } |
| |
| /* 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()); |
| /* Raw ev_qs that haven't been connected to an mbox, user bug: */ |
| assert(ev_q->ev_mbox); |
| 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; |
| } |
| |
| /* 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) |
| { |
| 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); |
| } |