Tracks proc's vcoreids in pcpu info
[akaros.git] / kern / src / process.c
index 4f6dcac..4a4366a 100644 (file)
@@ -230,8 +230,9 @@ error_t proc_alloc(struct proc **pp, struct proc *parent)
 
        { INITSTRUCT(*p)
 
-       /* one reference for the proc existing, and one for the ref we pass back. */
-       kref_init(&p->p_kref, __proc_free, 2);
+       /* only one ref, which we pass back.  the old 'existence' ref is managed by
+        * the ksched */
+       kref_init(&p->p_kref, __proc_free, 1);
        // Setup the default map of where to get cache colors from
        p->cache_colors_map = global_cache_colors_map;
        p->next_cache_color = 0;
@@ -306,6 +307,8 @@ error_t proc_alloc(struct proc **pp, struct proc *parent)
  * push setting the state to CREATED into here. */
 void __proc_ready(struct proc *p)
 {
+       /* Tell the ksched about us */
+       register_proc(p);
        spin_lock(&pid_hash_lock);
        hashtable_insert(pid_hash, (void*)(long)p->pid, p);
        spin_unlock(&pid_hash_lock);
@@ -470,6 +473,7 @@ void proc_run_s(struct proc *p)
                         * for now.  can simply clear_owning if we want to. */
                        assert(!pcpui->owning_proc);
                        pcpui->owning_proc = p;
+                       pcpui->owning_vcoreid = 0; /* TODO (VC#) */
                        /* TODO: (HSS) set silly state here (__startcore does it instantly) */
                        /* similar to the old __startcore, start them in vcore context if
                         * they have notifs and aren't already in vcore context.  o/w, start
@@ -563,7 +567,8 @@ void __proc_run_m(struct proc *p)
                                 * turn online */
                                TAILQ_FOREACH(vc_i, &p->online_vcs, list) {
                                        send_kernel_message(vc_i->pcoreid, __startcore, (long)p,
-                                                           0, 0, KMSG_IMMEDIATE);
+                                                           (long)vcore2vcoreid(p, vc_i), 0,
+                                                           KMSG_IMMEDIATE);
                                }
                        } else {
                                warn("Tried to proc_run() an _M with no vcores!");
@@ -655,10 +660,12 @@ void proc_restartcore(void)
        __proc_startcore(pcpui->owning_proc, pcpui->cur_tf);
 }
 
-/*
- * Destroys the given process.  This may be called from another process, a light
- * kernel thread (no real process context), asynchronously/cross-core, or from
- * the process on its own core.
+/* Destroys the process.  This should be called by the ksched, which needs to
+ * hold the lock.  It will destroy the process and return any cores allocated to
+ * the proc via pc_arr and nr_revoked.  It's up to the caller to have enough
+ * space for pc_arr.  This will return TRUE if we successfully killed it, FALSE
+ * otherwise.  Failure isn't a big deal either - it can happen due to concurrent
+ * calls to proc_destroy. 
  *
  * Here's the way process death works:
  * 0. grab the lock (protects state transition and core map)
@@ -678,21 +685,16 @@ void proc_restartcore(void)
  * come in, making you abandon_core, as if you weren't running.  It may be that
  * the only reference to p is the one you passed in, and when you decref, it'll
  * get __proc_free()d. */
