OSDI Microbenchmarks
[akaros.git] / kern / src / process.c
index 54a1d5d..d176b65 100644 (file)
@@ -26,6 +26,7 @@
 #include <sys/queue.h>
 #include <frontend.h>
 #include <monitor.h>
+#include <resource.h>
 
 /* Process Lists */
 struct proc_list proc_runnablelist = TAILQ_HEAD_INITIALIZER(proc_runnablelist);
@@ -42,7 +43,7 @@ uint32_t num_mgmtcores = 1;
 /* Helper function to return a core to the idlemap.  It causes some more lock
  * acquisitions (like in a for loop), but it's a little easier.  Plus, one day
  * we might be able to do this without locks (for the putting). */
-static void put_idle_core(uint32_t coreid)
+void put_idle_core(uint32_t coreid)
 {
        spin_lock(&idle_lock);
 #ifdef __CONFIG_EXPER_TRADPROC__ /* often a good check, but hurts performance */
@@ -255,13 +256,6 @@ int fake_proc_alloc(struct proc **pp, struct proc *parent, uint32_t vcoreid)
        error_t r;
        struct proc *p;
 
-       /* So we can call this anytime we want to run a vcore, including vcore0,
-        * which is the true_proc / parent.  Probably horribly broken. */
-       if (!vcoreid) {
-               *pp = parent;
-               return 0;
-       }
-
        if (!(p = kmem_cache_alloc(proc_cache, 0)))
                return -ENOMEM;
 
@@ -283,8 +277,14 @@ int fake_proc_alloc(struct proc **pp, struct proc *parent, uint32_t vcoreid)
        // p->syscallbackring = not happening
        p->true_proc = parent;
        p->vcoreid = vcoreid;
-       /* map us to the true parent vcoremap */
+       /* there is a slight race on the old vcore mapping.  for a brief period, it
+        * is unmapped, but still tracked by the parent.  it's between the unmapping
+        * and the freeing (where the vcore_procs[i] is cleared, which we need to
+        * hold on to until the fake_proc has abandoned core.  a brief spin should
+        * be okay. */
+       spin_on(parent->vcore_procs[vcoreid]);
        assert(!parent->vcore_procs[vcoreid]);
+       /* map us to the true parent vcoremap */
        parent->vcore_procs[vcoreid] = p;
        parent->env_refcnt++;
 
@@ -428,7 +428,7 @@ static void __proc_free(struct proc *p)
                        for (int j = 0; p->vcore_procs[i]; j++) {
                                cpu_relax();
                                if (j == 10000) {
-                                       printd("Core %d stalled while waiting on peep %d\n",
+                                       printk("Core %d stalled while waiting on peep %d\n",
                                               core_id(), i);
                                        //send_kernel_message(p->procinfo->vcoremap[i].pcoreid,
                                        //                    __death, 0, 0, 0, KMSG_ROUTINE);
@@ -469,6 +469,16 @@ static void __proc_free(struct proc *p)
 
        /* Dealloc the struct proc */
        kmem_cache_free(proc_cache, p);
+
+#ifdef __CONFIG_OSDI__ /* for experiment coordination */
+       extern struct proc *mgr_p1, *mgr_p2;
+       /* Signal to the monitor we're done */
+       if (p == mgr_p1)
+               mgr_p1 = 0;
+       if (p == mgr_p2)
+               mgr_p2 = 0;
+       printk("[T]:004:E:%llu\n", read_tsc());
+#endif /* __CONFIG_EXPER_TRADPROC__ */
 }
 
 /* Whether or not actor can control target.  Note we currently don't need
@@ -638,8 +648,10 @@ static void __proc_startcore(struct proc *p, trapframe_t *tf)
         * different context.
         * for now, we load this silly state here. (TODO) (HSS)
         * We also need this to be per trapframe, and not per process...
-        */
-       env_pop_ancillary_state(p);
+        * For now / OSDI, only load it when in _S mode.  _M mode was handled in
+        * __startcore.  */
+       if (p->state == PROC_RUNNING_S)
+               env_pop_ancillary_state(p);
        env_pop_tf(tf);
 }
 
@@ -704,7 +716,7 @@ void proc_destroy(struct proc *p)
        if (current == p)
 #endif /* __CONFIG_EXPER_TRADPROC__ */
                self_ipi_pending = TRUE;
-       
+
        switch (p->state) {
                case PROC_DYING: // someone else killed this already.
                        spin_unlock(&p->proc_lock);
@@ -831,12 +843,16 @@ 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];
 
+#ifdef __CONFIG_OSDI__
+       bool new_idle_core = FALSE;
+#endif /* __CONFIG_OSDI__ */
+
        /* no reason to be nice, return */
        if (being_nice && !vc->preempt_pending)
                return;
 
 #ifdef __CONFIG_EXPER_TRADPROC__
-       if (p->state == (PROC_RUNNING_M | PROC_DYING))
+       if (p->state & (PROC_RUNNING_M | PROC_DYING))
                return;
 #endif /* __CONFIG_EXPER_TRADPROC__ */
 
@@ -856,20 +872,20 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
        switch (p->state) {
                case (PROC_RUNNING_S):
                        p->env_tf= *current_tf;
-                       env_push_ancillary_state(p);
+                       env_push_ancillary_state(p); // TODO: (HSS)
                        __proc_set_state(p, PROC_RUNNABLE_S);
                        schedule_proc(p);
                        break;
                case (PROC_RUNNING_M):
-#ifdef __CONFIG_OSDI__
-                       /* Ghetto, for OSDI: */
                        printd("[K] Process %d (%p) is yielding on vcore %d\n", p->pid, p,
                               get_vcoreid(p, core_id()));
+                       /* TODO: (RMS) the Scheduler cannot handle the Runnable Ms (RMS), so
+                        * don't yield the last vcore.  It's ghetto and for OSDI, but it
+                        * needs to be fixed for all builds, not just CONFIG_OSDI. */
                        if (p->procinfo->num_vcores == 1) {
                                spin_unlock(&p->proc_lock);
                                return;
                        }
-#endif /* __CONFIG_OSDI__ */
                        __seq_start_write(&p->procinfo->coremap_seqctr);
                        // give up core
                        __unmap_vcore(p, get_vcoreid(p, core_id()));
@@ -879,7 +895,11 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
                        __seq_end_write(&p->procinfo->coremap_seqctr);
                        // add to idle list
                        put_idle_core(core_id());
+#ifdef __CONFIG_OSDI__
+                       new_idle_core = TRUE;
+#endif /* __CONFIG_OSDI__ */
                        // last vcore?  then we really want 1, and to yield the gang
+                       // TODO: (RMS) will actually do this.
                        if (p->procinfo->num_vcores == 0) {
                                p->resources[RES_CORES].amt_wanted = 1;
                                __proc_set_state(p, PROC_RUNNABLE_M);
@@ -893,6 +913,21 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
        }
        spin_unlock(&p->proc_lock);
        proc_decref(p, 1); // need to eat the ref passed in.
+#ifdef __CONFIG_OSDI__
+       /* If there was a change to the idle cores, try and give our core to someone who was
+        * preempted.  core_request likely won't return.  if that happens, p's
+        * context ought to be cleaned up in the proc_startcore of the new guy. (if
+        * we actually yielded)
+        * TODO: (RMS) do this more intelligently e.g.: kick_scheduler(); */
+       extern struct proc *victim;
+       if (new_idle_core && victim) {
+               /* this ghetto victim pointer is not an edible reference, and core
+                * request will eat it when it doesn't return. */
+               proc_incref(victim, 1);
+               core_request(victim);
+               proc_decref(victim, 1);
+       }
+#endif /* __CONFIG_OSDI__ */
        /* Clean up the core and idle.  For mgmt cores, they will ultimately call
         * manager, which will call schedule() and will repick the yielding proc. */
        abandon_core();
@@ -968,6 +1003,152 @@ void proc_notify(struct proc *p, unsigned int notif, struct notif_event *ne)
        do_notify(p, nm->vcoreid, ne->ne_type, ne);
 }
 
+/************************  Preemption Functions  ******************************
+ * Don't rely on these much - I'll be sure to change them up a bit.
+ *
+ * Careful about what takes a vcoreid and what takes a pcoreid.  Also, there may
+ * be weird glitches with setting the state to RUNNABLE_M.  It is somewhat in
+ * flux.  The num_vcores is changed after take_cores, but some of the messages
+ * (or local traps) may not yet be ready to handle seeing their future state.
+ * But they should be, so fix those when they pop up.
+ *
+ * TODO: (RMS) we need to actually make the scheduler handle RUNNABLE_Ms and
+ * then schedule these, or change proc_destroy to not assume they need to be
+ * descheduled.
+ *
+ * Another thing to do would be to make the _core functions take a pcorelist,
+ * and not just one pcoreid. */
+
+/* Sets a preempt_pending warning for p's vcore, to go off 'when'.  If you care
+ * about locking, do it before calling.  Takes a vcoreid! */
+void __proc_preempt_warn(struct proc *p, uint32_t vcoreid, uint64_t when)
+{
+       /* danger with doing this unlocked: preempt_pending is set, but never 0'd,
+        * since it is unmapped and not dealt with (TODO)*/
+       p->procinfo->vcoremap[vcoreid].preempt_pending = when;
+       /* notify, if they want to hear about this event.  regardless of how they
+        * want it, we can send this as a bit.  Subject to change. */
+       if (p->procdata->notif_methods[NE_PREEMPT_PENDING].flags | NOTIF_WANTED)
+               do_notify(p, vcoreid, NE_PREEMPT_PENDING, 0);
+       /* 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)) */
+}
+
+/* Warns all active vcores of an impending preemption.  Hold the lock if you
+ * 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++;
+       }
+       /* 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)) */
+}
+
+// TODO: function to set an alarm, if none is outstanding
+
+/* Raw function to preempt a single core.  Returns TRUE if the calling core will
+ * get a kmsg.  If you care about locking, do it before calling. */
+bool __proc_preempt_core(struct proc *p, uint32_t pcoreid)
+{
+       uint32_t vcoreid = get_vcoreid(p, pcoreid);
+
+       p->procinfo->vcoremap[vcoreid].preempt_served = TRUE;
+       // expects a pcorelist.  assumes pcore is mapped and running_m
+       return __proc_take_cores(p, &pcoreid, 1, __preempt, p, 0, 0);
+}
+
+/* Raw function to preempt every vcore.  Returns TRUE if the calling core will
+ * get a kmsg.  If you care about locking, do it before calling. */
+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++;
+       }
+       return __proc_take_allcores(p, __preempt, p, 0, 0);
+}
+
+/* Warns and preempts a vcore from p.  No delaying / alarming, or anything.  The
+ * warning will be for u usec from now. */
+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;
+
+       /* DYING could be okay */
+       if (p->state != PROC_RUNNING_M) {
+               warn("Tried to preempt from a non RUNNING_M proc!");
+               return;
+       }
+       spin_lock(&p->proc_lock);
+       if (is_mapped_vcore(p, pcoreid)) {
+               __proc_preempt_warn(p, get_vcoreid(p, pcoreid), warn_time);
+               self_ipi_pending = __proc_preempt_core(p, pcoreid);
+       } else {
+               warn("Pcore doesn't belong to the process!!");
+       }
+       /* TODO: (RMS) do this once a scheduler can handle RUNNABLE_M, and make sure
+        * to schedule it */
+       #if 0
+       if (!p->procinfo->num_vcores) {
+               __proc_set_state(p, PROC_RUNNABLE_M);
+               schedule_proc(p);
+       }
+       #endif
+       spin_unlock(&p->proc_lock);
+       __proc_kmsg_pending(p, self_ipi_pending);
+}
+
+/* Warns and preempts all from p.  No delaying / alarming, or anything.  The
+ * warning will be for u usec from now. */
+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;
+
+       spin_lock(&p->proc_lock);
+       /* DYING could be okay */
+       if (p->state != PROC_RUNNING_M) {
+               warn("Tried to preempt from a non RUNNING_M proc!");
+               spin_unlock(&p->proc_lock);
+               return;
+       }
+       __proc_preempt_warnall(p, warn_time);
+       self_ipi_pending = __proc_preempt_all(p);
+       assert(!p->procinfo->num_vcores);
+       /* TODO: (RMS) do this once a scheduler can handle RUNNABLE_M, and make sure
+        * to schedule it */
+       #if 0
+       __proc_set_state(p, PROC_RUNNABLE_M);
+       schedule_proc(p);
+       #endif
+       spin_unlock(&p->proc_lock);
+       __proc_kmsg_pending(p, self_ipi_pending);
+}
+
+/* Give the specific pcore to proc p.  Lots of assumptions, so don't really use
+ * this.  The proc needs to be _M and prepared for it.  the pcore needs to be
+ * free, etc. */
+void proc_give(struct proc *p, uint32_t pcoreid)
+{
+       bool self_ipi_pending = FALSE;
+
+       spin_lock(&p->proc_lock);
+       // expects a pcorelist, we give it a list of one
+       self_ipi_pending = __proc_give_cores(p, &pcoreid, 1);
+       spin_unlock(&p->proc_lock);
+       __proc_kmsg_pending(p, self_ipi_pending);
+}
+
 /* Global version of the helper, for sys_get_vcoreid (might phase that syscall
  * out). */
 uint32_t proc_get_vcoreid(struct proc *SAFE p, uint32_t pcoreid)
