Removes __proc_set_allcores()
[akaros.git] / kern / src / process.c
index 19f8016..4aa0046 100644 (file)
@@ -261,6 +261,7 @@ 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->is_mcp = FALSE;
        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
@@ -309,7 +310,6 @@ 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->heap_top = (void*)UTEXT;     /* heap_bottom set in proc_init_procinfo */
@@ -778,25 +778,55 @@ void __proc_yield_s(struct proc *p, struct trapframe *tf)
  * yielders). */
 void proc_yield(struct proc *SAFE p, bool being_nice)
 {
-       uint32_t coreid = core_id();
-       uint32_t vcoreid = get_vcoreid(p, coreid);
-       struct vcore *vc = vcoreid2vcore(p, vcoreid);
-       struct preempt_data *vcpd = &p->procdata->vcore_preempt_data[vcoreid];
+       uint32_t vcoreid, pcoreid = core_id();
+       struct vcore *vc;
+       struct preempt_data *vcpd;
        int8_t state = 0;
-
-       /* no reason to be nice, return */
-       if (being_nice && !vc->preempt_pending)
-               return;
-
+       /* Need to disable before even reading vcoreid, since we could be unmapped
+        * by a __preempt or __death.  _S also needs ints disabled, so we'll just do
+        * it immediately. */
+       disable_irqsave(&state);
+       /* Need to lock before checking the vcoremap to find out who we are, in case
+        * we're getting __preempted and __startcored, from a remote core (in which
+        * case we might have come in thinking we were vcore X, but had X preempted
+        * and Y restarted on this pcore, and we suddenly are the wrong vcore
+        * yielding).  Arguably, this is incredibly rare, since you'd need to
+        * preempt the core, then decide to give it back with another grant in
+        * between. */
        spin_lock(&p->proc_lock); /* horrible scalability.  =( */
-
-       /* fate is sealed, return and take the preempt message on the way out.
-        * we're making this check while holding the lock, since the preemptor
-        * should hold the lock when sending messages. */
-       if (vc->preempt_served) {
-               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 */
+                       goto out_yield_core;
+               case (PROC_RUNNING_M):
+                       break;                          /* will handle this stuff below */
+               case (PROC_DYING):              /* incoming __death */
+               case (PROC_RUNNABLE_M): /* incoming (bulk) preempt/myield TODO:(BULK) */
+                       goto out_failed;
+               default:
+                       panic("Weird state(%s) in %s()", procstate2str(p->state),
+                             __FUNCTION__);
        }
+       /* If we're already unmapped (__preempt or a __death hit us), bail out.
+        * Note that if a __death hit us, we should have bailed when we saw
+        * PROC_DYING. */
+       if (!is_mapped_vcore(p, pcoreid))
+               goto out_failed;
+       vcoreid = get_vcoreid(p, pcoreid);
+       vc = vcoreid2vcore(p, vcoreid);
+       vcpd = &p->procdata->vcore_preempt_data[vcoreid];
+       /* no reason to be nice, return */
+       if (being_nice && !vc->preempt_pending)
+               goto out_failed;
+       /* Fate is sealed, return and take the preempt message when we enable_irqs.
+        * Note this keeps us from mucking with our lists, since we were already
+        * removed from the online_list.  We have a similar concern with __death,
+        * but we check for DYING to handle that. */
+       if (vc->preempt_served)
+               goto out_failed;
+       /* At this point, AFAIK there should be no preempt/death messages on the
+        * way, and we're on the online list.  So we'll go ahead and do the yielding
+        * business. */
        /* no need to preempt later, since we are yielding (nice or otherwise) */
        if (vc->preempt_pending)
                vc->preempt_pending = 0;
@@ -807,70 +837,60 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
         * 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)
