Process running / destruction outline
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 11 Aug 2009 02:14:41 +0000 (19:14 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 12 Aug 2009 19:43:28 +0000 (12:43 -0700)
Sorts out a decent outline for how proc_run, proc_startcore, and
proc_destroy interact, given multicored processes.  The state
transitions should be good with regard to a lot of races.  cr3's are
protected with incref.

Schedule related functions have a better interface, and processes now
sit on TAILQs (lists) and can move around between lists (like a runnable
list).

Things to keep in mind: when do we leave a process's context, and when
do we need to bundle kernel state (stack) so we can "restart" kernel
processing later?  Among other things.

22 files changed:
kern/arch/i386/atomic.h
kern/arch/i386/env.c
kern/arch/i386/smp_boot.c
kern/arch/i386/trap.c
kern/arch/i386/trapentry.S
kern/arch/sparc/trap.c
kern/include/env.h
kern/include/process.h
kern/include/sched.h [deleted file]
kern/include/schedule.h [new file with mode: 0644]
kern/include/smp.h
kern/src/Makefrag
kern/src/env.c
kern/src/manager.c
kern/src/monitor.c
kern/src/pmap.c
kern/src/process.c
kern/src/schedule.c [new file with mode: 0644]
kern/src/smp.c
kern/src/syscall.c
kern/src/testing.c
kern/src/workqueue.c

index 932887c..a01aff6 100644 (file)
@@ -8,6 +8,7 @@
 #define wmb() 
 
 typedef void* atomic_t;
+typedef volatile uint32_t spinlock_t;
 
 static inline void atomic_init(atomic_t *number, int32_t val);
 static inline int32_t atomic_read(atomic_t *number);
index 42df5a8..986b96a 100644 (file)
@@ -44,6 +44,7 @@ void env_pop_tf(trapframe_t *tf)
                              "popfl;                   "
                              "movl %%ebp, %%ecx;       "
                              "movl %%esi, %%edx;       "
+                             "sti;                     "
                              "sysexit                  "
                              : : "g" (tf) : "memory");
                panic("sysexit failed");  /* mostly to placate the compiler */
index 5a7fd8f..37dafba 100644 (file)
@@ -23,6 +23,7 @@
 #include <pmap.h>
 #include <env.h>
 #include <trap.h>
+#include <timing.h>
 
 extern handler_wrapper_t handler_wrappers[NUM_HANDLER_WRAPPERS];
 volatile uint8_t num_cpus = 0xee;
index 472c238..02af765 100644 (file)
@@ -186,7 +186,7 @@ static void
                                panic("Damn Damn!  Unhandled trap in the kernel!");
                        else {
                                warn("Unexpected trap from userspace");
-                               env_destroy(current);
+                               proc_destroy(current);
                                return;
                        }
        }
@@ -210,6 +210,8 @@ void
 {
        //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.
        env_push_ancillary_state(current);
 
        if ((tf->tf_cs & ~3) != GD_UT && (tf->tf_cs & ~3) != GD_KT) {
@@ -327,7 +329,7 @@ page_fault_handler(trapframe_t *tf)
        cprintf("[%08x] user fault va %08x ip %08x from core %d\n",
                current->env_id, fault_va, tf->tf_eip, core_id());
        print_trapframe(tf);
-       env_destroy(current);
+       proc_destroy(current);
 }
 
 void sysenter_init(void)
index 95c341a..3087851 100644 (file)
@@ -207,4 +207,5 @@ sysenter_handler:
        popfl                                   # restore EFLAGS
        movl %ebp, %ecx
        movl %esi, %edx
+       sti                                             # interrupts are turned off when starting a core
        sysexit
index 92714f6..704ef11 100644 (file)
@@ -142,7 +142,7 @@ unhandled_trap(trapframe_t* state)
        {
                warn("Unhandled trap in user!\nTrap type: %s",buf);
                assert(current);
-               env_destroy(current);
+               proc_destroy(current);
                panic("I shouldn't have gotten here!");
        }
 }
index 097ffd7..6089e0d 100644 (file)
@@ -41,8 +41,8 @@ typedef int32_t envid_t;
 
 // TODO: clean this up.
 struct Env {
-       LIST_ENTRY(Env) env_link NOINIT;        // Free list link pointers
-       uint32_t lock;
+       TAILQ_ENTRY(Env) proc_link NOINIT;      // Free list link pointers
+       spinlock_t proc_lock;
        trapframe_t env_tf                                              // Saved registers
          __attribute__((aligned (8)));                 // for sparc --asw
        ancillary_state_t env_ancillary_state   // State saved when descheduled
@@ -78,9 +78,6 @@ extern env_t *COUNT(NENV) envs;               // All environments
 extern atomic_t num_envs;              // Number of envs
 extern env_t* NORACE curenvs[MAX_NUM_CPUS];
 
-LIST_HEAD(env_list, Env);              // Declares 'struct env_list'
-typedef struct env_list env_list_t;
-
 void   env_init(void);
 int            env_alloc(env_t *SAFE*SAFE e, envid_t parent_id);
 void   env_init_trapframe(env_t* e);
@@ -89,12 +86,7 @@ void env_push_ancillary_state(env_t* e);
 void   env_pop_ancillary_state(env_t* e);
 void   env_free(env_t *SAFE e);
 void   env_user_mem_free(env_t* e);
-error_t        env_incref(env_t* e);
-void   env_decref(env_t *SAFE e);
 env_t* env_create(uint8_t *COUNT(size) binary, size_t size);
-void   (IN_HANDLER env_destroy)(env_t *SAFE e);        // Does not return if e == curenv
-// Temporary scheduler function
-void   schedule(void);
 
 /*
  * Allows the kernel to figure out what process is running on its core.
@@ -104,21 +96,10 @@ void       schedule(void);
 
 int    envid2env(envid_t envid, env_t **env_store, bool checkperm);
 // The following three functions do not return
-void   (IN_HANDLER env_run)(env_t *e) __attribute__((noreturn));
 void   env_pop_tf(trapframe_t *tf) __attribute__((noreturn));
 
 
 /* Helper handler for smp_call to dispatch jobs to other cores */
 void run_env_handler(trapframe_t *tf, void* data);
 
-// TODO remove this legacy crap
-#define ENV_CREATE(x)                  ({                                             \
-       extern uint8_t _binary_obj_user_apps_##x##_start[],                        \
-               _binary_obj_user_apps_##x##_size[];                                    \
-       env_t *e = env_create(_binary_obj_user_apps_##x##_start,                   \
-               (int)_binary_obj_user_apps_##x##_size);                                \
-       proc_set_state(e, PROC_RUNNABLE_S);                                        \
-       e;                                                                         \
-})
-
 #endif // !ROS_KERN_ENV_H
index 4975a15..5f7868a 100644 (file)
@@ -11,6 +11,7 @@
 #define ROS_KERN_PROCESS_H
 
 #include <arch/types.h>
+#include <atomic.h>
 
 /* Process States.  Not 100% on the names yet. */
 #define PROC_CREATED                   0x01
 // Till we remove the old struct Env
 #define proc Env
 
+TAILQ_HEAD(proc_list, proc);           // Declares 'struct proc_list'
+extern struct proc_list proc_freelist;
+extern spinlock_t freelist_lock;
+extern struct proc_list proc_runnablelist;
+extern spinlock_t runnablelist_lock;
+
 int proc_set_state(struct proc *p, uint32_t state) WRITES(p->state);
 struct proc *get_proc(unsigned pid);
 bool proc_controls(struct proc *actor, struct proc *target);
