proc_run supports dispatching of RUNNABLE_Ms
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 18 Aug 2009 21:52:40 +0000 (14:52 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Mon, 31 Aug 2009 21:06:22 +0000 (14:06 -0700)
Using proc_run, you can dispatch a process on a set of cores.  Set the
physical coreid of cores allocated to a process in its vcoremap[] list
and set num_vcores (virtual cores).  It will pop the main struct
trapframe on vcore 0.

Still are issues with the silly state, trapframes, and RAMP needing
corresponding wrappers for send_ipi.  Also, userspace still just spins
on its off-cores.

kern/arch/i386/atomic.h
kern/arch/i386/process.c
kern/arch/i386/trap.c
kern/arch/i386/trap.h
kern/arch/i386/trapentry.S
kern/arch/sparc/atomic.h
kern/include/env.h
kern/src/env.c
kern/src/manager.c
kern/src/process.c
kern/src/schedule.c

index 3e7d651..c85f385 100644 (file)
@@ -6,6 +6,9 @@
 #define mb() {rmb(); wmb();}
 #define rmb() ({ asm volatile("lfence"); })
 #define wmb() 
+/* Force a wmb, used in cases where an IPI could beat a write, even though
+ * write-orderings are respected. */
+#define wmb_f() ({ asm volatile("sfence"); })
 
 typedef void* atomic_t;
 typedef volatile uint32_t spinlock_t;
index b802291..ff25784 100644 (file)
@@ -6,18 +6,21 @@
 #include <string.h>
 #include <assert.h>
 
-void __startcore(void)
+void __startcore(trapframe_t *tf)
 {
        uint32_t coreid = core_id();
        struct proc *p_to_run = per_cpu_info[coreid].p_to_run;
        trapframe_t local_tf, *tf_to_pop = per_cpu_info[coreid].tf_to_pop;
 
-       // EOI - we received the interrupt.  probably no issues with receiving
-       // further interrupts in this function.
+       /* EOI - we received the interrupt.  probably no issues with receiving
+        * further interrupts in this function.  need to do this before
+        * proc_startcore.  incidentally, interrupts are off in this handler
+        * anyways, since it's set up as an interrupt gate for now. */
        lapic_send_eoi();
 
        printk("Startcore on core %d\n", coreid);
        assert(p_to_run);
+       // TODO: handle silly state (HSS)
        if (!tf_to_pop) {
                tf_to_pop = &local_tf;
                memset(tf_to_pop, 0, sizeof(*tf_to_pop));
index 708e1c1..e567d4d 100644 (file)
@@ -211,7 +211,7 @@ trap(trapframe_t *tf)
        //cprintf("Incoming TRAP frame at %p\n", tf);
 
        // TODO: do this once we know we are are not returning to the current
-       // context.  doing it now is safe.
+       // context.  doing it now is safe. (HSS)
        env_push_ancillary_state(current);
 
        if ((tf->tf_cs & ~3) != GD_UT && (tf->tf_cs & ~3) != GD_KT) {
@@ -248,6 +248,10 @@ irq_handler(trapframe_t *tf)
        //      cprintf("Incoming IRQ, ISR: %d on core %d\n", tf->tf_trapno, core_id());
        // merge this with alltraps?  other than the EOI... or do the same in all traps
 
+       // TODO: do this once we know we are are not returning to the current
+       // context.  doing it now is safe. (HSS)
+       env_push_ancillary_state(current);
+
        extern handler_wrapper_t handler_wrappers[NUM_HANDLER_WRAPPERS];
 
        // determine the interrupt handler table to use.  for now, pick the global
index d737467..0d1d81d 100644 (file)
 #define T_DEFAULT   0xdeadbeef         // catchall
 
 /* IPIs */
-/* Direct/Hardwired IPIs.  Hardwired in trapentry.S */
-#define I_STARTCORE 230
-#define I_DEATH     231
+/* Direct/Hardwired IPIs.  Hardwired in trapentry.S
+ * DEATH must come before (have lower priority than) STARTCORE.  see comments in
+ * i386/process.c's proc_run() for details. */
+#define I_DEATH     230
+#define I_STARTCORE 231
 /* smp_call_function IPIs, keep in sync with NUM_HANDLER_WRAPPERS (and < 16)
- * it's important that this begins with 0xf0.  check i386/trap.c for details.
- */
+ * it's important that this begins with 0xf0.  check i386/trap.c for details. */
 #define I_SMP_CALL0 0xf0 // 240
 #define I_SMP_CALL1 0xf1
 #define I_SMP_CALL2 0xf2
index 4a6d213..2d898fd 100644 (file)
@@ -141,8 +141,8 @@ IRQ_HANDLER(IRQ13, 45)
 IRQ_HANDLER(IRQ14, 46)
 IRQ_HANDLER(IRQ15, 47)
 /* 25 general purpose vectors, for use by the LAPIC.  Can expand later. */
-IRQ_HANDLER_SPEC(IRQ198, I_STARTCORE, __startcore) 
-IRQ_HANDLER_SPEC(IRQ199, I_DEATH, irq_handler) 
+IRQ_HANDLER_SPEC(IRQ198, I_DEATH, irq_handler)
+IRQ_HANDLER_SPEC(IRQ199, I_STARTCORE, __startcore)
 IRQ_HANDLER(IRQ200, 232)
 IRQ_HANDLER(IRQ201, 233)
 IRQ_HANDLER(IRQ202, 234)
index fb8275c..73d370f 100644 (file)
@@ -6,6 +6,9 @@
 #define mb() {rmb(); wmb();}
 #define rmb()
 #define wmb() ({ asm volatile("stbar"); })
+/* Force a wmb, used in cases where an IPI could beat a write, even though
+ * write-orderings are respected.  (Used by x86) */
+#define wmb_f() wmb()
 
 typedef volatile uint32_t spinlock_t;
 
index a18f80a..d3cb93c 100644 (file)
@@ -55,6 +55,10 @@ struct Env {
        uint32_t env_refcnt;            // Reference count of kernel contexts using this
        uint32_t env_flags;
        uint32_t env_entry;
+       /* Virtual coremap: each index is the virtual core id, the contents at that
+        * index is the physical core_id() corresponding to the vcore. */
+       uint32_t vcoremap[MAX_NUM_CPUS];
+       uint32_t num_vcores;
 
 #ifdef __SHARC__
        // held spin-locks
@@ -84,6 +88,7 @@ struct Env {
 
 extern env_t *COUNT(NENV) envs;                // All environments
 extern atomic_t num_envs;              // Number of envs
+// TODO: consider moving this to struct per_cpu_info
 extern env_t* curenvs[MAX_NUM_CPUS];
 
 void   env_init(void);
index 61b13fa..27a2699 100644 (file)
@@ -273,11 +273,12 @@ env_alloc(env_t **newenv_store, envid_t parent_id)
        e->env_refcnt = 1;
        e->env_flags = 0;
        e->env_entry = 0; // cheating.  this really gets set in load_icode
-
 #ifdef __SHARC__
        /* init SharC state */
        sharC_env_init(&e->sharC_env);
 #endif
+       e->num_vcores = 0; // only used in RUNN*_M states
+       memset(&e->vcoremap, 0, sizeof(e->vcoremap));
 
        memset(&e->env_ancillary_state, 0, sizeof(e->env_ancillary_state));
        memset(&e->env_tf, 0, sizeof(e->env_tf));
index d7d33e5..f9c0e96 100644 (file)
@@ -32,24 +32,16 @@ void manager(void)
        static uint8_t progress = 0;
        struct proc *envs[256];
 
-envs[0] = kfs_proc_create(kfs_lookup_path("roslib_mhello"));
+struct proc *p = kfs_proc_create(kfs_lookup_path("roslib_mhello"));
 // being proper and all:
-proc_set_state(envs[0], PROC_RUNNABLE_S);
-proc_set_state(envs[0], PROC_RUNNING_S);
-proc_set_state(envs[0], PROC_RUNNABLE_M);
-proc_set_state(envs[0], PROC_RUNNING_M);
-for (int i = 1; i < 8; i++) {
-       per_cpu_info[i].p_to_run = envs[0];
-       //per_cpu_info[i].tf_to_pop = &envs[0]->env_tf; // starts the main core
-}
-// needed to make sure the writes beat the IPI.  could have done it with other
-// mem accesses, like grabbing a lock, and the write would have made it (no wmb() on
-// x86)
-asm volatile("sfence");
-// actually send the IPI to do this
-for (int i = 1; i < 8; i++)
-       send_ipi(i, 0, I_STARTCORE);
-
+proc_set_state(p, PROC_RUNNABLE_S);
+proc_set_state(p, PROC_RUNNING_S);
+proc_set_state(p, PROC_RUNNABLE_M);
+// set vcoremap with dispatch plan.  usually done by schedule()
+p->num_vcores = 5;
+for (int i = 0; i < 5; i++)
+       p->vcoremap[i] = i + 1; // vcore0 -> pcore1, etc, for 3 cores
+proc_run(p);
 udelay(5000000);
 panic("This is okay");
 
index 1cc59ac..ba28b05 100644 (file)
@@ -8,6 +8,7 @@
 #pragma nosharc
 #endif
 
+#include <arch/arch.h>
 #include <process.h>
 #include <atomic.h>
 #include <smp.h>
 #include <manager.h>
 #include <stdio.h>
 #include <assert.h>
+#include <timing.h>
 #include <sys/queue.h>
 
+/* Process Lists */
 struct proc_list proc_freelist = TAILQ_HEAD_INITIALIZER(proc_freelist);
 spinlock_t freelist_lock = 0;
 struct proc_list proc_runnablelist = TAILQ_HEAD_INITIALIZER(proc_runnablelist);
@@ -127,26 +130,36 @@ void proc_run(struct proc *p)
                        proc_startcore(p, &p->env_tf);
                        break;
                case (PROC_RUNNABLE_M):
-                       // BIG TODO: do this.
-                       // Check for how we're supposed to dispatch this
-                       // Update core map or whatever with what we're about to do
-
+                       proc_set_state(p, PROC_RUNNING_M);
+                       /* Prep each core that we are going to run this process on.
+                        * vcoremap[i] holds the coreid of the physical core allocated to
+                        * this process.  It is set outside proc_run */
+                       for (int i = 0; i < p->num_vcores; i++) {
+                               per_cpu_info[p->vcoremap[i]].p_to_run = p;
+                       }
+                       // set virtual core 0 to run the main context
+                       // TODO: handle silly state (HSS)
+                       per_cpu_info[p->vcoremap[0]].tf_to_pop = &p->env_tf;
+                       /* needed to make sure the writes beat the IPI.  could have done it
+                        * with other mem accesses, like grabbing a lock, and the write
+                        * would have made it */
+                       wmb_f();
                        /* There a subtle (attempted) race avoidance here.  proc_startcore
                         * can handle a death IPI, but we can't have the startcore come
                         * after the death IPI.  Otherwise, it would look like a new
                         * process.  So we hold the lock to make sure our IPI went out
-                        * before a possible death IPI.  We don't IPI ourselves, since we
-                        * need to let go of the lock.  This could change if we
-                        * process_workqueue in the interrupt handler path and do something
-                        * like light kernel threading, which ties into state bundling.
-                        * Also, we may never allow proc_run to run on a target/worker core.
-                        */
-                       // Send IPIs to everyone else involved
+                        * before a possible death IPI.
+                        * Likewise, we disable interrupts, in case one of the IPIs was for
+                        * us, and reenable them after letting go of the lock.
+                        * Note there is no guarantee this core's interrupts were on, so it
+                        * may not get the IPI for a while... */
+                       int8_t state = 0;
+                       disable_irqsave(&state);
+                       for (int i = 0; i < p->num_vcores; i++)
+                               send_ipi(p->vcoremap[i], 0, I_STARTCORE);
                        spin_unlock_irqsave(&p->proc_lock);
-                       // if (am_involved)
-                       //      proc_startcore(p, &appropriate_trapframe);
-                       // if not, need to make sure we don't return to the process's core 0
-                       panic("Unimplemented");
+                       enable_irqsave(&state);
+                       break;
                default:
                        spin_unlock_irqsave(&p->proc_lock);
                        panic("Invalid process state in proc_run()!!");
@@ -217,7 +230,7 @@ void proc_startcore(struct proc *p, trapframe_t *tf) {
         * sufficient to do it in the "new process" if-block above (could be things
         * like page faults that cause us to keep the same process, but want a
         * different context.
-        * for now, we load this silly state here. (TODO)
+        * 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);
index ec3cc49..7ab2d9e 100644 (file)
 #include <atomic.h>
 #include <sys/queue.h>
 
+// This could be useful for making scheduling decisions.  
+/* Physical coremap: each index is a physical core id, with a proc ptr for
+ * whoever *should be or is* running.  Very similar to current / curenvs[],
+ * which is what process is *really* running there. */
+struct proc *pcoremap[MAX_NUM_CPUS];
+
 void schedule_init(void)
 {
        TAILQ_INIT(&proc_runnablelist);