Process state work, initial steps
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 24 Jul 2009 18:19:51 +0000 (11:19 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 24 Jul 2009 18:19:51 +0000 (11:19 -0700)
Created the states we want, put in some basic framework for using them,
and started moving away from env.c/env.h.  Outstanding issues include
transition between complicated states, esp with races during the
transitions.  env_run needs to be split up, and we need to make sure we
never automatically bundle the kernel state and try to restart it, since
everything on the stack will be gone.

18 files changed:
include/env.h
include/kfs.h
include/pmap.h
include/process.h [new file with mode: 0644]
include/syscall.h
kern/src/Makefrag
kern/src/env.c
kern/src/init.c
kern/src/kdebug.c
kern/src/kfs.c
kern/src/manager.c
kern/src/monitor.c
kern/src/pmap.c
kern/src/process.c [new file with mode: 0644]
kern/src/smp.c
kern/src/syscall.c
kern/src/testing.c
kern/src/trap.c

index 7b4edc0..68534ef 100644 (file)
@@ -37,22 +37,13 @@ typedef int32_t envid_t;
 #define NENV                   (1 << LOG2NENV)
 #define ENVX(envid)            ((envid) & (NENV - 1))
 
-// Values of env_status in struct Env
-// TODO: think about what states we want.
-#define ENV_FREE                       0
-#define ENV_RUNNING                    1
-#define ENV_RUNNABLE           2
-#define ENV_NOT_RUNNABLE       3
-#define ENV_DYING                      4
-#define ENV_CREATED                    5
-
 struct Env {
        LIST_ENTRY(Env) env_link NOINIT;        // Free list link pointers
        uint32_t lock;
        trapframe_t env_tf;                     // Saved registers
        envid_t env_id;                         // Unique environment identifier
        envid_t env_parent_id;          // env_id of this env's parent
-       unsigned env_status;            // Status of the environment
+       proc_state_t state;         // Status of the process
        uint32_t env_runs;                      // Number of times environment has run
        uint32_t env_refcnt;            // Reference count of kernel contexts using this
        uint32_t env_flags;
@@ -106,12 +97,13 @@ void       env_pop_tf_sysexit(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);                                \
-       e->env_status = ENV_RUNNABLE;                                              \
+       proc_set_state(e, PROC_RUNNABLE_S);                                        \
        e;                                                                         \
 })
 
index d0c4efe..60fadc1 100644 (file)
@@ -16,7 +16,7 @@
 #define ROS_KERN_KFS_H
 
 #include <arch/types.h>
-#include <env.h>
+#include <process.h>
 
 #pragma nodeputy
 