+void proc_run(struct proc *p);
 void proc_startcore(struct proc *p, trapframe_t *tf) __attribute__((noreturn));
+void proc_destroy(struct proc *SAFE p);
 
+/* The reference counts are mostly to track how many cores loaded the cr3 */
+error_t proc_incref(struct proc *SAFE p);
+void proc_decref(struct proc *SAFE p);
 #endif // !ROS_KERN_PROCESS_H
diff --git a/kern/include/sched.h b/kern/include/sched.h
deleted file mode 100644 (file)
index d751fb5..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-/* See COPYRIGHT for copyright information. */
-
-#ifndef ROS_KERN_SCHED_H
-#define ROS_KERN_SCHED_H
-#ifndef ROS_KERNEL
-# error "This is a ROS kernel header; user programs should not #include it"
-#endif
-
-// This function does not return.
-void sched_yield(void) __attribute__((noreturn));
-
-#endif // !ROS_KERN_SCHED_H
diff --git a/kern/include/schedule.h b/kern/include/schedule.h
new file mode 100644 (file)
index 0000000..7270df1
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2009 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Scheduling and dispatching.
+ */
+
+#ifndef ROS_KERN_SCHEDULE_H
+#define ROS_KERN_SCHEDULE_H
+
+#include <process.h>
+
+void schedule_init(void);
+
+/*
+ * Add a process to the runnable list.  If you do this, do not manually env_run
+ * the process.  It must be selected with schedule().  This also applies to
+ * smp_call_function to indirectly call env_run.  Currently, this is always
+ * called with changing the state to RUNNABLE_S, but in the future, it might
+ * need a RUNNABLE_M instead - but one or the other should be done before
+ * calling this.
+ */
+void schedule_proc(struct proc *p);
+
+/* Rip a process out of the runnable list */
+void deschedule_proc(struct proc *p);
+
+/* Pick and run a process.  Note that this can return. */
+void schedule(void);
+
+/* Debugging */
+void dump_proclist(struct proc_list *list);
+
+#endif // !ROS_KERN_SCHEDULE_H
index d8c1fb0..29d1f92 100644 (file)
@@ -18,6 +18,7 @@
 // will want this padded out to cacheline alignment
 struct per_cpu_info {
        uint32_t lock;
+       bool preempt_pending;
        struct workqueue workqueue;
 };
 extern struct per_cpu_info  per_cpu_info[MAX_NUM_CPUS];
index 9723732..7a1fae5 100644 (file)
@@ -30,6 +30,7 @@ KERN_SRCFILES := $(KERN_ARCH_SRCFILES) \
                  $(KERN_SRC_DIR)/kfs.c \
                  $(KERN_SRC_DIR)/process.c \
                  $(KERN_SRC_DIR)/kmalloc.c \
+                 $(KERN_SRC_DIR)/schedule.c \
                  $(KERN_SRC_DIR)/testing.c
 
 # Only build files if they exist.
index 2e9cf85..39c972e 100644 (file)
@@ -1,6 +1,5 @@
 /* See COPYRIGHT for copyright information. */
 #ifdef __DEPUTY__
-//#pragma nodeputy
 #pragma noasync
 #endif
 
@@ -18,6 +17,7 @@
 #include <monitor.h>
 #include <manager.h>
 #include <stdio.h>
+#include <schedule.h>
 
 #include <ros/syscall.h>
 #include <ros/error.h>
@@ -29,7 +29,6 @@ atomic_t num_envs;
 // A lot of the Env business, including this and its usage, will change when we
 // redesign the env as a multi-process.
 env_t* curenvs[MAX_NUM_CPUS] = {[0 ... (MAX_NUM_CPUS-1)] NULL};
-static env_list_t env_free_list;       // Free list
 
 #define ENVGENSHIFT    12              // >= LOGNENV
 
@@ -80,7 +79,7 @@ envid2env(envid_t envid, env_t **env_store, bool checkperm)
 
 //
 // Mark all environments in 'envs' as free, set their env_ids to 0,
-// and insert them into the env_free_list.
+// and insert them into the proc_freelist.
 // Insert in reverse order, so that the first call to env_alloc()
 // returns envs[0].
 // TODO: get rid of this whole array bullshit
@@ -90,14 +89,15 @@ env_init(void)
 {
        int i;
 
+       schedule_init();
        atomic_init(&num_envs, 0);
-       LIST_INIT(&env_free_list);
+       TAILQ_INIT(&proc_freelist);
        assert(envs != NULL);
        for (i = NENV-1; i >= 0; i--) { TRUSTEDBLOCK // asw ivy workaround
                // these should already be set from when i memset'd the array to 0
                envs[i].state = ENV_FREE;
                envs[i].env_id = 0;
-               LIST_INSERT_HEAD(&env_free_list, &envs[i], env_link);
+               TAILQ_INSERT_HEAD(&proc_freelist, &envs[i], proc_link);
        }
 }
 
