Cleans up userspace headers
[akaros.git] / user / parlib / vcore.c
index c0571ea..7c982cc 100644 (file)
 #include <uthread.h>
 #include <ucq.h>
 #include <ros/arch/membar.h>
+#include <printf-ext.h>
 
 /* starting with 1 since we alloc vcore0's stacks and TLS in vcore_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;
 bool vc_initialized = FALSE;
 __thread struct syscall __vcore_one_sysc = {.flags = (atomic_t)SC_DONE, 0};
 
@@ -29,10 +29,11 @@ static __thread void (*__vcore_reentry_func)(void) = NULL;
 /* 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);
        }
 }
 
@@ -46,11 +47,11 @@ 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;
        }
+       set_vcpd_tls_desc(id, tcb);
        return 0;
 }
 
@@ -83,16 +84,11 @@ void vcore_init(void)
        /* Note this is racy, but okay.  The first time through, we are _S */
        init_once_racy(return);
 
-       vcore_thread_control_blocks = (void**)calloc(max_vcores(),sizeof(void*));
-
-       if(!vcore_thread_control_blocks)
-               goto vcore_init_fail;
-
        /* 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;
+               goto vcore_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(),
@@ -120,8 +116,6 @@ void vcore_init(void)
         * _M) */
        vc_initialized = TRUE;
        return;
-vcore_init_tls_fail:
-       free(vcore_thread_control_blocks);
 vcore_init_fail:
        assert(0);
 }
@@ -151,6 +145,7 @@ void vcore_reenter(void (*entry_func)(void))
  * go to vcore 0. */
 void vcore_event_init(void)
 {
+       register_printf_specifier('r', printf_errstr, printf_errstr_info);
        /* set up our thread0 as a uthread */
        uthread_slim_init();
        /* TODO: register for other kevents/signals and whatnot (can probably reuse
@@ -161,11 +156,13 @@ void vcore_event_init(void)
 /* 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());
-       assert(!sys_change_to_m());
+       ret = sys_change_to_m();
+       assert(!ret);
        assert(in_multi_mode());
        assert(!in_vcore_context());
 }
@@ -264,7 +261,7 @@ try_handle_it:
         * 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(RES_CORES);
+               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 */
@@ -281,6 +278,7 @@ try_handle_it:
  * 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);
        __sync_fetch_and_and(&vcpd->flags, ~VC_CAN_RCV_MSG);
@@ -296,14 +294,36 @@ void vcore_yield(bool preempt_pending)
                return;
        }
        /* If we are yielding since we don't want the core, tell the kernel we want
-        * one less vcore.  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
+        * 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).  We need to
-        * atomically decrement, though I don't want the kernel's data type here to
-        * be atomic_t (only userspace cares in this one case). */
-       if (!preempt_pending)
-               __sync_fetch_and_sub(&__procdata.res_req[RES_CORES].amt_wanted, 1);
+        * 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);
@@ -335,17 +355,42 @@ 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.  This will return if
- * an event was pending (could be the one you were waiting for). */
+ * 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();
+       /* 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
@@ -397,13 +442,12 @@ void ensure_vcore_runs(uint32_t vcoreid)
  * context.  sys_change_vcore will probably mess you up. */
 void cpu_relax_vc(uint32_t vcoreid)
 {
-       static __thread unsigned int spun;              /* vcore TLS */
+       static __thread unsigned int __vc_relax_spun = 0;
        assert(in_vcore_context());
-       spun = 0;
-       if (spun++ >= NR_RELAX_SPINS) {
+       if (__vc_relax_spun++ >= NR_RELAX_SPINS) {
                /* if vcoreid == vcore_id(), this might be expensive */
                ensure_vcore_runs(vcoreid);
-               spun = 0;
+               __vc_relax_spun = 0;
        }
        cpu_relax();
 }