-void proc_destroy(struct proc *p)
+bool __proc_destroy(struct proc *p, uint32_t *pc_arr, uint32_t *nr_revoked)
 {
-       uint32_t num_revoked = 0;
        struct kthread *sleeper;
-       spin_lock(&p->proc_lock);
-       /* storage for pc_arr is alloced at decl, which is after grabbing the lock*/
-       uint32_t pc_arr[p->procinfo->num_vcores];
        switch (p->state) {
                case PROC_DYING: // someone else killed this already.
-                       spin_unlock(&p->proc_lock);
-                       return;
+                       return FALSE;
                case PROC_RUNNABLE_M:
                        /* Need to reclaim any cores this proc might have, even though it's
                         * not running yet. */
-                       num_revoked = __proc_take_allcores(p, pc_arr, FALSE);
+                       *nr_revoked = __proc_take_allcores(p, pc_arr, FALSE);
                        // fallthrough
                case PROC_RUNNABLE_S:
                        /* might need to pull from lists, though i'm currently a fan of the
@@ -724,42 +726,40 @@ void proc_destroy(struct proc *p)
                         * deallocate the cores.
                         * The rule is that the vcoremap is set before proc_run, and reset
                         * within proc_destroy */
-                       num_revoked = __proc_take_allcores(p, pc_arr, FALSE);
+                       *nr_revoked = __proc_take_allcores(p, pc_arr, FALSE);
                        break;
                case PROC_CREATED:
                        break;
                default:
-                       panic("Weird state(%s) in %s()", procstate2str(p->state),
-                             __FUNCTION__);
+                       warn("Weird state(%s) in %s()", procstate2str(p->state),
+                            __FUNCTION__);
+                       return FALSE;
        }
+       /* At this point, a death IPI should be on its way, either from the
+        * RUNNING_S one, or from proc_take_cores with a __death.  in general,
+        * interrupts should be on when you call proc_destroy locally, but currently
+        * aren't for all things (like traphandlers). */
        __proc_set_state(p, PROC_DYING);
        /* This prevents processes from accessing their old files while dying, and
         * will help if these files (or similar objects in the future) hold
         * references to p (preventing a __proc_free()). */
        close_all_files(&p->open_files, FALSE);
-       /* This decref is for the process's existence. */
-       proc_decref(p);
        /* Signal our state change.  Assuming we only have one waiter right now. */
        sleeper = __up_sem(&p->state_change, TRUE);
        if (sleeper)
                kthread_runnable(sleeper);
-       /* Unlock.  A death IPI should be on its way, either from the RUNNING_S one,
-        * or from proc_take_cores with a __death.  in general, interrupts should be
-        * on when you call proc_destroy locally, but currently aren't for all
-        * things (like traphandlers). */
-       spin_unlock(&p->proc_lock);
-       /* Return the cores to the ksched */
-       if (num_revoked)
-               put_idle_cores(pc_arr, num_revoked);
-       return;
+       return TRUE;
 }
 
 /* Turns *p into an MCP.  Needs to be called from a local syscall of a RUNNING_S
- * process.  Currently, this ignores whether or not you are an _M already.  You
- * should hold the lock before calling. */
-void __proc_change_to_m(struct proc *p)
+ * process.  Returns 0 if it succeeded, an error code otherwise.  You should
+ * hold the lock before calling. */
+int __proc_change_to_m(struct proc *p)
 {
        int8_t state = 0;
+       /* in case userspace erroneously tries to change more than once */
+       if (__proc_is_mcp(p))
+               return -EINVAL;
        switch (p->state) {
                case (PROC_RUNNING_S):
                        /* issue with if we're async or not (need to preempt it)
@@ -806,13 +806,15 @@ void __proc_change_to_m(struct proc *p)
                         * it, and not clearly thinking through how this would happen.
                         * Perhaps an async call that gets serviced after you're
                         * descheduled? */
-                       panic("Not supporting RUNNABLE_S -> RUNNABLE_M yet.\n");
-                       break;
+                       warn("Not supporting RUNNABLE_S -> RUNNABLE_M yet.\n");
+                       return -EINVAL;
                case (PROC_DYING):
                        warn("Dying, core request coming from %d\n", core_id());
+                       return -EINVAL;
                default:
-                       break;
+                       return -EINVAL;
        }
+       return 0;
 }
 
 /* Old code to turn a RUNNING_M to a RUNNING_S, with the calling context
@@ -872,21 +874,13 @@ static uint32_t get_pcoreid(struct proc *p, uint32_t vcoreid)
 
 /* Helper: saves the SCP's tf state and unmaps vcore 0.  In the future, we'll
  * probably use vc0's space for env_tf and the silly state. */
-static void __proc_save_context_s(struct proc *p, struct trapframe *tf)
+void __proc_save_context_s(struct proc *p, struct trapframe *tf)
 {
        p->env_tf= *tf;
        env_push_ancillary_state(p);                    /* TODO: (HSS) */
        __unmap_vcore(p, 0);    /* VC# keep in sync with proc_run_s */
 }
 