@@ -117,19 +117,19 @@ WRITES(e->env_pgdir, e->env_cr3, e->env_procinfo, e->env_procdata)
 {
        int i, r;
        page_t *pgdir = NULL;
-       page_t *pginfo[PROCINFO_NUM_PAGES] = {NULL}; 
+       page_t *pginfo[PROCINFO_NUM_PAGES] = {NULL};
        page_t *pgdata[PROCDATA_NUM_PAGES] = {NULL};
        static page_t* shared_page = 0;
 
-       /* 
-        * First, allocate a page for the pgdir of this process and up 
+       /*
+        * First, allocate a page for the pgdir of this process and up
         * its reference count since this will never be done elsewhere
         */
        r = page_alloc(&pgdir);
        if(r < 0) return r;
        pgdir->pp_ref++;
 
-       /* 
+       /*
         * Next, set up the e->env_pgdir and e->env_cr3 pointers to point
         * to this newly allocated page and clear its contents
         */
@@ -137,7 +137,7 @@ WRITES(e->env_pgdir, e->env_cr3, e->env_procinfo, e->env_procdata)
        e->env_pgdir = (pde_t *COUNT(NPDENTRIES)) TC(page2kva(pgdir));
        e->env_cr3 =   (physaddr_t) TC(page2pa(pgdir));
 
-       /* 
+       /*
         * Now start filling in the pgdir with mappings required by all newly
         * created address spaces
         */
@@ -156,18 +156,18 @@ WRITES(e->env_pgdir, e->env_cr3, e->env_procinfo, e->env_procdata)
        e->env_pgdir[PDX(UVPT)] = PTE(PPN(e->env_cr3), PTE_P | PTE_USER_RO);
 
        /*
-        * Now allocate and insert all pages required for the shared 
+        * Now allocate and insert all pages required for the shared
         * procinfo structure into the page table
         */
        for(int i=0; i<PROCINFO_NUM_PAGES; i++) {
-               if(page_alloc(&pginfo[i]) < 0) 
+               if(page_alloc(&pginfo[i]) < 0)
                        goto env_setup_vm_error;
                if(page_insert(e->env_pgdir, pginfo[i], (void*SNT)(UINFO + i*PGSIZE), PTE_USER_RO) < 0)
                        goto env_setup_vm_error;
        }
-       
+
        /*
-        * Now allocate and insert all pages required for the shared 
+        * Now allocate and insert all pages required for the shared
         * procdata structure into the page table
         */
        for(int i=0; i<PROCDATA_NUM_PAGES; i++) {
@@ -177,18 +177,18 @@ WRITES(e->env_pgdir, e->env_cr3, e->env_procinfo, e->env_procdata)
                        goto env_setup_vm_error;
        }
 
-       /* 
-        * Now, set e->env_procinfo, and e->env_procdata to point to 
+       /*
+        * Now, set e->env_procinfo, and e->env_procdata to point to
         * the proper pages just allocated and clear them out.
         */
        e->env_procinfo = (procinfo_t *SAFE) TC(page2kva(pginfo[0]));
        e->env_procdata = (procdata_t *SAFE) TC(page2kva(pgdata[0]));
-       
+
        memset(e->env_procinfo, 0, sizeof(procinfo_t));
        memset(e->env_procdata, 0, sizeof(procdata_t));
-       
-       /* Finally, set up the Global Shared Data page for all processes.  
-        * Can't be trusted, but still very useful at this stage for us.  
+
+       /* Finally, set up the Global Shared Data page for all processes.
+        * Can't be trusted, but still very useful at this stage for us.
         * Consider removing when we have real processes.
         * (TODO).  Note the page is alloced only the first time through
         */
@@ -207,7 +207,7 @@ WRITES(e->env_pgdir, e->env_cr3, e->env_procinfo, e->env_procdata)
 
        return 0;
 
-env_setup_vm_error:    
+env_setup_vm_error:
        page_free(shared_page);
        for(int i=0; i< PROCDATA_NUM_PAGES; i++) {
                page_free(pgdata[i]);
@@ -235,14 +235,25 @@ env_alloc(env_t **newenv_store, envid_t parent_id)
        int r;
        env_t *e;
 
-       if (!(e = LIST_FIRST(&env_free_list)))
+       spin_lock(&freelist_lock);
+       e = TAILQ_FIRST(&proc_freelist);
+       if (e) {
+               TAILQ_REMOVE(&proc_freelist, e, proc_link);
+               spin_unlock(&freelist_lock);
+       } else {
+               spin_unlock(&freelist_lock);
                return -ENOFREEENV;
-       
+       }
+
     { INITSTRUCT(*e)
 
        // Allocate and set up the page directory for this environment.
-       if ((r = env_setup_vm(e)) < 0)
+       if ((r = env_setup_vm(e)) < 0) {
+               spin_lock(&freelist_lock);
+               TAILQ_INSERT_HEAD(&proc_freelist, e, proc_link);
+               spin_unlock(&freelist_lock);
                return r;
+       }
 
        // Generate an env_id for this environment.
        generation = (e->env_id + (1 << ENVGENSHIFT)) & ~(NENV - 1);
@@ -251,7 +262,7 @@ env_alloc(env_t **newenv_store, envid_t parent_id)
        e->env_id = generation | (e - envs);
 
        // Set the basic status variables.
-    e->lock = 0;
+    e->proc_lock = 0;
        e->env_parent_id = parent_id;
        proc_set_state(e, PROC_CREATED);
        e->env_runs = 0;
@@ -263,30 +274,28 @@ env_alloc(env_t **newenv_store, envid_t parent_id)
        memset(&e->env_tf, 0, sizeof(e->env_tf));
        env_init_trapframe(e);
 
-       /* 
+       /*
         * Initialize the contents of the e->env_procinfo structure
         */
         e->env_procinfo->id = (e->env_id & 0x3FF);
-        
-       /* 
+
+       /*
         * Initialize the contents of the e->env_procdata structure
         */
        // Initialize the generic syscall ring buffer
        SHARED_RING_INIT(&e->env_procdata->syscallring);
        // Initialize the backend of the syscall ring buffer
-       BACK_RING_INIT(&e->syscallbackring, 
-                      &e->env_procdata->syscallring, 
+       BACK_RING_INIT(&e->syscallbackring,
+                      &e->env_procdata->syscallring,
                       SYSCALLRINGSIZE);
-                      
+
        // Initialize the generic sysevent ring buffer
        SHARED_RING_INIT(&e->env_procdata->syseventring);
        // Initialize the frontend of the sysevent ring buffer
-       FRONT_RING_INIT(&e->syseventfrontring, 
-                       &e->env_procdata->syseventring, 
+       FRONT_RING_INIT(&e->syseventfrontring,
+                       &e->env_procdata->syseventring,
                        SYSEVENTRINGSIZE);
 
-       // commit the allocation
-       LIST_REMOVE(e, env_link);
        *newenv_store = e;
        atomic_inc(&num_envs);
 
@@ -378,7 +387,7 @@ load_icode(env_t *SAFE e, uint8_t *COUNT(size) binary, size_t size)
         * This can get a bit tricky if this code blocks (will need to think about a
         * decref then), if we try to change states, etc.
         */
-       env_incref(e);
+       proc_incref(e);
        lcr3(e->env_cr3);
 
        // TODO: how do we do a runtime COUNT?
@@ -403,7 +412,7 @@ load_icode(env_t *SAFE e, uint8_t *COUNT(size) binary, size_t size)
 
        // reload the original address space
        lcr3(old_cr3);
-       env_decref(e);
+       proc_decref(e);
 }
 
 //
@@ -414,8 +423,8 @@ env_t* env_create(uint8_t *binary, size_t size)
        env_t *e;
        int r;
        envid_t curid;
-       
-       curid = (current ? current->env_id : 0);        
+
+       curid = (current ? current->env_id : 0);
        if ((r = env_alloc(&e, curid)) < 0)
                panic("env_create: %e", r);
        load_icode(e, binary, size);
@@ -432,7 +441,7 @@ env_free(env_t *e)
 
        // Note the environment's demise.
        printk("[%08x] free env %08x\n", current ? current->env_id : 0, e->env_id);
-       // All parts of the kernel should have decref'd before env_free was called. 
+       // All parts of the kernel should have decref'd before env_free was called.
        assert(e->env_refcnt == 0);
 
        // Flush all mapped pages in the user portion of the address space
@@ -446,159 +455,14 @@ env_free(env_t *e)
 
        // return the environment to the free list
        e->state = ENV_FREE;
-       LIST_INSERT_HEAD(&env_free_list, e, env_link);
-}
-
-/*
- * The process refcnt is the number of places the process 'exists' in the
- * system.  Creation counts as 1.  Having your page tables loaded somewhere
- * (lcr3) counts as another 1.  A non-RUNNING_* process should have refcnt at
- * least 1.  If the kernel is on another core and in a processes address space
- * (like processing its backring), that counts as another 1.
- *
- * Note that the actual loading and unloading of cr3 is up to the caller, since
- * that's not the only use for this (and decoupling is more flexible).
- *
- * The refcnt should always be greater than 0 for processes that aren't dying.
- * When refcnt is 0, the process is dying and should not allow any more increfs.
- * A process can be dying with a refcnt greater than 0, since it could be
- * waiting for other cores to "get the message" to die, or a kernel core can be
- * finishing work in the processes's address space.
- *
- * Implementation aside, the important thing is that we atomically increment
- * only if it wasn't already 0.  If it was 0, then we shouldn't be attaching to
- * the process, so we return an error, which should be handled however is
- * appropriate.  We currently use spinlocks, but some sort of clever atomics
- * would work too.
- *
- * Also, no one should ever update the refcnt outside of these functions.
- * Eventually, we'll have Ivy support for this. (TODO)
- */
-error_t env_incref(env_t* e)
-{
-       error_t retval = 0;
-       spin_lock_irqsave(&e->lock);
-       if (e->env_refcnt)
-               e->env_refcnt++;
-       else
-               retval = -EBADENV;
-       spin_unlock_irqsave(&e->lock);
-       return retval;
-}
-
-/*
- * When the kernel is done with a process, it decrements its reference count.
- * When the count hits 0, no one is using it and it should be freed.
- * "Last one out" actually finalizes the death of the process.  This is tightly
- * coupled with the previous function (incref)
- * Be sure to load a different cr3 before calling this!
- */
-void env_decref(env_t* e)
-{
-       spin_lock_irqsave(&e->lock);
-       e->env_refcnt--;
-       spin_unlock_irqsave(&e->lock);
-       // if we hit 0, no one else will increment and we can check outside the lock
-       if (e->env_refcnt == 0)
-               env_free(e);
-}
-
-
-/*
- * Destroys the given process.  Can be called by a different process (checked
- * via current), though that's unable to handle an async call (TODO current does
- * not work asyncly, though it could be made to in the async processing
- * function. 
- */
-void
-env_destroy(env_t *e)
-{
-       // TODO: XME race condition with env statuses, esp when running / destroying
-       proc_set_state(e, PROC_DYING);
-
-       /*
-        * If we are currently running this address space on our core, we need a
-        * known good pgdir before releasing the old one.  This is currently the
-        * major practical implication of the kernel caring about a processes
-        * existence (the inc and decref).  This decref corresponds to the incref in
-        * proc_startcore (though it's not the only one).
-        */
-       if (current == e) {
-               lcr3(boot_cr3);
-               env_decref(e); // this decref is for the cr3
-       }
-       env_decref(e); // this decref is for the process in general
-       atomic_dec(&num_envs);
-
-       /*
-        * Could consider removing this from destroy and having the caller specify
-        * these actions
-        */
-       // for old envs that die on user cores.  since env run never returns, cores
-       // never get back to their old hlt/relaxed/spin state, so we need to force
-       // them back to an idle function.
-       uint32_t id = core_id();
-       // There is no longer a current process for this core. (TODO: Think about this.)
-       current = NULL;
-       if (id) {
-               smp_idle();
-               panic("should never see me");
-       }
-       // else we're core 0 and can do the usual
-
-       /* Instead of picking a new environment to run, or defaulting to the monitor
-        * like before, for now we'll hop into the manager() function, which
-        * dispatches jobs.  Note that for now we start the manager from the top,
-        * and not from where we left off the last time we called manager.  That
-        * would require us to save some context (and a stack to work on) here.
-        */
-       manager();
-       assert(0); // never get here
-}
-
-/* ugly, but for now just linearly search through all possible
- * environments for a runnable one.
- * the current *policy* is to round-robin the search
- */
-void schedule(void)
-{
-       env_t *e;
-       static int last_picked = 0;
-       
-       for (int i = 0, j = last_picked + 1; i < NENV; i++, j = (j + 1) % NENV) {
-               e = &envs[ENVX(j)];
-               // TODO: XME race here, if another core is just about to start this env.
-               // Fix it by setting the status in something like env_dispatch when
-               // we have multi-contexted processes
-               if (e && e->state == PROC_RUNNABLE_S) {
-                       last_picked = j;
-                       env_run(e);
-               }
-       }
-
-       cprintf("Destroyed the only environment - nothing more to do!\n");
-       while (1)
-               monitor(NULL);
-}
-
-//
-// Context switch from curenv to env e.
-// Note: if this is the first call to env_run, curenv is NULL.
-//  (This function does not return.)
-//
-void
-env_run(env_t *e)
-{
-       // TODO: XME race here with env destroy on the status and refcnt
-       // Could up the refcnt and down it when a process is not running
-       
-       proc_set_state(e, PROC_RUNNING_S);
-       proc_startcore(e, &e->env_tf);
+       TAILQ_INSERT_HEAD(&proc_freelist, e, proc_link);
 }
 
 /* This is the top-half of an interrupt handler, where the bottom half is
- * env_run (which never returns).  Just add it to the delayed work queue,
+ * proc_run (which never returns).  Just add it to the delayed work queue,
  * which (incidentally) can only hold one item at this point.
+ *
+ * Note this is rather old, and meant to run a RUNNABLE_S on a worker core.
  */
 void run_env_handler(trapframe_t *tf, void *data)
 {
@@ -606,7 +470,7 @@ void run_env_handler(trapframe_t *tf, void *data)
        struct work job;
        struct workqueue *workqueue = &per_cpu_info[core_id()].workqueue;
        { TRUSTEDBLOCK // TODO: how do we make this func_t cast work?
-       job.func = (func_t)env_run;
+       job.func = (func_t)proc_run;
        job.data = data;
        }
        if (enqueue_work(workqueue, &job))
index 0bcef2e..4764e84 100644 (file)
@@ -14,6 +14,7 @@
 #include <assert.h>
 #include <manager.h>
 #include <process.h>
+#include <schedule.h>
 #include <workqueue.h>
 #include <syscall.h>
 #include <testing.h>
@@ -21,8 +22,8 @@
 #include <stdio.h>
 
 /*
- * Currently, if you leave this function by way of env_run (process_workqueue
- * that env_runs), you will never come back to where you left off, and the
+ * Currently, if you leave this function by way of proc_run (process_workqueue
+ * that proc_runs), you will never come back to where you left off, and the
  * function will start from the top.  Hence the hack 'progress'.
  */
 void manager(void)
@@ -34,13 +35,13 @@ void manager(void)
                case 0:
                        envs[0] = kfs_proc_create(kfs_lookup_path("roslib_hello"));
                        proc_set_state(envs[0], PROC_RUNNABLE_S);
-                       env_run(envs[0]);
+                       proc_run(envs[0]);
                        break;
        #ifdef __i386__
                case 1:
                        panic("Do not panic");
-                       envs[0] = ENV_CREATE(parlib_channel_test_client);
-                       envs[1] = ENV_CREATE(parlib_channel_test_server);
+                       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;
@@ -49,15 +50,15 @@ void manager(void)
        #else // sparc
                case 1:
                        panic("Do not panic");
-                       envs[0] = ENV_CREATE(roslib_proctests);
-                       envs[1] = ENV_CREATE(roslib_proctests);
-                       envs[2] = ENV_CREATE(roslib_proctests);
-                       envs[3] = ENV_CREATE(roslib_fptest);
-                       envs[4] = ENV_CREATE(roslib_fptest);
-                       envs[4] = ENV_CREATE(roslib_fptest);
-                       envs[5] = ENV_CREATE(roslib_hello);
-                       envs[6] = ENV_CREATE(roslib_null);
-                       env_run(envs[0]);
+                       envs[0] = kfs_proc_create(kfs_lookup_path("roslib_proctests"));
+                       envs[1] = kfs_proc_create(kfs_lookup_path("roslib_proctests"));
+                       envs[2] = kfs_proc_create(kfs_lookup_path("roslib_proctests"));
+                       envs[3] = kfs_proc_create(kfs_lookup_path("roslib_fptest"));
+                       envs[4] = kfs_proc_create(kfs_lookup_path("roslib_fptest"));
+                       envs[4] = kfs_proc_create(kfs_lookup_path("roslib_fptest"));
+                       envs[5] = kfs_proc_create(kfs_lookup_path("roslib_hello"));
+                       envs[6] = kfs_proc_create(kfs_lookup_path("roslib_null"));
+                       proc_run(envs[0]);
                        break;
                case 2:
                        #if 0
@@ -78,8 +79,8 @@ void manager(void)
                        test_run_measurements(progress-1);  // should never return
                        break;
                case 5:
-                       envs[0] = ENV_CREATE(parlib_channel_test_client);
-                       envs[1] = ENV_CREATE(parlib_channel_test_server);
+                       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);
                case 6:
index 44064ca..9648a37 100644 (file)
@@ -20,6 +20,7 @@
 #include <testing.h>
 #include <kfs.h>
 #include <manager.h>
+#include <schedule.h>
 
 #include <ros/memlayout.h>
 
@@ -72,19 +73,19 @@ int mon_kerninfo(int argc, char **argv, trapframe_t *tf)
        return 0;
 }
 
-static char* function_of(uint32_t address) 
+static char* function_of(uint32_t address)
 {
        extern stab_t stab[], estab[];
        extern char stabstr[];
        stab_t* symtab;
        stab_t* best_symtab = 0;
        uint32_t best_func = 0;
-       
+
        // ugly and unsorted
        for (symtab = stab; symtab < estab; symtab++) {
                // only consider functions, type = N_FUN
-               if ((symtab->n_type == N_FUN) && 
-                   (symtab->n_value <= address) && 
+               if ((symtab->n_type == N_FUN) &&
+                   (symtab->n_value <= address) &&
                        (symtab->n_value > best_func)) {
                        best_func = symtab->n_value;
                        best_symtab = symtab;
@@ -161,15 +162,15 @@ int mon_setmapperm(int argc, char **argv, trapframe_t *tf)
        pde = &pgdir[PDX(va)];
        cprintf("   Virtual    Physical  Ps Dr Ac CD WT U W\n");
        cprintf("------------------------------------------\n");
-       cprintf("%08p  %08p  %1d  %1d  %1d  %1d  %1d  %1d %1d\n", va, page2pa(page), 
-              (*pte & PTE_PS) >> 7, (*pte & PTE_D) >> 6, (*pte & PTE_A) >> 5, 
-              (*pte & PTE_PCD) >> 4, (*pte & PTE_PWT) >> 3, (*pte & *pde & PTE_U) >> 2, 
+       cprintf("%08p  %08p  %1d  %1d  %1d  %1d  %1d  %1d %1d\n", va, page2pa(page),
+              (*pte & PTE_PS) >> 7, (*pte & PTE_D) >> 6, (*pte & PTE_A) >> 5,
+              (*pte & PTE_PCD) >> 4, (*pte & PTE_PWT) >> 3, (*pte & *pde & PTE_U) >> 2,
               (*pte & *pde & PTE_W) >> 1);
        *pte = PTE_ADDR(*pte) | (*pte & PTE_PS) |
               (PGOFF(strtol(argv[2], 0, 16)) & ~PTE_PS ) | PTE_P;
-       cprintf("%08p  %08p  %1d  %1d  %1d  %1d  %1d  %1d %1d\n", va, page2pa(page), 
-              (*pte & PTE_PS) >> 7, (*pte & PTE_D) >> 6, (*pte & PTE_A) >> 5, 
-              (*pte & PTE_PCD) >> 4, (*pte & PTE_PWT) >> 3, (*pte & *pde & PTE_U) >> 2, 
+       cprintf("%08p  %08p  %1d  %1d  %1d  %1d  %1d  %1d %1d\n", va, page2pa(page),
+              (*pte & PTE_PS) >> 7, (*pte & PTE_D) >> 6, (*pte & PTE_A) >> 5,
+              (*pte & PTE_PCD) >> 4, (*pte & PTE_PWT) >> 3, (*pte & *pde & PTE_U) >> 2,
               (*pte & *pde & PTE_W) >> 1);
        return 0;
 #endif
@@ -179,7 +180,7 @@ int mon_cpuinfo(int argc, char **argv, trapframe_t *tf)
 {
        extern uint8_t num_cpus;
 
-       cprintf("Number of CPUs detected: %d\n", num_cpus);     
+       cprintf("Number of CPUs detected: %d\n", num_cpus);
        cprintf("Calling CPU's ID: 0x%08x\n", core_id());
 
 #ifdef __i386__
@@ -253,7 +254,10 @@ int mon_kfs_run(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf)
        }
        struct proc *p = kfs_proc_create(kfs_inode);
        // go from PROC_CREATED->PROC_RUNNABLE_S
+       spin_lock_irqsave(&p->proc_lock); // might not be necessary for a mon function
        proc_set_state(p, PROC_RUNNABLE_S);
+       schedule_proc(p);
+       spin_unlock_irqsave(&p->proc_lock);
        // Should never return from schedule (env_pop in there)
        // also note you may not get the process you created, in the event there
        // are others floating around that are runnable
index d886fb2..6011bc7 100644 (file)
@@ -359,7 +359,7 @@ user_mem_assert(env_t *env, const void *DANGEROUS va, size_t len, int perm)
        if (!res) {
                cprintf("[%08x] user_mem_check assertion failure for "
                        "va %08x\n", env->env_id, user_mem_check_addr);
-               env_destroy(env);       // may not return
+               proc_destroy(env);      // may not return
         return NULL;
        }
     return res;
index 5f9686a..616aa64 100644 (file)
@@ -6,7 +6,18 @@
 
 #include <process.h>
 #include <atomic.h>
+#include <smp.h>
+#include <pmap.h>
+#include <schedule.h>
+#include <manager.h>
+#include <stdio.h>
 #include <assert.h>
+#include <sys/queue.h>
+
+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);
+spinlock_t runnablelist_lock = 0;
 
 /*
  * While this could be done with just an assignment, this gives us the
@@ -28,7 +39,7 @@ int proc_set_state(struct proc *p, uint32_t state)
         * RGM -> RBS
         * RGS -> D
         * RGM -> D
-        * 
+        *
         * These ought to be implemented later (allowed, not thought through yet).
         * RBS -> D
         * RBM -> D
@@ -87,57 +98,254 @@ bool proc_controls(struct proc *actor, struct proc *target)
 }
 
 /*
+ * Dispatches a process to run, either on the current core in the case of a
+ * RUNNABLE_S, or on its partition in the case of a RUNNABLE_M.
+ * This should never be called to "restart" a core.
+ */
+void proc_run(struct proc *p)
+{
+       spin_lock_irqsave(&p->proc_lock);
+       switch (p->state) {
+               case (PROC_DYING):
+                       spin_unlock_irqsave(&p->proc_lock);
+                       printk("Process %d not starting due to async death\n", p->env_id);
+                       // There should be no core cleanup to do (like decref).
+                       assert(current != p);
+                       // if we're a worker core, smp_idle, o/w return
+                       // TODO considering encapsulating this block (core_id too)
+                       if (core_id())
+                               smp_idle(); // this never returns
+                       return;
+               case (PROC_RUNNABLE_S):
+                       proc_set_state(p, PROC_RUNNING_S);
+                       spin_unlock_irqsave(&p->proc_lock);
+                       // This normally doesn't return, but might error out in the future.
+                       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
+
+                       /* 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
+                       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");
+               default:
+                       spin_unlock_irqsave(&p->proc_lock);
+                       panic("Invalid process state in proc_run()!!");
+       }
+}
+
+/*
  * Runs the given context (trapframe) of process p on the core this code
  * executes on.  The refcnt tracks how many cores have "an interest" in this
  * process, which so far just means it uses the process's page table.  See the
  * massive comments around the incref function
  *
- * TODO: think about how an interrupt could abort this, esp when we want to
- * destroy it.  need a way to not lose the kernel stack.  For example, we could
- * receive an IPI that tells us to preempt this process (or even kill it) and
- * run something different.
+ * Given we are RUNNING_*, an IPI for death or preemption could come in:
+ * 1. death attempt (IPI to kill whatever is on your core):
+ *             we don't need to worry about protecting the stack, since we're
+ *             abandoning ship - just need to get a good cr3 and decref current, which
+ *             the death handler will do.
+ *             If a death IPI comes in, we immediately stop this function and will
+ *             never come back.
+ * 2. preempt attempt (IPI to package state and maybe run something else):
+ *             - if a preempt attempt comes in while we're in the kernel, it'll
+ *             just set a flag.  we could attempt to bundle the kernel state
+ *             and rerun it later, but it's really messy (and possibly given
+ *             back to userspace).  we'll disable ints, check this flag, and if
+ *             so, handle the preemption using the same funcs as the normal
+ *             preemption handler.  nonblocking kernel calls will just slow
+ *             down the preemption while they work.  blocking kernel calls will
+ *             need to package their state properly anyway.
+ *
  * TODO: in general, think about when we no longer need the stack, in case we
  * are preempted and expected to run again from somewhere else.  we can't
- * expect to have the kernel stack around anymore.
- *
- * I think we need to make it such that the kernel in "process context"
- * never gets removed from the core (displaced from its stack)
- * would like to leave interrupts on too, so long as we come back.
- * Consider a moveable flag or something.
+ * expect to have the kernel stack around anymore.  the nice thing about being
+ * at this point is that we are just about ready to give up the stack anyways.
  *
- * Perhaps we could have a workqueue with the todo item put there by the
- * interrupt handler when it realizes we were in the kernel in the first place.
- * disable ints before checking the queue and deciding to pop out or whatever to
- * ensure atomicity.
+ * I think we need to make it such that the kernel in "process context" never
+ * gets removed from the core (displaced from its stack) without going through
+ * some "bundling" code.
  */
 void proc_startcore(struct proc *p, trapframe_t *tf) {
-       /*
-        * TODO: okay, we have this.  now handle scenarios based on these
-        * assumptions (transitions from these states) like:
-        *              death attempt
-        *              preempt attempt
-        */
+       // TODO it's possible to be DYING, but it's a rare race.  remove this soon.
        assert(p->state & (PROC_RUNNING_S | PROC_RUNNING_M));
+       // sucks to have ints disabled when doing env_decref and possibly freeing
+       disable_irq();
+       if (per_cpu_info[core_id()].preempt_pending) {
+               // TODO: handle preemption
+               // the functions will need to consider deal with current like down below
+               panic("Preemption not supported!");
+       }
        /* If the process wasn't here, then we need to load its address space. */
        if (p != current) {
-               if (env_incref(p))
+               if (proc_incref(p))
                        // getting here would mean someone tried killing this while we tried
                        // to start one of it's contexts (from scratch, o/w we had it's CR3
                        // loaded already)
-                       panic("Proc is dying, handle me!"); lcr3(p->env_cr3);
+                       // if this happens, the death-IPI ought to be on its way...  we can
+                       // either wait, or just cleanup_core() and smp_idle.
+                       panic("Proc is dying, handle me!"); // TODO
+               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".
                if (current)
-                       env_decref(current);
+                       proc_decref(current);
                current = p;
-               /* also need to load our silly state, though this implies it's the same
-                * context, and not just the same process
-                * TODO: this is probably a lie, think about page faults
-                * for now, we load this silly state down below
-                */
-               // load_our_silly_state();
        }
+       /* need to load our silly state, preferably somewhere other than here so we
+        * can avoid the case where the context was just running here.  it's not
+        * 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)
+        * We also need this to be per trapframe, and not per process...
+        */
        env_pop_ancillary_state(p);
        env_pop_tf(&p->env_tf);
 }
+
+/*
+ * Destroys the given process.  This may be called from another process, a light
+ * kernel thread (no real process context), asynchronously/cross-core, or from
+ * the process on its own core.
+ *
+ * Here's the way process death works:
+ * 0. grab the lock (protects state transition and core map)
+ * 1. set state to dying.  that keeps the kernel from doing anything for the
+ * process (like proc_running it).
+ * 2. figure out where the process is running (cross-core/async or RUNNING_M)
+ * 3. IPI to clean up those cores (decref, etc).
+ * 4. Unlock
+ * 5. Clean up your core, if applicable
+ * (Last core/kernel thread to decref cleans up and deallocates resources.)
+ *
+ * Note that some cores can be processing async calls, but will eventually
+ * decref.  Should think about this more.
+ */
+void proc_destroy(struct proc *p)
+{
+       spin_lock_irqsave(&p->proc_lock);
+       // Could save the state and do this outside the lock
+       switch (p->state) {
+               case PROC_DYING:
+                       return; // someone else killed this already.
+               case PROC_RUNNABLE_S:
+               case PROC_RUNNABLE_M:
+                       deschedule_proc(p);
+                       break;
+               default:
+                       // Think about other lists, or better ways to do this
+       }
+       proc_set_state(p, PROC_DYING);
+       // BIG TODO: check the coremap to find out who needs to die
+       // send the death IPI to everyone else involved
+       spin_unlock_irqsave(&p->proc_lock);
+
+       proc_decref(p); // this decref is for the process in general
+       atomic_dec(&num_envs);
+
+       /*
+        * If we are currently running this address space on our core, we need a
+        * known good pgdir before releasing the old one.  This is currently the
+        * major practical implication of the kernel caring about a processes
+        * existence (the inc and decref).  This decref corresponds to the incref in
+        * proc_startcore (though it's not the only one).
+        */
+       // TODO - probably make this a function, which the death IPI calls
+       if (current == p) {
+               lcr3(boot_cr3);
+               proc_decref(p); // this decref is for the cr3
+               current = NULL;
+       } else {
+               return;
+       }
+
+       // for old envs that die on user cores.  since env run never returns, cores
+       // never get back to their old hlt/relaxed/spin state, so we need to force
+       // them back to an idle function.
+
+       if (core_id()) {
+               smp_idle();
+               panic("should never see me");
+       }
+       // else we're core 0 and can do the usual
+
+       /* Instead of picking a new environment to run, or defaulting to the monitor
+        * like before, for now we'll hop into the manager() function, which
+        * dispatches jobs.  Note that for now we start the manager from the top,
+        * and not from where we left off the last time we called manager.  That
+        * would require us to save some context (and a stack to work on) here.
+        */
+       manager();
+       assert(0); // never get here
+}
+
+/*
+ * The process refcnt is the number of places the process 'exists' in the
+ * system.  Creation counts as 1.  Having your page tables loaded somewhere
+ * (lcr3) counts as another 1.  A non-RUNNING_* process should have refcnt at
+ * least 1.  If the kernel is on another core and in a processes address space
+ * (like processing its backring), that counts as another 1.
+ *
+ * Note that the actual loading and unloading of cr3 is up to the caller, since
+ * that's not the only use for this (and decoupling is more flexible).
+ *
+ * The refcnt should always be greater than 0 for processes that aren't dying.
+ * When refcnt is 0, the process is dying and should not allow any more increfs.
+ * A process can be dying with a refcnt greater than 0, since it could be
+ * waiting for other cores to "get the message" to die, or a kernel core can be
+ * finishing work in the processes's address space.
+ *
+ * Implementation aside, the important thing is that we atomically increment
+ * only if it wasn't already 0.  If it was 0, then we shouldn't be attaching to
+ * the process, so we return an error, which should be handled however is
+ * appropriate.  We currently use spinlocks, but some sort of clever atomics
+ * would work too.
+ *
+ * Also, no one should ever update the refcnt outside of these functions.
+ * Eventually, we'll have Ivy support for this. (TODO)
+ */
+error_t proc_incref(struct proc *p)
+{
+       error_t retval = 0;
+       spin_lock_irqsave(&p->proc_lock);
+       if (p->env_refcnt)
+               p->env_refcnt++;
+       else
+               retval = -EBADENV;
+       spin_unlock_irqsave(&p->proc_lock);
+       return retval;
+}
+
+/*
+ * When the kernel is done with a process, it decrements its reference count.
+ * When the count hits 0, no one is using it and it should be freed.
+ * "Last one out" actually finalizes the death of the process.  This is tightly
+ * coupled with the previous function (incref)
+ * Be sure to load a different cr3 before calling this!
+ */
+void proc_decref(struct proc *p)
+{
+       spin_lock_irqsave(&p->proc_lock);
+       p->env_refcnt--;
+       spin_unlock_irqsave(&p->proc_lock);
+       // if we hit 0, no one else will increment and we can check outside the lock
+       if (p->env_refcnt == 0)
+               env_free(p);
+}
diff --git a/kern/src/schedule.c b/kern/src/schedule.c
new file mode 100644 (file)
index 0000000..a4ff0c7
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2009 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Scheduling and dispatching.
+ */
+
+#include <schedule.h>
+#include <process.h>
+#include <monitor.h>
+#include <stdio.h>
+#include <assert.h>
+#include <atomic.h>
+#include <sys/queue.h>
+
+void schedule_init(void)
+{
+       TAILQ_INIT(&proc_runnablelist);
+       return;
+}
+
+void schedule_proc(struct proc *p)
+{
+       spin_lock_irqsave(&runnablelist_lock);
+       printd("Scheduling PID: %d\n", p->env_id);
+       TAILQ_INSERT_TAIL(&proc_runnablelist, p, proc_link);
+       spin_unlock_irqsave(&runnablelist_lock);
+       return;
+}
+
+void deschedule_proc(struct proc *p)
+{
+       spin_lock_irqsave(&runnablelist_lock);
+       printd("Descheduling PID: %d\n", p->env_id);
+       TAILQ_REMOVE(&proc_runnablelist, p, proc_link);
+       spin_unlock_irqsave(&runnablelist_lock);
+       return;
+}
+
+/*
+ * FIFO - just pop the head from the list and run it.
+ * Using irqsave spinlocks for now, since this could be called from a timer
+ * interrupt handler (though ought to be in a bottom half or something).
+ */
+void schedule(void)
+{
+       struct proc *p;
+       
+       spin_lock_irqsave(&runnablelist_lock);
+       p = TAILQ_FIRST(&proc_runnablelist);
+       if (p) {
+               TAILQ_REMOVE(&proc_runnablelist, p, proc_link);
+               spin_unlock_irqsave(&runnablelist_lock);
+               printd("PID of proc i'm running: %d\n", p->env_id);
+               proc_run(p);
+       } else {
+               spin_unlock_irqsave(&runnablelist_lock);
+               printk("No processes to schedule, enjoy the Monitor!\n");
+               while (1)
+                       monitor(NULL);
+       }
+       return;
+}
+
+void dump_proclist(struct proc_list *list)
+{
+       struct proc *p;
+       TAILQ_FOREACH(p, list, proc_link)
+               printk("PID: %d\n", p->env_id);
+       return;
+}
index c25bfdb..62573ac 100644 (file)
@@ -27,6 +27,7 @@ atomic_t outstanding_calls = 0;
 /* All non-zero cores call this at the end of their boot process.  They halt,
  * and wake up when interrupted, do any work on their work queue, then halt
  * when there is nothing to do.  
+ * TODO: think about resetting the stack pointer at the beginning.
  */
 void smp_idle(void)
 {
index 9e861e0..20cdda0 100644 (file)
@@ -13,6 +13,7 @@
 #include <string.h>
 #include <assert.h>
 #include <process.h>
+#include <schedule.h>
 #include <pmap.h>
 #include <trap.h>
 #include <syscall.h>
@@ -26,12 +27,12 @@ static void sys_null(void)
 }
 
 //Write a buffer over the serial port
-static ssize_t sys_serial_write(env_t* e, const char *DANGEROUS buf, size_t len) 
+static ssize_t sys_serial_write(env_t* e, const char *DANGEROUS buf, size_t len)
 {
        #ifdef SERIAL_IO
                char *COUNT(len) _buf = user_mem_assert(e, buf, len, PTE_USER_RO);
                for(int i =0; i<len; i++)
-                       serial_send_byte(buf[i]);       
+                       serial_send_byte(buf[i]);
                return (ssize_t)len;
        #else
                return -EINVAL;
@@ -39,7 +40,7 @@ static ssize_t sys_serial_write(env_t* e, const char *DANGEROUS buf, size_t len)
 }
 
 //Read a buffer over the serial port
-static ssize_t sys_serial_read(env_t* e, char *DANGEROUS buf, size_t len) 
+static ssize_t sys_serial_read(env_t* e, char *DANGEROUS buf, size_t len)
 {
        #ifdef SERIAL_IO
            char *COUNT(len) _buf = user_mem_assert(e, buf, len, PTE_USER_RO);
@@ -55,10 +56,10 @@ static ssize_t sys_serial_read(env_t* e, char *DANGEROUS buf, size_t len)
        #endif
 }
 
-static ssize_t sys_shared_page_alloc(env_t* p1, 
-                                     void** addr, envid_t p2_id, 
+static ssize_t sys_shared_page_alloc(env_t* p1,
+                                     void** addr, envid_t p2_id,
                                      int p1_flags, int p2_flags
-                                    ) 
+                                    )
 {
        //if (!VALID_USER_PERMS(p1_flags)) return -EPERM;
        //if (!VALID_USER_PERMS(p2_flags)) return -EPERM;
@@ -67,13 +68,13 @@ static ssize_t sys_shared_page_alloc(env_t* p1,
        env_t* p2 = &(envs[ENVX(p2_id)]);
        error_t e = page_alloc(&page);
        if(e < 0) return e;
-       
-       void* p2_addr = page_insert_in_range(p2->env_pgdir, page, 
+
+       void* p2_addr = page_insert_in_range(p2->env_pgdir, page,
                                             (void*)UTEXT, (void*)UTOP, p2_flags);
-       if(p2_addr == NULL) 
+       if(p2_addr == NULL)
                return -EFAIL;
-               
-       void* p1_addr = page_insert_in_range(p1->env_pgdir, page, 
+
+       void* p1_addr = page_insert_in_range(p1->env_pgdir, page,
                                            (void*)UTEXT, (void*)UTOP, p1_flags);
        if(p1_addr == NULL) {
                page_remove(p2->env_pgdir, p2_addr);
@@ -119,7 +120,7 @@ static void sys_cache_buster(env_t* e, uint32_t num_writes, uint32_t num_pages,
                stride = 16;
                num_writes *= 16;
        }
-       
+
        /* Shared Accesses or Not (adjust to use per-core regions)
         * Careful, since this gives 8MB to each core, starting around 512MB.
         * Also, doesn't separate memory for core 0 if it's an async call.
@@ -226,8 +227,9 @@ static error_t sys_env_destroy(env_t* e, envid_t envid)
        if (env_to_die == e)
                printk("[%08x] exiting gracefully\n", e->env_id);
        else
-               printk("[%08x] destroying %08x\n", e->env_id, env_to_die->env_id);
-       env_destroy(env_to_die);
+               panic("Destroying other processes is not supported yet.");
+               //printk("[%08x] destroying %08x\n", e->env_id, env_to_die->env_id);
+       proc_destroy(env_to_die);
        return ESUCCESS;
 }
 
@@ -239,18 +241,19 @@ static error_t sys_env_destroy(env_t* e, envid_t envid)
  */
 static void sys_yield(struct proc *p)
 {
-       // TODO: watch for races throughout anything related to process statuses
-       // and schedule/yielding
+       // This is all standard single-core, local call
+       spin_lock_irqsave(&p->proc_lock);
        assert(p->state == PROC_RUNNING_S);
-       p->state = PROC_RUNNABLE_S;
-       // the implied thing here is that all state has been saved.  and you need
-       // todo that before changing the state to RUNNABLE_S, since the process can
-       // get picked up somewhere else. TODO
+       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 runnable_m
+        * if running_m and 2+ cores are left, give yours up, stay running_m
         * if running_m and last core, switch to runnable_s
         */
 }
@@ -288,15 +291,17 @@ static error_t sys_proc_run(struct proc *p, unsigned pid)
 {
        struct proc *target = get_proc(pid);
        error_t retval = 0;
-       spin_lock(&p->lock); // note we can get interrupted here.  it's not bad.
+       spin_lock_irqsave(&p->proc_lock); // note we can get interrupted here. it's not bad.
        // make sure we have access and it's in the right state to be activated
-       if (!proc_controls(p, target))
+       if (!proc_controls(p, target)) {
                retval = -EPERM;
-       else if (target->state != PROC_CREATED)
+       } else if (target->state != PROC_CREATED) {
                retval = -EINVAL;
-       else
+       } else {
                proc_set_state(target, PROC_RUNNABLE_S);
-       spin_unlock(&p->lock);
+               schedule_proc(target);
+       }
+       spin_unlock_irqsave(&p->proc_lock);
        return retval;
 }
 
@@ -309,7 +314,7 @@ intreg_t syscall(env_t* e, uint32_t syscallno, uint32_t a1, uint32_t a2,
        // Return any appropriate return value.
 
        //cprintf("Incoming syscall number: %d\n    a1: %x\n   "
-       //        " a2: %x\n    a3: %x\n    a4: %x\n    a5: %x\n", 
+       //        " a2: %x\n    a3: %x\n    a4: %x\n    a5: %x\n",
        //        syscallno, a1, a2, a3, a4, a5);
 
        assert(e); // should always have an env for every syscall
@@ -328,7 +333,7 @@ intreg_t syscall(env_t* e, uint32_t syscallno, uint32_t a1, uint32_t a2,
                        sys_cache_invalidate();
                        return 0;
                case SYS_shared_page_alloc:
-                       return sys_shared_page_alloc(e, (void** DANGEROUS) a1, 
+                       return sys_shared_page_alloc(e, (void** DANGEROUS) a1,
                                                 a2, (int) a3, (int) a4);
                case SYS_shared_page_free:
                        sys_shared_page_free(e, (void* DANGEROUS) a1, a2);
@@ -381,9 +386,9 @@ intreg_t process_generic_syscalls(env_t* e, size_t max)
        size_t count = 0;
        syscall_back_ring_t* sysbr = &e->syscallbackring;
 
-       // make sure the env is still alive.  
+       // make sure the env is still alive.
        // incref will return ESUCCESS on success.
-       if (env_incref(e))
+       if (proc_incref(e))
                return -EFAIL;
 
        // max is the most we'll process.  max = 0 means do as many as possible
@@ -414,6 +419,6 @@ intreg_t process_generic_syscalls(env_t* e, size_t max)
        }
        // load sane page tables (and don't rely on decref to do it for you).
        lcr3(boot_cr3);
-       env_decref(e);
+       proc_decref(e);
        return (intreg_t)count;
 }
index d436e32..15cde23 100644 (file)
@@ -17,6 +17,7 @@
 #include <process.h>
 #include <syscall.h>
 #include <timing.h>
+#include <kfs.h>
 
 #define test_vector 0xeb
 
@@ -427,7 +428,7 @@ static void sync_tests(int start_core, int num_threads, int job_num)
        assert(start_core + num_threads <= num_cpus);
        wait_for_all_envs_to_die();
        for (int i = start_core; i < start_core + num_threads; i++)
-               env_batch[i] = ENV_CREATE(roslib_measurements);
+               env_batch[i] = kfs_proc_create(kfs_lookup_path("roslib_measurements"));
        lcr3(env_batch[start_core]->env_cr3);
        init_barrier(bar, num_threads);
        *job_to_run = job_num;
@@ -435,7 +436,7 @@ static void sync_tests(int start_core, int num_threads, int job_num)
                smp_call_function_single(i, run_env_handler, env_batch[i], 0);
        process_workqueue();
        // we want to fake a run, to reenter manager for the next case
-       env_t *env = ENV_CREATE(roslib_null);
+       env_t *env = kfs_proc_create(kfs_lookup_path("roslib_null"));
        smp_call_function_single(0, run_env_handler, env, 0);
        process_workqueue();
        panic("whoops!\n");
@@ -448,7 +449,7 @@ static void async_tests(int start_core, int num_threads, int job_num)
        assert(start_core + num_threads <= num_cpus);
        wait_for_all_envs_to_die();
        for (int i = start_core; i < start_core + num_threads; i++)
-               env_batch[i] = ENV_CREATE(roslib_measurements);
+               env_batch[i] = kfs_proc_create(kfs_lookup_path("roslib_measurements"));
        printk("async_tests: checkpoint 0\n");
        lcr3(env_batch[start_core]->env_cr3);
        init_barrier(bar, num_threads);
@@ -465,7 +466,7 @@ static void async_tests(int start_core, int num_threads, int job_num)
                cpu_relax();
        }
        // we want to fake a run, to reenter manager for the next case
-       env_t *env = ENV_CREATE(roslib_null);
+       env_t *env = kfs_proc_create(kfs_lookup_path("roslib_null"));
        smp_call_function_single(0, run_env_handler, env, 0);
        process_workqueue();
        // this all never returns
index e0952d6..fa07e9f 100644 (file)
@@ -30,7 +30,7 @@ void process_workqueue()
                spin_lock_irqsave(&cpuinfo->lock);
                cpuinfo->workqueue.statics[0].func = 0;
                spin_unlock_irqsave(&cpuinfo->lock);
-               // We may never return from this (if it is env_run)
+               // We may never return from this (if it is proc_run)
                work.func(work.data);
        }
 }