+               goto out_failed;
+       /* Now we'll actually try to yield */
+       printd("[K] Process %d (%p) is yielding on vcore %d\n", p->pid, p,
+              get_vcoreid(p, coreid));
+       /* 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) */
+       wrmb(); /* prev write must hit before reading notif_pending */
+       /* Note we need interrupts disabled, since a __notify can come in
+        * and set pending to FALSE */
        if (vcpd->notif_pending) {
-               spin_unlock(&p->proc_lock);
-               return;
+               /* We lost, put it back on the list and abort the yield */
+               TAILQ_INSERT_TAIL(&p->online_vcs, vc, list); /* could go HEAD */
+               goto out_failed;
        }
-       disable_irqsave(&state);
-       switch (p->state) {
-               case (PROC_RUNNING_S):
-                       __proc_yield_s(p, current_tf);  /* current_tf 0'd in abandon core */
-                       break;
-               case (PROC_RUNNING_M):
-                       printd("[K] Process %d (%p) is yielding on vcore %d\n", p->pid, p,
-                              get_vcoreid(p, coreid));
-                       /* 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) */
-                       wrmb(); /* prev write must hit before reading notif_pending */
-                       /* Note we need interrupts disabled, since a __notify can come in
-                        * and set pending to FALSE */
-                       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);
-                               enable_irqsave(&state);
-                               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);
-                       __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;
-                       __seq_end_write(&p->procinfo->coremap_seqctr);
-                       // add to idle list
-                       put_idle_core(coreid);  /* TODO: prod the ksched? */
-                       // last vcore?  then we really want 1, and to yield the gang
-                       if (p->procinfo->num_vcores == 0) {
-                               p->resources[RES_CORES].amt_wanted = 1;
-                               /* wait on an event (not supporting 'being nice' for now */
-                               __proc_set_state(p, PROC_WAITING);
-                       }
-                       break;
-               case (PROC_DYING):
-                       /* just return and take the death message (which should be otw) */
-                       spin_unlock(&p->proc_lock);
-                       enable_irqsave(&state);
-                       return;
-               default:
-                       // there are races that can lead to this (async death, preempt, etc)
-                       panic("Weird state(%s) in %s()", procstate2str(p->state),
-                             __FUNCTION__);
+       /* 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);
+       /* Next time the vcore starts, it starts fresh */
+       vcpd->notif_disabled = FALSE;
+       __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;
+       __seq_end_write(&p->procinfo->coremap_seqctr);
+       // add to idle list
+       put_idle_core(pcoreid); /* TODO: prod the ksched? */
+       // last vcore?  then we really want 1, and to yield the gang
+       if (p->procinfo->num_vcores == 0) {
+               p->resources[RES_CORES].amt_wanted = 1;
+               /* wait on an event (not supporting 'being nice' for now */
+               __proc_set_state(p, PROC_WAITING);
        }
+       goto out_yield_core;
+out_failed:
+       /* for some reason we just want to return, either to take a KMSG that cleans
+        * us up, or because we shouldn't yield (ex: notif_pending). */
+       spin_unlock(&p->proc_lock);
+       enable_irqsave(&state);
+       return;
+out_yield_core:                        /* successfully yielded the core */
        spin_unlock(&p->proc_lock);
        proc_decref(p);                 /* need to eat the ref passed in */
        /* TODO: (RMS) If there was a change to the idle cores, try and give our
         * core to someone who was preempted. */
        /* Clean up the core and idle.  Need to do this before enabling interrupts,
         * since once we put_idle_core() and unlock, we could get a startcore. */
-       clear_owning_proc(coreid);      /* so we don't restart */
+       clear_owning_proc(pcoreid);     /* so we don't restart */
        abandon_core();
        smp_idle();                             /* will reenable interrupts */
 }