-/* Helper function: yields / wraps up current_tf */
-void __proc_yield_s(struct proc *p, struct trapframe *tf)
-{
-       assert(p->state == PROC_RUNNING_S);
-       __proc_set_state(p, PROC_RUNNABLE_S);
-       __proc_save_context_s(p, tf);
-}
-
 /* Yields the calling core.  Must be called locally (not async) for now.
  * - If RUNNING_S, you just give up your time slice and will eventually return,
  *   possibly after WAITING on an event.
@@ -960,12 +954,16 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
                                 * hasn't already written notif_pending will have seen WAITING,
                                 * and will be spinning while we do this. */
                                __proc_save_context_s(p, current_tf);
+                               spin_unlock(&p->proc_lock);     /* note irqs are not enabled yet */
                        } else {
-                               /* yielding to allow other processes to run */
-                               __proc_yield_s(p, current_tf);
-                               schedule_scp(p);
+                               /* yielding to allow other processes to run.  we're briefly
+                                * WAITING, til we are woken up */
+                               __proc_set_state(p, PROC_WAITING);
+                               __proc_save_context_s(p, current_tf);
+                               spin_unlock(&p->proc_lock);     /* note irqs are not enabled yet */
+                               /* immediately wake up the proc (makes it runnable) */
+                               proc_wakeup(p);
                        }
-                       spin_unlock(&p->proc_lock);     /* note that irqs are not enabled yet */
                        goto out_yield_core;
                case (PROC_RUNNING_M):
                        break;                          /* will handle this stuff below */
@@ -996,9 +994,17 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
        /* 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)
+       /* If there's a preempt pending, we don't need to preempt later since we are
+        * yielding (nice or otherwise).  If not, this is just a regular yield. */
+       if (vc->preempt_pending) {
                vc->preempt_pending = 0;
+       } else {
+               /* Optional: on a normal yield, check to see if we are putting them
+                * below amt_wanted (help with user races) and bail. */
+               if (p->procdata->res_req[RES_CORES].amt_wanted >=
+                                      p->procinfo->num_vcores)
+                       goto out_failed;
+       }
        /* 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.
@@ -1008,10 +1014,6 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
         * posting). */
        if (vcpd->notif_pending)
                goto out_failed;
-       /* Optional: check to see if we are putting them below amt_wanted (help with
-        * correctness-benign user races) and bail */
-       if (p->procdata->res_req[RES_CORES].amt_wanted >= p->procinfo->num_vcores)
-               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));
@@ -1046,7 +1048,7 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
        }
        spin_unlock(&p->proc_lock);
        /* Hand the now-idle core to the ksched */
-       put_idle_core(pcoreid);
+       put_idle_core(p, pcoreid);
        goto out_yield_core;
 out_failed:
        /* for some reason we just want to return, either to take a KMSG that cleans
@@ -1091,23 +1093,53 @@ void proc_notify(struct proc *p, uint32_t vcoreid)
        }
 }
 
-/* Hold the lock before calling this.  If the process is WAITING, it will wake
- * it up and schedule it. */
+/* Makes sure p is runnable.  May be spammed, via the ksched.  Called only by
+ * the ksched when it holds the ksched lock (or whatever).  We need to lock both
+ * the ksched and the proc at some point, so we need to start this call in the
+ * ksched (lock ordering).
+ *
+ * Will call back to the ksched via one of the __sched_.cp_wakeup() calls. */
 void __proc_wakeup(struct proc *p)
 {
-       if (p->state != PROC_WAITING)
-               return;
+       spin_lock(&p->proc_lock);
        if (__proc_is_mcp(p)) {
-               /* Need to make sure they want at least 1 vcore, so the ksched gives
-                * them something.  Might do this via short handler later. */
+               /* we only wake up WAITING mcps */
+               if (p->state != PROC_WAITING)
+                       goto out_unlock;
                if (!p->procdata->res_req[RES_CORES].amt_wanted)
                        p->procdata->res_req[RES_CORES].amt_wanted = 1;
                __proc_set_state(p, PROC_RUNNABLE_M);
+               spin_unlock(&p->proc_lock);
+               __sched_mcp_wakeup(p);
+               goto out;
        } else {
+               /* SCPs can wake up for a variety of reasons.  the only times we need
+                * to do something is if it was waiting or just created.  other cases
+                * are either benign (just go out), or potential bugs (_Ms) */
+               switch (p->state) {
+                       case (PROC_CREATED):
+                       case (PROC_WAITING):
+                               __proc_set_state(p, PROC_RUNNABLE_S);
+                               break;
+                       case (PROC_RUNNABLE_S):
+                       case (PROC_RUNNING_S):
+                       case (PROC_DYING):
+                               goto out_unlock;
+                       case (PROC_RUNNABLE_M):
+                       case (PROC_RUNNING_M):
+                               warn("Weird state(%s) in %s()", procstate2str(p->state),
+                                    __FUNCTION__);
+                               goto out_unlock;
+               }
                printd("[kernel] FYI, waking up an _S proc\n"); /* thanks, past brho! */
-               __proc_set_state(p, PROC_RUNNABLE_S);
-               schedule_scp(p);
+               spin_unlock(&p->proc_lock);
+               __sched_scp_wakeup(p);
+               goto out;
        }
+out_unlock:
+       spin_unlock(&p->proc_lock);
+out:
+       return;
 }
 
 /* Is the process in multi_mode / is an MCP or not?  */