@@ -30,6 +30,6 @@ struct kfs_entry {
 extern struct kfs_entry kfs[MAX_KFS_FILES];
 
 ssize_t kfs_lookup_path(char* path);
-env_t* kfs_proc_create(int kfs_inode);
+struct proc *kfs_proc_create(int kfs_inode);
 
 #endif // !ROS_KERN_KFS_H
index 512794f..9e45f66 100644 (file)
@@ -9,7 +9,7 @@
 #include <ros/memlayout.h>
 #include <arch/multiboot.h>
 #include <atomic.h>
-#include <env.h>
+#include <process.h>
 #include <assert.h>
 #include <sys/queue.h>
 
diff --git a/include/process.h b/include/process.h
new file mode 100644 (file)
index 0000000..40a7a66
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2009 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * All things processes!  As we move away from the old envs to processes,
+ * we'll move things into here that are designed for multicore processes.
+ */
+
+#ifndef ROS_KERN_PROCESS_H
+#define ROS_KERN_PROCESS_H
+
+/* Process States.  Not 100% on the names yet. */
+typedef enum {
+       ENV_FREE, // TODO don't use this shit for process allocation flagging
+       PROC_CREATED,
+       PROC_RUNNABLE_S,
+       PROC_RUNNING_S,
+       PROC_WAITING,             // can split out to INT and UINT
+       PROC_DYING,
+       PROC_RUNNABLE_M,          // ready, just needs all of its resources (cores)
+       PROC_RUNNING_M            // running, manycore style
+} proc_state_t;
+
+#include <env.h>
+
+// Till we remove the old struct Env
+#define proc Env
+
+int proc_set_state(struct proc *p, proc_state_t state) WRITES(p->state);
+struct proc *get_proc(unsigned pid);
+bool proc_controls(struct proc *actor, struct proc *target);
+
+#endif // !ROS_KERN_PROCESS_H
index 3b8240b..5677a99 100644 (file)
@@ -5,7 +5,7 @@
 #endif
 
 #include <ros/syscall.h>
-#include <env.h>
+#include <process.h>
 
 int32_t (SYNCHRONOUS syscall)(env_t* e, uint32_t num, uint32_t a1, uint32_t a2,
                               uint32_t a3, uint32_t a4, uint32_t a5);
index b10ec95..05b3fbb 100644 (file)
@@ -37,6 +37,7 @@ KERN_SRCFILES := $(KERN_SRC_DIR)/entry.S \
                  $(KERN_SRC_DIR)/string.c \
                  $(KERN_SRC_DIR)/timer.c \
                  $(KERN_SRC_DIR)/kfs.c \
+                 $(KERN_SRC_DIR)/process.c \
                  $(KERN_SRC_DIR)/kmalloc.c
 # Only build files if they exist.
 KERN_SRCFILES := $(wildcard $(KERN_SRCFILES))
index 4d51d38..da395c2 100644 (file)
@@ -13,7 +13,7 @@
 #include <atomic.h>
 #include <string.h>
 #include <assert.h>
-#include <env.h>
+#include <process.h>
 #include <pmap.h>
 #include <trap.h>
 #include <monitor.h>
@@ -58,7 +58,7 @@ envid2env(envid_t envid, env_t **env_store, bool checkperm)
        // (i.e., does not refer to a _previous_ environment
        // that used the same slot in the envs[] array).
        e = &envs[ENVX(envid)];
-       if (e->env_status == ENV_FREE || e->env_id != envid) {
+       if (e->state == ENV_FREE || e->env_id != envid) {
                *env_store = 0;
                return -EBADENV;
        }
@@ -83,6 +83,7 @@ envid2env(envid_t envid, env_t **env_store, bool checkperm)
 // and insert them into the env_free_list.
 // Insert in reverse order, so that the first call to env_alloc()
 // returns envs[0].
+// TODO: get rid of this whole array bullshit
 //
 void
 env_init(void)
@@ -91,7 +92,7 @@ env_init(void)
        LIST_INIT(&env_free_list);
        for (i = NENV-1; i >= 0; i--) {
                // these should already be set from when i memset'd the array to 0
-               envs[i].env_status = ENV_FREE;
+               envs[i].state = ENV_FREE;
                envs[i].env_id = 0;
                LIST_INSERT_HEAD(&env_free_list, &envs[i], env_link);
        }
@@ -160,6 +161,7 @@ WRITES(e->env_pgdir, e->env_cr3, e->env_procinfo, e->env_procdata,
 
        // should be able to do this so long as boot_pgdir never has
        // anything put below UTOP
+       // TODO check on this!  had a nasty bug because of it
        memcpy(e->env_pgdir, boot_pgdir, PGSIZE);
 
        // something like this.  TODO, if you want
@@ -225,8 +227,6 @@ env_alloc(env_t **newenv_store, envid_t parent_id)
        if (!(e = LIST_FIRST(&env_free_list)))
                return -ENOFREEENV;
        
-       //memset((void*)e + sizeof(e->env_link), 0, sizeof(*e) - sizeof(e->env_link));
-
     { INITSTRUCT(*e)
 
        // Allocate and set up the page directory for this environment.
@@ -242,7 +242,7 @@ env_alloc(env_t **newenv_store, envid_t parent_id)
        // Set the basic status variables.
     e->lock = 0;
        e->env_parent_id = parent_id;
-       e->env_status = ENV_CREATED;
+       proc_set_state(e, PROC_CREATED);
        e->env_runs = 0;
        e->env_refcnt = 1;
        e->env_flags = 0;
@@ -479,7 +479,7 @@ env_free(env_t *e)
        page_decref(pa2page(pa));
 
        // return the environment to the free list
-       e->env_status = ENV_FREE;
+       e->state = ENV_FREE;
        LIST_INSERT_HEAD(&env_free_list, e, env_link);
 }
 
@@ -531,8 +531,8 @@ void env_decref(env_t* e)
 void
 env_destroy(env_t *e)
 {
-       // TODO: race condition with env statuses, esp when running / destroying
-       e->env_status = ENV_DYING;
+       // TODO: XME race condition with env statuses, esp when running / destroying
+       proc_set_state(e, PROC_DYING);
 
        env_decref(e);
        atomic_dec(&num_envs);
@@ -570,10 +570,10 @@ void schedule(void)
        
        for (int i = 0, j = last_picked + 1; i < NENV; i++, j = (j + 1) % NENV) {
                e = &envs[ENVX(j)];
-               // TODO: race here, if another core is just about to start this env.
+               // 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->env_status == ENV_RUNNABLE) {
+               if (e && e->state == PROC_RUNNABLE_S) {
                        last_picked = j;
                        env_run(e);
                }
@@ -638,17 +638,38 @@ env_run(env_t *e)
        //      and make sure you have set the relevant parts of
        //      e->env_tf to sensible values.
 
-       // TODO: race here with env destroy on the status and refcnt
+       // 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
-       e->env_status = ENV_RUNNING;
+       
+       // TODO XME need different code paths for retarting a core (pop) and
+       // running for the first time
+       if (e->state == PROC_RUNNABLE_S)
+               proc_set_state(e, PROC_RUNNING_S);
+
        if (e != current) {
                current = e;
                e->env_runs++;
-               lcr3(e->env_cr3);
+               lcr3(e->env_cr3); // think about the refcnt here
        }
        /* If the process entered the kernel via sysenter, we need to leave via
         * sysexit.  sysenter trapframes have 0 for a CS, which is pushed in
         * sysenter_handler.
+        *
+        *           FFFFFFFFF UU     UU    CCCCCC  KK     KK  #
+        *           FF        UU     UU  CCC       KK    KK   #
+        *           FF        UU     UU CC         KK  KK     #
+        *           FFFFFF    UU     UU CC         KKKKK      #
+        *           FF        UU     UU CC         KK  KK     #
+        *           FF         UU   UU   CCC       KK    KK    
+        *           FF          UUUUU      CCCCCC  KK     KK  #
+        *
+        * TODO 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
         */
        if (e->env_tf.tf_cs)
                env_pop_tf(&e->env_tf);
@@ -658,7 +679,7 @@ env_run(env_t *e)
 
 /* 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,
- * which isn't really a queue yet.
+ * which isn't really a queue yet. (TODO: better encapsulation)
  */
 void run_env_handler(trapframe_t *tf, void* data)
 {
index 3a6c6a8..db708cf 100644 (file)
@@ -21,7 +21,7 @@
 #include <assert.h>
 #include <monitor.h>
 #include <pmap.h>
-#include <env.h>
+#include <process.h>
 #include <trap.h>
 #include <testing.h>
 #include <syscall.h>
index dcc09d2..2853037 100644 (file)
@@ -7,7 +7,7 @@
 #include <assert.h>
 #include <kdebug.h>
 #include <pmap.h>
-#include <env.h>
+#include <process.h>
 
 #include <ros/memlayout.h>
 
index b335394..f5091ed 100644 (file)
@@ -48,7 +48,7 @@ ssize_t kfs_lookup_path(char* path)
  * This should take a real inode or something to point to the real location,
  * and env_create shouldn't assume everything is contiguous
  */
-env_t* kfs_proc_create(size_t kfs_inode)
+struct proc *kfs_proc_create(size_t kfs_inode)
 {
        if (kfs_inode < 0 || kfs_inode >= MAX_KFS_FILES)
                panic("Invalid kfs_inode.  Check you error codes!");
index 6d0363d..2aaba72 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2009 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
  * See LICENSE for details.
  */
 
@@ -13,7 +14,7 @@
 
 #include <assert.h>
 #include <manager.h>
-#include <env.h>
+#include <process.h>
 #include <workqueue.h>
 #include <syscall.h>
 #include <testing.h>
@@ -32,17 +33,19 @@ void manager(void)
        switch (progress++) {
                case 0:
                        // careful, should check error codes returned from kfs_lookup_path
-                       envs[0] = kfs_proc_create(kfs_lookup_path("roslib_spawn"));
+                       //envs[0] = kfs_proc_create(kfs_lookup_path("roslib_spawn"));
+                       envs[0] = kfs_proc_create(kfs_lookup_path("roslib_hello"));
+                       proc_set_state(envs[0], PROC_RUNNABLE_S);
                        env_run(envs[0]);
                        break;
                case 1:
-                       #if 0
                        envs[0] = ENV_CREATE(roslib_proctests);
                        envs[1] = ENV_CREATE(roslib_proctests);
                        envs[2] = ENV_CREATE(roslib_proctests);
                        envs[3] = ENV_CREATE(roslib_proctests);
                        env_run(envs[0]);
                        break;
+                       #if 0
                        #endif
                case 2:
                case 3:
index 42e4010..0d13da7 100644 (file)
@@ -280,8 +280,9 @@ int mon_kfs_run(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf)
                printk("Bad filename!\n");
                return 1;
        }
-       env_t *new_e = kfs_proc_create(kfs_inode);
-       new_e->env_status = ENV_RUNNABLE;
+       struct proc *p = kfs_proc_create(kfs_inode);
+       // go from PROC_CREATED->PROC_RUNNABLE_S
+       proc_set_state(p, PROC_RUNNABLE_S);
        // 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 5d85506..ba4c5de 100644 (file)
@@ -14,7 +14,7 @@
 #include <assert.h>
 #include <pmap.h>
 #include <kclock.h>
-#include <env.h>
+#include <process.h>
 
 // These variables are set by i386_detect_memory()
 static physaddr_t maxpa;       // Maximum physical address
diff --git a/kern/src/process.c b/kern/src/process.c
new file mode 100644 (file)
index 0000000..0e02b90
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2009 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ */
+
+#include <process.h>
+#include <atomic.h>
+#include <assert.h>
+
+/*
+ * While this could be done with just an assignment, this gives us the
+ * opportunity to check for bad transitions.  Might compile these out later, so
+ * we shouldn't rely on them for sanity checking from userspace.
+ */
+int proc_set_state(struct proc *p, proc_state_t state)
+{
+       proc_state_t curstate = p->state;
+       /* Valid transitions:
+        * C   -> RBS
+        * RBS -> RGS
+        * RGS -> RBS
+        * RGS -> W
+        * W   -> RBS
+        * RGS -> RBM
+        * RBM -> RGM
+        * RGM -> RBM
+        * RGM -> RBS
+        * RGS -> D
+        * RGM -> D
+        * 
+        * These ought to be implemented later
+        * RBS -> D
+        * RBM -> D
+        */
+       #if 1 // some sort of correctness flag
+       switch (curstate) {
+               case PROC_CREATED:
+                       if (state != PROC_RUNNABLE_S)
+                               panic("Invalid State Transition! PROC_CREATED to %d", state);
+                       break;
+               case PROC_RUNNABLE_S:
+                       if (state != PROC_RUNNING_S && state != PROC_DYING)
+                               panic("Invalid State Transition! PROC_RUNNABLE_S to %d", state);
+                       break;
+               case PROC_RUNNING_S:
+                       if (state != PROC_RUNNABLE_S && state != PROC_RUNNABLE_M &&
+                           state != PROC_WAITING && state != PROC_DYING)
+                               panic("Invalid State Transition! PROC_RUNNING_S to %d", state);
+                       break;
+               case PROC_WAITING:
+                       if (state != PROC_RUNNABLE_S)
+                               panic("Invalid State Transition! PROC_WAITING to %d", state);
+                       break;
+               case PROC_DYING:
+                       if (state != PROC_CREATED) // when it is reused
+                               panic("Invalid State Transition! PROC_DYING to %d", state);
+                       break;
+               case PROC_RUNNABLE_M:
+                       if (state != PROC_RUNNING_M && state != PROC_DYING)
+                               panic("Invalid State Transition! PROC_RUNNABLE_M to %d", state);
+                       break;
+               case PROC_RUNNING_M:
+                       if (state != PROC_RUNNABLE_S && state != PROC_RUNNABLE_M &&
+                           state != PROC_DYING)
+                               panic("Invalid State Transition! PROC_RUNNING_M to %d", state);
+                       break;
+       }
+       #endif
+       p->state = state;
+       return 0;
+}
+
+/* Change this when we aren't using an array */
+struct proc *get_proc(unsigned pid)
+{
+       // should have some error checking when we do this for real
+       return &envs[ENVX(pid)];
+}
+
+/* Whether or not actor can control target */
+bool proc_controls(struct proc *actor, struct proc *target)
+{
+       return target->env_parent_id == actor->env_id;
+}
index d9052da..94fb914 100644 (file)
@@ -13,7 +13,7 @@
 #include <string.h>
 #include <assert.h>
 #include <pmap.h>
-#include <env.h>
+#include <process.h>
 #include <trap.h>
 
 volatile uint8_t num_cpus = 0xee;
index 8524025..cd0116e 100644 (file)
@@ -13,7 +13,7 @@
 
 #include <string.h>
 #include <assert.h>
-#include <env.h>
+#include <process.h>
 #include <pmap.h>
 #include <trap.h>
 #include <syscall.h>
@@ -197,6 +197,7 @@ static envid_t sys_getcpuid(void)
        return lapic_get_id();
 }
 
+// TODO FIX Me!!!! for processes
 // Destroy a given environment (possibly the currently running environment).
 //
 // Returns 0 on success, < 0 on error.  Errors are:
@@ -220,14 +221,22 @@ 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(env_t *e)
+static void sys_yield(struct proc *p)
 {
        // TODO: watch for races throughout anything related to process statuses
        // and schedule/yielding
-       assert(e->env_status == ENV_RUNNING);
-       e->env_status = ENV_RUNNABLE;
+       assert(p->state == PROC_RUNNING_S);
+       p->state = PROC_RUNNABLE_S;
        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 last core, switch to runnable_s
+        */
 }
 
 /*
@@ -235,8 +244,9 @@ static void sys_yield(env_t *e)
  * 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.
  * TODO: once we have a decent VFS, consider splitting this up
+ * and once there's an mmap, can have most of this in process.c
  */
-static int sys_proc_create(env_t *e, const char *DANGEROUS path)
+static int sys_proc_create(struct proc *p, const char *DANGEROUS path)
 {
        #define MAX_PATH_LEN 256 // totally arbitrary
        int pid = 0;
@@ -247,30 +257,31 @@ static int sys_proc_create(env_t *e, const char *DANGEROUS path)
         * whether or not it's a big deal that the pointer could be into kernel
         * space, and resolving both of these without knowing the length of the
         * string. (TODO)
+        * Change this so that all syscalls with a pointer take a length.
         */
        strncpy(tpath, path, MAX_PATH_LEN);
        int kfs_inode = kfs_lookup_path(tpath);
        if (kfs_inode < 0)
                return -EINVAL;
-       env_t *new_e = kfs_proc_create(kfs_inode);
-       return new_e->env_id; // TODO replace this with a real proc_id
+       struct proc *new_p = kfs_proc_create(kfs_inode);
+       return new_p->env_id; // TODO replace this with a real proc_id
 }
 
 /* Makes process PID runnable.  Consider moving the functionality to env.c */
-static error_t sys_proc_run(env_t *e, int pid)
+static error_t sys_proc_run(struct proc *p, unsigned pid)
 {
-       // TODO PIDs are currently env_id's and encapsulate these functions better
-       // TODO worry about concurrency on the statuses
-       // get the target process (techincally a neg number is invalid)
-       env_t *target = &envs[ENVX(pid)];
-       // make sure it's in the right state to be activated
-       if (target->env_status != ENV_CREATED)
-               return -EINVAL;
-       // make sure we have access (are the parent of)
-       if (target->env_parent_id != e->env_id)
-               return -EINVAL;
-       target->env_status = ENV_RUNNABLE;      
-       return 0;
+       struct proc *target = get_proc(pid);
+       error_t retval = 0;
+       spin_lock(&p->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))
+               retval = -EPERM;
+       else if (target->state != PROC_CREATED)
+               retval = -EINVAL;
+       else
+               proc_set_state(target, PROC_RUNNABLE_S);
+       spin_unlock(&p->lock);
+       return retval;
 }
 
 // TODO: Build a dispatch table instead of switching on the syscallno
index b99ce2e..3e27500 100644 (file)
@@ -15,7 +15,7 @@
 #include <string.h>
 #include <testing.h>
 #include <trap.h>
-#include <env.h>
+#include <process.h>
 #include <syscall.h>
 
 #define test_vector 0xeb
index 561fddf..2767f67 100644 (file)
@@ -11,7 +11,7 @@
 #include <pmap.h>
 #include <trap.h>
 #include <monitor.h>
-#include <env.h>
+#include <process.h>
 
 #include <syscall.h>
 
@@ -219,9 +219,8 @@ void
        // should this be if == 3?  Sort out later when we handle traps.
        // so far we never get here
        assert(0);
-        // Return to the current environment, which should be runnable.
-        assert(current && current->env_status == ENV_RUNNABLE);
-        env_run(current);
+       // Return to the current environment, which should be runnable.
+       env_run(current);
 }
 
 void