Active messages for x86
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 21 Aug 2009 21:32:21 +0000 (14:32 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Mon, 31 Aug 2009 21:07:55 +0000 (14:07 -0700)
Moved the sparc active messages interfaces to inc/trap.h.  Only supports
a unicast active messages.  Both arches should be providing in-order
reception and some sort of push-back if delivery fails.  x86's involves
modifying the destinations's per_cpu_info (memory).

17 files changed:
kern/arch/i386/process.c
kern/arch/i386/smp.c
kern/arch/i386/smp.h
kern/arch/i386/trap.c
kern/arch/i386/trap.h
kern/arch/i386/trapentry.S
kern/arch/sparc/smp.c
kern/arch/sparc/smp.h
kern/arch/sparc/sparc.h
kern/arch/sparc/trap.h
kern/include/ros/common.h [new file with mode: 0644]
kern/include/smp.h
kern/include/testing.h
kern/include/trap.h
kern/src/manager.c
kern/src/process.c
kern/src/testing.c

index e76981c..c559978 100644 (file)
@@ -60,5 +60,5 @@ void __death(trapframe_t *tf)
        // TODO: replace this ghetto with an active message (AM)
        per_cpu_info[core_id()].proc_ipi_pending = 0;
        lapic_send_eoi();
-       smp_idle();             
+       smp_idle();     
 }
index e299bab..b56dd91 100644 (file)
@@ -25,7 +25,7 @@
 // need to be global, since there is no function that will always exist for them
 handler_wrapper_t             handler_wrappers[NUM_HANDLER_WRAPPERS];
 
