Allow backtrace_user_ctx() on remote cores
[akaros.git] / Documentation / async_events.txt
index e163077..0bb1854 100644 (file)
@@ -4,7 +4,8 @@ Barret Rhoden
 1. Overview
 2. Async Syscalls and I/O
 3. Event Delivery / Notification
-4. Misc Things That Aren't Sorted Completely:
+4. Single-core Process (SCP) Events
+5. Misc Things That Aren't Sorted Completely:
 
 1. Overview
 ====================
@@ -62,6 +63,13 @@ all things related to the management of vcores, such as its event_mbox (queue
 of incoming messages/notifications/events).  Both the kernel and the vcore
 code know to look here for a variety of things.
 
+Vcore-business: This is a term I use for a class of messages where the receiver
+is the actual vcore, and not just using the vcore as a place to receive the
+message.  Examples of vcore-business are INDIR events, preempt_pending events,
+scheduling events (self-ipis by the 2LS from one vcore to another), and things
+like that.  There are two types: public and private.  Private will only be
+handled by that vcore.  Public might be handled by another vcore.
+
 Notif_table: This is a list of event_q*s that correspond to certain
 unexpected/"one-sided" events the kernel sends to the process.  It is similar
 to an IRQ table in the kernel.  Each event_q tells the kernel how the process
@@ -85,6 +93,10 @@ other either (in the event of weirdness, the producers give up and say the
 buffer is full).  This means that a process can produce for one of its ev_qs
 (which is what they need to do to send message to itself).
 
+UCQ: "unbounded concurrent queue".  This is a data structure allowing the kernel
+to produce an unbounded number of messages for the process to consume.  The main
+limitation to the number of messages is RAM.  Check out its documentation.
+
 2. Async Syscalls and I/O
 ====================
 2.1 Basics
@@ -323,19 +335,20 @@ There are also interfaces in the kernel to put a message in an ev_mbox
 regardless of the process's wishes (post_vcore_event()), and can send an IPI
 at any time (proc_notify()).
 
-3.3 IPIs, Indirection Events, and Fallback
+3.3 IPIs, Indirection Events, and Fallback (Spamming Indirs)
 ----------------------------------------------
