Allow ev_qs to not have INDIR throttling (XCC)
[akaros.git] / kern / src / process.c
index e2bccf8..448e3d2 100644 (file)
@@ -54,8 +54,6 @@ void put_idle_core(uint32_t coreid)
 
 /* Other helpers, implemented later. */
 static void __proc_startcore(struct proc *p, trapframe_t *tf);
-static uint32_t get_free_vcoreid(struct proc *SAFE p, uint32_t prev);
-static uint32_t get_busy_vcoreid(struct proc *SAFE p, uint32_t prev);
 static bool is_mapped_vcore(struct proc *p, uint32_t pcoreid);
 static uint32_t get_vcoreid(struct proc *p, uint32_t pcoreid);
 static uint32_t get_pcoreid(struct proc *p, uint32_t vcoreid);
@@ -113,7 +111,9 @@ int __proc_set_state(struct proc *p, uint32_t state)
         * RBS -> RGS
         * RGS -> RBS
         * RGS -> W
+        * RGM -> W
         * W   -> RBS
+        * W   -> RBM
         * RGS -> RBM
         * RBM -> RGM
         * RGM -> RBM
@@ -141,7 +141,7 @@ int __proc_set_state(struct proc *p, uint32_t state)
                                panic("Invalid State Transition! PROC_RUNNING_S to %02x", state);
                        break;
                case PROC_WAITING:
-                       if (state != PROC_RUNNABLE_S)
+                       if (!(state & (PROC_RUNNABLE_S | PROC_RUNNABLE_M)))
                                panic("Invalid State Transition! PROC_WAITING to %02x", state);
                        break;
                case PROC_DYING:
@@ -153,7 +153,8 @@ int __proc_set_state(struct proc *p, uint32_t state)
                                panic("Invalid State Transition! PROC_RUNNABLE_M to %02x", state);
                        break;
                case PROC_RUNNING_M:
-                       if (!(state & (PROC_RUNNABLE_S | PROC_RUNNABLE_M | PROC_DYING)))
+                       if (!(state & (PROC_RUNNABLE_S | PROC_RUNNABLE_M | PROC_WAITING |
+                                      PROC_DYING)))
                                panic("Invalid State Transition! PROC_RUNNING_M to %02x", state);
                        break;
        }
@@ -184,6 +185,8 @@ struct proc *pid2proc(pid_t pid)
  * any process related function. */
 void proc_init(void)
 {
+       /* Catch issues with the vcoremap and TAILQ_ENTRY sizes */
+       static_assert(sizeof(TAILQ_ENTRY(vcore)) == sizeof(void*) * 2);
        proc_cache = kmem_cache_create("proc", sizeof(struct proc),
                     MAX(HW_CACHE_ALIGN, __alignof__(struct proc)), 0, 0, 0);
        /* Init PID mask and hash.  pid 0 is reserved. */
@@ -238,23 +241,38 @@ void proc_init(void)
        atomic_init(&num_envs, 0);
 }
 
-void
-proc_init_procinfo(struct proc* p)
+/* Be sure you init'd the vcore lists before calling this. */
+static void proc_init_procinfo(struct proc* p)
 {
-       memset(&p->procinfo->vcoremap, 0, sizeof(p->procinfo->vcoremap));
-       memset(&p->procinfo->pcoremap, 0, sizeof(p->procinfo->pcoremap));
-       p->procinfo->num_vcores = 0;
-       p->procinfo->coremap_seqctr = SEQCTR_INITIALIZER;
-       // TODO: change these too
        p->procinfo->pid = p->pid;
        p->procinfo->ppid = p->ppid;
-       p->procinfo->tsc_freq = system_timing.tsc_freq;
        // TODO: maybe do something smarter here
 #ifdef __CONFIG_DISABLE_SMT__
        p->procinfo->max_vcores = num_cpus >> 1;
 #else
        p->procinfo->max_vcores = MAX(1,num_cpus-num_mgmtcores);
 #endif /* __CONFIG_DISABLE_SMT__ */
+       p->procinfo->tsc_freq = system_timing.tsc_freq;
+       p->procinfo->heap_bottom = (void*)UTEXT;
+       /* 0'ing the arguments.  Some higher function will need to set them */
+       memset(p->procinfo->argp, 0, sizeof(p->procinfo->argp));
+       memset(p->procinfo->argbuf, 0, sizeof(p->procinfo->argbuf));
+       /* 0'ing the vcore/pcore map.  Will link the vcores later. */
+       memset(&p->procinfo->vcoremap, 0, sizeof(p->procinfo->vcoremap));
+       memset(&p->procinfo->pcoremap, 0, sizeof(p->procinfo->pcoremap));
+       p->procinfo->num_vcores = 0;
+       p->procinfo->coremap_seqctr = SEQCTR_INITIALIZER;
+       /* For now, we'll go up to the max num_cpus (at runtime).  In the future,
+        * there may be cases where we can have more vcores than num_cpus, but for
+        * now we'll leave it like this. */
+       for (int i = 0; i < num_cpus; i++) {
+               TAILQ_INSERT_TAIL(&p->inactive_vcs, &p->procinfo->vcoremap[i], list);
+       }
+}
+
+static void proc_init_procdata(struct proc *p)
+{
+       memset(p->procdata, 0, sizeof(struct procdata));
 }
 
 /* Allocates and initializes a process, with the given parent.  Currently
@@ -291,18 +309,23 @@ error_t proc_alloc(struct proc **pp, struct proc *parent)
        p->exitcode = 1337;     /* so we can see processes killed by the kernel */
        p->ppid = parent ? parent->pid : 0;
        p->state = PROC_CREATED; /* shouldn't go through state machine for init */
