Remove the option to spawn_thread for an evq (XCC)
[akaros.git] / user / parlib / vcore.c
index 124ada8..b440c74 100644 (file)
@@ -1,76 +1,91 @@
-#include <arch/arch.h>
+#include <parlib/arch/arch.h>
 #include <stdbool.h>
 #include <errno.h>
-#include <vcore.h>
-#include <mcs.h>
+#include <parlib/vcore.h>
+#include <parlib/mcs.h>
 #include <sys/param.h>
-#include <parlib.h>
+#include <parlib/parlib.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <sys/mman.h>
 #include <stdio.h>
-#include <glibc-tls.h>
-#include <event.h>
-#include <uthread.h>
-#include <ucq.h>
+#include <parlib/event.h>
+#include <parlib/uthread.h>
+#include <parlib/ucq.h>
 #include <ros/arch/membar.h>
+#include <parlib/printf-ext.h>
 
-/* starting with 1 since we alloc vcore0's stacks and TLS in vcore_init(). */
+__thread int __vcoreid = 0;
+__thread bool __vcore_context = FALSE;
+
+/* starting with 1 since we alloc vcore0's stacks and TLS in vcore_lib_init(). */
 static size_t _max_vcores_ever_wanted = 1;
 atomic_t nr_new_vcores_wanted;
 atomic_t vc_req_being_handled;
 
-extern void** vcore_thread_control_blocks;
+__thread struct syscall __vcore_one_sysc = {.flags = (atomic_t)SC_DONE, 0};
 
-/* Get a TLS, returns 0 on failure.  Vcores have their own TLS, and any thread
- * created by a user-level scheduler needs to create a TLS as well. */
-void *allocate_tls(void)
-{
-       extern void *_dl_allocate_tls(void *mem) internal_function;
-       void *tcb = _dl_allocate_tls(NULL);
-       if (!tcb)
-               return 0;
-       /* Make sure the TLS is set up properly - its tcb pointer points to itself.
-        * Keep this in sync with sysdeps/ros/XXX/tls.h.  For whatever reason,
-        * dynamically linked programs do not need this to be redone, but statics
-        * do. */
-       tcbhead_t *head = (tcbhead_t*)tcb;
-       head->tcb = tcb;
-       head->self = tcb;
-       return tcb;
-}
+/* Per vcore entery function used when reentering at the top of a vcore's stack */
+static __thread void (*__vcore_reentry_func)(void) = NULL;
 
-/* Free a previously allocated TLS region */
-void free_tls(void *tcb)
+/* The default user vcore_entry function. */
+void __attribute__((noreturn)) __vcore_entry(void)
 {
-       extern void _dl_deallocate_tls (void *tcb, bool dealloc_tcb) internal_function;
-       assert(tcb);
-       _dl_deallocate_tls(tcb, TRUE);
+       extern void uthread_vcore_entry(void);
+       uthread_vcore_entry();
+       fprintf(stderr, "vcore_entry() should never return!\n");
+       abort();
+       __builtin_unreachable();
 }
+void vcore_entry(void) __attribute__((weak, alias ("__vcore_entry")));
 