@@ -1042,8 +1223,6 @@ bool __proc_give_cores(struct proc *SAFE p, uint32_t *pcorelist, size_t num)
                        // add new items to the vcoremap
 #ifdef __CONFIG_EXPER_TRADPROC__
                        __proc_set_state(p, PROC_RUNNING_M);
-                       // want an extra one since res_req jacked on on our transition
-                       p->env_refcnt++;
 #endif /* __CONFIG_EXPER_TRADPROC__ */
                        __seq_start_write(&p->procinfo->coremap_seqctr);
                        for (int i = 0; i < num; i++) {
@@ -1201,7 +1380,7 @@ bool __proc_take_allcores(struct proc *SAFE p, amr_t message,
        __seq_start_write(&p->procinfo->coremap_seqctr);
 #ifdef __CONFIG_EXPER_TRADPROC__
        /* Decref each child, so they will free themselves when they unmap */
-       for (int i = 1; i < MAX_NUM_CPUS; i++) {
+       for (int i = 0; i < MAX_NUM_CPUS; i++) {
                if (p->vcore_procs[i])
                        proc_decref(p->vcore_procs[i], 1);
        }
@@ -1240,7 +1419,7 @@ bool __proc_take_allcores(struct proc *SAFE p, amr_t message,
  * There should already be a kmsg waiting for us, since when we checked state to
  * see a message was coming, the message had already been sent before unlocking.
  * Note we do not need interrupts enabled for this to work (you can receive a
- * message before its IPI by polling), though in most cases they will be. 
+ * message before its IPI by polling), though in most cases they will be.
  *
  * TODO: consider inlining this, so __FUNCTION__ works (will require effort in
  * core_request(). */
@@ -1311,6 +1490,42 @@ void proc_decref(struct proc *p, size_t count)
                panic("Too many decrefs!");
 }
 
+/* Stop running whatever context is on this core, load a known-good cr3, and
+ * 'idle'.  Note this leaves no trace of what was running. This "leaves the
+ * process's context. */
+void abandon_core(void)
+{
+       if (current)
+               __abandon_core();
+       smp_idle();
+}
+
+/* Will send a TLB shootdown message to every vcore in the main address space
+ * (aka, all vcores for now).  The message will take the start and end virtual
+ * addresses as well, in case we want to be more clever about how much we
+ * 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)
+{
+       uint32_t active_vcoreid = 0, pcoreid;
+       /* 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);
+               pcoreid = p->procinfo->vcoremap[active_vcoreid].pcoreid;
+               send_kernel_message(pcoreid, __tlbshootdown, (void*)start, (void*)end,
+                                   (void*)0, KMSG_IMMEDIATE);
+               active_vcoreid++; /* for the next loop, skip the one we just used */
+       }
+}
+
 /* Kernel message handler to start a process's context on this core.  Tightly
  * coupled with proc_run().  Interrupts are disabled. */
 void __startcore(trapframe_t *tf, uint32_t srcid, void *a0, void *a1, void *a2)
@@ -1390,16 +1605,6 @@ void __notify(trapframe_t *tf, uint32_t srcid, void *a0, void *a1, void *a2)
        __proc_startcore(p, &local_tf);
 }
 
