Decouples running remote envs from smp_call
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 21 May 2009 22:14:33 +0000 (15:14 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Tue, 26 May 2009 04:51:38 +0000 (21:51 -0700)
Adds a workqueue, checked from smp_idle, that allows the env to run to
be added from within the smp_call interrupt handler, but then run after
iretting from the interrupt (in smp_idle).  Needs some more logical
organization, as well as work with env_destroy and run from a management
core (core 0).

include/arch/smp.h
include/arch/x86.h
include/ros/env.h
include/workqueue.h [new file with mode: 0644]
kern/src/Makefrag
kern/src/env.c
kern/src/init.c
kern/src/smp.c
kern/src/smp_entry.S
kern/src/workqueue.c [new file with mode: 0644]

index 3599d0d..a52fb5c 100644 (file)
@@ -7,6 +7,7 @@
 #include <ros/queue.h>
 #include <atomic.h>
 #include <trap.h>
+#include <workqueue.h>
 
 #ifdef __BOCHS__
 #define SMP_CALL_FUNCTION_TIMEOUT    0x00ffffff
@@ -24,6 +25,13 @@ typedef struct HandlerWrapper {
        uint8_t vector;
 } handler_wrapper_t;
 
+typedef struct per_cpu_info {
+       // Once we have a real kmalloc, we can make this dynamic.  Want a queue.
+       work_t delayed_work;
+       // will want it padded out to an even cacheline
+} per_cpu_info_t;
+extern per_cpu_info_t per_cpu_info[MAX_NUM_CPUS];
+
 /* SMP bootup functions */
 void smp_boot(void);
 void smp_idle(void);
index fc21511..6aacc53 100644 (file)
@@ -76,6 +76,7 @@ static __inline void disable_irq(void) __attribute__((always_inline));
 static __inline void enable_irqsave(int8_t* state) __attribute__((always_inline));
 static __inline void disable_irqsave(int8_t* state) __attribute__((always_inline));
 static __inline void cpu_relax(void) __attribute__((always_inline));
+static __inline void cpu_halt(void) __attribute__((always_inline));
 static __inline void wbinvd(void) __attribute__((always_inline));
 static __inline void clflush(uintptr_t* addr) __attribute__((always_inline));
 
@@ -408,6 +409,12 @@ cpu_relax(void)
 }
 
 static __inline void