@@ -1217,7 +1249,7 @@ void proc_preempt_core(struct proc *p, uint32_t pcoreid, uint64_t usec)
        }
        spin_unlock(&p->proc_lock);
        if (preempted)
-               put_idle_core(pcoreid);
+               put_idle_core(p, pcoreid);
 }
 
 /* Warns and preempts all from p.  No delaying / alarming, or anything.  The
@@ -1242,7 +1274,7 @@ void proc_preempt_all(struct proc *p, uint64_t usec)
        spin_unlock(&p->proc_lock);
        /* Return the cores to the ksched */
        if (num_revoked)
-               put_idle_cores(pc_arr, num_revoked);
+               put_idle_cores(p, pc_arr, num_revoked);
 }
 
 /* Give the specific pcore to proc p.  Lots of assumptions, so don't really use
@@ -1261,25 +1293,8 @@ void proc_give(struct proc *p, uint32_t pcoreid)
  * out). */
 uint32_t proc_get_vcoreid(struct proc *SAFE p, uint32_t pcoreid)
 {
-       uint32_t vcoreid;
-       // TODO: the code currently doesn't track the vcoreid properly for _S (VC#)
-       spin_lock(&p->proc_lock);
-       switch (p->state) {
-               case PROC_RUNNING_S:
-                       spin_unlock(&p->proc_lock);
-                       return 0; // TODO: here's the ugly part
-               case PROC_RUNNING_M:
-                       vcoreid = get_vcoreid(p, pcoreid);
-                       spin_unlock(&p->proc_lock);
-                       return vcoreid;
-               case PROC_DYING: // death message is on the way
-                       spin_unlock(&p->proc_lock);
-                       return 0;
-               default:
-                       spin_unlock(&p->proc_lock);
-                       panic("Weird state(%s) in %s()", procstate2str(p->state),
-                             __FUNCTION__);
-       }
+       struct per_cpu_info *pcpui = &per_cpu_info[pcoreid];
+       return pcpui->owning_vcoreid;
 }
 
 /* TODO: make all of these static inlines when we gut the env crap */
