Allow multiple handlers per IRQ
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 30 Oct 2013 22:02:18 +0000 (15:02 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 16 Jan 2014 19:20:43 +0000 (11:20 -0800)
We don't allow unregistering yet - requires RCU-like sync with the read
side.

12 files changed:
kern/arch/riscv/trap.c
kern/arch/x86/init.c
kern/arch/x86/smp.c
kern/arch/x86/trap.c
kern/arch/x86/trapentry32.S
kern/arch/x86/trapentry64.S
kern/drivers/net/e1000.c
kern/drivers/net/ne2k.c
kern/drivers/net/rl8168.c
kern/include/smp.h
kern/include/trap.h
kern/src/testing.c

index 28598e3..39fe97c 100644 (file)
@@ -347,10 +347,15 @@ handle_trap(struct hw_trapframe *hw_tf)
 /* We don't have NMIs now. */
 void send_nmi(uint32_t os_coreid)
 {
+       printk("%s not implemented\n", __FUNCTION);
+}
+
+void register_raw_irq(unsigned int vector, isr_t handler, void *data)
+{
+       printk("%s not implemented\n", __FUNCTION);
 }
 
-void register_interrupt_handler(handler_t table[], uint8_t int_num,
-                                poly_isr_t handler, void *data)
+void unregister_raw_irq(unsigned int vector, isr_t handler, void *data)
 {
        printk("%s not implemented\n", __FUNCTION);
 }
index 42db860..e8bccf0 100644 (file)
@@ -58,19 +58,8 @@ static void cons_irq_init(void)
 {
        struct cons_dev *i;
        /* Register interrupt handlers for all console devices */
-       SLIST_FOREACH(i, &cdev_list, next) {
-               register_interrupt_handler(interrupt_handlers, i->irq + PIC1_OFFSET,
-                                          irq_console, i);
-               /* Route any console IRQs to core 0 */
-       #ifdef CONFIG_ENABLE_MPTABLES
-               ioapic_route_irq(i->irq, 0);
-       #else
-               pic_unmask_irq(i->irq);
-               unmask_lapic_lvt(LAPIC_LVT_LINT0);
-       #endif /* CONFIG_ENABLE_MPTABLES */
-               printd("Registered handler for IRQ %d (ISR %d)\n", i->irq,
-                      i->irq + PIC1_OFFSET);
-       }
+       SLIST_FOREACH(i, &cdev_list, next)
+               register_dev_irq(i->irq, irq_console, i);
 }
 
 void arch_init()
index a3d8555..5318076 100644 (file)
@@ -30,8 +30,8 @@ int os_coreid_lookup[MAX_NUM_CPUS] = {[0 ... (MAX_NUM_CPUS - 1)] -1};
 // need to be global, since there is no function that will always exist for them
 handler_wrapper_t (RO handler_wrappers)[NUM_HANDLER_WRAPPERS];
 
-static int smp_call_function(uint8_t type, uint32_t dest, poly_isr_t handler, TV(t) data,
-                             handler_wrapper_t** wait_wrapper)
+static int smp_call_function(uint8_t type, uint32_t dest, isr_t handler,
+                             void *data, handler_wrapper_t **wait_wrapper)
 {
        int8_t state = 0;
        uint32_t wrapper_num;
@@ -116,7 +116,7 @@ static int smp_call_function(uint8_t type, uint32_t dest, poly_isr_t handler, TV
        }
 
        // now register our handler to run
-       register_interrupt_handler(interrupt_handlers, wrapper->vector, handler, data);
+       register_raw_irq(wrapper->vector, handler, data);
        // WRITE MEMORY BARRIER HERE
        enable_irqsave(&state);
        // Send the proper type of IPI.  I made up these numbers.
@@ -146,20 +146,20 @@ static int smp_call_function(uint8_t type, uint32_t dest, poly_isr_t handler, TV
 }
 
 // Wrapper functions.  Add more as they are needed.
-int smp_call_function_self(poly_isr_t handler, TV(t) data,
-                           handler_wrapper_t** wait_wrapper)
+int smp_call_function_self(isr_t handler, void *data,
+                           handler_wrapper_t **wait_wrapper)
 {
        return smp_call_function(1, 0, handler, data, wait_wrapper);
 }
 
-int smp_call_function_all(poly_isr_t handler, TV(t) data,
-                          handler_wrapper_t** wait_wrapper)
+int smp_call_function_all(isr_t handler, void *data,
+                          handler_wrapper_t **wait_wrapper)
 {
        return smp_call_function(2, 0, handler, data, wait_wrapper);
 }
 
-int smp_call_function_single(uint32_t dest, poly_isr_t handler, TV(t) data,
-                             handler_wrapper_t** wait_wrapper)
+int smp_call_function_single(uint32_t dest, isr_t handler, void *data,
+                             handler_wrapper_t **wait_wrapper)
 {
        return smp_call_function(4, dest, handler, data, wait_wrapper);
 }
index 6ba6975..0b89652 100644 (file)
@@ -28,10 +28,10 @@ taskstate_t RO ts;
 gatedesc_t __attribute__((aligned (16))) idt[256] = { { 0 } };
 pseudodesc_t idt_pd;
 
-/* global handler table, used by core0 (for now).  allows the registration
- * of functions to be called when servicing an interrupt.  other cores
- * can set up their own later. */
-handler_t interrupt_handlers[NUM_IRQS];
+/* interrupt handler table, each element is a linked list of handlers for a
+ * given IRQ.  Modification requires holding the lock (TODO: RCU) */
+struct irq_handler *irq_handlers[NUM_IRQS];
+spinlock_t irq_handler_wlock = SPINLOCK_INITIALIZER_IRQSAVE;
 
 /* Which pci devices hang off of which irqs */
 /* TODO: make this an array of SLISTs (pain from ioapic.c, etc...) */
@@ -169,11 +169,9 @@ void idt_init(void)
        // and turn it on
        lapic_enable();
        /* register the generic timer_interrupt() handler for the per-core timers */
-       register_interrupt_handler(interrupt_handlers, LAPIC_TIMER_DEFAULT_VECTOR,
-                                  timer_interrupt, NULL);
+       register_raw_irq(LAPIC_TIMER_DEFAULT_VECTOR, timer_interrupt, NULL);
        /* register the kernel message handler */
-       register_interrupt_handler(interrupt_handlers, I_KERNEL_MSG,
-                                  handle_kmsg_ipi, NULL);
+       register_raw_irq(I_KERNEL_MSG, handle_kmsg_ipi, NULL);
 }
 
 static void handle_fperr(struct hw_trapframe *hw_tf)
@@ -463,9 +461,10 @@ static void send_eoi(uint32_t trap_nr)
  *
  * Note that from hardware's perspective (PIC, etc), IRQs start from 0, but they
  * are all mapped up at PIC1_OFFSET for the cpu / irq_handler. */
-void irq_handler(struct hw_trapframe *hw_tf)
+void handle_irq(struct hw_trapframe *hw_tf)
 {
        struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       struct irq_handler *irq_h;
        /* Copy out the TF for now */
        if (!in_kernel(hw_tf))
                set_current_ctx_hw(pcpui, hw_tf);
@@ -480,13 +479,14 @@ void irq_handler(struct hw_trapframe *hw_tf)
                       core_id());
        if (check_spurious_irq(hw_tf->tf_trapno))
                goto out_no_eoi;
-       extern handler_wrapper_t (RO handler_wrappers)[NUM_HANDLER_WRAPPERS];
-       // determine the interrupt handler table to use.  for now, pick the global
-       handler_t *handler_tbl = interrupt_handlers;
-       if (handler_tbl[hw_tf->tf_trapno].isr != 0)
-               handler_tbl[hw_tf->tf_trapno].isr(hw_tf,
-                                                 handler_tbl[hw_tf->tf_trapno].data);
+       /* TODO: RCU read lock */
+       irq_h = irq_handlers[hw_tf->tf_trapno];
+       while (irq_h) {
+               irq_h->isr(hw_tf, irq_h->data);
+               irq_h = irq_h->next;
+       }
        // if we're a general purpose IPI function call, down the cpu_list
+       extern handler_wrapper_t handler_wrappers[NUM_HANDLER_WRAPPERS];
        if ((I_SMP_CALL0 <= hw_tf->tf_trapno) &&
            (hw_tf->tf_trapno <= I_SMP_CALL_LAST))
                down_checklist(handler_wrappers[hw_tf->tf_trapno & 0x0f].cpu_list);
@@ -504,19 +504,29 @@ out_no_eoi:
        assert(0);
 }
 