+cpu_halt(void)
+{
+       asm volatile("hlt" : : : "memory");
+}
+
+static __inline void
 wbinvd(void) __attribute__((always_inline))
 {
        asm volatile("wbinvd");
index 98bf62f..f07fa11 100644 (file)
@@ -35,9 +35,10 @@ typedef int32_t envid_t;
 #define ENVX(envid)            ((envid) & (NENV - 1))
 
 // Values of env_status in struct Env
-#define ENV_FREE               0
-#define ENV_RUNNABLE           1
-#define ENV_NOT_RUNNABLE       2
+#define ENV_FREE                       0
+#define ENV_RUNNING                    1
+#define ENV_RUNNABLE           2
+#define ENV_NOT_RUNNABLE       3
 
 struct Env {
        trapframe_t env_tf;                     // Saved registers
diff --git a/include/workqueue.h b/include/workqueue.h
new file mode 100644 (file)
index 0000000..7010d74
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef ROS_KERN_WORKQUEUE_H
+#define ROS_KERN_WORKQUEUE_H
+#ifndef ROS_KERNEL
+# error "This is an ROS kernel header; user programs should not #include it"
+#endif
+
+// Once we have a real kmalloc, we can make this dynamic.  Want a list.
+typedef void (*func_t)(void* data);
+typedef struct work {
+       func_t func;
+       void* data;
+} work_t;
+
+void process_workqueue(void);
+
+#endif /* ROS_KERN_WORKQUEUE_H */
index d6dba6b..4e323ee 100644 (file)
@@ -28,6 +28,7 @@ KERN_SRCFILES := $(KERN_SRC_DIR)/entry.S \
                  $(KERN_SRC_DIR)/kdebug.c \
                  $(KERN_SRC_DIR)/apic.c \
                  $(KERN_SRC_DIR)/testing.c \
+                 $(KERN_SRC_DIR)/workqueue.c \
                  $(KERN_SRC_DIR)/atomic.c \
                  $(KERN_SRC_DIR)/smp.c \
                  $(KERN_SRC_DIR)/printfmt.c \
index 0ca5f94..ce56600 100644 (file)
@@ -461,6 +461,7 @@ env_free(env_t *e)
 void
 env_destroy(env_t *e)
 {
+       uint32_t status;
        env_free(e);
 
        // for old envs that die on user cores.  since env run never returns, cores
@@ -473,10 +474,15 @@ env_destroy(env_t *e)
        }
        // else we're core 0 and can do the usual
 
+//TODO: consider returning to a dispatching function instead of this, since we
+//can't get back to init.
        // ugly, but for now just linearly search through all possible
        // environments for a runnable one.
        for (int i = 0; i < NENV; i++) {
                e = &envs[ENVX(i)];
+               // TODO: 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)
                        env_run(e);
        }
@@ -525,7 +531,7 @@ env_run(env_t *e)
        //      and make sure you have set the relevant parts of
        //      e->env_tf to sensible values.
 
-               // would set the curenv->env_status if we had more states
+       e->env_status = ENV_RUNNING;
        if (e != curenvs[lapic_get_id()]) {
                curenvs[lapic_get_id()] = e;
                e->env_runs++;
index fd0a753..a83efe0 100644 (file)
 
 static void print_cpuinfo(void);
 
+static void work_env_run(void* data)
+{
+       env_run((env_t*)data);
+}
+
 static void run_env_handler(trapframe_t *tf, void* data)
 {
        assert(data);
-       env_run((env_t*)data);
+       per_cpu_info[lapic_get_id()].delayed_work.func = work_env_run;
+       per_cpu_info[lapic_get_id()].delayed_work.data = data;
 }
 
 void kernel_init(multiboot_info_t *mboot_info)
@@ -82,25 +88,35 @@ void kernel_init(multiboot_info_t *mboot_info)
        //ENV_CREATE(user_buggyhello);
        //ENV_CREATE(user_evilhello);
        //ENV_CREATE(user_hello);
-       ENV_CREATE(user_apps_parlib_hello);
-       ENV_CREATE(user_apps_roslib_hello);
+       //ENV_CREATE(user_apps_parlib_hello);
+       //ENV_CREATE(user_apps_roslib_hello);
        //ENV_CREATE(user_hello);
        //ENV_CREATE(user_hello);
        //ENV_CREATE(user_hello);
-       //ENV_CREATE(user_null);
-       //ENV_CREATE(user_null);
-       env_run(&envs[0]);
-       //env_run(&envs[1]);
-       panic("Don't Panic");
-       // ENV_CREATE(user_null);
-       /*ENV_CREATE(user_null);
+       ENV_CREATE(user_null);
+       ENV_CREATE(user_null);
+       ENV_CREATE(user_null);
+       ENV_CREATE(user_null);
+       ENV_CREATE(user_null);
+       ENV_CREATE(user_null);
+       ENV_CREATE(user_null);
        ENV_CREATE(user_null);
 
        //env_run(&envs[0]);
-       smp_call_function_single(2, run_env_handler, &envs[0], 0);
-       //smp_call_function_single(4, run_env_handler, &envs[1], 0);
-       //smp_call_function_single(6, run_env_handler, &envs[2], 0);
+       //env_run(&envs[1]);
+       smp_call_function_single(0, run_env_handler, &envs[0], 0);
+       smp_call_function_single(1, run_env_handler, &envs[1], 0);
+       smp_call_function_single(2, run_env_handler, &envs[2], 0);
+       smp_call_function_single(3, run_env_handler, &envs[3], 0);
+       smp_call_function_single(4, run_env_handler, &envs[4], 0);
+       smp_call_function_single(5, run_env_handler, &envs[5], 0);
+       smp_call_function_single(6, run_env_handler, &envs[6], 0);
+       smp_call_function_single(7, run_env_handler, &envs[7], 0);
+       process_workqueue();
+       udelay(5000000);
+       panic("Don't Panic");
 
+       /*
        // wait 5 sec, then print what's in shared mem
        udelay(5000000);
        printk("Servicing syscalls from Core 0:\n\n");
index c9f9438..f3e84e2 100644 (file)
@@ -17,6 +17,7 @@
 
 volatile uint8_t num_cpus = 0xee;
 uintptr_t smp_stack_top;
+per_cpu_info_t per_cpu_info[MAX_NUM_CPUS];
 
 /*************************** IPI Wrapper Stuff ********************************/
 // checklists to protect the global interrupt_handlers for 0xf0, f1, f2, f3, f4
@@ -209,12 +210,18 @@ uint32_t smp_main(void)
        return my_stack_top; // will be loaded in smp_entry.S
 }
 
-// this idles a core, sometimes we need to call it directly.  never returns.
-// the pause is somewhat of a hack, since kvm isn't halting.  not sure what the
-// deal is with that.
+/* 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.  
+ */
 void smp_idle(void)
 {
-       asm volatile("1: hlt; pause; jmp 1b;");
+       enable_irq();
+       while (1) {
+               process_workqueue();
+               // consider races with work added after we started leaving the last func
+               cpu_halt();
+       }
 }
 
 static int smp_call_function(uint8_t type, uint8_t dest, isr_t handler, void* data,
index 95bcb9f..feac967 100644 (file)
@@ -85,12 +85,7 @@ here:
        # note the next two lines are using the direct mapping from smp_boot()
        movw    $0, smp_boot_lock - smp_entry + 0x1000  # release lock
        lock decw       smp_semaphore - smp_entry + 0x1000  # show we are done
-
-       sti                                             # turn on interrupts, and wait for an IPI
-spinwait:
-       hlt                                             # hlts, else will just spin.
-       pause                                   # hack, since kvm is ignoring the hlt.
-       jmp             spinwait
+       call    smp_idle                # idle loop, will have interrupts turned on
 
        # Below here is just data, stored with the code text
        .p2align        2                                               # force 4 byte alignment
diff --git a/kern/src/workqueue.c b/kern/src/workqueue.c
new file mode 100644 (file)
index 0000000..ca0e691
--- /dev/null
@@ -0,0 +1,22 @@
+#include <inc/x86.h>
+
+#include <kern/workqueue.h>
+#include <kern/apic.h>
+#include <kern/smp.h>
+
+/*
+ * TODO: actually use a queue, which will change some things all over.
+ */
+void process_workqueue()
+{
+       work_t work;
+       // copy the work in, since we may never return to this stack frame
+       work = per_cpu_info[lapic_get_id()].delayed_work;
+       if (work.func) {
+               // TODO: possible race with this.  sort it out when we have a queue.
+               // probably want a spin_lock_irqsave
+               per_cpu_info[lapic_get_id()].delayed_work.func = 0;
+               // We may never return from this (if it is env_run)
+               work.func(work.data);
+       }
+}