+       p->is_mcp = FALSE;
        p->env_flags = 0;
        p->env_entry = 0; // cheating.  this really gets set later
-       p->procinfo->heap_bottom = (void*)UTEXT;
-       p->heap_top = (void*)UTEXT;
+       p->heap_top = (void*)UTEXT;     /* heap_bottom set in proc_init_procinfo */
        memset(&p->resources, 0, sizeof(p->resources));
        memset(&p->env_ancillary_state, 0, sizeof(p->env_ancillary_state));
        memset(&p->env_tf, 0, sizeof(p->env_tf));
+       spinlock_init(&p->mm_lock);
        TAILQ_INIT(&p->vm_regions); /* could init this in the slab */
-
-       /* Initialize the contents of the e->procinfo structure */
+       /* Initialize the vcore lists, we'll build the inactive list so that it includes
+        * all vcores when we initialize procinfo.  Do this before initing procinfo. */
+       TAILQ_INIT(&p->online_vcs);
+       TAILQ_INIT(&p->bulk_preempted_vcs);
+       TAILQ_INIT(&p->inactive_vcs);
+       /* Init procinfo/procdata.  Procinfo's argp/argb are 0'd */
        proc_init_procinfo(p);
-       /* Initialize the contents of the e->procdata structure */
+       proc_init_procdata(p);
 
        /* Initialize the generic sysevent ring buffer */
        SHARED_RING_INIT(&p->procdata->syseventring);
