SYS_abort_syscall (XCC)
[akaros.git] / user / parlib / vcore.c
index dafec35..0b94ec2 100644 (file)
@@ -19,7 +19,6 @@ 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 +28,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 +46,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 +83,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 +115,6 @@ void vcore_init(void)
         * _M) */
        vc_initialized = TRUE;
        return;
-vcore_init_tls_fail:
-       free(vcore_thread_control_blocks);
 vcore_init_fail:
        assert(0);
 }
@@ -266,7 +259,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 */
@@ -283,6 +276,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);
@@ -298,14 +292,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);
@@ -399,9 +415,8 @@ 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 */
+       unsigned int spun = 0;
        assert(in_vcore_context());
-       spun = 0;
        if (spun++ >= NR_RELAX_SPINS) {
                /* if vcoreid == vcore_id(), this might be expensive */
                ensure_vcore_runs(vcoreid);