-An ev_q can ask for an IPI, an indirection event, and a fallback in case a vcore
-is offline.  Or any combination of these.  Note that these have little to do
-with the actual message being sent.  The actual message is dropped in the
-ev_mbox pointed to by the ev_q.
+An ev_q can ask for an IPI, for an indirection event, and for an indirection
+event to be spammed in case a vcore is offline (sometimes called the 'fallback'
+option.  Or any combination of these.  Note that these have little to do with
+the actual message being sent.  The actual message is dropped in the ev_mbox
+pointed to by the ev_q.
 
 The main use for all of this is for syscalls.  If you want to receive an event
 when a syscall completes or has a change in status, simply allocate an event_q,
 and point the syscall at it.  syscall: ev_q* -> "vcore for IPI, syscall message
 in the ev_q mbox", etc.  You can also point it to an existing ev_q.  Pthread
 code has examples of two ways to do this.  Both have per vcore ev_qs, requesting
-IPIs, INDIRS, and FALLBACK.  One way is to have an ev_mbox per vcore, and
+IPIs, INDIRS, and SPAM_INDIR.  One way is to have an ev_mbox per vcore, and
 another is to have a global ev_mbox that all ev_qs point to.  As a side note, if
 you do the latter, you don't need to worry about a vcore's ev_q if it gets
 preempted: just check the global ev_mbox (which is done by checking your own
@@ -352,56 +365,71 @@ poll its own ev_q, so you won't need the indirection event.
 
 Additionally, note that IPIs and INDIRs can be spurious.  It's not a big deal to
 receive and IPI and have nothing to do, or to be told to check an empty ev_q.
-All of the event handling code can deal with this.  The only thing you shouldn't
-do is have one vcore handle another's VCPD.  That mbox is meant for
-vcore-business.  Never use a VCPD for messages you might want to receive if that
-vcore is offline.
-
-3.3.2: Fallback
+All of the event handling code can deal with this.
+
+INDIR events are sent to the VCPD public mbox, which means they will get handled
+if the vcore gets preempted.  Any other messages sent here will also get handled
+during a preemption.  However, the only type of messages you should use this for
+are ones that can handle spurious messages.  The completion of a syscall is an
+example of a message that cannot be spurious.  Since INDIRs can be spurious, we
+can use the public mbox.  (Side note: the kernel may spam INDIRs in attempting
+to make sure you get the message on a vcore that didn't yield.)
+
+Never use a VCPD mbox (public or private) for messages you might want to receive
+if that vcore is offline.  If you want to be sure to get a message, create your
+own ev_q and set flags for INDIR, SPAM_INDIR, and IPI.  There's no guarantee a
+*specific* message will get looked at.  In cases where it won't, the kernel will
+send that message to another vcore.  For example, if the kernel posts an INDIR
+to a VCPD mbox (the public one btw) and it loses a race with the vcore yielding,
+the vcore might never see that message.  However, the kernel knows it lost the
+race, and will find another vcore to send it to.
+
+3.3.2: Spamming Indirs / Fallback
 ---------------
 Both IPI and INDIR need an actual vcore.  If that vcore is unavailable and if
-EVENT_FALLBACK is set, the kernel will pick another vcore and send the messages
-there.  This allows an ev_q to be set up to handle work when the vcore is
-online, while allowing the program to handle events when that core yields,
+EVENT_SPAM_INDIR is set, the kernel will pick another vcore and send the
+messages there.  This allows an ev_q to be set up to handle work when the vcore
+is online, while allowing the program to handle events when that core yields,
 without having to reset all of its ev_qs to point to "known" available vcores
 (and avoiding those races).  Note 'online' is synonymous with 'mapped', when
-talking about vcores.  A vcore technically isn't always online, only destined to
-be online, when it is mapped to a pcore (kmsg on the way, etc).  It's easiest to
-think of it being online for the sake of this discussion.
-
-One question is whether or not 2LSs need a FALLBACK flag for their ev_qs.  The
-main use for FALLBACK is so that vcores can yield.  (Note that fallback won't
-help you *miss* INDIR messages in the event of a preemption; you can always lose
-that race due to it taking too long to process the messages).  An alternative
-would be for vcores to pick another vcore and change all of its ev_qs to that
-vcore.  There are a couple problems with this.  One is that it'll be a pain to
-get those ev_qs back when the vcore comes back online (if ever).  Another issue
-is that other vcores will build up a list of ev_qs that they aren't aware of,
-which will be hard to deal with when *they* yield.  FALLBACK avoids all of those
-problems.
-
-An important aspect of FALLBACK is that it works with yielded vcores, not
-preempted vcores.  It could be that there are no cores that are online, but
+talking about vcores.  A vcore technically isn't always online, only destined
+to be online, when it is mapped to a pcore (kmsg on the way, etc).  It's
+easiest to think of it being online for the sake of this discussion.
+
+One question is whether or not 2LSs need a SPAM_INDIR flag for their ev_qs.
+The main use for SPAM_INDIR is so that vcores can yield.  (Note that fallback
+won't help you *miss* INDIR messages in the event of a preemption; you can
+always lose that race due to it taking too long to process the messages).  An
+alternative would be for vcores to pick another vcore and change all of its
+ev_qs to that vcore.  There are a couple problems with this.  One is that it'll
+be a pain to get those ev_qs back when the vcore comes back online (if ever).
+Another issue is that other vcores will build up a list of ev_qs that they
+aren't aware of, which will be hard to deal with when *they* yield.  SPAM_INDIR
+avoids all of those problems.
+
+An important aspect of spamming indirs is that it works with yielded vcores,
+not preempted vcores.  It could be that there are no cores that are online, but
 there should always be at least one core that *will* be online in the future, a
 core that the process didn't want to lose and will deal with in the future.  If
-not for this distinction, FALLBACK could fail.  An older idea would be to have
+not for this distinction, SPAM_INDIR could fail.  An older idea would be to have
 fallback send the msg to the desired vcore if there were no others.  This would
 not work if the vcore yielded and then the entire process was preempted or
 otherwise not running.  Another way to put this is that we need a field to
 determine whether a vcore is offline temporarily or permanently.
 
-This is why we have the VCPD field 'can_rcv_msg'.  It tells the kernel's event
+This is why we have the VCPD flag 'VC_CAN_RCV_MSG'.  It tells the kernel's event
 delivery code that the vcore will check the messages: it is an acceptable
-destination for a FALLBACK.  There are two reasons to put this in VCPD:
+destination for a spammed indir.  There are two reasons to put this in VCPD:
 1) Userspace can remotely turn off a vcore's msg reception.  This is necessary
 for handling preemption of a vcore that was in uthread context, so that we can
 remotely 'yield' the core without having to sys_change_vcore() (which I discuss
 below, and is meant to 'unstick' a vcore).
 2) Yield is simplified.  The kernel no longer races with itself nor has to worry
 about turning off that flag - userspace can do it when it wants to yield.  (turn
-off the flag, check messages, then yield).
+off the flag, check messages, then yield).  This is less big of a deal now that
+the kernel races with vcore membership in the online_vcs list.
 