@@ -1302,9 +1317,10 @@ struct vcore *vcoreid2vcore(struct proc *p, uint32_t vcoreid)
 /********** Core granting (bulk and single) ***********/
 
 /* Helper: gives pcore to the process, mapping it to the next available vcore
- * from list vc_list.  Returns TRUE if we succeeded (non-empty). */
+ * from list vc_list.  Returns TRUE if we succeeded (non-empty).  If you pass in
+ * **vc, we'll tell you which vcore it was. */
 static bool __proc_give_a_pcore(struct proc *p, uint32_t pcore,
-                                struct vcore_tailq *vc_list)
+                                struct vcore_tailq *vc_list, struct vcore **vc)
 {
        struct vcore *new_vc;
        new_vc = TAILQ_FIRST(vc_list);
@@ -1315,6 +1331,8 @@ static bool __proc_give_a_pcore(struct proc *p, uint32_t pcore,
        TAILQ_REMOVE(vc_list, new_vc, list);
        TAILQ_INSERT_TAIL(&p->online_vcs, new_vc, list);
        __map_vcore(p, vcore2vcoreid(p, new_vc), pcore);
+       if (vc)
+               *vc = new_vc;
        return TRUE;
 }
 
@@ -1328,12 +1346,12 @@ static void __proc_give_cores_runnable(struct proc *p, uint32_t *pc_arr,
        p->procinfo->num_vcores += num;
        for (int i = 0; i < num; i++) {
                /* Try from the bulk list first */
-               if (__proc_give_a_pcore(p, pc_arr[i], &p->bulk_preempted_vcs))
+               if (__proc_give_a_pcore(p, pc_arr[i], &p->bulk_preempted_vcs, 0))
                        continue;
                /* o/w, try from the inactive list.  at one point, i thought there might
                 * be a legit way in which the inactive list could be empty, but that i
                 * wanted to catch it via an assert. */
-               assert(__proc_give_a_pcore(p, pc_arr[i], &p->inactive_vcs));
+               assert(__proc_give_a_pcore(p, pc_arr[i], &p->inactive_vcs, 0));
        }
        __seq_end_write(&p->procinfo->coremap_seqctr);
 }
@@ -1341,6 +1359,7 @@ static void __proc_give_cores_runnable(struct proc *p, uint32_t *pc_arr,
 static void __proc_give_cores_running(struct proc *p, uint32_t *pc_arr,
                                       uint32_t num)
 {
+       struct vcore *vc_i;
        /* Up the refcnt, since num cores are going to start using this
         * process and have it loaded in their owning_proc and 'current'. */
        proc_incref(p, num * 2);        /* keep in sync with __startcore */
@@ -1348,9 +1367,9 @@ static void __proc_give_cores_running(struct proc *p, uint32_t *pc_arr,
        p->procinfo->num_vcores += num;
        assert(TAILQ_EMPTY(&p->bulk_preempted_vcs));
        for (int i = 0; i < num; i++) {
-               assert(__proc_give_a_pcore(p, pc_arr[i], &p->inactive_vcs));
-               send_kernel_message(pc_arr[i], __startcore, (long)p, 0, 0,
-                                   KMSG_IMMEDIATE);
+               assert(__proc_give_a_pcore(p, pc_arr[i], &p->inactive_vcs, &vc_i));
+               send_kernel_message(pc_arr[i], __startcore, (long)p,
+                                   (long)vcore2vcoreid(p, vc_i), 0, KMSG_IMMEDIATE);
        }
        __seq_end_write(&p->procinfo->coremap_seqctr);
 }
@@ -1560,6 +1579,7 @@ void clear_owning_proc(uint32_t coreid)
        struct proc *p = pcpui->owning_proc;
        assert(!irq_is_enabled());
        pcpui->owning_proc = 0;
+       pcpui->owning_vcoreid = 0xdeadbeef;
        pcpui->cur_tf = 0;                      /* catch bugs for now (will go away soon) */
        if (p);
                proc_decref(p);
@@ -1695,6 +1715,7 @@ void proc_change_to_vcore(struct proc *p, uint32_t new_vcoreid,
                           bool enable_my_notif)
 {
        uint32_t caller_vcoreid, pcoreid = core_id();
+       struct per_cpu_info *pcpui = &per_cpu_info[pcoreid];
        struct preempt_data *caller_vcpd;
        struct vcore *caller_vc, *new_vc;
        struct event_msg preempt_msg = {0};
@@ -1724,7 +1745,8 @@ void proc_change_to_vcore(struct proc *p, uint32_t new_vcoreid,
        if (!is_mapped_vcore(p, pcoreid))
                goto out_failed;
        /* Get all our info */
-       caller_vcoreid = get_vcoreid(p, pcoreid);
+       caller_vcoreid = get_vcoreid(p, pcoreid);       /* holding lock, we can check */
+       assert(caller_vcoreid == pcpui->owning_vcoreid);
        caller_vcpd = &p->procdata->vcore_preempt_data[caller_vcoreid];
        caller_vc = vcoreid2vcore(p, caller_vcoreid);
        /* Should only call from vcore context */
@@ -1767,6 +1789,8 @@ void proc_change_to_vcore(struct proc *p, uint32_t new_vcoreid,
        __unmap_vcore(p, caller_vcoreid);
        __map_vcore(p, new_vcoreid, pcoreid);
        __seq_end_write(&p->procinfo->coremap_seqctr);
+       /* So this core knows which vcore is here: */
+       pcpui->owning_vcoreid = new_vcoreid;
        /* 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. */
@@ -1786,7 +1810,8 @@ out_failed:
  * Interrupts are disabled. */
 void __startcore(struct trapframe *tf, uint32_t srcid, long a0, long a1, long a2)
 {
-       uint32_t vcoreid, coreid = core_id();
+       uint32_t vcoreid = (uint32_t)a1;
+       uint32_t coreid = core_id();
        struct per_cpu_info *pcpui = &per_cpu_info[coreid];
        struct proc *p_to_run = (struct proc *CT(1))a0;
 
@@ -1795,6 +1820,7 @@ void __startcore(struct trapframe *tf, uint32_t srcid, long a0, long a1, long a2
        assert(!pcpui->owning_proc);
        /* the sender of the amsg increfed already for this saved ref to p_to_run */
        pcpui->owning_proc = p_to_run;
+       pcpui->owning_vcoreid = vcoreid;
        /* sender increfed again, assuming we'd install to cur_proc.  only do this
         * if no one else is there.  this is an optimization, since we expect to
         * send these __startcores to idles cores, and this saves a scramble to
@@ -1807,7 +1833,6 @@ void __startcore(struct trapframe *tf, uint32_t srcid, long a0, long a1, long a2
                proc_decref(p_to_run);          /* can't install, decref the extra one */
        }
        /* Note we are not necessarily in the cr3 of p_to_run */
-       vcoreid = get_vcoreid(p_to_run, coreid);
        /* Now that we sorted refcnts and know p / which vcore it should be, set up
         * pcpui->cur_tf so that it will run that particular vcore */
        __set_curtf_to_vcoreid(p_to_run, vcoreid);
@@ -1829,10 +1854,8 @@ void __notify(struct trapframe *tf, uint32_t srcid, long a0, long a1, long a2)
        /* Common cur_tf sanity checks.  Note cur_tf could be an _S's env_tf */
        assert(pcpui->cur_tf);
        assert(!in_kernel(pcpui->cur_tf));
-       /* We shouldn't need to lock here, since unmapping happens on the pcore and
-        * mapping would only happen if the vcore was free, which it isn't until
-        * after we unmap. */
-       vcoreid = get_vcoreid(p, coreid);
+       vcoreid = pcpui->owning_vcoreid;
+       assert(vcoreid == get_vcoreid(p, coreid));
        vcpd = &p->procdata->vcore_preempt_data[vcoreid];
        /* for SCPs that haven't (and might never) call vc_event_init, like rtld.
         * this is harmless for MCPS to check this */
@@ -1869,10 +1892,8 @@ void __preempt(struct trapframe *tf, uint32_t srcid, long a0, long a1, long a2)
        assert(pcpui->cur_tf);
        assert(pcpui->cur_tf == &pcpui->actual_tf);
        assert(!in_kernel(pcpui->cur_tf));
-       /* We shouldn't need to lock here, since unmapping happens on the pcore and
-        * mapping would only happen if the vcore was free, which it isn't until
-        * after we unmap. */
-       vcoreid = get_vcoreid(p, coreid);
+       vcoreid = pcpui->owning_vcoreid;
+       assert(vcoreid == get_vcoreid(p, coreid));
        p->procinfo->vcoremap[vcoreid].preempt_served = FALSE;
        /* either __preempt or proc_yield() ends the preempt phase. */
        p->procinfo->vcoremap[vcoreid].preempt_pending = 0;
@@ -1910,7 +1931,8 @@ void __death(struct trapframe *tf, uint32_t srcid, long a0, long a1, long a2)
        struct per_cpu_info *pcpui = &per_cpu_info[coreid];
        struct proc *p = pcpui->owning_proc;
        if (p) {
-               vcoreid = get_vcoreid(p, coreid);
+               vcoreid = pcpui->owning_vcoreid;
+               assert(vcoreid == get_vcoreid(p, coreid));
                printd("[kernel] death on physical core %d for process %d's vcore %d\n",
                       coreid, p->pid, vcoreid);
                __unmap_vcore(p, vcoreid);