Can process async calls on processes that die
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 25 May 2009 23:15:15 +0000 (16:15 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Tue, 26 May 2009 04:59:35 +0000 (21:59 -0700)
Has protections and a ref count to keep a process alive while its
address space/context is being used.  Specifically, when processing
async calls.

include/env.h
include/ros/env.h
include/syscall.h
kern/src/env.c
kern/src/manager.c
kern/src/syscall.c
user/apps/roslib/print_tests.c

index 7f76553..c814d77 100644 (file)
@@ -5,6 +5,7 @@
 
 #include <arch/x86.h>
 #include <ros/env.h>
+#include <error.h>
 
 extern env_t *envs;            // All environments
 extern env_t* NORACE curenvs[MAX_NUM_CPUS];
@@ -14,6 +15,8 @@ LIST_HEAD(env_list_t, env_t);         // Declares 'struct Env_list'
 void   env_init(void);
 int            env_alloc(env_t **e, envid_t parent_id);
 void   env_free(env_t *e);
+error_t        env_incref(env_t* e);
+void   env_decref(env_t* e);
 env_t* env_create(uint8_t *binary, size_t size);
 void   (IN_HANDLER env_destroy)(env_t *e);     // Does not return if e == curenv
 
index f07fa11..6a374bf 100644 (file)
@@ -39,14 +39,17 @@ typedef int32_t envid_t;
 #define ENV_RUNNING                    1
 #define ENV_RUNNABLE           2
 #define ENV_NOT_RUNNABLE       3
+#define ENV_DYING                      4
 
 struct Env {
+       uint32_t lock;
        trapframe_t env_tf;                     // Saved registers
        LIST_ENTRY(env_t) env_link;     // Free list link pointers
        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
        uint32_t env_runs;                      // Number of times environment has run
+       uint32_t env_refcnt;            // Reference count of kernel contexts using this
        // Note this is the actual backring, not a pointer to it somewhere else
        syscall_back_ring_t env_sysbackring;    // BackRing for generic syscalls
 
index 744def3..3497a34 100644 (file)
@@ -10,6 +10,6 @@
 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);
 int32_t syscall_async(env_t* e, syscall_req_t *syscall);
-uint32_t process_generic_syscalls(env_t* e, uint32_t max);
+int32_t process_generic_syscalls(env_t* e, uint32_t max);
 void syscall_wrapper(struct Trapframe *tf);
 #endif /* !ROS_KERN_SYSCALL_H */
index 53fd138..85799d3 100644 (file)
@@ -217,6 +217,7 @@ env_alloc(env_t **newenv_store, envid_t parent_id)
        e->env_parent_id = parent_id;
        e->env_status = ENV_RUNNABLE;
        e->env_runs = 0;
+       e->env_refcnt = 1;
 
        // Clear out all the saved register state,
        // to prevent the register values
@@ -447,6 +448,42 @@ env_free(env_t *e)
        LIST_INSERT_HEAD(&env_free_list, e, env_link);
 }
 
