Yield for single and parallel processes
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 2 Oct 2009 01:09:10 +0000 (18:09 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Mon, 19 Oct 2009 00:20:02 +0000 (17:20 -0700)
Can yield cores out of order, get the appropriate ones back, and
somewhat save vcore0's state.  Currently it doesn't restart vcore0 where
it left if you stay in RUNNING_M.  We can do whatever - pending some
discussions with Ben about how we really want to handle core0.

You can do some yield testing with proctests (single core yields), and
mhello (a variety).  You'll need to program it to do whatever thing you
want, like yield out of order, then ask for more cores.  There are a
bunch of tests built in that need to be selected at compile time.

The enum is error.h is ghetto and needs a fix that works with newlib.
Right now, we conflict on error numbers (and naming, if we #define like
we ought to).

Disclaimer: doesn't compile with ivy.

15 files changed:
kern/arch/i386/nic_common.h
kern/arch/i386/rl8168.c
kern/arch/i386/trap.c
kern/include/process.h
kern/include/resource.h
kern/include/ros/error.h
kern/src/manager.c
kern/src/mm.c
kern/src/monitor.c
kern/src/process.c
kern/src/resource.c
kern/src/syscall.c
user/apps/roslib/mhello.c
user/apps/roslib/proctests.c
user/roslib/src/i386/entry.S

index 0387bcd..4fba183 100644 (file)
 #define MTU                    1500    
 #define MAX_FRAME_SIZE         ETHERNET_ENCAP_SIZE + MTU       
        
+// This is to make it simply compile when not in __NETWORK__ mode.
+#ifndef USER_MAC_ADDRESS
+#define USER_MAC_ADDRESS {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+#endif
+
 // v----- Evil line ------v
 // Hacky stuff for syscalls go away.
 
index ae1afc9..13a4ff9 100644 (file)
  * TODO: CONCURRENCY!
  */
 
-// This is to make it simply compile when not in __NETWORK__ mode.
-#ifndef USER_MAC_ADDRESS
-#define USER_MAC_ADDRESS {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
-#endif
-
 struct Descriptor
 {
     unsigned int command,  /* command/status dword */
index 001c23a..35a458d 100644 (file)
@@ -150,6 +150,9 @@ print_regs(push_regs_t *regs)
 void
 print_trapframe(trapframe_t *tf)
 {
+       static spinlock_t ptf_lock;
+
+       spin_lock_irqsave(&ptf_lock);
        cprintf("TRAP frame at %p on core %d\n", tf, core_id());
        print_regs(&tf->tf_regs);
        cprintf("  es   0x----%04x\n", tf->tf_es);
@@ -161,6 +164,7 @@ print_trapframe(trapframe_t *tf)
        cprintf("  flag 0x%08x\n", tf->tf_eflags);
        cprintf("  esp  0x%08x\n", tf->tf_esp);
        cprintf("  ss   0x----%04x\n", tf->tf_ss);
+       spin_unlock_irqsave(&ptf_lock);
 }
 
 static void
@@ -227,10 +231,9 @@ trap(trapframe_t *tf)
                panic("Trapframe with invalid CS!");
        }
 
-       /* If we're PROC_RUNNING_S, save the trapframe (which is vcore0's) in the
-        * proc's env_tf.  make sure silly state is sorted (HSS).  Consider
-        * extending this for PROC_RUNNING_M and vcore0. */
-       if (current->state == PROC_RUNNING_S) {
+       /* If we're vcore0, save the trapframe in the proc's env_tf.  make sure
+        * silly state is sorted (HSS). This applies to any RUNNING_* state. */
+       if (current->vcoremap[0] == core_id()) {
                current->env_tf = *tf;
                tf = &current->env_tf;
        }
@@ -238,7 +241,9 @@ trap(trapframe_t *tf)
        trap_dispatch(tf);
 
        // should this be if == 3?  Sort out later when we handle traps.
-       // so far we never get here
+       // so far we never get here.  managed to get here once when a MCP took two
+       // page faults and both called proc_destroy().  one of which returned since
+       // it was already DYING (though it should have waited for it's death IPI).
        assert(0);
        // Return to the current environment, which should be runnable.
        proc_startcore(current, tf); // Note the comment in syscall.c
@@ -356,10 +361,9 @@ void sysenter_init(void)
 /* This is called from sysenter's asm, with the tf on the kernel stack. */
 void sysenter_callwrapper(struct Trapframe *tf)
 {
-       /* If we're PROC_RUNNING_S, save the trapframe (which is vcore0's) in the
-        * proc's env_tf.  make sure silly state is sorted (HSS).  Consider
-        * extending this for PROC_RUNNING_M and vcore0. */
-       if (current->state == PROC_RUNNING_S) {
+       /* If we're vcore0, save the trapframe in the proc's env_tf.  make sure
+        * silly state is sorted (HSS). This applies to any RUNNING_* state. */
+       if (current->vcoremap[0] == core_id()) {
                current->env_tf = *tf;
                tf = &current->env_tf;
        }
index 2fe0e95..85e36b0 100644 (file)
@@ -69,6 +69,7 @@ void proc_run(struct proc *SAFE p);
 void proc_startcore(struct proc *SAFE p, trapframe_t *SAFE tf)
      __attribute__((noreturn));
 void proc_destroy(struct proc *SAFE p);
+void proc_yield(struct proc *SAFE p);
 
 /* Process core management.  Only call these if you are RUNNING_M or RUNNABLE_M.
  * These all adjust the vcoremap and take appropriate actions (like __startcore
@@ -98,6 +99,8 @@ void proc_set_syscall_retval(trapframe_t *SAFE tf, intreg_t value);
 error_t proc_incref(struct proc *SAFE p);
 void proc_decref(struct proc *SAFE p);
 
+void abandon_core(void);
+
 /* Active message handlers for process management */
 #ifdef __IVY__
 void __startcore(trapframe_t *tf, uint32_t srcid, struct proc *CT(1) a0,
@@ -113,5 +116,6 @@ void __death(trapframe_t *tf, uint32_t srcid, void * a0, void * a1,
 
 /* Degubbing */
 void print_idlecoremap(void);
+void print_proc_info(pid_t pid);
 
 #endif // !ROS_KERN_PROCESS_H
index d8c7f09..543eaa7 100644 (file)
@@ -15,6 +15,7 @@
 #include <arch/trap.h>
 #include <process.h>
 
+ssize_t core_request(struct proc *p);
 error_t resource_req(struct proc *p, int type, size_t amount, uint32_t flags);
 
 void print_resources(struct proc *p);
index 5662f05..41086b5 100644 (file)
@@ -9,25 +9,45 @@
 
 // define this to prevent conflicts with newlib's errno.h
 #define __error_t_defined
-typedef enum {
-       ESUCCESS = 0,            // Success
-       EFAIL,                   // Generic Failure
-       EPERM,                   // Wrong permissions
-       EDEADLOCK,               // Would cause deadlock
-       EBUSY,                   // Currently busy, try again later
-       ENOMEM,                  // No memory available
-       ENOCACHE,                // No cache available
-       EINVAL,                  // Invalid arguments
-       EFAULT,                  // Segmentation fault
-       EBADENV,                 // Bad environment 
-       ENOFREEENV,              // No free environment
-       EUNSPECIFIED,            // Unspecified
-       EMORON,                  // Moron
-       NUMERRORS,               // Total number of error codes
-} error_t;
-
-/* 
- * The special format for printk %e takes an integer 
+#if 0
+#define ESUCCESS                        0              // Success
+#define EFAIL                           1              // Generic Failure
+#define EPERM                           2              // Wrong permissions
+#define EDEADLOCK                       3              // Would cause deadlock
+#define EBUSY                           4              // Currently busy, try again later
+#define ENOMEM                          5              // No memory available
+#define ENOCACHE                        6              // No memory available
+#define EINVAL                          7              // Invalid arguments
+#define EFAULT                          8              // Segmentation fault
+#define EBADENV                                 9              // Bad environment
+#define ENOFREEENV                     10              // No free environment
+#define EUNSPECIFIED           11              // Unspecified
+#define EMORON                         12              // Moron
+#define NUMERRORS                      13              // Total number of error codes
+#endif
+
+/* this enum is ghetto, but #defining collides with newlib.  right now, we
+ * collide on names *and* numbers, which needs to be sorted.  (TODO) */
+enum {
+       ESUCCESS,
+       EFAIL,
+       EPERM,
+       EDEADLOCK,
+       EBUSY,
+       ENOMEM,
+       ENOCACHE,
+       EINVAL,
+       EFAULT,
+       EBADENV,
+       ENOFREEENV,
+       EUNSPECIFIED,
+       EMORON,
+       NUMERRORS,
+};
+
+typedef int error_t;
+
+/* The special format for printk %e takes an integer
  * error code and prints a string describing the error.
  * The integer may be positive or negative,
  * so that -ENOMEM and ENOMEM are equivalent.
@@ -46,8 +66,8 @@ static const char *NTS const (RO error_string)[NUMERRORS] =
        "Segmentation fault",
        "Bad environment",
        "No free environment",
-       "You are a moron",
        "Unspecified",
+       "You are a moron",
 };
 
 #endif // !ROS_INC_ERROR_H */
index 79122d0..c16ccb8 100644 (file)
@@ -40,41 +40,39 @@ void manager(void)
        // This is a bypass of the standard manager structure, for network use
        // If enabled, this spawns parlib_matrix, and allows the execution
        // of a remote binary to function correctly (schedule() call below)
-       //#ifdef __NETWORK__    
+       #ifdef __NETWORK__      
        if (progress++ == 0) {
                envs[0] = kfs_proc_create(kfs_lookup_path("parlib_matrix"));
                proc_set_state(envs[0], PROC_RUNNABLE_S);
                proc_run(envs[0]);
        }
        schedule();
-       //#endif 
+       #endif 
 
        switch (progress++) {
                case 0:
-                       // Here's how to do a multicored/parallel process:
+                       //p = kfs_proc_create(kfs_lookup_path("roslib_proctests"));
                        p = kfs_proc_create(kfs_lookup_path("roslib_mhello"));
                        // being proper and all:
                        spin_lock_irqsave(&p->proc_lock);
                        proc_set_state(p, PROC_RUNNABLE_S);
-                       /* // uncomment this to transition to a parallel process manually
+                       // normal single-cored way
+                       spin_unlock_irqsave(&p->proc_lock);
+                       proc_run(p);
+                       #if 0
+                       // this is how you can transition to a parallel process manually
+                       // make sure you don't proc run first
                        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; // assuming 5 are free, this is just an example
-                       spin_lock(&idle_lock); // need to grab the cores
-                       for (int i = 0; i < 5; i++) {
-                               // grab the last one on the list
-                               p->vcoremap[i] = idlecoremap[num_idlecores-1];
-                               num_idlecores--;
-                       }
-                       spin_unlock(&idle_lock);
-                       */
+                       p->resources[RES_CORES].amt_wanted = 5;
                        spin_unlock_irqsave(&p->proc_lock);
-                       proc_run(p);
+                       core_request(p);
+                       panic("This is okay");
+                       #endif
                        break;
                case 1:
-                       //panic("This is okay");
+                       #if 0
+                       panic("This is okay");
                        udelay(10000000);
                        printk("taking 3 cores from p\n");
                        for (int i = 0; i < num; i++)
@@ -93,14 +91,17 @@ void manager(void)
                        proc_set_state(envs[0], PROC_RUNNABLE_S);
                        proc_run(envs[0]);
                        break;
+                       #endif
        #ifdef __i386__
                case 2:
+                       #if 0
                        panic("Do not panic");
                        envs[0] = kfs_proc_create(kfs_lookup_path("parlib_channel_test_client"));
                        envs[1] = kfs_proc_create(kfs_lookup_path("parlib_channel_test_server"));
                        smp_call_function_single(1, run_env_handler, envs[0], 0);
                        smp_call_function_single(2, run_env_handler, envs[1], 0);
                        break;
+                       #endif
                case 3:
        #else // sparc
                case 2:
@@ -162,6 +163,8 @@ void manager(void)
                        //test_run_measurements(progress-1);
                default:
                        printk("Manager Progress: %d\n", progress);
+                       // delay if you want to test rescheduling an MCP that yielded
+                       //udelay(15000000);
                        schedule();
        }
        panic("If you see me, then you probably screwed up");
index ff3c9f2..734afdd 100644 (file)
@@ -73,7 +73,8 @@ void *mmap(struct proc *p, uintptr_t addr, size_t len, int prot, int flags,
        mmap_abort:
                // TODO: release the appropriate mm_lock
                spin_unlock_irqsave(&p->proc_lock);
-               printk("[kernel] mmap() aborted!\n");
+               // not a kernel problem, like if they ask to mmap a mapped location.
+               printd("[kernel] mmap() aborted!\n");
                // mmap's semantics.  we need a better error propagation system
                return (void*SAFE)TC(-1); // this is also ridiculous
 }
index 9d7541c..8981b05 100644 (file)
@@ -270,21 +270,30 @@ int mon_kfs_run(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf)
 
 int mon_procinfo(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf)
 {
-       if (argc != 2) {
+       if (argc < 2) {
                printk("Usage: procinfo OPTION\n");
                printk("\tidle_cores: show idle core map\n");
                printk("\trunnable: show proc_runnablelist\n");
                printk("\tresources: show resources wanted/granted for all procs\n");
+               printk("\tpid NUM: show a lot of info for proc NUM\n");
                return 1;
        }
-       if (!strcmp(argv[1], "idle_cores"))
+       if (!strcmp(argv[1], "idle_cores")) {
                print_idlecoremap();
-       else if (!strcmp(argv[1], "runnable"))
+       } else if (!strcmp(argv[1], "runnable")) {
                dump_proclist(&proc_runnablelist);
-       else if (!strcmp(argv[1], "resources"))
+       } else if (!strcmp(argv[1], "resources")) {
                print_all_resources();
-       else
+       } else if (!strcmp(argv[1], "pid")) {
+               if (argc != 3) {
+                       printk("Give me a pid number.\n");
+                       return 1;
+               }
+               print_proc_info(strtol(argv[2], 0, 0));
+       } else {
                printk("Bad option\n");
+               return 1;
+       }
        return 0;
 }
 
index c753679..0e55bbc 100644 (file)
@@ -136,10 +136,12 @@ void proc_run(struct proc *p)
                        return;
                case (PROC_RUNNABLE_S):
                        proc_set_state(p, PROC_RUNNING_S);
-                       // We will want to know where this process is running, even if it is
-                       // only in RUNNING_S.  can use the vcoremap, which makes death easy.
-                       // we may need the pcoremap entry to mark it as a RUNNING_S core, or
-                       // else update it here. (TODO) (PCORE)
+                       /* We will want to know where this process is running, even if it is
+                        * only in RUNNING_S.  can use the vcoremap, which makes death easy.
+                        * Also, this is the signal used in trap.c to know to save the tf in
+                        * env_tf.
+                        * We may need the pcoremap entry to mark it as a RUNNING_S core, or
+                        * else update it here. (TODO) (PCORE) */
                        p->num_vcores = 0;
                        p->vcoremap[0] = core_id();
                        spin_unlock_irqsave(&p->proc_lock);
@@ -190,7 +192,7 @@ void proc_run(struct proc *p)
                        break;
                default:
                        spin_unlock_irqsave(&p->proc_lock);
-                       panic("Invalid process state in proc_run()!!");
+                       panic("Invalid process state %p in proc_run()!!", p->state);
        }
 }
 
@@ -251,7 +253,7 @@ void proc_startcore(struct proc *p, trapframe_t *tf) {
                lcr3(p->env_cr3);
                // we unloaded the old cr3, so decref it (if it exists)
                // TODO: Consider moving this to wherever we really "mean to leave the
-               // process's context".
+               // process's context".  abandon_core() does this.
                if (current)
                        proc_decref(current);
                set_cpu_curenv(p);
@@ -268,6 +270,28 @@ void proc_startcore(struct proc *p, trapframe_t *tf) {
        env_pop_tf(tf);
 }
 
+/* Helper function, helps with receiving local death IPIs, for the cases when
+ * this core is running the process.  We should received an IPI shortly.  If
+ * not, odds are interrupts are disabled, which shouldn't happen while servicing
+ * syscalls. */
+static void check_for_local_death(struct proc *p)
+{
+       if (current == p) {
+               /* 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).  since we're dying anyway, it seems
+                * reasonable to turn on interrupts.  note this means all non-proc
+                * management interrupt handlers must return (which they need to do
+                * anyway), so that we get back to this point.  Eventually, we can
+                * remove the enable_irq.  think about this (TODO) */
+               enable_irq();
+               udelay(1000000);
+               panic("Waiting too long on core %d for an IPI in proc_destroy()!",
+                     core_id());
+       }
+}
+
 /*
  * Destroys the given process.  This may be called from another process, a light
  * kernel thread (no real process context), asynchronously/cross-core, or from
@@ -294,8 +318,10 @@ void proc_destroy(struct proc *p)
        size_t num_cores_freed;
        spin_lock_irqsave(&p->proc_lock);
        switch (p->state) {
-               case PROC_DYING:
-                       return; // someone else killed this already.
+               case PROC_DYING: // someone else killed this already.
+                       spin_unlock_irqsave(&p->proc_lock);
+                       check_for_local_death(p); // IPI may be on it's way.
+                       return;
                case PROC_RUNNABLE_M:
                        /* Need to reclaim any cores this proc might have, even though it's
                         * not running yet. */
@@ -329,10 +355,6 @@ 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 */
-
-                               send_active_msg_sync(p->vcoremap[i], __death, (void *SNT)0,
-                                                    (void *SNT)0, (void *SNT)0);
-
                        proc_take_allcores(p, __death);
                        break;
                default:
@@ -361,22 +383,8 @@ void proc_destroy(struct proc *p)
        if (!refcnt)
                env_free(p);
 
-       /* If we were running the process, we should have received an IPI by now.
-        * If not, odds are interrupts are disabled, which shouldn't happen while
-        * servicing syscalls. */
-       if (current == p) {
-               /* 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).  since we're dying anyway, it seems
-                * reasonable to turn on interrupts.  note this means all non-proc
-                * management interrupt handlers must return (which they need to do
-                * anyway), so that we get back to this point.  Eventually, we can
-                * remove the enable_irq.  think about this (TODO) */
-               enable_irq();
-               udelay(1000000);
-               panic("Waiting too long for an IPI in proc_destroy()!");
-       }
+       /* if our core is part of process p, then check/wait for the death IPI. */
+       check_for_local_death(p);
        return;
 }
 
@@ -409,7 +417,8 @@ static uint32_t get_busy_vcoreid(struct proc *SAFE p, uint32_t prev)
 }
 
 /* Helper function.  Find the vcoreid for a given physical core id.
- * You better hold the lock before calling this. */
+ * You better hold the lock before calling this.  If we use some sort of
+ * pcoremap, we can avoid this linear search. */
 static uint32_t get_vcoreid(struct proc *SAFE p, int32_t pcoreid)
 {
        uint32_t i;
@@ -421,6 +430,51 @@ static uint32_t get_vcoreid(struct proc *SAFE p, int32_t pcoreid)
        return i;
 }
 
+/* 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.
+ * - If RUNNING_M, you give up the current vcore (which never returns), and
+ *   adjust the amount of cores wanted/granted.
+ * - If you have only one vcore, you switch to RUNNABLE_S.
+ * - If you yield from vcore0 but are still RUNNING_M, your context will be
+ *   saved, but may not be restarted, depending on how you get that core back.
+ *   (currently)  see proc_give_cores for details.
+ * - RES_CORES amt_wanted will be the amount running after taking away the
+ *   yielder.
+ */
+void proc_yield(struct proc *SAFE p)
+{
+       spin_lock_irqsave(&p->proc_lock);
+       switch (p->state) {
+               case (PROC_RUNNING_S):
+                       proc_set_state(p, PROC_RUNNABLE_S);
+                       schedule_proc(p);
+                       break;
+               case (PROC_RUNNING_M):
+                       p->resources[RES_CORES].amt_granted = --(p->num_vcores);
+                       p->resources[RES_CORES].amt_wanted = p->num_vcores;
+                       // give up core
+                       p->vcoremap[get_vcoreid(p, core_id())] = -1;
+                       // add to idle list
+                       spin_lock(&idle_lock);
+                       idlecoremap[num_idlecores++] = core_id();
+                       spin_unlock(&idle_lock);
+                       // out of vcores?  if so, we're now a regular process
+                       if (p->num_vcores == 0) {
+                               // switch to runnable_s
+                               proc_set_state(p, PROC_RUNNABLE_S);
+                               schedule_proc(p);
+                       }
+                       break;
+               default:
+                       // there are races that can lead to this (async death, preempt, etc)
+                       panic("Weird state(0x%08x) in sys_yield", p->state);
+       }
+       spin_unlock_irqsave(&p->proc_lock);
+       // clean up the core and idle.  for mgmt cores, they will ultimately call
+       // manager, which will call schedule(), which will repick the yielding proc.
+       abandon_core();
+}
+
 /* Gives process p the additional num cores listed in corelist.  You must be
  * RUNNABLE_M or RUNNING_M before calling this.  If you're RUNNING_M, this will
  * startup your new cores at the entry point with their virtual IDs.  If you're
@@ -484,8 +538,15 @@ error_t proc_give_cores(struct proc *SAFE p, uint32_t corelist[], size_t *num)
                                p->num_vcores++;
                                // TODO: careful of active message deadlock (AMDL)
                                assert(corelist[i] != core_id()); // sanity
-                               send_active_msg_sync(corelist[i], __startcore, (uint32_t)p,
-                                                    0, free_vcoreid);
+                               /* if we want to allow yielding of vcore0 and restarting it at
+                                * its yield point *while still RUNNING_M*, uncomment this */
+                               /*
+                               if (i == 0)
+                                       send_active_msg_sync(p->vcoremap[0], __startcore,
+                                                            (uint32_t)p, (uint32_t)&p->env_tf, 0);
+                               else */
+                               send_active_msg_sync(corelist[i], __startcore, p,
+                                                    (void*)0, (void*)free_vcoreid);
                        }
                        break;
                default:
@@ -673,12 +734,9 @@ void __startcore(trapframe_t *tf, uint32_t srcid, void * a0, void * a1,
        proc_startcore(p_to_run, tf_to_pop);
 }
 
-/* Active message handler to stop running whatever context is on this core and
- * to idle.  Note this leaves no trace of what was running.
- * It's okay if death comes to a core that's already idling and has no current.
- * It could happen if a process decref'd before proc_startcore could incref. */
-void __death(trapframe_t *tf, uint32_t srcid, void *SNT a0, void *SNT a1,
-             void *SNT a2)
+/* Stop running whatever context is on this core and to 'idle'.  Note this
+ * leaves no trace of what was running. This "leaves the process's context. */
+void abandon_core(void)
 {
        /* If we are currently running an address space on our core, we need a known
         * good pgdir before releasing the old one.  This is currently the major
@@ -692,6 +750,15 @@ void __death(trapframe_t *tf, uint32_t srcid, void *SNT a0, void *SNT a1,
        }
        smp_idle();
 }
+/* Active message handler to clean up the core when a process is dying.
+ * Note this leaves no trace of what was running.
+ * It's okay if death comes to a core that's already idling and has no current.
+ * It could happen if a process decref'd before proc_startcore could incref. */
+void __death(trapframe_t *tf, uint32_t srcid, void *SNT a0, void *SNT a1,
+             void *SNT a2)
+{
+       abandon_core();
+}
 
 void print_idlecoremap(void)
 {
@@ -701,3 +768,38 @@ void print_idlecoremap(void)
                printk("idlecoremap[%d] = %d\n", i, idlecoremap[i]);
        spin_unlock(&idle_lock);
 }
+
+void print_proc_info(pid_t pid)
+{
+       int j = 0;
+       struct proc *p = 0;
+       envid2env(pid, &p, 0);
+       // not concerned with a race on the state...
+       if ((!p) || (p->state == ENV_FREE)) {
+               printk("Bad PID.\n");
+               return;
+       }
+       spin_lock_irqsave(&p->proc_lock);
+       printk("PID: %d\n", p->env_id);
+       printk("PPID: %d\n", p->env_parent_id);
+       printk("State: 0x%08x\n", p->state);
+       printk("Runs: %d\n", p->env_runs);
+       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->num_vcores);
+       printk("Vcoremap:\n");
+       for (int i = 0; i < p->num_vcores; i++) {
+               j = get_busy_vcoreid(p, j);
+               printk("\tVcore %d: Pcore %d\n", j, p->vcoremap[j]);
+               j++;
+       }
+       printk("Resources:\n");
+       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_irqsave(&p->proc_lock);
+}
+
index 3571841..6ccbdaf 100644 (file)
@@ -21,7 +21,7 @@
  *
  * Will return either the number actually granted or an error code.
  */
-static ssize_t core_request(struct proc *p)
+ssize_t core_request(struct proc *p)
 {
        size_t num_granted;
        ssize_t amt_new;
@@ -96,9 +96,9 @@ static ssize_t core_request(struct proc *p)
                        proc_run(p);
                /* if we are moving to a partitionable core from a RUNNING_S on a
                 * management core, the kernel needs to do something else on this core
-                * (just like in proc_destroy).  __death cleans up the core and idles. */
+                * (just like in proc_destroy).  this cleans up the core and idles. */
                if (need_to_idle)
-                       __death(0, 0, 0, 0, 0);
+                       abandon_core();
        } else { // nothing granted, just return
                spin_unlock_irqsave(&p->proc_lock);
        }
index 566ad81..cfa98bc 100644 (file)
@@ -31,8 +31,6 @@ extern char *CT(PACKET_HEADER_SIZE + len) (*packet_wrap)(const char *CT(len) dat
 extern int (*send_frame)(const char *CT(len) data, size_t len);
 #endif
 
-static void sys_yield(struct proc *p);
-
 //Do absolutely nothing.  Used for profiling.
 static void sys_null(void)
 {
@@ -92,8 +90,7 @@ static ssize_t sys_run_binary(env_t* e, void *DANGEROUS binary_buf,
        kfree(new_binary);
        proc_set_state(env, PROC_RUNNABLE_S);
        schedule_proc(env);
-       sys_yield(e);
-
+       proc_yield(e); // changed from sys_yield.  did not test this at all.
        return 0;
 }
 
@@ -357,31 +354,6 @@ static error_t sys_env_destroy(env_t* e, envid_t envid)
 }
 
 /*
- * Current process yields its remaining "time slice".  Currently works for
- * single-core processes.
- * TODO: think about how this works with async calls and multicored procs.
- * Want it to only be callable locally.
- */
-static void sys_yield(struct proc *p)
-{
-       // This is all standard single-core, local call
-       spin_lock_irqsave(&p->proc_lock);
-       assert(p->state == PROC_RUNNING_S);
-       proc_set_state(p, PROC_RUNNABLE_S);
-       schedule_proc(p);
-       spin_unlock_irqsave(&p->proc_lock);
-       // the implied thing here is that all state has been saved before leaving
-       // could do the "leaving the process context" here, mentioned in startcore
-       schedule();
-
-       /* TODO
-        * if running_s, give up your time slice (schedule, save silly state, block)
-        * if running_m and 2+ cores are left, give yours up, stay running_m
-        * if running_m and last core, switch to runnable_s
-        */
-}
-
-/*
  * Creates a process found at the user string 'path'.  Currently uses KFS.
  * Not runnable by default, so it needs it's status to be changed so that the
  * next call to schedule() will try to run it.
@@ -488,7 +460,7 @@ intreg_t syscall(struct proc *p, trapframe_t *tf, uintreg_t syscallno,
                case SYS_proc_destroy:
                        return sys_env_destroy(p, (envid_t)a1);
                case SYS_yield:
-                       sys_yield(p);
+                       proc_yield(p);
                        return ESUCCESS;
                case SYS_proc_create:
                        return sys_proc_create(p, (char *DANGEROUS)a1);
index 41ea04d..33e5d73 100644 (file)
@@ -1,6 +1,7 @@
 #include <lib.h>
 #include <ros/mman.h>
 #include <ros/resource.h>
+#include <syswrapper.h>
 #include <stdio.h>
 
 // ghetto udelay, put in a lib somewhere and export the tsc freq
@@ -19,41 +20,132 @@ void udelay(uint64_t usec, uint64_t tsc_freq)
        return;
 }
 
+#define TEST_MMAP                                      1
+#define TEST_ONE_CORE                          2
+#define TEST_ASK_FOR_TOO_MANY_CORES    3
+#define TEST_INCREMENTAL_CHANGES       4
+#define TEST_YIELD_OUT_OF_ORDER                5
+#define TEST_YIELD_0_OUT_OF_ORDER      6
+#define TEST_SWITCH_TO_RUNNABLE_S      7
+#define TEST_CRAZY_YIELDS                      8
+#define TEST_CONCURRENT_SYSCALLS       9
+
 int main(int argc, char** argv)
 {
        uint32_t vcoreid;
+       error_t retval;
+
+       int test = TEST_INCREMENTAL_CHANGES;
+
+       static int first_time = 1; // used by vcore2
+
        if ((vcoreid = newcore())) {
                cprintf("Hello from vcore %d\n", vcoreid);
-       } else {
+       } else { // core 0
                cprintf("Hello from else vcore 0\n");
-               void* addr;
                cprintf("Multi-Goodbye, world, from PID: %d!\n", sys_getpid());
-               /*
-               addr = sys_mmap((void*SNT)USTACKTOP - 20*PGSIZE, 8*PGSIZE, 3, MAP_FIXED, 0, 0);
-               cprintf("got addr = 0x%08x\n", addr);
-               *(int*)addr = 0xdeadbeef;
-               *(int*)(addr + 3*PGSIZE) = 0xcafebabe;
-               // these should work
-               cprintf("reading addr: 0x%08x\n", *(int*)addr);
-               cprintf("reading addr+3pg: 0x%08x\n", *(int*)(addr + 3*PGSIZE));
-               // this should fault
-               // *(int*)(addr - 3*PGSIZE) = 0xdeadbeef;
-               */
-               sys_resource_req(RES_CORES, 3, 0);
+               switch (test) {
+                       case TEST_MMAP:
+                               cprintf("Testing MMAP\n");
+                               void* addr;
+                               addr = sys_mmap((void*SNT)USTACKTOP - 20*PGSIZE, 8*PGSIZE, 3,
+                                               MAP_FIXED, 0, 0);
+                               cprintf("got addr = 0x%08x\n", addr);
+                               *(int*)addr = 0xdeadbeef;
+                               *(int*)(addr + 3*PGSIZE) = 0xcafebabe;
+                               // these should work
+                               cprintf("reading addr: 0x%08x\n", *(int*)addr);
+                               cprintf("reading addr+3pg: 0x%08x\n", *(int*)(addr + 3*PGSIZE));
+                               // this should fault
+                               cprintf("Should page fault and die now.\n");
+                               *(int*)(addr - 3*PGSIZE) = 0xdeadbeef;
+                               cprintf("Should not see me!!!!!!!!!!!!!!!!!!\n");
+                               while(1);
+                       case TEST_ONE_CORE:
+                               retval = sys_resource_req(RES_CORES, 1, 0);
+                               cprintf("One core test's core0's retval: %d\n", retval);
+                               cprintf("Check to see it's on a worker core.\n");
+                               while(1);
+                       case TEST_ASK_FOR_TOO_MANY_CORES:
+                               retval = sys_resource_req(RES_CORES, 12, 0);
+                               cprintf("Asked for too many, retval: %d\n", retval);
+                               return 0;
+                       case TEST_INCREMENTAL_CHANGES:
+                               retval = sys_resource_req(RES_CORES, 4, 0);
+                               break;
+                       default:
+                               retval = sys_resource_req(RES_CORES, 7, 0);
+               }
+               cprintf("Should see me if you want to relocate core0's context "
+                       "when moving from RUNNING_S\n");
+       }
+       if ((vcoreid == 2) && first_time) {
+               first_time = 0;
+               switch (test) {
+                       case TEST_INCREMENTAL_CHANGES:
+                               // Testing asking for less than we already have
+                               udelay(1000000, 1995014570); // KVM's freq.  Whatever.
+                               cprintf("Asking for too few:\n");
+                               retval = sys_resource_req(RES_CORES, 2, 0);
+                               cprintf("Should be -EINVAL(7): %d\n", retval);
+                               // Testing getting more while running
+                               cprintf("Asking for more while running:\n");
+                               udelay(1000000, 1995014570);
+                               retval = sys_resource_req(RES_CORES, 7, 0);
+                               cprintf("core2's retval: %d\n", retval);
+                               break;
+                       case TEST_YIELD_OUT_OF_ORDER:
+                               cprintf("Core %d yielding\n", vcoreid);
+                               yield();
+                               break;
+                       case TEST_YIELD_0_OUT_OF_ORDER:
+                               udelay(5000000, 1995014570);
+                               cprintf("Core 0 should have yielded, asking for another\n");
+                               retval = sys_resource_req(RES_CORES, 7, 0);
+               }
+       }
+       if (vcoreid == 0) {
+               switch (test) {
+                       case TEST_YIELD_OUT_OF_ORDER:
+                               udelay(10000000, 1995014570);
+                               cprintf("Core 2 should have yielded, asking for another\n");
+                               retval = sys_resource_req(RES_CORES, 7, 0);
+                               break;
+                       case TEST_YIELD_0_OUT_OF_ORDER:
+                               udelay(5000000, 1995014570);
+                               cprintf("Core %d yielding\n", vcoreid);
+                               yield();
+                               cprintf("Core 0 came back where it left off in RUNNING_M!!!\n");
+                               break;
+               }
        }
-       // request a couple more (RUNNING_M when the request comes in)
-       if (vcoreid == 2)
-               sys_resource_req(RES_CORES, 5, 0);
-       /*
-       // heavy concurrent syscall tests
-       for (int i = 0; i < 10; i++) {
-               for (int j = 0; j < 100; j++)
-                       sys_null();
-               cprintf("Hello from vcore %d, iteration %d\n", vcoreid, i);
+       /* This assumes the "core0 is the one who gets saved" style */
+       switch (test) {
+               case TEST_SWITCH_TO_RUNNABLE_S:
+                       if (vcoreid) {
+                               yield();
+                       } else {
+                               udelay(5000000, 1995014570);
+                               cprintf("Core %d yielding\n", vcoreid);
+                               yield();
+                               cprintf("Should see me if you are ever scheduled again.\n");
+                       }
+                       while(1);
+               case TEST_CRAZY_YIELDS:
+                       udelay(300000*vcoreid, 1995014570);
+                       sys_resource_req(RES_CORES, 7, 0);
+                       yield();
+                       cprintf("should  never see me, unless you slip into *_S\n");
+                       break;
+               case TEST_CONCURRENT_SYSCALLS:
+                       for (int i = 0; i < 10; i++) {
+                               for (int j = 0; j < 100; j++)
+                                       sys_null();
+                               cprintf("Hello from vcore %d, iteration %d\n", vcoreid, i);
+                       }
+                       break;
        }
-       */
        cprintf("Vcore %d Done!\n", vcoreid);
        while (1);
-       udelay(5000000, 1995014570); // KVM's freq.  Whatever.
        return 0;
 }
index fe12755..37fbe9b 100644 (file)
@@ -3,10 +3,26 @@
 #include <syswrapper.h>
 #include <arch/arch.h>
 
+/* This runs a variety of process tests.  For now, it just tests single-core
+ * yielding among a bunch of processes (which it creates).  It needs the
+ * manager() to call schedule repeatedly (not panic at some weird point) for it
+ * to make progress. */
 int main(int argc, char** argv)
 {
        int pid = sys_getpid();
-       cprintf("Process %x, Starting and yielding.\n", pid);
+       /* first instance.  this is ghetto, since it relies on being the first proc
+        * ever.  fix this when we can pass arguments.  (TODO) */
+       #define NUM_KIDS 5
+       int child_pid[NUM_KIDS];
+       if (pid == 0x1000) {
+               for (int i = 0; i < NUM_KIDS; i++)
+                       child_pid[i] = proc_create("roslib_proctests");
+               for (int i = 0; i < NUM_KIDS; i++) {
+                       cprintf("U: attempting to spawn yielders (pid: %d)\n", child_pid[i]);
+                       proc_run(child_pid[i]);
+               }
+       }
+       cprintf("Process %x, Started and yielding.\n", pid);
        yield();
        cprintf("Process %x, Return from yield1, starting yield2.\n", pid);
        yield();
index 8e036ee..7adcac1 100644 (file)
@@ -64,8 +64,13 @@ new_core:
        movl $mmap_args, %edi
        mov $SYS_mmap, %eax
        int $T_SYSCALL
-       addl $PGSIZE, %eax
-       movl %eax, %esp
+       // by using edx instead of eax, we're assuming our request was satisfied.
+       // eax has the actual address we can use.  since we would have used eax
+       // without checking, it would fail anyways, and now we can handle mmap
+       // aborting in the event a stack is already allocated here.  this is all
+       // extremely dangerous to do, so be careful...
+       addl $PGSIZE, %edx
+       movl %edx, %esp
        movl %esi, %eax // restore the vcoreid
        call main // main will check to see if it came from here or not
        // spin when we return.  ought to yield before this ever happens