-void
-register_interrupt_handler(handler_t TP(TV(t)) table[],
-                           uint8_t int_num, poly_isr_t handler, TV(t) data)
+void register_raw_irq(unsigned int vector, isr_t handler, void *data)
+{
+       struct irq_handler *irq_h;
+       irq_h = kmalloc(sizeof(struct irq_handler), 0);
+       assert(irq_h);
+       spin_lock_irqsave(&irq_handler_wlock);
+       irq_h->isr = handler;
+       irq_h->data = data;
+       irq_h->next = irq_handlers[vector];
+       wmb();  /* make sure irq_h is done before publishing to readers */
+       irq_handlers[vector] = irq_h;
+       spin_unlock_irqsave(&irq_handler_wlock);
+}
+
+void unregister_raw_irq(unsigned int vector, isr_t handler, void *data)
 {
-       table[int_num].isr = handler;
-       table[int_num].data = data;
+       /* TODO: RCU */
+       printk("Unregistering not supported\n");
 }
 
-int register_dev_irq(int irq, void (*handler)(struct hw_trapframe *, void *),
-                     void *irq_arg)
+int register_dev_irq(int irq, isr_t handler, void *irq_arg)
 {
-       register_interrupt_handler(interrupt_handlers,
-                                  KERNEL_IRQ_OFFSET + irq, handler, irq_arg);
+       register_raw_irq(KERNEL_IRQ_OFFSET + irq, handler, irq_arg);
 
        /* TODO: whenever we sort out the ACPI/IOAPIC business, we'll probably want
         * a helper to reroute an irq? */
index d4ffdd7..3d7b4b7 100644 (file)
@@ -245,7 +245,7 @@ _allirqs:
        movw %ax, %es
        pushl %esp
        movl $0, %ebp                   # so we can backtrace to this point
-       call irq_handler
+       call handle_irq
        popl %esp
        popal
        popl %gs
index d71678a..62a3b23 100644 (file)
@@ -277,7 +277,7 @@ irq_kernel_tf:
 irq_all_tf:
        movq $0, %rbp                   # so we can backtrace to this point
        movq %rsp, %rdi
-       call irq_handler
+       call handle_irq
        # the return paths are only used by the kernel
        addq $0x10, %rsp                        # skip fs/gs base
        popq %rax
index fbb635b..4e98023 100644 (file)
@@ -623,8 +623,6 @@ void e1000_irq_enable() {
 // Configure and enable interrupts
 void e1000_setup_interrupts() {
        
-       extern handler_t interrupt_handlers[];
-       
        e1000_debug("-->Setting interrupts.\n");
        
        // Set throttle register
index 9c4f105..d8845b4 100644 (file)
@@ -168,8 +168,6 @@ void ne2k_configure_nic() {
 
 void ne2k_setup_interrupts() {
        
-       extern handler_t interrupt_handlers[];
-       
        ne2k_debug("-->Setting interrupts.\n");
        
        // Kernel based interrupt stuff
index a749114..f62109f 100644 (file)
@@ -300,8 +300,6 @@ void rl8168_reset() {
 
 void rl8168_setup_interrupts() {
        
-       extern handler_t interrupt_handlers[];
-       
        rl8168_debug("-->Setting interrupts.\n");
        
        // Enable NIC interrupts
index 7a32892..250cdd9 100644 (file)
@@ -82,13 +82,13 @@ void smp_percpu_init(void); // this must be called by each core individually
 void __arch_pcpu_init(uint32_t coreid);        /* each arch has one of these */
 
 /* SMP utility functions */
-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(uint32_t dest, poly_isr_t handler, TV(t) data,
-                             handler_wrapper_t** wait_wrapper);
-int smp_call_wait(handler_wrapper_t*SAFE wrapper);
+int smp_call_function_self(isr_t handler, void *data,
+                           handler_wrapper_t **wait_wrapper);
+int smp_call_function_all(isr_t handler, void *data,
+                          handler_wrapper_t **wait_wrapper);
+int smp_call_function_single(uint32_t dest, isr_t handler, void *data,
+                             handler_wrapper_t **wait_wrapper);
+int smp_call_wait(handler_wrapper_t *wrapper);
 
 /* PCPUI Trace Rings: */
 struct pcpu_trace_event {
index ecd51a5..2a91f78 100644 (file)
 #include <arch/trap.h>
 
 // func ptr for interrupt service routines
-typedef void (*poly_isr_t)(struct hw_trapframe *hw_tf, void *data);
 typedef void (*isr_t)(struct hw_trapframe *hw_tf, void *data);
-typedef struct InterruptHandler {
-       poly_isr_t isr;
-       TV(t) data;
-} handler_t;
-
-extern handler_t interrupt_handlers[];
+struct irq_handler {
+       isr_t isr;
+       void *data;
+       struct irq_handler *next;
+};
 
 void idt_init(void);
-void register_interrupt_handler(handler_t table[],
-                                uint8_t int_num,
-                                poly_isr_t handler, void *data);
-int register_dev_irq(int irq, void (*handler)(struct hw_trapframe *, void *),
-                     void *irq_arg);
+void register_raw_irq(unsigned int vector, isr_t handler, void *data);
+void unregister_raw_irq(unsigned int vector, isr_t handler, void *data);
+int register_dev_irq(int irq, isr_t handler, void *irq_arg);
 void print_trapframe(struct hw_trapframe *hw_tf);
 void page_fault_handler(struct hw_trapframe *hw_tf);
 /* Generic per-core timer interrupt handler.  set_percore_timer() will fire the
index d3b449c..794361e 100644 (file)
 
 void test_ipi_sending(void)
 {
-       extern handler_t interrupt_handlers[];
        int8_t state = 0;
 
-       register_interrupt_handler(interrupt_handlers, I_TESTING,
-                                  test_hello_world_handler, NULL);
+       register_raw_irq(I_TESTING, test_hello_world_handler, NULL);
        enable_irqsave(&state);
        cprintf("\nCORE 0 sending broadcast\n");
        send_broadcast_ipi(I_TESTING);
@@ -90,7 +88,7 @@ void test_ipi_sending(void)
 // Note this never returns and will muck with any other timer work
 void test_pic_reception(void)
 {
-       register_interrupt_handler(interrupt_handlers, 0x20, test_hello_world_handler, NULL);
+       register_raw_irq(0x20, test_hello_world_handler, NULL);
        pit_set_timer(100,TIMER_RATEGEN); // totally arbitrary time
        pic_unmask_irq(0);
        cprintf("PIC1 Mask = 0x%04x\n", inb(PIC1_DATA));
@@ -103,7 +101,7 @@ void test_pic_reception(void)
 
 void test_ioapic_pit_reroute(void) 
 {
-       register_interrupt_handler(interrupt_handlers, 0x20, test_hello_world_handler, NULL);
+       register_raw_irq(0x20, test_hello_world_handler, NULL);
        ioapic_route_irq(0, 3); 
 
        cprintf("Starting pit on core 3....\n");
@@ -552,8 +550,7 @@ void test_smp_call_functions(void)
 #ifdef CONFIG_X86
 void test_lapic_status_bit(void)
 {
-       register_interrupt_handler(interrupt_handlers, I_TESTING,
-                                  test_incrementer_handler, &a);
+       register_raw_irq(I_TESTING, test_incrementer_handler, &a);
        #define NUM_IPI 100000
        atomic_set(&a,0);
        printk("IPIs received (should be 0): %d\n", a);
@@ -655,8 +652,7 @@ void test_pit(void)
 
        atomic_t waiting;
        atomic_init(&waiting, 1);
-       register_interrupt_handler(interrupt_handlers, I_TESTING,
-                                  test_waiting_handler, &waiting);
+       register_raw_irq(I_TESTING, test_waiting_handler, &waiting);
        while(atomic_read(&waiting))
                cpu_relax();
        cprintf("End now\n");
@@ -1431,9 +1427,9 @@ static void __test_try_halt(uint32_t srcid, long a0, long a1, long a2)
        printk("Returned from halting on core 1\n");
 }
 
-/* x86 test, making sure our cpu_halt() and irq_handler() work.  If you want to
+/* x86 test, making sure our cpu_halt() and handle_irq() work.  If you want to
  * see it fail, you'll probably need to put a nop in the asm for cpu_halt(), and
- * comment out abort_halt() in irq_handler(). */
+ * comment out abort_halt() in handle_irq(). */
 void test_abort_halt(void)
 {
 #ifdef CONFIG_X86