blob: c5bf0f4d179867db95c6a3a4d5fcb3cb7f125687 [file] [log] [blame] [edit]
/* 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);
}