Tracks proc's vcoreids in pcpu info
[akaros.git] / kern / src / process.c
index 3ed127e..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!");
@@ -739,8 +744,6 @@ bool __proc_destroy(struct proc *p, uint32_t *pc_arr, uint32_t *nr_revoked)
         * 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)
@@ -749,11 +752,14 @@ bool __proc_destroy(struct proc *p, uint32_t *pc_arr, uint32_t *nr_revoked)
 }
 
 /* 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)
@@ -800,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
@@ -866,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.
@@ -954,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 */
@@ -1044,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
@@ -1089,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?  */
@@ -1215,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
@@ -1240,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
@@ -1259,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 */
@@ -1300,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);
@@ -1313,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;
 }
 
@@ -1326,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);
 }
@@ -1339,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 */
@@ -1346,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);
 }
@@ -1558,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);
@@ -1693,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};
@@ -1722,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 */
@@ -1765,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. */
@@ -1784,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;
 
@@ -1793,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
@@ -1805,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);
@@ -1827,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 */
@@ -1867,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;
@@ -1908,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);