@@ -470,6 +493,7 @@ static void __set_proc_current(struct proc *p)
 void proc_run(struct proc *p)
 {
        bool self_ipi_pending = FALSE;
+       struct vcore *vc_i;
        spin_lock(&p->proc_lock);
 
        switch (p->state) {
@@ -488,7 +512,9 @@ void proc_run(struct proc *p)
                         * Also, this is the signal used in trap.c to know to save the tf in
                         * env_tf. */
                        __seq_start_write(&p->procinfo->coremap_seqctr);
-                       p->procinfo->num_vcores = 0;
+                       p->procinfo->num_vcores = 0;    /* TODO (VC#) */
+                       /* TODO: For now, we won't count this as an active vcore (on the
+                        * lists).  This gets unmapped in resource.c, and needs work. */
                        __map_vcore(p, 0, core_id()); // sort of.  this needs work.
                        __seq_end_write(&p->procinfo->coremap_seqctr);
                        /* __set_proc_current assumes the reference we give it is for
@@ -518,9 +544,13 @@ void proc_run(struct proc *p)
                                 * an IPI (once we reenable interrupts) and never return. */
                                if (is_mapped_vcore(p, core_id()))
                                        self_ipi_pending = TRUE;
-                               for (int i = 0; i < p->procinfo->num_vcores; i++)
-                                       send_kernel_message(get_pcoreid(p, i), __startcore, (long)p,
+                               /* Send kernel messages to all online vcores (which were added
+                                * to the list and mapped in __proc_give_cores()), making them
+                                * turn online */
+                               TAILQ_FOREACH(vc_i, &p->online_vcs, list) {
+                                       send_kernel_message(vc_i->pcoreid, __startcore, (long)p,
                                                            0, 0, KMSG_ROUTINE);
+                               }
                        } else {
                                warn("Tried to proc_run() an _M with no vcores!");
                        }
@@ -703,34 +733,6 @@ void proc_destroy(struct proc *p)
        return;
 }
 
-/* Helper function.  Starting from prev, it will find the next free vcoreid,
- * which is the next vcore that is not valid.
- * You better hold the lock before calling this. */
-static uint32_t get_free_vcoreid(struct proc *SAFE p, uint32_t prev)
-{
-       uint32_t i;
-       for (i = prev; i < MAX_NUM_CPUS; i++)
-               if (!p->procinfo->vcoremap[i].valid)
-                       break;
-       if (i + 1 >= MAX_NUM_CPUS)
-               warn("At the end of the vcorelist.  Might want to check that out.");
-       return i;
-}
-
-/* Helper function.  Starting from prev, it will find the next busy vcoreid,
- * which is the next vcore that is valid.
- * You better hold the lock before calling this. */
-static uint32_t get_busy_vcoreid(struct proc *SAFE p, uint32_t prev)
-{
-       uint32_t i;
-       for (i = prev; i < MAX_NUM_CPUS; i++)
-               if (p->procinfo->vcoremap[i].valid)
-                       break;
-       if (i + 1 >= MAX_NUM_CPUS)
-               warn("At the end of the vcorelist.  Might want to check that out.");
-       return i;
-}
-
 /* Helper function.  Is the given pcore a mapped vcore?  No locking involved, be
  * careful. */
 static bool is_mapped_vcore(struct proc *p, uint32_t pcoreid)
@@ -750,7 +752,7 @@ static uint32_t get_vcoreid(struct proc *p, uint32_t pcoreid)
  * No locking involved, be careful.  Panics on failure. */
 static uint32_t get_pcoreid(struct proc *p, uint32_t vcoreid)
 {
-       assert(p->procinfo->vcoremap[vcoreid].valid);
+       assert(vcore_is_mapped(p, vcoreid));
        return p->procinfo->vcoremap[vcoreid].pcoreid;
 }
 
@@ -783,7 +785,8 @@ void __proc_yield_s(struct proc *p, struct trapframe *tf)
 void proc_yield(struct proc *SAFE p, bool being_nice)
 {
        uint32_t vcoreid = get_vcoreid(p, core_id());
-       struct vcore *vc = &p->procinfo->vcoremap[vcoreid];
+       struct vcore *vc = vcoreid2vcore(p, vcoreid);
+       struct preempt_data *vcpd = &p->procdata->vcore_preempt_data[vcoreid];
 
        /* no reason to be nice, return */
        if (being_nice && !vc->preempt_pending)
@@ -801,7 +804,17 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
        /* no need to preempt later, since we are yielding (nice or otherwise) */
        if (vc->preempt_pending)
                vc->preempt_pending = 0;
-
+       /* Don't let them yield if they are missing a notification.  Userspace must
+        * not leave vcore context without dealing with notif_pending.  pop_ros_tf()
+        * handles leaving via uthread context.  This handles leaving via a yield.
+        *
+        * This early check is an optimization.  The real check is below when it
+        * works with the online_vcs list (syncing with event.c and INDIR/IPI
+        * posting). */
+       if (vcpd->notif_pending) {
+               spin_unlock(&p->proc_lock);
+               return;
+       }
        switch (p->state) {
                case (PROC_RUNNING_S):
                        __proc_yield_s(p, current_tf);  /* current_tf 0'd in abandon core */
@@ -815,9 +828,25 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
                                spin_unlock(&p->proc_lock);
                                return;
                        }
+                       /* Remove from the online list, add to the yielded list, and unmap
+                        * the vcore, which gives up the core. */
+                       TAILQ_REMOVE(&p->online_vcs, vc, list);
+                       /* Now that we're off the online list, check to see if an alert made
+                        * it through (event.c sets this) */
+                       cmb();
+                       if (vcpd->notif_pending) {
+                               /* We lost, put it back on the list and abort the yield */
+                               TAILQ_INSERT_TAIL(&p->online_vcs, vc, list); /* could go HEAD */
+                               spin_unlock(&p->proc_lock);
+                               return;
+                       }
+                       /* We won the race with event sending, we can safely yield */
+                       TAILQ_INSERT_HEAD(&p->inactive_vcs, vc, list);
+                       /* Note this protects stuff userspace should look at, which doesn't
+                        * include the TAILQs. */
                        __seq_start_write(&p->procinfo->coremap_seqctr);
-                       // give up core
-                       __unmap_vcore(p, get_vcoreid(p, core_id()));
+                       __unmap_vcore(p, vcoreid);
+                       /* Adjust implied resource desires */
                        p->resources[RES_CORES].amt_granted = --(p->procinfo->num_vcores);
                        if (!being_nice)
                                p->resources[RES_CORES].amt_wanted = p->procinfo->num_vcores;
@@ -870,19 +899,40 @@ void proc_notify(struct proc *p, uint32_t vcoreid)
                if (vcpd->notif_enabled) {
                        /* GIANT WARNING: we aren't using the proc-lock to protect the
                         * vcoremap.  We want to be able to use this from interrupt context,
-                        * and don't want the proc_lock to be an irqsave. */
+                        * and don't want the proc_lock to be an irqsave.  Spurious
+                        * __notify() kmsgs are okay (it checks to see if the right receiver
+                        * is current). */
                        if ((p->state & PROC_RUNNING_M) && // TODO: (VC#) (_S state)
-                                     (p->procinfo->vcoremap[vcoreid].valid)) {
+                                     vcore_is_mapped(p, vcoreid)) {
                                printd("[kernel] sending notif to vcore %d\n", vcoreid);
                                send_kernel_message(get_pcoreid(p, vcoreid), __notify, (long)p,
                                                    0, 0, KMSG_ROUTINE);
-                       } else { // TODO: think about this, fallback, etc
-                               warn("Vcore unmapped, not receiving an active notif");
                        }
                }
        }
 }
 