-Two aspects of the code make this work nicely.  The 'can_rcv_msg' flag greatly
+Two aspects of the code make this work nicely.  The VC_CAN_RCV_MSG flag greatly
 simplifies the kernel's job.  There are a lot of weird races we'd have to deal
 with, such as process state (RUNNING_M), whether a mass preempt is going on, or
 just one core, or a bunch of cores, mass yields, etc.  A flag that does one
@@ -410,11 +438,11 @@ other useful thing is being able to handle spurious events.  Vcore code can
 handle extra IPIs and INDIRs to non-VCPD ev_qs.  Any vcore can handle an ev_q
 that is "non-VCPD business".
 
-Worth mentioning is the difference between 'notif_pending' and 'can_rcv_msg'.
-'can_rcv_msg' is the process saying it will check for messages.  'notif_pending'
-is when the kernel says it *has* sent a message.  'notif_pending' is also used
-by the kernel in proc_yield() and the 2LS in pop_ros_tf() to make sure the sent
-message is not missed.
+Worth mentioning is the difference between 'notif_pending' and VC_CAN_RCV_MSG.
+VC_CAN_RCV_MSG is the process saying it will check for messages.
+'notif_pending' is when the kernel says it *has* sent a message.
+'notif_pending' is also used by the kernel in proc_yield() and the 2LS in
+pop_user_ctx() to make sure the sent message is not missed.
 
 Also, in case this comes up, there's a slight race on changing the mbox* and the
 vcore number within the event_q.  The message could have gone to the wrong (old)
@@ -426,21 +454,8 @@ this can be a bit tricky if you have multiple places pointing to the same ev_q
 
 3.3.3: Fallback and Preemption
 ---------------
-FALLBACK doesn't protect you from preemptions.  A vcore can be preempted and
-have INDIRs in its VCPD.  Dealing with this is part of the 2LSs job.  The 2LS
-must check the ev_mboxes/ev_qs of all ev_qs that could send INDIRS to the
-offline vcore.  There could be INDIRS in the VCPD that are just lying there.
-The 2LS knows which ev_qs these are (such as for completed syscalls), and for
-many things, this will be a common ev_q (such as for 'vcore-x-was-preempted').
-
-A couple things will ameliorate this:
-1) the 2LS can inspect the VCPD of the target.  If it is empty, then we don't
-have to worry about any INDIRs, and we're done worrying about their messages
-(though we still have to worry about them being stuck in vcore context and
-having a uthread).  Also note you only need to check the UCQ, since INDIRs must
-be messages, not bits.
-2) the kernel will initiate FALLBACK if the vcore has a preempt pending, which
-ought to cut down on this happening frequently.
+SPAM_INDIR doesn't protect you from preemptions.  A vcore can be preempted and
+have INDIRs in its VCPD.
 
 It is tempting to just use sys_change_vcore(), which will change the calling
 vcore to the new one.  This should only be used to "unstick" a vcore.  A vcore
@@ -461,6 +476,64 @@ sys_change_vcore().  We want the new vcore to know the old vcore was put
 offline: a preemption (albeit one that it chose to do, and one that isn't stuck
 in vcore context).
 
