Workqueue interface and coreid()
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 24 Jul 2009 22:39:57 +0000 (15:39 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 24 Jul 2009 22:39:57 +0000 (15:39 -0700)
Cleaned up the workqueue interface, even though it still only has capacity for
one item.  This will make things easier when we have to check it when
env_running, among other things.

Also, replaced all lapic_get_id()s with coreid(), which uses the cpuid
instruction.  We can change this later, but for now I'm stuck with it. (KVM
thing, maybe).

16 files changed:
include/arch/apic.h
include/arch/smp.h
include/arch/x86.h
include/env.h
include/workqueue.h
kern/src/apic.c
kern/src/atomic.c
kern/src/env.c
kern/src/init.c
kern/src/manager.c
kern/src/monitor.c
kern/src/smp.c
kern/src/syscall.c
kern/src/testing.c
kern/src/trap.c
kern/src/workqueue.c

index 613f42e..427c03e 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.
  */
 
@@ -110,6 +111,7 @@ static inline void lapic_send_eoi(void);
 static inline uint32_t lapic_get_version(void);
 static inline uint32_t lapic_get_error(void);
 static inline uint32_t lapic_get_id(void);
+static inline void lapic_set_id(uint8_t id); // Careful, may not actually work
 static inline uint8_t lapic_get_logid(void);
 static inline void lapic_set_logid(uint8_t id);
 static inline void lapic_disable(void);
@@ -156,6 +158,11 @@ static inline uint32_t lapic_get_id(void)
        return read_mmreg32(LAPIC_ID) >> 24;
 }
 
+static inline void lapic_set_id(uint8_t id)
+{
+       write_mmreg32(LAPIC_ID, id << 24);
+}
+
 static inline uint8_t lapic_get_logid(void)
 {
        return read_mmreg32(LAPIC_LOGICAL_ID) >> 24;
index f0ddba9..7d03cd3 100644 (file)
@@ -1,3 +1,9 @@
+/*
+ * Copyright (c) 2009 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ */
+
 #ifndef ROS_INC_SMP_H
 #define ROS_INC_SMP_H
 
@@ -9,14 +15,6 @@
 #include <trap.h>
 #include <workqueue.h>
 
-#ifdef __BOCHS__
-#define SMP_CALL_FUNCTION_TIMEOUT    0x00ffffff
-#define SMP_BOOT_TIMEOUT             0x0000ffff
-#else
-#define SMP_CALL_FUNCTION_TIMEOUT    0x7ffffff0
-#define SMP_BOOT_TIMEOUT             0x002fffff
-#endif
-
 // be careful changing this, esp if you go over 16
 #define NUM_HANDLER_WRAPPERS           5
 
@@ -25,13 +23,12 @@ typedef struct HandlerWrapper {
        uint8_t vector;
 } handler_wrapper_t;
 
-typedef struct per_cpu_info {
+// will want this padded out to cacheline alignment
+struct per_cpu_info {
        uint32_t lock;
-       // 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];
+       struct workqueue workqueue;
+};
+extern struct per_cpu_info  per_cpu_info[MAX_NUM_CPUS];
 extern volatile uint8_t num_cpus;
 
 /* SMP bootup functions */
index 6aacc53..506dd0a 100644 (file)
@@ -79,6 +79,7 @@ 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));
+static __inline uint32_t coreid(void) __attribute__((always_inline));
 
 static __inline void
 breakpoint(void)
@@ -425,4 +426,18 @@ clflush(uintptr_t* addr) __attribute__((always_inline))
 {
        asm volatile("clflush %0" : : "m"(*addr));
 }
+
+/*
+ * Returns the core id.  Unfortunately, this is a serializing instruction, and
+ * may not be the best way either.  This is ripped from lapic_get_default_id().
+ */
+static __inline uint32_t
+coreid(void)
+{
+       uint32_t ebx;
+       cpuid(1, 0, &ebx, 0, 0);
+       // p6 family only uses 4 bits here, and 0xf is reserved for the IOAPIC
+       return (ebx & 0xFF000000) >> 24;
+}
+
 #endif /* !ROS_INC_X86_H */
index 68534ef..323a2f2 100644 (file)
@@ -85,7 +85,7 @@ void  schedule(void);
  * Allows the kernel to figure out what process is running on its core.
  * Can be used just like a pointer to a struct process.
  */