+/* Hold the lock before calling this.  If the process is WAITING, it will wake
+ * it up and schedule it. */
+void __proc_wakeup(struct proc *p)
+{
+       if (p->state != PROC_WAITING)
+               return;
+       if (__proc_is_mcp(p))
+               __proc_set_state(p, PROC_RUNNABLE_M);
+       else
+               __proc_set_state(p, PROC_RUNNABLE_S);
+       schedule_proc(p);
+}
+
+/* Is the process in multi_mode / is an MCP or not?  */
+bool __proc_is_mcp(struct proc *p)
+{
+       /* in lieu of using the amount of cores requested, or having a bunch of
+        * states (like PROC_WAITING_M and _S), I'll just track it with a bool. */
+       return p->is_mcp;
+}
+
 /************************  Preemption Functions  ******************************
  * Don't rely on these much - I'll be sure to change them up a bit.
  *
@@ -921,12 +971,9 @@ void __proc_preempt_warn(struct proc *p, uint32_t vcoreid, uint64_t when)
  * care about the mapping (and you should). */
 void __proc_preempt_warnall(struct proc *p, uint64_t when)
 {
-       uint32_t active_vcoreid = 0;
-       for (int i = 0; i < p->procinfo->num_vcores; i++) {
-               active_vcoreid = get_busy_vcoreid(p, active_vcoreid);
-               __proc_preempt_warn(p, active_vcoreid, when);
-               active_vcoreid++;
-       }
+       struct vcore *vc_i;
+       TAILQ_FOREACH(vc_i, &p->online_vcs, list)
+               __proc_preempt_warn(p, vcore2vcoreid(p, vc_i), when);
        /* TODO: consider putting in some lookup place for the alarm to find it.
         * til then, it'll have to scan the vcoremap (O(n) instead of O(m)) */
 }
@@ -951,12 +998,9 @@ bool __proc_preempt_all(struct proc *p)
        /* instead of doing this, we could just preempt_served all possible vcores,
         * and not just the active ones.  We would need to sort out a way to deal
         * with stale preempt_serveds first.  This might be just as fast anyways. */
-       uint32_t active_vcoreid = 0;
-       for (int i = 0; i < p->procinfo->num_vcores; i++) {
-               active_vcoreid = get_busy_vcoreid(p, active_vcoreid);
-               p->procinfo->vcoremap[active_vcoreid].preempt_served = TRUE;
-               active_vcoreid++;
-       }
+       struct vcore *vc_i;
+       TAILQ_FOREACH(vc_i, &p->online_vcs, list)
+               vc_i->preempt_served = TRUE;
        return __proc_take_allcores(p, __preempt, (long)p, 0, 0);
 }
 