-static int smp_call_function(uint8_t type, uint8_t dest, poly_isr_t handler, TV(t) data,
+static int smp_call_function(uint8_t type, uint32_t dest, poly_isr_t handler, TV(t) data,
                              handler_wrapper_t** wait_wrapper)
 {
        int8_t state = 0;
@@ -41,7 +41,7 @@ static int smp_call_function(uint8_t type, uint8_t dest, poly_isr_t handler, TV(
                atomic_dec(&outstanding_calls);
                return -EBUSY;
        }
-       
+
        // assumes our cores are numbered in order
        if ((type == 4) && (dest >= num_cpus))
                panic("Destination CPU does not exist!");
@@ -153,7 +153,7 @@ int smp_call_function_all(poly_isr_t handler, TV(t) data,
        return smp_call_function(2, 0, handler, data, wait_wrapper);
 }
 
-int smp_call_function_single(uint8_t dest, poly_isr_t handler, TV(t) data,
+int smp_call_function_single(uint32_t dest, poly_isr_t handler, TV(t) data,
                              handler_wrapper_t** wait_wrapper)
 {
        return smp_call_function(4, dest, handler, data, wait_wrapper);
index 3f33c6e..957e919 100644 (file)
@@ -19,4 +19,4 @@ typedef struct HandlerWrapper {
        uint8_t vector;
 } handler_wrapper_t;
 
-#endif
+#endif /* !ROS_ARCH_SMP_H */
index e567d4d..ef5e1df 100644 (file)
@@ -7,6 +7,7 @@
 #include <arch/arch.h>
 #include <arch/console.h>
 #include <arch/apic.h>
+#include <ros/common.h>
 #include <smp.h>
 #include <assert.h>
 #include <pmap.h>
@@ -347,7 +348,7 @@ void sysenter_init(void)
 void sysenter_callwrapper(struct Trapframe *tf)
 {
        current->env_tf = *tf;
-       
+
        // The trapframe on the stack should be ignored from here on.
        tf = &current->env_tf;
        tf->tf_regs.reg_eax = (intreg_t) syscall(current,
@@ -365,3 +366,59 @@ void sysenter_callwrapper(struct Trapframe *tf)
         */
        proc_startcore(current, tf);
 }
+
+uint32_t send_active_message(uint32_t dst, amr_t pc, uint32_t arg0,
+                             uint32_t arg1, uint32_t arg2)
+{
+       error_t retval = -EBUSY;
+       spin_lock_irqsave(&per_cpu_info[dst].amsg_lock);
+       size_t current_amsg = per_cpu_info[dst].amsg_current;
+       // If there's a PC there, then that means it's an outstanding message
+       FOR_CIRC_BUFFER(current_amsg, NUM_ACTIVE_MESSAGES, i) {
+               if (per_cpu_info[dst].active_msgs[i].pc)
+                       continue;
+               per_cpu_info[dst].active_msgs[i].pc = pc;
+               per_cpu_info[dst].active_msgs[i].arg0 = arg0;
+               per_cpu_info[dst].active_msgs[i].arg1 = arg1;
+               per_cpu_info[dst].active_msgs[i].arg2 = arg2;
+               per_cpu_info[dst].active_msgs[i].srcid = core_id();
+               retval = 0;
+               break;
+       }
+       spin_unlock_irqsave(&per_cpu_info[dst].amsg_lock);
+       if (!retval)
+               send_ipi(dst, 0, I_ACTIVE_MSG);
+       return retval;
+}
+
+/* Active message handler.  We don't want to block other AMs from coming in, so
+ * we'll copy out the message and let go of the lock.  This won't return until
+ * all pending AMs are executed.  If the PC is 0, then this was an extra IPI and
+ * we already handled the message (or someone is sending IPIs without loading
+ * the active message...)
+ * Note that all of this happens from interrupt context, and interrupts are
+ * currently disabled for this gate. */
+void __active_message(trapframe_t *tf)
+{
+       struct per_cpu_info *myinfo = &per_cpu_info[core_id()];
+       active_message_t amsg;
+
+       while (1) { // will break out when we find an empty amsg
+               /* Get the message */
+               spin_lock_irqsave(&myinfo->amsg_lock);
+               if (myinfo->active_msgs[myinfo->amsg_current].pc) {
+                       amsg = myinfo->active_msgs[myinfo->amsg_current];
+                       myinfo->active_msgs[myinfo->amsg_current].pc = 0;
+                       myinfo->amsg_current = (myinfo->amsg_current + 1) %
+                                              NUM_ACTIVE_MESSAGES;
+               } else { // was no PC in the current active message, meaning we do nothing
+                       spin_unlock_irqsave(&myinfo->amsg_lock);
+                       lapic_send_eoi();
+                       return;
+               }
+               spin_unlock_irqsave(&myinfo->amsg_lock);
+               /* Execute the active message */
+               amsg.pc(tf, amsg.srcid, amsg.arg0, amsg.arg1, amsg.arg2);
+       }
+
+}
index a04ebee..23fc032 100644 (file)
@@ -40,6 +40,7 @@
 // TODO: replace this ghetto with an active message (AM)
 #define I_DEATH     230
 #define I_STARTCORE 231
+#define I_ACTIVE_MSG 232
 /* smp_call_function IPIs, keep in sync with NUM_HANDLER_WRAPPERS (and < 16)
  * it's important that this begins with 0xf0.  check i386/trap.c for details. */
 #define I_SMP_CALL0 0xf0 // 240
@@ -51,6 +52,8 @@
 /* Testing IPI (used in testing.c) */
 #define I_TESTING 0xff
 
+/* Number of active messages available per core (arbitrary) */
+#define NUM_ACTIVE_MESSAGES 5
 
 
 #ifndef __ASSEMBLER__
index e020726..632e2db 100644 (file)
@@ -143,7 +143,7 @@ IRQ_HANDLER(IRQ15, 47)
 /* 25 general purpose vectors, for use by the LAPIC.  Can expand later. */
 IRQ_HANDLER_SPEC(IRQ198, I_DEATH, __death) 
 IRQ_HANDLER_SPEC(IRQ199, I_STARTCORE, __startcore) 
-IRQ_HANDLER(IRQ200, 232)
+IRQ_HANDLER_SPEC(IRQ200, I_ACTIVE_MSG, __active_message) 
 IRQ_HANDLER(IRQ201, 233)
 IRQ_HANDLER(IRQ202, 234)
 IRQ_HANDLER(IRQ203, 235)
index 942bc40..b5e1783 100644 (file)
@@ -107,7 +107,7 @@ int smp_call_function_all(isr_t handler, void* data,
        return 0;
 }
 
-int smp_call_function_single(uint8_t dest, isr_t handler, void* data,
+int smp_call_function_single(uint32_t dest, isr_t handler, void* data,
                              handler_wrapper_t** wait_wrapper)
 {
        int8_t state = 0;
index 604c7e9..fc07ba7 100644 (file)
@@ -13,4 +13,4 @@ typedef struct
        spinlock_t lock;
 } handler_wrapper_t;
 
-#endif
+#endif /* !ROS_ARCH_SMP_H */
index 0df458c..7ceb7d9 100644 (file)
@@ -23,7 +23,7 @@
 #define XSTR(arg) STR(arg)
 
 #include <arch/types.h>
-#include <arch/trap.h>
+#include <trap.h>
 #include <arch/frontend.h>
 
 static __inline uint32_t read_psr(void) __attribute__((always_inline));
@@ -42,7 +42,6 @@ static __inline void write_fsr(uint32_t val) __attribute__((always_inline));
 static __inline uint32_t memsize_mb(void) __attribute__((always_inline));
 static __inline uint32_t mmu_probe(uint32_t va) __attribute__((always_inline));
 
-uint32_t send_active_message(uint32_t dst, amr_t pc, uint32_t arg0, uint32_t arg1, uint32_t arg2);
 void flush_windows();
 
 #define store_alternate(addr,asi,data) ({ uint32_t __my_addr = (addr); uint32_t __my_data = (data); __asm__ __volatile__ ("sta %0,[%1] %2" : : "r"(__my_data),"r"(__my_addr),"i"(asi)); })
index 1e6b700..b6ae9c0 100644 (file)
@@ -22,18 +22,6 @@ typedef struct
        uint64_t timestamp;
 } trapframe_t;
 
-typedef void (*amr_t)(trapframe_t* tf, uint32_t srcid, uint32_t a0, uint32_t a1, uint32_t a2);
-
-typedef struct
-{
-       uint32_t srcid;
-       amr_t pc;
-       uint32_t arg0;
-       uint32_t arg1;
-       uint32_t arg2;
-       uint32_t pad;
-} active_message_t;
-
 typedef struct
 {
        uint32_t fpr[32];
diff --git a/kern/include/ros/common.h b/kern/include/ros/common.h
new file mode 100644 (file)
index 0000000..1aa2486
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef ROS_COMMON_H
+#define ROS_COMMON_H
+
+#define FOR_CIRC_BUFFER(next, size, var) \
+       for (int _var = 0, var = (next); _var < (size); _var++, var = (var + 1) % (size))
+
+#endif /* ROS_COMMON_H */
index 05d4ca2..0120de1 100644 (file)
 
 // will want this padded out to cacheline alignment
 struct per_cpu_info {
-       uint32_t lock;
+       spinlock_t lock;
        bool preempt_pending;
-       /* this flag is only ever set when holding the global scheduler lock, and
-        * only cleared (without a lock) by the core in a proc_mgmt IPI.  it is
-        * somewhat arch specific (no in order active messages).  same with the
-        * p_to_run and tf_to_pop */
-       // TODO: replace this ghetto with an active message (AM)
-       bool proc_ipi_pending;
-       /* a proc_startcore IPI will run these.  replace with a dispatch map? */
-       struct proc *p_to_run;
-       trapframe_t *SAFE tf_to_pop;
        struct workqueue workqueue;
+#ifdef __i386__
+       spinlock_t amsg_lock;
+       unsigned amsg_current;
+       active_message_t active_msgs[NUM_ACTIVE_MESSAGES];
+
+                       /* this flag is only ever set when holding the global scheduler lock, and
+                        * only cleared (without a lock) by the core in a proc_mgmt IPI.  it is
+                        * somewhat arch specific (no in order active messages).  same with the
+                        * p_to_run and tf_to_pop */
+                       // TODO: replace this ghetto with an active message (AM)
+                       bool proc_ipi_pending;
+                       /* a proc_startcore IPI will run these.  replace with a dispatch map? */
+                       struct proc *p_to_run;
+                       trapframe_t *SAFE tf_to_pop;
+#endif
 };
 extern struct per_cpu_info per_cpu_info[MAX_NUM_CPUS];
 extern volatile uint8_t num_cpus;
@@ -44,7 +50,7 @@ int smp_call_function_self(poly_isr_t handler, TV(t) data,
                            handler_wrapper_t** wait_wrapper);
 int smp_call_function_all(poly_isr_t handler, TV(t) data,
                           handler_wrapper_t** wait_wrapper);
-int smp_call_function_single(uint8_t dest, poly_isr_t handler, TV(t) data,
+int smp_call_function_single(uint32_t dest, poly_isr_t handler, TV(t) data,
                              handler_wrapper_t** wait_wrapper);
 int smp_call_wait(handler_wrapper_t*SAFE wrapper);
 
index 91b05dd..6d97c16 100644 (file)
@@ -20,6 +20,8 @@ void test_pit(void);
 void test_smp_call_functions(void);
 void test_lapic_status_bit(void);
 void test_run_measurements(uint32_t job_num);
+void test_circ_buffer(void);
+void test_active_messages(void);
 
 struct trapframe_t;
 
index 7c76abd..264f15d 100644 (file)
@@ -26,4 +26,33 @@ void ( page_fault_handler)(trapframe_t *tf);
 
 void sysenter_init(void);
 extern void sysenter_handler();
+
+/* Active messages.  Each arch implements them in their own way.  Both should be
+ * guaranteeing in-order delivery.  Kept here in trap.h, since sparc is using
+ * trap.h for AMs
+ *
+ * These are different (for now) than the smp_calls in smp.h, since
+ * they will be executed immediately, and in the order in which they are sent.
+ * smp_calls are currently not run in order, and if they put things on the
+ * workqueue, they don't get run until smp_idle (for now).
+ *
+ * Also, a big difference is that smp_calls can use the same message (registered
+ * in the interrupt_handlers[] for x86) for every recipient, but the active
+ * messages require a unique message.  Also for now, but it might be like that
+ * for a while on x86. */
+
+typedef void (*amr_t)(trapframe_t* tf, uint32_t srcid, uint32_t a0, uint32_t a1,
+                      uint32_t a2);
+typedef struct
+{
+       uint32_t srcid;
+       amr_t pc;
+       uint32_t arg0;
+       uint32_t arg1;
+       uint32_t arg2;
+       uint32_t pad;
+} active_message_t;
+
+uint32_t send_active_message(uint32_t dst, amr_t pc, uint32_t arg0,
+                             uint32_t arg1, uint32_t arg2);
 #endif /* ROS_KERN_TRAP_H */
index d7aca05..e161b06 100644 (file)
@@ -32,6 +32,8 @@ void manager(void)
        static uint8_t progress = 0;
        struct proc *envs[256];
 
+test_active_messages();
+panic("");
 struct proc *p = kfs_proc_create(kfs_lookup_path("roslib_mhello"));
 // being proper and all:
 proc_set_state(p, PROC_RUNNABLE_S);
index 84e2e7d..9d12868 100644 (file)
@@ -157,7 +157,7 @@ void proc_run(struct proc *p)
                         * - This turns out to not be enough, although it is necessary.  We
                         *   also need to make sure no other proc management IPIs are sent,
                         *   since IPIs can be received out of order, hence the use of the
-                        *   pending flag. 
+                        *   pending flag.
                        // TODO: replace this ghetto with an active message (AM)
                         * - Be *very* careful with this, since there may be a deadlock when
                         *   sending an IPI to yourself when another is outstanding.  This
@@ -329,11 +329,11 @@ void proc_destroy(struct proc *p)
         * CAS instead (another lock would be slightly less ghetto).  but we need to
         * decref before releasing the lock, since that could enable interrupts,
         * which would have us receive the DEATH IPI if this was called locally by
-        * the target process. */ 
+        * the target process. */
        //proc_decref(p); // this decref is for the process in general
        p->env_refcnt--;
        size_t refcnt = p->env_refcnt; // need to copy this in so it's not reloaded
-       
+
        /* After unlocking, we can receive a DEATH IPI and clean up */
        spin_unlock_irqsave(&p->proc_lock);
 
index 0b90060..c2a5c96 100644 (file)
@@ -8,6 +8,7 @@
 #include <smp.h>
 
 #include <ros/memlayout.h>
+#include <ros/common.h>
 
 #include <atomic.h>
 #include <stdio.h>
@@ -706,4 +707,40 @@ void test_pit(void)
                cpu_relax();
        cprintf("End now\n");
 }
+
+void test_circ_buffer(void)
+{
+       int arr[5] = {0, 1, 2, 3, 4};
+
+       for (int i = 0; i < 5; i++) {
+               FOR_CIRC_BUFFER(i, 5, j)
+                       printk("Starting with current = %d, each value = %d\n", i, j);
+       }
+       return;
+}
+
+void test_am_handler(trapframe_t* tf, uint32_t srcid, uint32_t a0, uint32_t a1,
+                     uint32_t a2)
+{
+       printk("Received AM on core %d from core %d: arg0= 0x%08x, arg1 = "
+              "0x%08x, arg2 = 0x%08x\n", core_id(), srcid, a0, a1, a2);
+       return;
+}
+
+void test_active_messages(void)
+{
+       // basic tests, make sure we can handle a wraparound and that the error
+       // messages work.
+       printk("sending NUM_ACTIVE_MESSAGES to core 1, sending (#,deadbeef,0)\n");
+       for (int i = 0; i < NUM_ACTIVE_MESSAGES; i++)
+               while (send_active_message(1, test_am_handler, i, 0xdeadbeef, 0))
+                       cpu_relax();
+       udelay(5000000);
+       printk("sending 2*NUM_ACTIVE_MESSAGES to core 1, sending (#,cafebabe,0)\n");
+       for (int i = 0; i < 2*NUM_ACTIVE_MESSAGES; i++)
+               while (send_active_message(1, test_am_handler, i, 0xcafebabe, 0))
+                       cpu_relax();
+       udelay(5000000);
+       return;
+}
 #endif // __i386__