@@ -886,8 +906,8 @@ void proc_notify(struct proc *p, uint32_t vcoreid)
 {
        struct preempt_data *vcpd = &p->procdata->vcore_preempt_data[vcoreid];
        vcpd->notif_pending = TRUE;
-       wrmb(); /* must write notif_pending before reading notif_enabled */
-       if (vcpd->notif_enabled) {
+       wrmb(); /* must write notif_pending before reading notif_disabled */
+       if (!vcpd->notif_disabled) {
                /* 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.  Spurious
@@ -920,7 +940,7 @@ 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;
+       return p->procinfo->is_mcp;
 }
 
 /************************  Preemption Functions  ******************************
@@ -993,6 +1013,8 @@ void __proc_preempt_all(struct proc *p)
         * 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. */
        struct vcore *vc_i;
+       /* TODO:(BULK) PREEMPT - don't bother with this, set a proc wide flag, or
+        * just make us RUNNABLE_M. */
        TAILQ_FOREACH(vc_i, &p->online_vcs, list)
                vc_i->preempt_served = TRUE;
        __proc_take_allcores(p, __preempt, (long)p, 0, 0);
@@ -1139,6 +1161,8 @@ static void __proc_give_a_pcore(struct proc *p, uint32_t pcore)
  * WARNING: You must hold the proc_lock before calling this! */
 void __proc_give_cores(struct proc *SAFE p, uint32_t *pcorelist, size_t num)
 {
+       /* should never happen: */
+       assert(num + p->procinfo->num_vcores <= MAX_NUM_CPUS);
        switch (p->state) {
                case (PROC_RUNNABLE_S):
                case (PROC_RUNNING_S):
@@ -1186,42 +1210,28 @@ void __proc_give_cores(struct proc *SAFE p, uint32_t *pcorelist, size_t num)
        p->resources[RES_CORES].amt_granted += num;
 }
 
-/* Makes process p's coremap look like pcorelist (add, remove, etc).  Caller
- * needs to know what cores are free after this call (removed, failed, etc).
- * This info will be returned via corelist and *num.  This will send message to
- * any cores that are getting removed.
- *
- * Before implementing this, we should probably think about when this will be
- * used.  Implies preempting for the message.  The more that I think about this,
- * the less I like it.  For now, don't use this, and think hard before
- * implementing it.
- *
- * WARNING: You must hold the proc_lock before calling this! */
-void __proc_set_allcores(struct proc *SAFE p, uint32_t *pcorelist,
-                         size_t *num, amr_t message,TV(a0t) arg0,
-                         TV(a1t) arg1, TV(a2t) arg2)
-{
-       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.
  */
 static void __proc_take_a_core(struct proc *p, struct vcore *vc, amr_t message,
                                long arg0, long arg1, long arg2)
 {
+       uint32_t vcoreid = vcore2vcoreid(p, vc);
+       struct preempt_data *vcpd = &p->procdata->vcore_preempt_data[vcoreid];
        /* 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) {
+               /* lock the vcore's state.  This is okay even if we kill the proc */
+               atomic_or(&vcpd->flags, VC_K_LOCK);
                send_kernel_message(vc->pcoreid, message, arg0, arg1, arg2,
                                    KMSG_IMMEDIATE);
        } 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));
+               __unmap_vcore(p, vcoreid);
        }
        /* give the pcore back to the idlecoremap */
        put_idle_core(vc->pcoreid);
@@ -1303,8 +1313,10 @@ void __map_vcore(struct proc *p, uint32_t vcoreid, uint32_t pcoreid)
        while (p->procinfo->vcoremap[vcoreid].valid)
                cpu_relax();
        p->procinfo->vcoremap[vcoreid].pcoreid = pcoreid;
+       wmb();
        p->procinfo->vcoremap[vcoreid].valid = TRUE;
        p->procinfo->pcoremap[pcoreid].vcoreid = vcoreid;
+       wmb();
        p->procinfo->pcoremap[pcoreid].valid = TRUE;
 }
 
@@ -1440,34 +1452,30 @@ static void __set_curtf_to_vcoreid(struct proc *p, uint32_t vcoreid)
         * 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;
+       /* Mark that this vcore as no longer preempted.  No danger of clobbering
+        * other writes, since this would get turned on in __preempt (which can't be
+        * concurrent with this function on this core), and the atomic is just
+        * toggling the one bit (a concurrent VC_K_LOCK will work) */
+       atomic_and(&vcpd->flags, ~VC_PREEMPTED);
        printd("[kernel] startcore on physical core %d for process %d's vcore %d\n",
               core_id(), p->pid, vcoreid);
-       if (seq_is_locked(vcpd->preempt_tf_valid)) {
-               __seq_end_write(&vcpd->preempt_tf_valid); /* mark tf as invalid */
+       /* If notifs are disabled, the vcore was in vcore context and we need to
+        * restart the preempt_tf.  o/w, we give them a fresh vcore (which is also
+        * what happens the first time a vcore comes online).  No matter what,
+        * they'll restart in vcore context.  It's just a matter of whether or not
+        * it is the old, interrupted vcore context. */
+       if (vcpd->notif_disabled) {
                restore_fp_state(&vcpd->preempt_anc);
-               /* notif_pending and enabled means the proc wants to receive the IPI,
-                * but might have missed it.  copy over the tf so they can restart it
-                * later, and give them a fresh vcore. */
-               if (vcpd->notif_pending && vcpd->notif_enabled) {
-                       vcpd->notif_tf = vcpd->preempt_tf; // could memset
-                       proc_init_trapframe(&pcpui->actual_tf, vcoreid, p->env_entry,
-                                           vcpd->transition_stack);
-                       if (!vcpd->transition_stack)
-                               warn("No transition stack!");
-                       vcpd->notif_enabled = FALSE;
-                       vcpd->notif_pending = FALSE;
-               } else {
-                       /* copy-in the tf we'll pop, then set all security-related fields */
-                       pcpui->actual_tf = vcpd->preempt_tf;
-                       proc_secure_trapframe(&pcpui->actual_tf);
-               }
+               /* copy-in the tf we'll pop, then set all security-related fields */
+               pcpui->actual_tf = vcpd->preempt_tf;
+               proc_secure_trapframe(&pcpui->actual_tf);
        } else { /* not restarting from a preemption, use a fresh vcore */
                assert(vcpd->transition_stack);
                /* TODO: consider 0'ing the FP state.  We're probably leaking. */
                proc_init_trapframe(&pcpui->actual_tf, vcoreid, p->env_entry,
                                    vcpd->transition_stack);
                /* Disable/mask active notifications for fresh vcores */
-               vcpd->notif_enabled = FALSE;
+               vcpd->notif_disabled = TRUE;
        }
        /* cur_tf was built above (in actual_tf), now use it */
        pcpui->cur_tf = &pcpui->actual_tf;
@@ -1477,31 +1485,66 @@ static void __set_curtf_to_vcoreid(struct proc *p, uint32_t vcoreid)
 /* Changes calling vcore to be vcoreid.  enable_my_notif tells us about how the
  * state calling vcore wants to be left in.  It will look like caller_vcoreid
  * was preempted.  Note we don't care about notif_pending.  */
-void proc_change_to_vcore(struct proc *p, uint32_t vcoreid,
+void proc_change_to_vcore(struct proc *p, uint32_t new_vcoreid,
                           bool enable_my_notif)
 {
-       uint32_t pcoreid = core_id();
-       uint32_t caller_vcoreid = get_vcoreid(p, pcoreid);
-       struct preempt_data *caller_vcpd =
-                           &p->procdata->vcore_preempt_data[caller_vcoreid];
-       struct vcore *caller_vc = vcoreid2vcore(p, caller_vcoreid);
-       struct vcore *new_vc = vcoreid2vcore(p, vcoreid);
+       uint32_t caller_vcoreid, pcoreid = core_id();
+       struct preempt_data *caller_vcpd;
+       struct vcore *caller_vc, *new_vc;
        struct event_msg preempt_msg = {0};
+       int8_t state = 0;
+       /* Need to disable before even reading caller_vcoreid, since we could be
+        * unmapped by a __preempt or __death, like in yield. */
+       disable_irqsave(&state);
+       /* Need to lock before reading the vcoremap, like in yield */
        spin_lock(&p->proc_lock);
-       /* vcoreid is already runing, abort */
-       if (vcore_is_mapped(p, vcoreid)) {
-               spin_unlock(&p->proc_lock);
-               return;
+       /* new_vcoreid is already runing, abort */
+       if (vcore_is_mapped(p, new_vcoreid))
+               goto out_failed;
+       /* Need to make sure our vcore is allowed to switch.  We might have a
+        * __preempt, __death, etc, coming in.  Similar to yield. */
+       switch (p->state) {
+               case (PROC_RUNNING_M):
+                       break;                          /* the only case we can proceed */
+               case (PROC_RUNNING_S):  /* user bug, just return */
+               case (PROC_DYING):              /* incoming __death */
+               case (PROC_RUNNABLE_M): /* incoming (bulk) preempt/myield TODO:(BULK) */
+                       goto out_failed;
+               default:
+                       panic("Weird state(%s) in %s()", procstate2str(p->state),
+                             __FUNCTION__);
+       }
+       /* Make sure we're still mapped in the proc. */
+       if (!is_mapped_vcore(p, pcoreid))
+               goto out_failed;
+       /* Get all our info */
+       caller_vcoreid = get_vcoreid(p, pcoreid);
+       caller_vcpd = &p->procdata->vcore_preempt_data[caller_vcoreid];
+       caller_vc = vcoreid2vcore(p, caller_vcoreid);
+       /* Should only call from vcore context */
+       if (!caller_vcpd->notif_disabled) {
+               printk("[kernel] You tried to change vcores from uthread ctx\n");
+               goto out_failed;
        }
+       /* Return and take the preempt message when we enable_irqs. */
+       if (caller_vc->preempt_served)
+               goto out_failed;
+       /* Ok, we're clear to do the switch.  Lets figure out who the new one is */
+       new_vc = vcoreid2vcore(p, new_vcoreid);
+       printd("[kernel] changing vcore %d to vcore %d\n", caller_vcoreid,
+              new_vcoreid);
+       /* enable_my_notif signals how we'll be restarted */
        if (enable_my_notif) {
                /* if they set this flag, then the vcore can just restart from scratch,
                 * and we don't care about either the notif_tf or the preempt_tf. */
-               caller_vcpd->notif_enabled = TRUE;
+               caller_vcpd->notif_disabled = FALSE;
        } else {
                /* need to set up the calling vcore's tf so that it'll get restarted by
                 * __startcore, to make the caller look like it was preempted. */
                caller_vcpd->preempt_tf = *current_tf;
-               caller_vcpd->preempt_tf_valid = TRUE;
+               save_fp_state(&caller_vcpd->preempt_anc);
+               /* Mark our core as preempted (for userspace recovery). */
+               atomic_or(&caller_vcpd->flags, VC_PREEMPTED);
        }
        /* Either way, unmap and offline our current vcore */
        /* Move the caller from online to inactive */
@@ -1516,16 +1559,20 @@ void proc_change_to_vcore(struct proc *p, uint32_t vcoreid,
        /* Change the vcore map (TODO: might get rid of this seqctr) */
        __seq_start_write(&p->procinfo->coremap_seqctr);
        __unmap_vcore(p, caller_vcoreid);
-       __map_vcore(p, vcoreid, pcoreid);
+       __map_vcore(p, new_vcoreid, pcoreid);
        __seq_end_write(&p->procinfo->coremap_seqctr);
-       /* send preempt message about the calling vcore.  might as well prefer
-        * directing it to the new_vc (vcoreid) to cut down on an IPI. */
-       preempt_msg.ev_type = EV_VCORE_PREEMPT;
+       /* Send either a PREEMPT msg or a CHECK_MSGS msg.  If they said to
+        * enable_my_notif, then all userspace needs is to check messages, not a
+        * full preemption recovery. */
+       preempt_msg.ev_type = (enable_my_notif ? EV_CHECK_MSGS : EV_VCORE_PREEMPT);
        preempt_msg.ev_arg2 = caller_vcoreid;   /* arg2 is 32 bits */
-       send_kernel_event(p, &preempt_msg, vcoreid);
+       send_kernel_event(p, &preempt_msg, new_vcoreid);
        /* Change cur_tf so we'll be the new vcoreid */
-       __set_curtf_to_vcoreid(p, vcoreid);
+       __set_curtf_to_vcoreid(p, new_vcoreid);
+       /* Fall through to exit (we didn't fail) */
+out_failed:
        spin_unlock(&p->proc_lock);
+       enable_irqsave(&state);
 }
 
 /* Kernel message handler to start a process's context on this core, when the
@@ -1585,9 +1632,10 @@ void __notify(struct trapframe *tf, uint32_t srcid, long a0, long a1, long a2)
        printd("received active notification for proc %d's vcore %d on pcore %d\n",
               p->procinfo->pid, vcoreid, coreid);
        /* sort signals.  notifs are now masked, like an interrupt gate */
-       if (!vcpd->notif_enabled)
+       if (vcpd->notif_disabled)
                return;
-       vcpd->notif_enabled = FALSE;
+       vcpd->notif_disabled = TRUE;
+       /* This bit shouldn't be important anymore */
        vcpd->notif_pending = FALSE; // no longer pending - it made it here
        /* save the old tf in the notify slot, build and pop a new one.  Note that
         * silly state isn't our business for a notification. */
@@ -1624,14 +1672,20 @@ void __preempt(struct trapframe *tf, uint32_t srcid, long a0, long a1, long a2)
        vcpd = &p->procdata->vcore_preempt_data[vcoreid];
        printd("[kernel] received __preempt for proc %d's vcore %d on pcore %d\n",
               p->procinfo->pid, vcoreid, coreid);
-       /* save the process's tf (current_tf) in the preempt slot, save the silly
-        * state, and signal the state is a valid tf.  when it is 'written,' it is
-        * valid.  Using the seq_ctrs so userspace can tell between different valid
-        * versions.  If the TF was already valid, it will panic (if CONFIGed that
-        * way). */
-       vcpd->preempt_tf = *pcpui->cur_tf;
+       /* if notifs are disabled, the vcore is in vcore context (as far as we're
+        * concerned), and we save it in the preempt slot. o/w, we save the
+        * process's cur_tf in the notif slot, and it'll appear to the vcore when it
+        * comes back up that it just took a notification. */
+       if (vcpd->notif_disabled)
+               vcpd->preempt_tf = *pcpui->cur_tf;
+       else
+               vcpd->notif_tf = *pcpui->cur_tf;
+       /* either way, we save the silly state (FP) */
        save_fp_state(&vcpd->preempt_anc);
-       __seq_start_write(&vcpd->preempt_tf_valid);
+       /* Mark the vcore as preempted and unlock (was locked by the sender). */
+       atomic_or(&vcpd->flags, VC_PREEMPTED);
+       atomic_and(&vcpd->flags, ~VC_K_LOCK);
+       wmb();  /* make sure everything else hits before we unmap */
        __unmap_vcore(p, vcoreid);
        /* We won't restart the process later.  current gets cleared later when we
         * notice there is no owning_proc and we have nothing to do (smp_idle,
@@ -1707,7 +1761,7 @@ 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 (%s)\n", p->state, p->is_mcp ? "M" : "S");
+       printk("State: %s (%p)\n", procstate2str(p->state), p->state);
        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);