+/*
+ * This allows the kernel to keep this process around, in case it is being used
+ * in some asynchronous processing.
+ * 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.
+ * TODO: Make sure this is never called from an interrupt handler (irq_save)
+ */
+error_t env_incref(env_t* e)
+{
+       error_t retval = 0;
+       spin_lock(&e->lock);
+       if (e->env_refcnt)
+               e->env_refcnt++;
+       else
+               retval = E_BAD_ENV;
+       spin_unlock(&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.
+ * env_destroy calls this.
+ * TODO: Make sure this is never called from an interrupt handler (irq_save)
+ */
+void env_decref(env_t* e)
+{
+       spin_lock(&e->lock);
+       e->env_refcnt--;
+       spin_unlock(&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);
+}
+
+
 //
 // Frees environment e.
 // If e was the current env, then runs a new environment (and does not return
@@ -455,8 +492,9 @@ env_free(env_t *e)
 void
 env_destroy(env_t *e)
 {
-       uint32_t status;
-       env_free(e);
+       // TODO: race condition with env statuses, esp when running / destroying
+       e->env_status = ENV_DYING;
+       env_decref(e);
 
        // 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
@@ -534,6 +572,8 @@ 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
+       // Could up the refcnt and down it when a process is not running
        e->env_status = ENV_RUNNING;
        if (e != curenvs[lapic_get_id()]) {
                curenvs[lapic_get_id()] = e;
index bf50ee6..61c5476 100644 (file)
@@ -15,6 +15,7 @@
 #include <kern/env.h>
 #include <kern/apic.h>
 #include <kern/workqueue.h>
+#include <kern/syscall.h>
 
 /* Helper handlers for smp_call to dispatch jobs to other cores */
 static void work_env_run(void* data)
@@ -41,12 +42,21 @@ void manager(void)
 
        switch (progress++) {
                case 0:
-                       for (int i = 0; i < 8; i++)
-                               env_batch[i] = ENV_CREATE(user_null);
-                       for (int i = 0; i < 8; i++)
+                       for (int i = 2; i < 8; i++)
+                               env_batch[i] = ENV_CREATE(user_hello);
+                       for (int i = 2; i < 8; i++)
                                smp_call_function_single(i, run_env_handler, env_batch[i], 0);
+                       int count = 0;
+                       while (count > -6) {
+                               count = 0;
+                               for (int i = 2; i < 8; i++) {
+                                       count += process_generic_syscalls(env_batch[i], 1);
+                               }
+                               cpu_relax();
+                       }
                        process_workqueue(); // Will run this core (0)'s env
-                       break;
+                       panic("Don't Panic");
+                       break; // only need this when planning to reenter manager
                case 1:
                        for (int i = 0; i < 4; i++)
                                env_batch[i] = ENV_CREATE(user_null);
index 5fe4521..fc898ab 100644 (file)
@@ -52,7 +52,7 @@ static void sys_cache_buster(env_t* e, uint32_t num_writes, uint32_t val)
        #define MAX_WRITES 1048576
        uint32_t* buster = (uint32_t*)BUSTER_ADDR;
        static uint32_t buster_lock = 0;
-       
+
        spin_lock(&buster_lock);
        for (int i = 0; i < MIN(num_writes, MAX_WRITES); i++)
                buster[i] = val;
@@ -171,15 +171,14 @@ int32_t syscall_async(env_t* e, syscall_req_t *call)
                       call->args[2], call->args[3], call->args[4]);
 }
 
-uint32_t process_generic_syscalls(env_t* e, uint32_t max)
+int32_t process_generic_syscalls(env_t* e, uint32_t max)
 {
        uint32_t count = 0;
        syscall_back_ring_t* sysbr = &e->env_sysbackring;
 
-       // make sure the env is still alive.  TODO: this cannot handle an env being
-       // freed async while this is processing.  (need a ref count or lock, etc).
-       if (e->env_status == ENV_FREE)
-               return 0;
+       // make sure the env is still alive.  incref will return 0 on success.
+       if (env_incref(e))
+               return -1;
 
        // need to switch to the right context, so we can handle the user pointer
        // that points to a data payload of the syscall
@@ -203,5 +202,6 @@ uint32_t process_generic_syscalls(env_t* e, uint32_t max)
                //printk("DEBUG POST: sring->req_prod: %d, sring->rsp_prod: %d\n",\
                           sysbr->sring->req_prod, sysbr->sring->rsp_prod);
        }
+       env_decref(e);
        return count;
 }
index 80e6411..0bbb6b7 100644 (file)
@@ -1,4 +1,5 @@
 #include <inc/lib.h>
+#include <inc/null.h>
 
 #ifdef __DEPUTY__
 #pragma nodeputy
@@ -6,26 +7,28 @@
 
 int main(int argc, char** argv)
 {
-       cprintf("goodbye, world!\n");
-       cprintf("i am environment %08x\n", env->env_id);
+       cprintf("i am environment %08x, running on core %d\n", env->env_id, getcpuid());
 
-       cprintf("about to write to shared mem.  hope it gets printed.  blimey! \n");
        async_desc_t *desc1, *desc2, *desc3;
        async_rsp_t rsp1, rsp2, rsp3;
        cprintf_async(&desc1, "Cross-Core call 1, coming from env %08x\n", env->env_id);
-       cprintf("Call 1 is sent!\n");
+       cprintf_async(&desc1, "Cross-Core call 1, coming from env %08x\n", env->env_id);
+       cprintf_async(&desc1, "Cross-Core call 1, coming from env %08x\n", env->env_id);
+       cprintf_async(&desc1, "Cross-Core call 1, coming from env %08x\n", env->env_id);
+       cprintf_async(&desc1, "Cross-Core call 1, coming from env %08x\n", env->env_id);
+       //cprintf("Call 1 is sent!\n");
        //cprintf_async(&desc2, "Cross-Core call 2, coming from env %08x\n", env->env_id);
-       cprintf_async(&desc2, "1111111111111111111111111111111122222222222222222222222222222222333333333333333333333333333333334444444444444444444444444444444455555555555555555555555555555555666666666666666666666666666666667777777777777777777777777777777788888888888888888888888888888888Cross-Core call 2, coming from env %08x\n", env->env_id);
-       cprintf("Call 2 is sent!\n");
-       cprintf("Waiting on Call 1 and 2\n");
+       //cprintf_async(&desc2, "1111111111111111111111111111111122222222222222222222222222222222333333333333333333333333333333334444444444444444444444444444444455555555555555555555555555555555666666666666666666666666666666667777777777777777777777777777777788888888888888888888888888888888Cross-Core call 2, coming from env %08x\n", env->env_id);
+       //cprintf("Call 2 is sent!\n");
+       //cprintf("Waiting on Call 1\n");
        waiton_async_call(desc1, &rsp1);
-       cprintf("Received 1\n");
-       waiton_async_call(desc2, &rsp2);
-       cprintf_async(&desc3, "Cross-Core call 3, coming from env %08x\n", env->env_id);
-       cprintf("Call 3 is sent!\n");
-       waiton_async_call(desc3, &rsp3);
+       //cprintf("Received 1\n");
+       //waiton_async_call(desc2, &rsp2);
+//     cprintf_async(&desc3, "Cross-Core call 3, coming from env %08x\n", env->env_id);
+//     cprintf("Call 3 is sent!\n");
+//     waiton_async_call(desc3, &rsp3);
        // might as well spin, just to make sure nothing gets deallocated
        // while we're waiting to test the async call
-       while (1);
-       return 0;
+       //while (1);
+       //cprintf("DYING: environment %08x\n", env->env_id);
 }