-/* Reinitialize / reset / refresh a TLS to its initial values.  This doesn't do
- * it properly yet, it merely frees and re-allocates the TLS, which is why we're
- * slightly ghetto and return the pointer you should use for the TCB. */
-void *reinit_tls(void *tcb)
+/* The lowest level function jumped to by the kernel on every vcore_entry.
+ * Currently, this function is only necessary so we can set the tls_desc from
+ * the vcpd for non x86_64 architectures. We should consider removing this and
+ * making it mandatory to set the tls_desc in the kernel. We wouldn't even
+ * need to pass the vcore id to user space at all if we did this.  It would
+ * already be set in the preinstalled TLS as __vcore_id. */
+static void __attribute__((noreturn)) __kernel_vcore_entry(void)
 {
-       /* TODO: keep this in sync with the methods used in
-        * allocate_transition_tls() */
-       free_tls(tcb);
-       return allocate_tls();
+       /* The kernel sets the TLS desc for us, based on whatever is in VCPD.
+        *
+        * x86 32-bit TLS is pretty jacked up, so the kernel doesn't set the TLS
+        * desc for us.  it's a little more expensive to do it here, esp for
+        * amd64.  Can remove this when/if we overhaul 32 bit TLS.
+        *
+        * AFAIK, riscv's TLS changes are really cheap, and they don't do it in
+        * the kernel (yet/ever), so they can set their TLS here too. */
+       int id = __vcore_id_on_entry;
+       #ifndef __x86_64__
+       set_tls_desc(vcpd_of(id)->vcore_tls_desc);
+       #endif
+       /* Every time the vcore comes up, it must set that it is in vcore context.
+        * uthreads may share the same TLS as their vcore (when uthreads do not have
+        * their own TLS), and if a uthread was preempted, __vcore_context == FALSE,
+        * and that will continue to be true the next time the vcore pops up. */
+       __vcore_context = TRUE;
+       vcore_entry();
+       fprintf(stderr, "vcore_entry() should never return!\n");
+       abort();
+       __builtin_unreachable();
 }
 
 /* TODO: probably don't want to dealloc.  Considering caching */
 static void free_transition_tls(int id)
 {
-       if(vcore_thread_control_blocks[id])
-       {
-               free_tls(vcore_thread_control_blocks[id]);
-               vcore_thread_control_blocks[id] = NULL;
+       if (get_vcpd_tls_desc(id)) {
+               /* Note we briefly have no TLS desc in VCPD.  This is fine so long as
+                * that vcore doesn't get started fresh before we put in a new desc */
+               free_tls(get_vcpd_tls_desc(id));
+               set_vcpd_tls_desc(id, NULL);
        }
 }
 
 static int allocate_transition_tls(int id)
 {
+       /* Libc function to initialize TLS-based locale info for ctype functions. */
+       extern void __ctype_init(void);
+
        /* We want to free and then reallocate the tls rather than simply 
         * reinitializing it because its size may have changed.  TODO: not sure if
         * this is right.  0-ing is one thing, but freeing and reallocating can be
@@ -79,23 +94,35 @@ static int allocate_transition_tls(int id)
        free_transition_tls(id);
 
        void *tcb = allocate_tls();
-
-       if ((vcore_thread_control_blocks[id] = tcb) == NULL) {
+       if (!tcb) {
                errno = ENOMEM;
                return -1;
        }
+
+       /* Setup some intitial TLS data for the newly allocated transition tls. */
+       void *temp_tcb = get_tls_desc();
+       set_tls_desc(tcb);
+       begin_safe_access_tls_vars();
+       __vcoreid = id;
+       __vcore_context = TRUE;
+       __ctype_init();
+       end_safe_access_tls_vars();
+       set_tls_desc(temp_tcb);
+
+       /* Install the new tls into the vcpd. */
+       set_vcpd_tls_desc(id, tcb);
        return 0;
 }
 
-static void free_transition_stack(int id)
+static void free_vcore_stack(int id)
 {
        // don't actually free stacks
 }
 
-static int allocate_transition_stack(int id)
+static int allocate_vcore_stack(int id)
 {
        struct preempt_data *vcpd = vcpd_of(id);
-       if (vcpd->transition_stack)
+       if (vcpd->vcore_stack)
                return 0; // reuse old stack
 
        void* stackbot = mmap(0, TRANSITION_STACK_SIZE,
@@ -105,30 +132,35 @@ static int allocate_transition_stack(int id)
        if(stackbot == MAP_FAILED)
                return -1; // errno set by mmap
 
-       vcpd->transition_stack = (uintptr_t)stackbot + TRANSITION_STACK_SIZE;
+       vcpd->vcore_stack = (uintptr_t)stackbot + TRANSITION_STACK_SIZE;
 
        return 0;
 }
 
-int vcore_init()
+/* Run libc specific early setup code. */
+static void vcore_libc_init(void)
 {
-       static int initialized = 0;
-       uintptr_t mmap_block;
-       /* Note this is racy, but okay.  The only time it'll be 0 is the first time
-        * through, when we are _S */
-       if(initialized)
-               return 0;
+       register_printf_specifier('r', printf_errstr, printf_errstr_info);
+       /* TODO: register for other kevents/signals and whatnot (can probably reuse
+        * the simple ev_q).  Could also do this via explicit functions from the
+        * program. */
+}
 
-       vcore_thread_control_blocks = (void**)calloc(max_vcores(),sizeof(void*));
+void __attribute__((constructor)) vcore_lib_init(void)
+{
+       uintptr_t mmap_block;
 
-       if(!vcore_thread_control_blocks)
-               goto vcore_init_fail;
+       /* Note this is racy, but okay.  The first time through, we are _S.
+        * Also, this is the "lowest" level constructor for now, so we don't need
+        * to call any other init functions after our run_once() call. This may
+        * change in the future. */
+       init_once_racy(return);
 
        /* Need to alloc vcore0's transition stuff here (technically, just the TLS)
         * so that schedulers can use vcore0's transition TLS before it comes up in
         * vcore_entry() */
-       if(allocate_transition_stack(0) || allocate_transition_tls(0))
-               goto vcore_init_tls_fail;
+       if (allocate_vcore_stack(0) || allocate_transition_tls(0))
+               goto vcore_lib_init_fail;
 
        /* Initialize our VCPD event queues' ucqs, two pages per ucq, 4 per vcore */
        mmap_block = (uintptr_t)mmap(0, PGSIZE * 4 * max_vcores(),
@@ -148,25 +180,71 @@ int vcore_init()
                ucq_init_raw(&vcpd_of(i)->ev_mbox_private.ev_msgs,
                             mmap_block + (4 * i + 2) * PGSIZE,
                             mmap_block + (4 * i + 3) * PGSIZE);
+               /* Set the lowest level entry point for each vcore. */
+               vcpd_of(i)->vcore_entry = (uintptr_t)__kernel_vcore_entry;
        }
        atomic_init(&vc_req_being_handled, 0);
        assert(!in_vcore_context());
-       initialized = 1;
-       /* no longer need to enable notifs on vcore 0, it is set like that by
-        * default (so you drop into vcore context immediately on transtioning to
-        * _M) */
-       return 0;
-vcore_init_tls_fail:
-       free(vcore_thread_control_blocks);
-vcore_init_fail:
-       errno = ENOMEM;
-       return -1;
+       vcore_libc_init();
+       return;
+vcore_lib_init_fail:
+       assert(0);
+}
+
+/* Helper functions used to reenter at the top of a vcore's stack for an
+ * arbitrary function */
+static void __attribute__((noinline, noreturn)) 
+__vcore_reenter()
+{
+  __vcore_reentry_func();
+  assert(0);
+}
+
+void vcore_reenter(void (*entry_func)(void))
+{
+  assert(in_vcore_context());
+  struct preempt_data *vcpd = vcpd_of(vcore_id());
+
+  __vcore_reentry_func = entry_func;
+  set_stack_pointer((void*)vcpd->vcore_stack);
+  cmb();
+  __vcore_reenter();
+}
+
+/* Helper, picks some sane defaults and changes the process into an MCP */
+void vcore_change_to_m(void)
+{
+       int ret;
+       __procdata.res_req[RES_CORES].amt_wanted = 1;
+       __procdata.res_req[RES_CORES].amt_wanted_min = 1;       /* whatever */
+       assert(!in_multi_mode());
+       assert(!in_vcore_context());
+       ret = sys_change_to_m();
+       assert(!ret);
+       assert(in_multi_mode());
+       assert(!in_vcore_context());
 }
 
 /* Returns -1 with errno set on error, or 0 on success.  This does not return
  * the number of cores actually granted (though some parts of the kernel do
  * internally).
  *
+ * This tries to get "more vcores", based on the number we currently have.
+ * We'll probably need smarter 2LSs in the future that just directly set
+ * amt_wanted.  What happens is we can have a bunch of 2LS vcore contexts
+ * trying to get "another vcore", which currently means more than num_vcores().
+ * If you have someone ask for two more, and then someone else ask for one more,
+ * how many you ultimately ask for depends on if the kernel heard you and
+ * adjusted num_vcores in between the two calls.  Or maybe your amt_wanted
+ * already was num_vcores + 5, so neither call is telling the kernel anything
+ * new.  It comes down to "one more than I have" vs "one more than I've already
+ * asked for".
+ *
+ * So for now, this will keep the older behavior (one more than I have).  It
+ * will try to accumulate any concurrent requests, and adjust amt_wanted up.
+ * Interleaving, repetitive calls (everyone asking for one more) may get
+ * ignored.
+ *
  * Note the doesn't block or anything (despite the min number requested is
  * 1), since the kernel won't block the call.
  *
@@ -196,8 +274,6 @@ int vcore_request(long nr_new_vcores)
 {
        long nr_to_prep_now, nr_vcores_wanted;
 
-       if (vcore_init() < 0)
-               return -1;      /* consider ERRNO */
        /* Early sanity checks */
        if ((nr_new_vcores < 0) || (nr_new_vcores + num_vcores() > max_vcores()))
                return -1;      /* consider ERRNO */
@@ -209,12 +285,14 @@ try_handle_it:
                /* We got a 1 back, so someone else is already working on it */
                return 0;
        }
-handle_it:
-       /* So now we're the ones supposed to handle things.  Figure out how many we
-        * have, though this is racy.  Yields/preempts/grants will change this over
-        * time, and we may end up asking for less than we had. */
+       /* So now we're the ones supposed to handle things.  This does things in the
+        * "increment based on the number we have", vs "increment on the number we
+        * said we want".
+        *
+        * Figure out how many we have, though this is racy.  Yields/preempts/grants
+        * will change this over time, and we may end up asking for less than we
+        * had. */
        nr_vcores_wanted = num_vcores();
-       cmb();  /* force a reread of num_vcores() later */
        /* Pull all of the vcores wanted into our local variable, where we'll deal
         * with prepping/requesting that many vcores.  Keep doing this til we think
         * no more are wanted. */
@@ -223,76 +301,91 @@ handle_it:
                /* Don't bother prepping or asking for more than we can ever get */
                nr_vcores_wanted = MIN(nr_vcores_wanted, max_vcores());
                /* Make sure all we might ask for are prepped */
-               for(long i = _max_vcores_ever_wanted; i < nr_vcores_wanted; i++) {
-                       if (allocate_transition_stack(i) || allocate_transition_tls(i)) {
+               for (long i = _max_vcores_ever_wanted; i < nr_vcores_wanted; i++) {
+                       if (allocate_vcore_stack(i) || allocate_transition_tls(i)) {
                                atomic_set(&vc_req_being_handled, 0);   /* unlock and bail out*/
                                return -1;
                        }
                        _max_vcores_ever_wanted++;      /* done in the loop to handle failures*/
                }
        }
-       /* Got all the ones we can get, let's submit it to the kernel.  We check
-        * against num_vcores() one last time, though we still have some races... */
-       if (nr_vcores_wanted > num_vcores()) {
-               sys_resource_req(RES_CORES, nr_vcores_wanted, 1, 0);
-               cmb();  /* force a reread of num_vcores() at handle_it: */
-               goto handle_it;
-       }
-       /* Here, we believe there are none left to do */
+       cmb();  /* force a reread of num_vcores() */
+       /* Update amt_wanted if we now want *more* than what the kernel already
+        * knows.  See notes in the func doc. */
+       if (nr_vcores_wanted > __procdata.res_req[RES_CORES].amt_wanted)
+               __procdata.res_req[RES_CORES].amt_wanted = nr_vcores_wanted;
+       /* If num_vcores isn't what we want, we can poke the ksched.  Due to some
+        * races with yield, our desires may be old.  Not a big deal; any vcores
+        * that pop up will just end up yielding (or get preempt messages.)  */
+       if (nr_vcores_wanted > num_vcores())
+               sys_poke_ksched(0, RES_CORES);  /* 0 -> poke for ourselves */
+       /* Unlock, (which lets someone else work), and check to see if more work
+        * needs to be done.  If so, we'll make sure it gets handled. */
        atomic_set(&vc_req_being_handled, 0);   /* unlock, to allow others to try */
-       /* Double check for any that might have come in while we were out */
+       wrmb();
+       /* check for any that might have come in while we were out */
        if (atomic_read(&nr_new_vcores_wanted))
                goto try_handle_it;
        return 0;
 }
 
-/* This can return, if you failed to yield due to a concurrent event. */
+/* This can return, if you failed to yield due to a concurrent event.  Note
+ * we're atomicly setting the CAN_RCV flag, and aren't bothering with CASing
+ * (either with the kernel or uthread's handle_indirs()).  We don't particularly
+ * care what other code does - we intend to set those flags no matter what. */
 void vcore_yield(bool preempt_pending)
 {
+       unsigned long old_nr;
        uint32_t vcoreid = vcore_id();
        struct preempt_data *vcpd = vcpd_of(vcoreid);
-       vcpd->can_rcv_msg = FALSE;
-       /* no wrmb() necessary, clear_notif() has an mb() */
-       /* Clears notif pending.  If we had an event outstanding, this will handle
-        * it and return TRUE, at which point we want to unwind and return to the
-        * 2LS loop (where we may not want to yield anymore).  Note that the kernel
-        * only cares about can_rcv_msg for the desired vcore, not for a FALLBACK.
-        * We need to deal with this notif_pending business regardless of
-        * can_rcv_msg.  We just want to avoid a yield syscall if possible.  It is
-        * important that clear_notif_pending will handle_events().  That is
-        * necessary to do/check after setting can_rcv_msg to FALSE. */
-       if (clear_notif_pending(vcoreid)) {
-               vcpd->can_rcv_msg = TRUE;
+       __sync_fetch_and_and(&vcpd->flags, ~VC_CAN_RCV_MSG);
+       /* no wrmb() necessary, handle_events() has an mb() if it is checking */
+       /* Clears notif pending and tries to handle events.  This is an optimization
+        * to avoid the yield syscall if we have an event pending.  If there is one,
+        * we want to unwind and return to the 2LS loop, where we may not want to
+        * yield anymore.
+        * Note that the kernel only cares about CAN_RCV_MSG for the desired vcore;
+        * when spamming, it relies on membership of lists within the kernel.  Look
+        * at spam_list_member() for more info (k/s/event.c). */
+       if (handle_events(vcoreid)) {
+               __sync_fetch_and_or(&vcpd->flags, VC_CAN_RCV_MSG);
                return;
        }
+       /* If we are yielding since we don't want the core, tell the kernel we want
+        * one less vcore (vc_yield assumes a dumb 2LS).
+        *
+        * If yield fails (slight race), we may end up having more vcores than
+        * amt_wanted for a while, and might lose one later on (after a
+        * preempt/timeslicing) - the 2LS will have to notice eventually if it
+        * actually needs more vcores (which it already needs to do).  amt_wanted
+        * could even be 0.
+        *
+        * In general, any time userspace decrements or sets to 0, it could get
+        * preempted, so the kernel will still give us at least one, until the last
+        * vcore properly yields without missing a message (and becomes a WAITING
+        * proc, which the ksched will not give cores to).
+        *
+        * I think it's possible for userspace to do this (lock, read amt_wanted,
+        * check all message queues for all vcores, subtract amt_wanted (not set to
+        * 0), unlock) so long as every event handler +1s the amt wanted, but that's
+        * a huge pain, and we already have event handling code making sure a
+        * process can't sleep (transition to WAITING) if a message arrives (can't
+        * yield if notif_pending, can't go WAITING without yielding, and the event
+        * posting the notif_pending will find the online VC or be delayed by
+        * spinlock til the proc is WAITING). */
+       if (!preempt_pending) {
+               do {
+                       old_nr = __procdata.res_req[RES_CORES].amt_wanted;
+                       if (old_nr == 0)
+                               break;
+               } while (!__sync_bool_compare_and_swap(
+                            &__procdata.res_req[RES_CORES].amt_wanted,
+                            old_nr, old_nr - 1));
+       }
        /* We can probably yield.  This may pop back up if notif_pending became set
         * by the kernel after we cleared it and we lost the race. */
        sys_yield(preempt_pending);
-       vcpd->can_rcv_msg = TRUE;
-}
-
-/* Clear pending, and try to handle events that came in between a previous call
- * to handle_events() and the clearing of pending.  While it's not a big deal,
- * we'll loop in case we catch any.  Will break out of this once there are no
- * events, and we will have send pending to 0. 
- *
- * Note that this won't catch every race/case of an incoming event.  Future
- * events will get caught in pop_ros_tf() or proc_yield().
- *
- * Also note that this handles events, which may change your current uthread or
- * might not return!  Be careful calling this.  Check run_uthread for an example
- * of how to use this. */
-bool clear_notif_pending(uint32_t vcoreid)
-{
-       bool handled_event = FALSE;
-       do {
-               vcpd_of(vcoreid)->notif_pending = 0;
-               /* need a full mb(), since handle events might be just a read or might
-                * be a write, either way, it needs to happen after notif_pending */
-               mb();
-               handled_event = handle_events(vcoreid);
-       } while (handled_event);
-       return handled_event;
+       __sync_fetch_and_or(&vcpd->flags, VC_CAN_RCV_MSG);
 }
 
 /* Enables notifs, and deals with missed notifs by self notifying.  This should
@@ -320,13 +413,126 @@ void disable_notifs(uint32_t vcoreid)
 }
 
 /* Like smp_idle(), this will put the core in a state that it can only be woken
- * up by an IPI.  In the future, we may halt or something. */
-void __attribute__((noreturn)) vcore_idle(void)
+ * up by an IPI.  For now, this is a halt.  Maybe an mwait in the future.
+ *
+ * This will return if an event was pending (could be the one you were waiting
+ * for) or if the halt failed for some reason, such as a concurrent RKM.  If
+ * successful, this will not return at all, and the vcore will restart from the
+ * top next time it wakes.  Any sort of IRQ will wake the core.
+ *
+ * Alternatively, I might make this so it never returns, if that's easier to
+ * work with (similar issues with yield). */
+void vcore_idle(void)
 {
        uint32_t vcoreid = vcore_id();
-       clear_notif_pending(vcoreid);
+       /* Once we enable notifs, the calling context will be treated like a uthread
+        * (saved into the uth slot).  We don't want to ever run it again, so we
+        * need to make sure there's no cur_uth. */
+       assert(!current_uthread);
+       /* This clears notif_pending (check, signal, check again pattern). */
+       if (handle_events(vcoreid))
+               return;
+       /* This enables notifs, but also checks notif pending.  At this point, any
+        * new notifs will restart the vcore from the top. */
        enable_notifs(vcoreid);
-       while (1) {
-               cpu_relax();
+       /* From now, til we get into the kernel, any notifs will permanently destroy
+        * this context and start the VC from the top.
+        *
+        * Once we're in the kernel, any messages (__notify, __preempt), will be
+        * RKMs.  halt will need to check for those atomically.  Checking for
+        * notif_pending in the kernel (sleep only if not set) is not enough, since
+        * not all reasons for the kernel to stay awak set notif_pending (e.g.,
+        * __preempts and __death).
+        *
+        * At this point, we're out of VC ctx, so anyone who sets notif_pending
+        * should also send an IPI / __notify */
+       sys_halt_core(0);
+       /* in case halt returns without actually restarting the VC ctx. */
+       disable_notifs(vcoreid);
+}
+
+/* Helper, that actually makes sure a vcore is running.  Call this is you really
+ * want vcoreid.  More often, you'll want to call the regular version. */
+static void __ensure_vcore_runs(uint32_t vcoreid)
+{
+       if (vcore_is_preempted(vcoreid)) {
+               printd("[vcore]: VC %d changing to VC %d\n", vcore_id(), vcoreid);
+               /* Note that at this moment, the vcore could still be mapped (we're
+                * racing with __preempt.  If that happens, we'll just fail the
+                * sys_change_vcore(), and next time __ensure runs we'll get it. */
+               /* We want to recover them from preemption.  Since we know they have
+                * notifs disabled, they will need to be directly restarted, so we can
+                * skip the other logic and cut straight to the sys_change_vcore() */
+               sys_change_vcore(vcoreid, FALSE);
+       }
+}
+
+/* Helper, looks for any preempted vcores, making sure each of them runs at some
+ * point.  This is pretty heavy-weight, and should be used to help get out of
+ * weird deadlocks (spinning in vcore context, waiting on another vcore).  If
+ * you might know which vcore you are waiting on, use ensure_vc_runs. */
+static void __ensure_all_run(void)
+{
+       for (int i = 0; i < max_vcores(); i++)
+               __ensure_vcore_runs(i);
+}
+
+/* Makes sure a vcore is running.  If it is preempted, we'll switch to
+ * it.  This will return, either immediately if the vcore is running, or later
+ * when someone preempt-recovers us.
+ *
+ * If you pass in your own vcoreid, this will make sure all other preempted
+ * vcores run. */
+void ensure_vcore_runs(uint32_t vcoreid)
+{
+       /* if the vcoreid is ourselves, make sure everyone else is running */
+       if (vcoreid == vcore_id()) {
+               __ensure_all_run();
+               return;
+       }
+       __ensure_vcore_runs(vcoreid);
+}
+
+#define NR_RELAX_SPINS 1000
+/* If you are spinning in vcore context and it is likely that you don't know who
+ * you are waiting on, call this.  It will spin for a bit before firing up the
+ * potentially expensive __ensure_all_run().  Don't call this from uthread
+ * context.  sys_change_vcore will probably mess you up. */
+void cpu_relax_vc(uint32_t vcoreid)
+{
+       static __thread unsigned int __vc_relax_spun = 0;
+       assert(in_vcore_context());
+       if (__vc_relax_spun++ >= NR_RELAX_SPINS) {
+               /* if vcoreid == vcore_id(), this might be expensive */
+               ensure_vcore_runs(vcoreid);
+               __vc_relax_spun = 0;
+       }
+       cpu_relax();
+}
+
+/* Check with the kernel to determine what vcore we are.  Normally, you should
+ * never call this, since your vcoreid is stored in your TLS.  Also, if you call
+ * it from a uthread, you could get migrated, so you should drop into some form
+ * of vcore context (DONT_MIGRATE on) */
+uint32_t get_vcoreid(void)
+{
+       if (!in_vcore_context()) {
+               assert(current_uthread);
+               assert(current_uthread->flags & UTHREAD_DONT_MIGRATE);
+       }
+       return __get_vcoreid();
+}
+
+/* Debugging helper.  Pass in the string you want printed if your vcoreid is
+ * wrong, and pass in what vcoreid you think you are.  Don't call from uthread
+ * context unless migrations are disabled.  Will print some stuff and return
+ * FALSE if you were wrong. */
+bool check_vcoreid(const char *str, uint32_t vcoreid)
+{
+       uint32_t kvcoreid = get_vcoreid();
+       if (vcoreid != kvcoreid) {
+               ros_debug("%s: VC %d thought it was VC %d\n", str, kvcoreid, vcoreid);
+               return FALSE;
        }
+       return TRUE;
 }