-#define current (curenvs[lapic_get_id()])
+#define current (curenvs[coreid()])
 
 int    envid2env(envid_t envid, env_t **env_store, bool checkperm);
 // The following three functions do not return
index c06d39c..ba7dc15 100644 (file)
@@ -1,21 +1,34 @@
 /*
  * Copyright (c) 2009 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
  * See LICENSE for details.
+ *
+ * Workqueue: This is a todo list of func, void* that get executed whenever
+ * process_workqueue is called.  Typically, this is called from smp_idle().
+ * Note that every core will run this, so be careful with dynamic memory mgmt.
  */
 
 #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 {
+#include <sys/queue.h>
+#include <arch/types.h>
+
+typedef void (*func_t)(void *data);
+struct work {
+       LIST_ENTRY(work) work_link;
        func_t func;
-       void* data;
-} work_t;
+       void *data;
+};
+
+// TODO make these dynamic and hold more than 1.  might want better list macros.
+#define WORKQUEUE_ELEMENTS 1
+struct workqueue {
+       struct work statics[WORKQUEUE_ELEMENTS];
+};
 
 void process_workqueue(void);
+// For now, the caller should free their struct work after this call
+int enqueue_work(struct workqueue *queue, struct work *job);
 
 #endif /* ROS_KERN_WORKQUEUE_H */
index dd17656..d151e5a 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.
  */
 
index e823ec3..84cf62f 100644 (file)
@@ -104,7 +104,7 @@ void reset_checklist(checklist_t* list)
 // CPU mask specific - this is how cores report in
 void down_checklist(checklist_t* list)
 {
-       CLR_BITMASK_BIT_ATOMIC(list->mask.bits, lapic_get_id());
+       CLR_BITMASK_BIT_ATOMIC(list->mask.bits, coreid());
 }
 
 /* Barriers */