+One older way to deal with this was to force the 2LS to deal with this. The 2LS
+would check the ev_mboxes/ev_qs of all ev_qs that could send INDIRS to the
+offline vcore.  There could be INDIRS in the VCPD that are just lying there.
+The 2LS knows which ev_qs these are (such as for completed syscalls), and for
+many things, this will be a common ev_q (such as for 'vcore-x-was-preempted').
+However, this is a huge pain in the ass, since a preempted vcore could have the
+spammed INDIR for an ev_q associated with another vcore.  To deal with this,
+the 2LS would need to check *every* ev_q that requests INDIRs.  We don't do
+this.
+
+Instead, we simply have the remote core check the VCPD public mbox of the
+preempted vcore.  INDIRs (and other vcore business that other vcores can handle)
+will get sorted here.
+
+3.3.5: Lists to Find Vcores
+---------------
+A process has three lists: online, bulk_preempt, and inactive.  These not only
+are good for process management, but also for helping alert_vcore() find
+potentially alertable vcores.  alert_vcore() and its associated helpers are
+failry complicated and heavily commented.  I've set things up so both the
+online_vcs and the bulk_preempted_vcs lists can be handled the same way: post to
+the first element, then see if it still VC_CAN_RCV_MSG.  If not, if it is still
+the first on the list, then it hasn't proc_yield()ed yet, and it will eventually
+restart when it tries to yield.  And this all works without locking the
+proc_lock.  There are a bunch more details and races avoided.  Check the code
+out.
+
+3.3.6: Vcore Business and the VCPD mboxs
+---------------
+There are two types of VCPD mboxes: public and private.  Public ones will get
+handled during preemption recovery.  Messages sent here need to be handle-able
+by any vcore.  Private messages are for that specific vcore.  In the common
+case, the public mbox will usually only get looked at by its vcore.  Only during
+recovery and some corner cases will we deal with it remotely.
+
+Here's some guidelines: if you message is spammy and the handler can deal with
+spurious events and it doesn't need to be on a specific vcore, then go with
+public.  Examples of public mbox events are ones that need to be spammed:
+preemption recovery, INDIRs, etc.  Note that you won't need to worry about
+these: uthread code and the kernel handle them.  But if you have something
+similar, then that's where it would go.  You can also send non-spammy things,
+but there's no guarantee they'll be looked at.
+
+Some messages should only be sent to the private mbox.  These include ones that
+make no sense for other vcores to handle.  Examples: 2LS IPIs/preemptions (like
+"change your scheduling policy vcore 3", preemption-pending notifs from the
+kernel, timer interrupts, etc.
+
+An example of something that shouldn't be sent to either is syscall completions.
+They can't be spammed, so you can't send them around like INDIRs.  And they need
+to be dealt with.  Other than carefully-spammed public messages, there's no
+guarantee of getting a message for certain scenarios (yields).  Instead, use an
+ev_q with INDIR set.
+
+Also note that a 2LS could set up a big ev_q with EVENT_IPI and not EVENT_INDIR,
+and then poll for that in their vcore_entry().  This is equivalent to setting up
+a small ev_q with EVENT_IPI and pointing it at the private mbox.
+
 3.4 Application-specific Event Handling
 ---------------------------------------
 So what happens when the vcore/2LS isn't handling an event queue, but has been
@@ -481,9 +554,10 @@ blocking u_threads).
 
 There are a couple ways to handle this.  Ultimately, the application is supposed
 to handle the event.  If it asked for an IPI, it is because something ought to
-be done, which really means running a handler.  If the application sets
-EVENT_THREAD in the ev_q's flags, the 2LS ought to spawn a thread to run the
-ev_q's handler.  If EVENT_JUSTHANDLEIT is set, the vcore will execute the
+be done, which really means running a handler.  We used to support the
+application setting EVENT_THREAD in the ev_q's flags, and the 2LS would spawn a
+thread to run the ev_q's handler.  Now we just have the application block a
+uthread on the evq.  If an ev_handler is set, the vcore will execute the
 handler itself.  Careful with this, since the only memory it touches must be
 pinned, the function must not block (this is only true for the handlers called
 directly out of vcore context), and it should return quickly.
@@ -586,14 +660,254 @@ functions in the kernel, much like the Round Robin set will need to do.  No
 need to force things to fit just for the sake of using a 'solution'.  We want
 tools to make solutions, not packaged solutions.
 