@@ -965,7 +1009,7 @@ bool __proc_preempt_all(struct proc *p)
 void proc_preempt_core(struct proc *p, uint32_t pcoreid, uint64_t usec)
 {
        bool self_ipi_pending = FALSE;
-       uint64_t warn_time = read_tsc() + usec * 1000000 / system_timing.tsc_freq;
+       uint64_t warn_time = read_tsc() + usec2tsc(usec);
 
        /* DYING could be okay */
        if (p->state != PROC_RUNNING_M) {
@@ -996,7 +1040,7 @@ void proc_preempt_core(struct proc *p, uint32_t pcoreid, uint64_t usec)
 void proc_preempt_all(struct proc *p, uint64_t usec)
 {
        bool self_ipi_pending = FALSE;
-       uint64_t warn_time = read_tsc() + usec * 1000000 / system_timing.tsc_freq;
+       uint64_t warn_time = read_tsc() + usec2tsc(usec);
 
        spin_lock(&p->proc_lock);
        /* DYING could be okay */
@@ -1057,6 +1101,37 @@ uint32_t proc_get_vcoreid(struct proc *SAFE p, uint32_t pcoreid)
        }
 }
 
+/* TODO: make all of these static inlines when we gut the env crap */
+bool vcore_is_mapped(struct proc *p, uint32_t vcoreid)
+{
+       return p->procinfo->vcoremap[vcoreid].valid;
+}
+
+/* Can do this, or just create a new field and save it in the vcoremap */
+uint32_t vcore2vcoreid(struct proc *p, struct vcore *vc)
+{
+       return (vc - p->procinfo->vcoremap);
+}
+
+struct vcore *vcoreid2vcore(struct proc *p, uint32_t vcoreid)
+{
+       return &p->procinfo->vcoremap[vcoreid];
+}
+
+/* Helper: gives pcore to the process, mapping it to the next available vcore */
+static void __proc_give_a_pcore(struct proc *p, uint32_t pcore)
+{
+       struct vcore *new_vc;
+       new_vc = TAILQ_FIRST(&p->inactive_vcs);
+       /* there are cases where this isn't true; deal with it later */
+       assert(new_vc);
+       printd("setting vcore %d to pcore %d\n", vcore2vcoreid(p, new_vc),
+              pcorelist[i]);
+       TAILQ_REMOVE(&p->inactive_vcs, new_vc, list);
+       TAILQ_INSERT_TAIL(&p->online_vcs, new_vc, list);
+       __map_vcore(p, vcore2vcoreid(p, new_vc), pcore);
+}
+
 /* Gives process p the additional num cores listed in pcorelist.  You must be
  * RUNNABLE_M or RUNNING_M before calling this.  If you're RUNNING_M, this will
  * startup your new cores at the entry point with their virtual IDs (or restore
@@ -1078,9 +1153,8 @@ uint32_t proc_get_vcoreid(struct proc *SAFE p, uint32_t pcoreid)
  *
  * WARNING: You must hold the proc_lock before calling this! */
 bool __proc_give_cores(struct proc *SAFE p, uint32_t *pcorelist, size_t num)
-{ TRUSTEDBLOCK
+{
        bool self_ipi_pending = FALSE;
-       uint32_t free_vcoreid = 0;
        switch (p->state) {
                case (PROC_RUNNABLE_S):
                case (PROC_RUNNING_S):
@@ -1098,18 +1172,14 @@ bool __proc_give_cores(struct proc *SAFE p, uint32_t *pcorelist, size_t num)
                                // somewhere, like someone forgot to take vcores after
                                // preempting.
                                for (int i = 0; i < p->procinfo->num_vcores; i++)
-                                       assert(p->procinfo->vcoremap[i].valid);
+                                       assert(vcore_is_mapped(p, i));
                        }
                        // add new items to the vcoremap
                        __seq_start_write(&p->procinfo->coremap_seqctr);
-                       for (int i = 0; i < num; i++) {
-                               // find the next free slot, which should be the next one
-                               free_vcoreid = get_free_vcoreid(p, free_vcoreid);
-                               printd("setting vcore %d to pcore %d\n", free_vcoreid,
-                                      pcorelist[i]);
-                               __map_vcore(p, free_vcoreid, pcorelist[i]);
-                               p->procinfo->num_vcores++;
-                       }
+                       p->procinfo->num_vcores += num;
+                       /* TODO: consider bulk preemption */
+                       for (int i = 0; i < num; i++)
+                               __proc_give_a_pcore(p, pcorelist[i]);
                        __seq_end_write(&p->procinfo->coremap_seqctr);
                        break;
                case (PROC_RUNNING_M):
@@ -1117,12 +1187,9 @@ bool __proc_give_cores(struct proc *SAFE p, uint32_t *pcorelist, size_t num)
                         * process and have it loaded in their 'current'. */
                        proc_incref(p, num);
                        __seq_start_write(&p->procinfo->coremap_seqctr);
+                       p->procinfo->num_vcores += num;
                        for (int i = 0; i < num; i++) {
-                               free_vcoreid = get_free_vcoreid(p, free_vcoreid);
-                               printd("setting vcore %d to pcore %d\n", free_vcoreid,
-                                      pcorelist[i]);
-                               __map_vcore(p, free_vcoreid, pcorelist[i]);
-                               p->procinfo->num_vcores++;
+                               __proc_give_a_pcore(p, pcorelist[i]);
                                send_kernel_message(pcorelist[i], __startcore, (long)p, 0, 0,
                                                    KMSG_ROUTINE);
                                if (pcorelist[i] == core_id())
@@ -1156,6 +1223,33 @@ bool __proc_set_allcores(struct proc *SAFE p, uint32_t *pcorelist,
        panic("Set all cores not implemented.\n");
 }
 
+/* Helper for the take_cores calls: takes a specific vcore from p, optionally
+ * sending the message (or just unmapping), gives the pcore to the idlecoremap,
+ * and returns TRUE if a self_ipi is pending. */
+static bool __proc_take_a_core(struct proc *p, struct vcore *vc, amr_t message,
+                               long arg0, long arg1, long arg2)
+{
+       bool self_ipi_pending = FALSE;
+       /* Change lists for the vcore.  We do this before either unmapping or
+        * sending the message, so the lists represent what will be very soon
+        * (before we unlock, the messages are in flight). */
+       TAILQ_REMOVE(&p->online_vcs, vc, list);
+       TAILQ_INSERT_HEAD(&p->inactive_vcs, vc, list);
+       if (message) {
+               if (vc->pcoreid == core_id())
+                       self_ipi_pending = TRUE;
+               send_kernel_message(vc->pcoreid, message, arg0, arg1, arg2,
+                                   KMSG_ROUTINE);
+       } else {
+               /* if there was a msg, the vcore is unmapped on the receive side.
+                * o/w, we need to do it here. */
+               __unmap_vcore(p, vcore2vcoreid(p, vc));
+       }
+       /* give the pcore back to the idlecoremap */
+       put_idle_core(vc->pcoreid);
+       return self_ipi_pending;
+}
+
 /* Takes from process p the num cores listed in pcorelist, using the given
  * message for the kernel message (__death, __preempt, etc).  Like the others
  * in this function group, bool signals whether or not an IPI is pending.
@@ -1163,8 +1257,8 @@ bool __proc_set_allcores(struct proc *SAFE p, uint32_t *pcorelist,
  * WARNING: You must hold the proc_lock before calling this! */
 bool __proc_take_cores(struct proc *p, uint32_t *pcorelist, size_t num,
                        amr_t message, long arg0, long arg1, long arg2)
-{ TRUSTEDBLOCK
-       uint32_t vcoreid, pcoreid;
+{
+       uint32_t vcoreid;
        bool self_ipi_pending = FALSE;
        switch (p->state) {
                case (PROC_RUNNABLE_M):
@@ -1184,21 +1278,10 @@ bool __proc_take_cores(struct proc *p, uint32_t *pcorelist, size_t num,
        __seq_start_write(&p->procinfo->coremap_seqctr);
        for (int i = 0; i < num; i++) {
                vcoreid = get_vcoreid(p, pcorelist[i]);
-               // while ugly, this is done to facilitate merging with take_all_cores
-               pcoreid = get_pcoreid(p, vcoreid);
-               assert(pcoreid == pcorelist[i]);
-               if (message) {
-                       if (pcoreid == core_id())
-                               self_ipi_pending = TRUE;
-                       send_kernel_message(pcoreid, message, arg0, arg1, arg2,
-                                           KMSG_ROUTINE);
-               } else {
-                       /* if there was a msg, the vcore is unmapped on the receive side.
-                        * o/w, we need to do it here. */
-                       __unmap_vcore(p, vcoreid);
-               }
-               // give the pcore back to the idlecoremap
-               put_idle_core(pcoreid);
+               /* Sanity check */
+               assert(pcorelist[i] == get_pcoreid(p, vcoreid));
+               self_ipi_pending = __proc_take_a_core(p, vcoreid2vcore(p, vcoreid),
+                                                     message, arg0, arg1, arg2);
        }
        p->procinfo->num_vcores -= num;
        __seq_end_write(&p->procinfo->coremap_seqctr);
@@ -1215,7 +1298,7 @@ bool __proc_take_cores(struct proc *p, uint32_t *pcorelist, size_t num,
 bool __proc_take_allcores(struct proc *p, amr_t message, long arg0, long arg1,
                           long arg2)
 {
-       uint32_t active_vcoreid = 0, pcoreid;
+       struct vcore *vc_i, *vc_temp;
        bool self_ipi_pending = FALSE;
        switch (p->state) {
                case (PROC_RUNNABLE_M):
@@ -1232,25 +1315,12 @@ bool __proc_take_allcores(struct proc *p, amr_t message, long arg0, long arg1,
        assert(num_idlecores + p->procinfo->num_vcores <= num_cpus); // sanity
        spin_unlock(&idle_lock);
        __seq_start_write(&p->procinfo->coremap_seqctr);
-       for (int i = 0; i < p->procinfo->num_vcores; i++) {
-               // find next active vcore
-               active_vcoreid = get_busy_vcoreid(p, active_vcoreid);
-               pcoreid = get_pcoreid(p, active_vcoreid);
-               if (message) {
-                       if (pcoreid == core_id())
-                               self_ipi_pending = TRUE;
-                       send_kernel_message(pcoreid, message, arg0, arg1, arg2,
-                                           KMSG_ROUTINE);
-               } else {
-                       /* if there was a msg, the vcore is unmapped on the receive side.
-                        * o/w, we need to do it here. */
-                       __unmap_vcore(p, active_vcoreid);
-               }
-               // give the pcore back to the idlecoremap
-               put_idle_core(pcoreid);
-               active_vcoreid++; // for the next loop, skip the one we just used
+       TAILQ_FOREACH_SAFE(vc_i, &p->online_vcs, list, vc_temp) {
+               self_ipi_pending = __proc_take_a_core(p, vc_i,
+                                                     message, arg0, arg1, arg2);
        }
        p->procinfo->num_vcores = 0;
+       assert(TAILQ_EMPTY(&p->online_vcs));
        __seq_end_write(&p->procinfo->coremap_seqctr);
        p->resources[RES_CORES].amt_granted = 0;
        return self_ipi_pending;
@@ -1350,28 +1420,25 @@ void switch_back(struct proc *new_p, struct proc *old_proc)
  * shootdown and batching our messages.  Should do the sanity about rounding up
  * and down in this function too.
  *
- * Hold the proc_lock before calling this.
- *
  * Would be nice to have a broadcast kmsg at this point.  Note this may send a
  * message to the calling core (interrupting it, possibly while holding the
  * proc_lock).  We don't need to process routine messages since it's an
  * immediate message. */
-void __proc_tlbshootdown(struct proc *p, uintptr_t start, uintptr_t end)
+void proc_tlbshootdown(struct proc *p, uintptr_t start, uintptr_t end)
 {
-       uint32_t active_vcoreid = 0;
+       struct vcore *vc_i;
+       /* TODO: we might be able to avoid locking here in the future (we must hit
+        * all online, and we can check __mapped).  it'll be complicated. */
+       spin_lock(&p->proc_lock);
        switch (p->state) {
                case (PROC_RUNNING_S):
                        tlbflush();
                        break;
                case (PROC_RUNNING_M):
                        /* TODO: (TLB) sanity checks and rounding on the ranges */
-                       for (int i = 0; i < p->procinfo->num_vcores; i++) {
-                               /* find next active vcore */
-                               active_vcoreid = get_busy_vcoreid(p, active_vcoreid);
-                               send_kernel_message(get_pcoreid(p, active_vcoreid),
-                                                   __tlbshootdown, start, end,
+                       TAILQ_FOREACH(vc_i, &p->online_vcs, list) {
+                               send_kernel_message(vc_i->pcoreid, __tlbshootdown, start, end,
                                                    0, KMSG_IMMEDIATE);
-                               active_vcoreid++; /* next loop, skip the one we just used */
                        }
                        break;
                case (PROC_DYING):
@@ -1383,6 +1450,7 @@ void __proc_tlbshootdown(struct proc *p, uintptr_t start, uintptr_t end)
                        warn("Unexpected case %s in %s", procstate2str(p->state),
                             __FUNCTION__);
        }
+       spin_unlock(&p->proc_lock);
 }
 
 /* Kernel message handler to start a process's context on this core.  Tightly
@@ -1400,9 +1468,15 @@ void __startcore(struct trapframe *tf, uint32_t srcid, long a0, long a1, long a2
                proc_decref(p_to_run);
        vcoreid = get_vcoreid(p_to_run, pcoreid);
        vcpd = &p_to_run->procdata->vcore_preempt_data[vcoreid];
+       /* We could let userspace do this, though they come into vcore entry many
+        * times, and we just need this to happen when the cores comes online the
+        * first time.  That, and they want this turned on as soon as we know a
+        * vcore *WILL* be online.  We could also do this earlier, when we map the
+        * vcore to its pcore, though we don't always have current loaded or
+        * otherwise mess with the VCPD in those code paths. */
+       vcpd->can_rcv_msg = TRUE;
        printd("[kernel] startcore on physical core %d for process %d's vcore %d\n",
               pcoreid, p_to_run->pid, vcoreid);
-
        if (seq_is_locked(vcpd->preempt_tf_valid)) {
                __seq_end_write(&vcpd->preempt_tf_valid); /* mark tf as invalid */
                restore_fp_state(&vcpd->preempt_anc);
@@ -1423,6 +1497,7 @@ void __startcore(struct trapframe *tf, uint32_t srcid, long a0, long a1, long a2
                        proc_secure_trapframe(&local_tf);
                }
        } else { /* not restarting from a preemption, use a fresh vcore */
+               assert(vcpd->transition_stack);
                proc_init_trapframe(&local_tf, vcoreid, p_to_run->env_entry,
                                    vcpd->transition_stack);
                /* Disable/mask active notifications for fresh vcores */
@@ -1555,6 +1630,7 @@ void print_proc_info(pid_t pid)
 {
        int j = 0;
        struct proc *p = pid2proc(pid);
+       struct vcore *vc_i;
        if (!p) {
                printk("Bad PID.\n");
                return;
@@ -1564,18 +1640,22 @@ void print_proc_info(pid_t pid)
        printk("struct proc: %p\n", p);
        printk("PID: %d\n", p->pid);
        printk("PPID: %d\n", p->ppid);
-       printk("State: 0x%08x\n", p->state);
+       printk("State: 0x%08x (%s)\n", p->state, p->is_mcp ? "M" : "S");
        printk("Refcnt: %d\n", atomic_read(&p->p_kref.refcount) - 1);
        printk("Flags: 0x%08x\n", p->env_flags);
        printk("CR3(phys): 0x%08x\n", p->env_cr3);
        printk("Num Vcores: %d\n", p->procinfo->num_vcores);
-       printk("Vcoremap:\n");
-       for (int i = 0; i < p->procinfo->num_vcores; i++) {
-               j = get_busy_vcoreid(p, j);
-               printk("\tVcore %d: Pcore %d\n", j, get_pcoreid(p, j));
-               j++;
-       }
-       printk("Resources:\n");
+       printk("Vcore Lists (may be in flux w/o locking):\n----------------------\n");
+       printk("Online:\n");
+       TAILQ_FOREACH(vc_i, &p->online_vcs, list)
+               printk("\tVcore %d -> Pcore %d\n", vcore2vcoreid(p, vc_i), vc_i->pcoreid);
+       printk("Bulk Preempted:\n");
+       TAILQ_FOREACH(vc_i, &p->bulk_preempted_vcs, list)
+               printk("\tVcore %d\n", vcore2vcoreid(p, vc_i));
+       printk("Inactive / Yielded:\n");
+       TAILQ_FOREACH(vc_i, &p->inactive_vcs, list)
+               printk("\tVcore %d\n", vcore2vcoreid(p, vc_i));
+       printk("Resources:\n------------------------\n");
        for (int i = 0; i < MAX_NUM_RESOURCES; i++)
                printk("\tRes type: %02d, amt wanted: %08d, amt granted: %08d\n", i,
                       p->resources[i].amt_wanted, p->resources[i].amt_granted);