index da395c2..fbfbc6e 100644 (file)
@@ -540,7 +540,7 @@ env_destroy(env_t *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
        // them back to an idle function.
-       uint32_t id = lapic_get_id();
+       uint32_t id = coreid();
        // There is no longer a current process for this core. (TODO: Think about this.)
        current = NULL;
        if (id) {
@@ -671,6 +671,10 @@ env_run(env_t *e)
         * would like to leave interrupts on too, so long as we come back.
         * Consider a moveable flag or something
         */
+       /* 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.
+        */
        if (e->env_tf.tf_cs)
                env_pop_tf(&e->env_tf);
        else
@@ -679,16 +683,17 @@ 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. (TODO: better encapsulation)
+ * which (incidentally) can only hold one item at this point.
  */
-void run_env_handler(trapframe_t *tf, voiddata)
+void run_env_handler(trapframe_t *tf, void *data)
 {
        assert(data);
-       per_cpu_info_t *cpuinfo = &per_cpu_info[lapic_get_id()];
-       spin_lock_irqsave(&cpuinfo->lock);
+       struct work job;
+       struct workqueue *workqueue = &per_cpu_info[coreid()].workqueue;
        { TRUSTEDBLOCK // TODO: how do we make this func_t cast work?
-       cpuinfo->delayed_work.func = (func_t)env_run;
-       cpuinfo->delayed_work.data = data;
+       job.func = (func_t)env_run;
+       job.data = data;
        }
-       spin_unlock_irqsave(&cpuinfo->lock);
+       if (enqueue_work(workqueue, &job))
+               panic("Failed to enqueue work!");
 }
index db708cf..d2702bd 100644 (file)
@@ -78,14 +78,14 @@ void _panic(const char *file, int line, const char *fmt,...)
        va_list ap;
 
        va_start(ap, fmt);
-       cprintf("kernel panic at %s:%d, from core %d: ", file, line, lapic_get_id());
+       cprintf("kernel panic at %s:%d, from core %d: ", file, line, coreid());
        vcprintf(fmt, ap);
        cprintf("\n");
        va_end(ap);
 
 dead:
        /* break into the kernel monitor, if we're core 0 */
-       if (lapic_get_id()) {
+       if (coreid()) {
                smp_idle();
                panic("should never see me");
        }
@@ -99,7 +99,7 @@ void _warn(const char *file, int line, const char *fmt,...)
        va_list ap;
 
        va_start(ap, fmt);
-       cprintf("kernel warning at %s:%d, from core %d: ", file, line, lapic_get_id());
+       cprintf("kernel warning at %s:%d, from core %d: ", file, line, coreid());
        vcprintf(fmt, ap);
        cprintf("\n");
        va_end(ap);
index 2aaba72..31b25a9 100644 (file)
@@ -32,13 +32,12 @@ 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_hello"));
                        proc_set_state(envs[0], PROC_RUNNABLE_S);
                        env_run(envs[0]);
                        break;
                case 1:
+                       panic("Do not panic");
                        envs[0] = ENV_CREATE(roslib_proctests);
                        envs[1] = ENV_CREATE(roslib_proctests);
                        envs[2] = ENV_CREATE(roslib_proctests);
@@ -48,6 +47,15 @@ void manager(void)
                        #if 0
                        #endif
                case 2:
+                       #if 0
+                       // reminder of how to spawn remotely
+                       for (int i = 0; i < 8; i++) {
+                               envs[i] = kfs_proc_create(kfs_lookup_path("roslib_hello"));
+                               proc_set_state(envs[i], PROC_RUNNABLE_S);
+                               smp_call_function_single(i, run_env_handler, envs[i], 0);
+                       }
+                       process_workqueue();
+                       #endif
                case 3:
                #if 0
                case 0:
index 0d13da7..8c405f0 100644 (file)
@@ -219,7 +219,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("Calling CPU's LAPIC ID: 0x%08x\n", lapic_get_id());
+       cprintf("Calling CPU's LAPIC ID: 0x%08x\n", coreid());
        if (argc < 2)
                smp_call_function_self(test_print_info_handler, 0, 0);
        else
@@ -337,8 +337,8 @@ static int runcmd(char *COUNT(CMDBUF_SIZE) real_buf, trapframe_t *tf) {
 void monitor(trapframe_t *tf) {
        char *buf;
 
-       cprintf("Welcome to the ROS kernel monitor!\n");
-       cprintf("Type 'help' for a list of commands.\n");
+       printk("Welcome to the ROS kernel monitor on core %d!\n", coreid());
+       printk("Type 'help' for a list of commands.\n");
 
        if (tf != NULL)
                print_trapframe(tf);
index 94fb914..367922f 100644 (file)
@@ -1,3 +1,9 @@
+/*
+ * Copyright (c) 2009 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ */
+
 #ifdef __DEPUTY__
 #pragma nodeputy
 #endif
@@ -18,7 +24,7 @@
 
 volatile uint8_t num_cpus = 0xee;
 uintptr_t smp_stack_top;
-per_cpu_info_t per_cpu_info[MAX_NUM_CPUS];
+struct per_cpu_info per_cpu_info[MAX_NUM_CPUS];
 
 /*************************** IPI Wrapper Stuff ********************************/
 // checklists to protect the global interrupt_handlers for 0xf0, f1, f2, f3, f4
@@ -210,7 +216,7 @@ uint32_t smp_main(void)
        lapic_enable();
 
        // set a default logical id for now
-       lapic_set_logid(lapic_get_id());
+       lapic_set_logid(coreid());
 
        return my_stack_top; // will be loaded in smp_entry.S
 }
@@ -256,14 +262,14 @@ static int smp_call_function(uint8_t type, uint8_t dest, isr_t handler, void* da
        cpu_mask.size = num_cpus;
        switch (type) {
                case 1: // self
-                       SET_BITMASK_BIT(cpu_mask.bits, lapic_get_id());
+                       SET_BITMASK_BIT(cpu_mask.bits, coreid());
                        break;
                case 2: // all
                        FILL_BITMASK(cpu_mask.bits, num_cpus);
                        break;
                case 3: // all but self
                        FILL_BITMASK(cpu_mask.bits, num_cpus);
-                       CLR_BITMASK_BIT(cpu_mask.bits, lapic_get_id());
+                       CLR_BITMASK_BIT(cpu_mask.bits, coreid());
                        break;
                case 4: // physical mode
                        // note this only supports sending to one specific physical id
@@ -287,7 +293,7 @@ static int smp_call_function(uint8_t type, uint8_t dest, isr_t handler, void* da
        // When we are done, wrapper points to the one we finally got.
        // this wrapper_num trick doesn't work as well if you send a bunch in a row
        // and wait, since you always check your main one (which is currently busy).
-       wrapper_num = lapic_get_id() % NUM_HANDLER_WRAPPERS;
+       wrapper_num = coreid() % NUM_HANDLER_WRAPPERS;
        while(1) {
                wrapper = &handler_wrappers[wrapper_num];
                if (!commit_checklist_wait(wrapper->cpu_list, &cpu_mask))
index cd0116e..85d2cb9 100644 (file)
@@ -112,7 +112,7 @@ static void sys_cache_buster(env_t* e, uint32_t num_writes, uint32_t num_pages,
         * Also, doesn't separate memory for core 0 if it's an async call.
         */
        if (!(flags & BUSTER_SHARED))
-               buster = (uint32_t*)(BUSTER_ADDR + lapic_get_id() * 0x00800000);
+               buster = (uint32_t*)(BUSTER_ADDR + coreid() * 0x00800000);
 
        /* Start the timer, if we're asked to print this info*/
        if (flags & BUSTER_PRINT_TICKS)
@@ -194,7 +194,7 @@ static envid_t sys_getenvid(env_t* e)
 // Returns the id of the cpu this syscall is executed on.
 static envid_t sys_getcpuid(void)
 {
-       return lapic_get_id();
+       return coreid();
 }
 
 // TODO FIX Me!!!! for processes
index 3e27500..fdcaf11 100644 (file)
@@ -68,7 +68,7 @@ void test_pic_reception(void)
        cprintf("PIC1 Mask = 0x%04x\n", inb(PIC1_DATA));
        cprintf("PIC2 Mask = 0x%04x\n", inb(PIC2_DATA));
        unmask_lapic_lvt(LAPIC_LVT_LINT0);
-       cprintf("Core %d's LINT0: 0x%08x\n", lapic_get_id(), read_mmreg32(LAPIC_LVT_LINT0));
+       cprintf("Core %d's LINT0: 0x%08x\n", coreid(), read_mmreg32(LAPIC_LVT_LINT0));
        enable_irq();
        while(1);
 }
@@ -225,8 +225,7 @@ checklist_t* the_global_list;
 
 void test_checklist_handler(trapframe_t *tf, void* data)
 {
-       for (int i = 0; i < SMP_BOOT_TIMEOUT; i++);
-       for (int i = 0; i < SMP_BOOT_TIMEOUT; i++);
+       udelay(1000000);
        down_checklist(the_global_list);
 }
 
@@ -246,7 +245,7 @@ void test_checklists(void)
        CLR_BITMASK(a_list.mask.bits, a_list.mask.size);
        INIT_CHECKLIST_MASK(a_mask, MAX_NUM_CPUS);
        FILL_BITMASK(a_mask.bits, num_cpus);
-       //CLR_BITMASK_BIT(a_mask.bits, lapic_get_id());
+       //CLR_BITMASK_BIT(a_mask.bits, coreid());
        //SET_BITMASK_BIT(a_mask.bits, 1);
        //printk("New mask (1, 17, 25):\n");
        printk("Created new mask, filled up to num_cpus\n");
@@ -281,7 +280,7 @@ void test_smp_call_functions(void)
 {
        handler_wrapper_t *waiter0 = 0, *waiter1 = 0, *waiter2 = 0, *waiter3 = 0,
                          *waiter4 = 0, *waiter5 = 0;
-       uint8_t me = lapic_get_id();
+       uint8_t me = coreid();
        printk("\nCore %d: SMP Call Self (nowait):\n", me);
        printk("---------------------\n");
        smp_call_function_self(test_hello_world_handler, 0, 0);
@@ -542,7 +541,7 @@ void test_run_measurements(uint32_t job_num)
 void test_hello_world_handler(trapframe_t *tf, void* data)
 {
        cprintf("Incoming IRQ, ISR: %d on core %d with tf at 0x%08x\n",
-               tf->tf_trapno, lapic_get_id(), tf);
+               tf->tf_trapno, coreid(), tf);
 }
 
 uint32_t print_info_lock = 0;
@@ -551,7 +550,7 @@ void test_print_info_handler(trapframe_t *tf, void* data)
 {
        spin_lock_irqsave(&print_info_lock);
        cprintf("----------------------------\n");
-       cprintf("This is Core %d\n", lapic_get_id());
+       cprintf("This is Core %d\n", coreid());
        cprintf("MTRR_DEF_TYPE = 0x%08x\n", read_msr(IA32_MTRR_DEF_TYPE));
        cprintf("MTRR Phys0 Base = 0x%016llx, Mask = 0x%016llx\n",
                read_msr(0x200), read_msr(0x201));
@@ -575,18 +574,18 @@ void test_print_info_handler(trapframe_t *tf, void* data)
 
 void test_barrier_handler(trapframe_t *tf, void* data)
 {
-       cprintf("Round 1: Core %d\n", lapic_get_id());
+       cprintf("Round 1: Core %d\n", coreid());
        waiton_barrier(&test_cpu_array);
        waiton_barrier(&test_cpu_array);
        waiton_barrier(&test_cpu_array);
        waiton_barrier(&test_cpu_array);
        waiton_barrier(&test_cpu_array);
        waiton_barrier(&test_cpu_array);
-       cprintf("Round 2: Core %d\n", lapic_get_id());
+       cprintf("Round 2: Core %d\n", coreid());
        waiton_barrier(&test_cpu_array);
-       cprintf("Round 3: Core %d\n", lapic_get_id());
+       cprintf("Round 3: Core %d\n", coreid());
        // uncomment to see it fucked up
-       //cprintf("Round 4: Core %d\n", lapic_get_id());
+       //cprintf("Round 4: Core %d\n", coreid());
 }
 
 static void test_waiting_handler(trapframe_t *tf, void* data)
index 2767f67..f49608e 100644 (file)
@@ -129,7 +129,7 @@ idt_init(void)
 void
 (IN_HANDLER print_trapframe)(trapframe_t *tf)
 {
-       cprintf("TRAP frame at %p on core %d\n", tf, lapic_get_id());
+       cprintf("TRAP frame at %p on core %d\n", tf, coreid());
        print_regs(&tf->tf_regs);
        cprintf("  es   0x----%04x\n", tf->tf_es);
        cprintf("  ds   0x----%04x\n", tf->tf_ds);
@@ -226,8 +226,8 @@ void
 void
 (IN_HANDLER irq_handler)(trapframe_t *tf)
 {
-       //if (lapic_get_id())
-       //      cprintf("Incoming IRQ, ISR: %d on core %d\n", tf->tf_trapno, lapic_get_id());
+       //if (coreid())
+       //      cprintf("Incoming IRQ, ISR: %d on core %d\n", tf->tf_trapno, coreid());
        // merge this with alltraps?  other than the EOI... or do the same in all traps
 
        extern handler_wrapper_t handler_wrappers[NUM_HANDLER_WRAPPERS];
@@ -309,7 +309,7 @@ page_fault_handler(trapframe_t *tf)
 
        // Destroy the environment that caused the fault.
        cprintf("[%08x] user fault va %08x ip %08x from core %d\n",
-               current->env_id, fault_va, tf->tf_eip, lapic_get_id());
+               current->env_id, fault_va, tf->tf_eip, coreid());
        print_trapframe(tf);
        env_destroy(current);
 }
index f5bdd40..0e1dc6f 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.
  */
 
@@ -7,8 +8,10 @@
 #include <arch/apic.h>
 #include <arch/smp.h>
 
-#include <atomic.h>
+#include <ros/error.h>
 
+#include <atomic.h>
+#include <stdio.h>
 #include <workqueue.h>
 
 /*
  */
 void process_workqueue()
 {
-       work_t work;
-       per_cpu_info_t *cpuinfo = &per_cpu_info[lapic_get_id()];
+       struct work work;
+       struct per_cpu_info *cpuinfo = &per_cpu_info[coreid()];
        // copy the work in, since we may never return to this stack frame
        spin_lock_irqsave(&cpuinfo->lock);
-       work = cpuinfo->delayed_work;
+       work = cpuinfo->workqueue.statics[0];
        spin_unlock_irqsave(&cpuinfo->lock);
        if (work.func) {
                // TODO: possible race with this.  sort it out when we have a queue.
                spin_lock_irqsave(&cpuinfo->lock);
-               cpuinfo->delayed_work.func = 0;
+               cpuinfo->workqueue.statics[0].func = 0;
                spin_unlock_irqsave(&cpuinfo->lock);
                // We may never return from this (if it is env_run)
                work.func(work.data);
        }
 }
+
+int enqueue_work(struct workqueue *queue, struct work *job)
+{
+       error_t retval = 0;
+       struct per_cpu_info *cpuinfo = &per_cpu_info[coreid()];
+
+       spin_lock_irqsave(&cpuinfo->lock);
+       printd("Enqueuing func 0x%08x and data 0x%08x on core %d.\n",
+              job->func, job->data, coreid());
+       if (queue->statics[0].func)
+               retval = -ENOMEM;
+       else
+               queue->statics[0] = *job;
+       spin_unlock_irqsave(&cpuinfo->lock);
+       return retval;
+}