-4. Misc Things That Aren't Sorted Completely:
+3.8 UTHREAD_DONT_MIGRATE
+---------------------------------------
+DONT_MIGRATE exists to allow uthreads to disable notifications/IPIs and enter
+vcore context.  It is needed since you need to read vcoreid to disable notifs,
+but once you read it, you need to not move to another vcore.  Here are a few
+rules/guidelines.
+
+We turn off the flag so that we can disable notifs, but turn the flag back on
+before enabling.  The thread won't get migrated in that instant since notifs are
+off.  But if it was the other way, we could miss a message (because we skipped
+an opportunity to be dropped into vcore context to read a message).
+
+Don't check messages/handle events when you have a DONT_MIGRATE uthread.  There
+are issues with preemption recovery if you do.  In short, if two uthreads are
+both DONT_MIGRATE with notifs enabled on two different vcores, and one vcore
+gets preempted while the other gets an IPI telling it to recover the other one,
+both could keep bouncing back and forth if they handle their preemption
+*messages* without dealing with their own DONT_MIGRATEs first.  Note that the
+preemption recovery code can handle having a DONT_MIGRATE thread on the vcore.
+This is a special case, and it is very careful about how cur_uthread works.
+
+All uses of DONT_MIGRATE must reenable notifs (and check messages) at some
+point.  One such case is uthread_yield().  Another is mcs_unlock_notifsafe().
+Note that mcs_notif_safe locks have uthreads that can't migrate for a
+potentially long time.  notifs are also disabled, so it's not a big deal.  It's
+basically just the same as if you were in vcore context (though technically you
+aren't) when it comes to preemption recovery: we'll just need to restart the
+vcore via a syscall.  Also note that it would be a real pain in the ass to
+migrate a notif_safe locking uthread.  The whole point of it is in case it grabs
+a lock that would be held by vcore context, and there's no way to know it isn't
+a lock on the restart-path.
+
+3.9 Why Preemption Handling Doesn't Lock Up (probably)
+---------------------------------------
+One of the concerns of preemption handling is that we don't get into some form
+of livelock, where we ping-pong back and forth between vcores (or a set of
+vcores), all of which are trying to handle each other's preemptions.  Part of
+the concern is that when a vcore sys_changes to another, it can result in
+another preemption message being sent.  We want to be sure that we're making
+progress, and not just livelocked doing sys_change_vcore()s.
+
+A few notes first:
+1) If a vcore is holding locks or otherwise isn't handling events and is
+preempted, it will let go of its locks before it gets to the point of
+attempting to handle any other vcore preemption events.  Event handling is only
+done when it is okay to never return (meaning no locks are held).  If this is
+the situation, eventually it'll work itself out or get to a potential ping-pong
+scenario.
+
+2) When you change_to while handling preemption, once you start back up, you
+will leave change_to and eventually fetch a new event.  This means any
+potential ping-pong needs to happen on a fresh event.
+
+3) If there are enough pcores for the vcores to all run, we won't issue any
+change_tos, since the vcores are no longer preempted.  This means we only are
+worried about situations with insufficient vcores.  We'll mostly talk about 1
+pcore and 2 vcores.
+
+4) Preemption handlers will not call change_to on their target vcore if they
+are also the one STEALING from that vcore.  The handler will stop STEALING
+first.
+
+So the only way to get stuck permanently is if both cores are stuck doing a
+sys_change_to(FALSE).  This means we want to become the other vcore, *and* we
+need to restart our vcore where it left off.  This is due to some invariant
+that keeps us from abandoning vcore context.  If we were to abandon vcore
+context (with a sys_change_to(TRUE)), we basically don't need to be
+preempt-recovered.  We already packaged up our cur_uthread, and we know we
+aren't holding any locks or otherwise breaking any invariants.  The system will
+work fine if we never run again.  (Someone just needs to check our messages).
+
+Now, there are only two cases where we will do a sys_change_to(FALSE) *while*
+handling preemptions.  Again, we aren't concerned about things like MCS-PDR
+locks; those all work because the change_tos are done where we'd normally just
+busy loop.  We are only concerned about change_tos during handle_vc_preempt.
+These two cases are when the changing/handling vcore has a DONT_MIGRATE uthread
+or when someone else is STEALING its uthread.  Note that both of these cases
+are about the calling vcore, not its target.
+
+If a vcore (referred to as "us") has a DONT_MIGRATE uthread and it is handling
+events, it is because someone else is STEALING from our vcore, and we are in
+the short one-shot event handling loop at the beginning of
+uthread_vcore_entry().  Whichever vcore is STEALING will quickly realize it
+can't steal (it sees the DONT_MIGRATE), and bail out.  If that vcore isn't
+running now, we will change_to it (which is the purpose of our handling their
+preemption).  Once that vcore realizes it can't steal, it will stop STEALING
+and change to us.  At this point, no one is STEALING from us, and we move along
+in the code.  Specifically, we do *not* handle events (we now have an event
+about the other vcore being preempted when it changed_to us), and instead we
+start up the DONT_MIGRATE uthread and let it run until it is migratable, at
+which point we handle events and will deal with the other vcore.  
+
+So DONT_MIGRATE will be sorted out.  Likewise, STEALING gets sorted out too,
+quite easily.  If someone is STEALING from us, they will quickly stop STEALING
+and change to us.  There are only two ways this could even happen: they are
+running concurrently with us, and somehow saw us out of vcore context before
+deciding to STEAL, or they were in the process of STEALING and got preempted by
+the kernel.  They would not have willingly stopped running while STEALING our
+cur_uthread.  So if we are running and someone is stealing, after a round of
+change_tos, eventually they run, and stop STEALING.
+
+Note that once someone stops STEALING from us, they will not start again,
+unless we leave vcore context.  If that happened, we basically broke out of the
+ping-pong, and now we're onto another set of preemptions.  We wouldn't leave
+vcore context if we still had preemption events to deal with.
+
+Finally, note that we needed to only check for one message at a time at the
+beginning of uthread_vcore_entry().  If we just handled the entire mbox without
+checking STEALING, then we might not break out of that loop if there is a
+constant supply of messages (perhaps from a vcore in a similar loop).
+
+Anyway, that's the basic plan behind the preemption handler and how we avoid
+the ping-ponging.  change_to_vcore() is built so that we handle our own
+preemption before changing (pack up our current uthread), so that we make
+progress.  The two cases where we can't do that get sorted out after everyone
+gets to run once, and since you can't steal or have other uthread's turn on
+DONT_MIGRATE while we're in vcore context, eventually we clear everything up.
+There might be other bugs or weird corner cases, possibly involving multiple
+vcores, but I think we're okay for now.
+
+3.10: Handling Messages for Other Vcores
+---------------------------------------
+First, remember that when a vcore handles an event, there's no guarantee that
+the vcore will return from the handler.  It may start fresh in vcore_entry().
+
+The issue is that when you handle another vcore's INDIRs, you may handle
+preemption messages.  If you have to do a change_to, the kernel will make sure
+a message goes out about your demise.  Thus someone who recovers that will
+check your public mbox.  However, the recoverer won't know that you were
+working on another vcore's mbox, so those messages might never be checked.
+
+The way around it is to send yourself a "check the other guy's messages" event.
+When we might change_to and never return, if we were dealing with another
+vcores mbox, we'll send ourselves a message to finish up that mbox (if there
+are any messages left).  Whoever reads our messages will eventually get that
+message, and deal with it.
+
+One thing that is a little ugly is that the way you deal with messages two
+layers deep is to send yourself the message.  So if VC1 is handling VC2's
+messages, and then wants to change_to VC3, VC1 sends a message to VC1 to check
+VC2.  Later, when VC3 is checking VC1's messages, it'll handle the "check VC2's messages"
+message.  VC3 can't directly handle VC2's messages, since it could run a
+handler that doesn't return.  Nor can we just forget about VC2.  So VC3 sends
+itself a message to check VC2 later.  Alternatively, VC3 could send itself a
+message to continue checking VC1, and then move on to VC2.  Both seem
+equivalent.  In either case, we ought to check to make sure the mbox has
+something before bothering sending the message.
+
+So for either a "change_to that might not return" or for a "check INDIRs on yet
+another vcore", we send messages to ourself so that we or someone else will
+deal with it.
+
+Note that we use TLS to track whether or not we are handling another vcore's
+messages, and if we do plan to change_to that might not return, we clear the
+bool so that when our vcore starts over at vcore_entry(), it starts over and
+isn't still checking someone elses message.
+
+As a reminder of why this is important: these messages we are hunting down
+include INDIRs, specifically ones to ev_qs such as the "syscall completed
+ev_q".  If we never get that message, a uthread will block forever.  If we
+accidentally yield a vcore instead of checking that message, we would end up
+yielding the process forever since that uthread will eventually be the last
+one, but our main thread is probably blocked on a join call.  Our process is
+blocked on a message that already came, but we just missed it. 
+
+4. Single-core Process (SCP) Events:
+====================
+4.1 Basics:
+---------------------------------------
+Event delivery is important for SCP's blocking syscalls.  It can also be used
+(in the future) to deliver POSIX signals, which would just be another kernel
+event.
+
+SCPs can receive events just like MCPs.  For the most part, the code paths are
+the same on both sides of the K/U interface.  The kernel sends events (which
+can detect an SCP and will send it to vcore0), the kernel will make sure you
+can't yield/miss an event, etc.  Userspace preps vcore context in advance, and
+can do all the things vcore context does: handle events, select thread to run.
+For an SCP, there is only one thread to run.
+
+4.2 Degenerate Event Delivery:
+---------------------------------------
+That being said, there are a few tricky things.  First, there is a time before
+the SCP is ready to fully receive events.  Specifically, before
+vcore_event_init(), which is called out of glibc's _start.  More importantly,
+the runtime linker never calls that function, yet it wants to block.
+
+The important thing to note is that there are a few parts to event delivery:
+registration (user), sending the event (kernel), making sure the proc wakes up
+(kernel), and actually handling the event (user).  For syscalls, the only thing
+the process (even rtld) needs is the first three.  Registration is easy - can be
+done with nothing more than kernel headers (no need for parlib) for NO_MSG ev_qs
+(no need to init the UCQ).  Event handling is trickier, and requires parlib
+(which rtld can't link against).  To support processes that could register for
+events, but not handle them (or even enter vcore context), the kernel needed a
+few changes (checking the VC_SCP_NOVCCTX flag) so that it would wake the
+process, but never put it in vcore context.  
+
+This degenerate event handling just wakes the process up, at which point it can
+check on its syscall.  Very early in the process's life, it'll init vcore0's
+UCQ and be able to handle full events, enter vcore context, etc.
+
+Once the SCP is up and running, it can receive events like normal.  One thing to
+note is that the SCPs are not using a handle_syscall() event handler, like the
+MCPs do.  They are only using the event to get the process restarted, at which
+point their vcore 0 restarts thread0.  One consequence of this is that if a
+process receives an unrelated event while blocking on a syscall, it'll handle
+that event, then restart thread0.  Thread0 will see its syscall isn't complete,
+and then re-block.  (It also re-registers its ev_q, which is harmless).  When
+that syscall is finally done, the kernel will send an event and wake it up
+again.
+
+4.3 Extra Tidbits:
+---------------------------------------
+If we receive an event right as we transition from SCP to MCP, vcore0 could get
+spammed with a message that is never received.  Right now, it's not a problem,
+since vcore0 is the first vcore that will get woken up as an MCP.  This could be
+an issue if we ever allow transitions from MCP back to SCP.
+
+On a related note, it's now wrong for SCPs to sys_yield(FALSE) (not being nice,
+meaning they are waiting for an event) in a loop that does not check events or
+otherwise allow them to break out of that loop.  This should be fairly obvious.
+A little more subtle is that these loops also need to sort out notif_pending.
+If you are trying to yield and still have an old notif_pending set, the kernel
+won't let you yield (it thinks you are missing the notif).  For the degenerate
+mode, (VC_SCP_NOVCCTX is set on vcore0), the kernel will handle dealing with
+this flag.
+
+Finally, note that while the SCP is in vcore context, it has none of the
+guarantees of an MCP.  It's somewhat meaningless to talk about being gang
+scheduled or knowing about the state of other vcores.  If you're running, you're
+on a physical core.  You may get unexpected interrupts, descheduled, etc.  Aside
+from the guarantees and being the only vcore, the main differences are really up
+to the kernel scheduler.  In that sense, we have somewhat of a new state for
+processes - SCPs that can enter vcore context.  From the user's perspective,
+they look a lot like an MCP, and the degenerate/early mode SCPs are like the
+old, dumb SCPs.  The big difference for userspace is that there isn't a 2LS yet
+(will need to reinit things slightly).  The kernel treats SCPs and MCPs very
+differently too, but that may not always be the case.
+
+5. Misc Things That Aren't Sorted Completely:
 ====================
-4.1 What about short handlers?
+5.1 What about short handlers?
 ---------------------------------------
 Once we sort the other issues, we can ask for them via a flag in the event_q,
 and run the handler in the event_q struct.
 
-4.2 What about blocking on a syscall?
+5.2 What about blocking on a syscall?
 ---------------------------------------
 The current plan is to set a flag, and let the kernel go from there.  The
 kernel knows which process it is, since that info is saved in the kthread that