-/* Stop running whatever context is on this core, load a known-good cr3, and
- * 'idle'.  Note this leaves no trace of what was running. This "leaves the
- * process's context. */
-void abandon_core(void)
-{
-       if (current)
-               __abandon_core();
-       smp_idle();
-}
-
 void __preempt(trapframe_t *tf, uint32_t srcid, void *a0, void *a1, void *a2)
 {
        struct preempt_data *vcpd;
@@ -1407,7 +1612,8 @@ void __preempt(trapframe_t *tf, uint32_t srcid, void *a0, void *a1, void *a2)
        struct proc *p = (struct proc*)a0;
 
        if (p != current)
-               warn("__preempt arrived for a process that was not current!");
+               panic("__preempt arrived for a process (%p) that was not current (%p)!",
+                     p, current);
        assert(!in_kernel(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
@@ -1417,7 +1623,7 @@ void __preempt(trapframe_t *tf, uint32_t srcid, void *a0, void *a1, void *a2)
        /* either __preempt or proc_yield() ends the preempt phase. */
        p->procinfo->vcoremap[vcoreid].preempt_pending = 0;
        vcpd = &p->procdata->vcore_preempt_data[vcoreid];
-       printk("[kernel] received __preempt for proc %d's vcore %d on pcore %d\n",
+       printd("[kernel] received __preempt for proc %d's vcore %d on pcore %d\n",
               p->procinfo->pid, vcoreid, core_id());
 
        /* save the old tf in the preempt slot, save the silly state, and signal the
@@ -1449,6 +1655,15 @@ void __death(trapframe_t *tf, uint32_t srcid, void *SNT a0, void *SNT a1,
        abandon_core();
 }
 
+/* Kernel message handler, usually sent IMMEDIATE, to shoot down virtual
+ * addresses from a0 to a1. */
+void __tlbshootdown(struct trapframe *tf, uint32_t srcid, void *a0, void *a1,
+                    void *a2)
+{
+       /* TODO: (TLB) something more intelligent with the range */
+       tlbflush();
+}
+
 void print_idlecoremap(void)
 {
        spin_lock(&idle_lock);
@@ -1477,19 +1692,23 @@ void print_allpids(void)
 void print_proc_info(pid_t pid)
 {
        int j = 0;
-       struct proc *p = pid2proc(pid);
+       /* Doing this without the incref! careful! (avoiding deadlocks) TODO (REF)*/
+       //struct proc *p = pid2proc(pid);
+       spin_lock(&pid_hash_lock);
+       struct proc *p = hashtable_search(pid_hash, (void*)pid);
+       spin_unlock(&pid_hash_lock);
        // not concerned with a race on the state...
        if (!p) {
                printk("Bad PID.\n");
                return;
        }
        spinlock_debug(&p->proc_lock);
-       spin_lock(&p->proc_lock);
+       //spin_lock(&p->proc_lock); // No locking!!
        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("Refcnt: %d\n", p->env_refcnt - 1); // don't report our ref
+       printk("Refcnt: %d\n", p->env_refcnt);
        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);
@@ -1503,8 +1722,31 @@ void print_proc_info(pid_t pid)
        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);
-       printk("Vcore 0's Last Trapframe:\n");
-       print_trapframe(&p->env_tf);
-       spin_unlock(&p->proc_lock);
-       proc_decref(p, 1); /* decref for the pid2proc reference */
+#ifdef __CONFIG_EXPER_TRADPROC__
+       void print_chain(struct proc *p)
+       {
+               if (!is_real_proc(p)) {
+                       printk("P is not a true_proc, parent is %p\n", p->true_proc);
+                       print_chain(p);
+               } else {
+                       printk("P is a true_proc\n");
+                       for (int i = 0; i < p->procinfo->num_vcores; i++) {
+                               printk("%p's child %d is %p\n", p, i, p->vcore_procs[i]);
+                               if (p->vcore_procs[i])
+                                       for (int j = 0; j < MAX_NUM_CPUS; j++)
+                                               if (p->vcore_procs[i]->vcore_procs[j])
+                                                       printk("Crap, child %p has its own child %p!!\n",
+                                                              p->vcore_procs[i],
+                                                              p->vcore_procs[i]->vcore_procs[j]);
+                       }
+               }
+       }
+       print_chain(p);
+#endif /* __CONFIG_EXPER_TRADPROC__ */
+       /* No one cares, and it clutters the terminal */
+       //printk("Vcore 0's Last Trapframe:\n");
+       //print_trapframe(&p->env_tf);
+       /* no locking / unlocking or refcnting */
+       // spin_unlock(&p->proc_lock);
+       // proc_decref(p, 1); /* decref for the pid2proc reference */
 }