[Patrick, does the attached patch fix your problem?]

On Mon, 5 Oct 1998, Linus Torvalds wrote:

> I'd really just prefer to get rid of the 1:1 mapping completely, and use a
> linked list approach everywhere instead. 
> 
> So you'd just build up something like irq_to_pin[] that's a singly linked
> list

ok, the attached patch implements the data structure described in your
pseudo-code. Note that pin == 0 is a valid case too. (usually it's
allocated to the timer interrupt which has different handling due to
(real) BIOS bugs, but i dont think we should take this granted). Also, we
want to safely handle unallocated irqs too, thus we have to do one more
extra check for pin == -1.

> and to make sure it's fast in the single-irq case you do the above by just
> expanding the irq_2_pin[] array to have two entries instead of just one,
> where the second one is the link ptr.. The only extra overhead for the
> common case is one single load (cached, because you already loaded the
> irq) and a test, and the code is about 100 times cleaner than your current
> patch (sorry, I don't think you were too proud of it either ;) 

nope but it was the simplest one, i wasnt even sure wether this approach
is the best way to fix the problem. it was just a suggestion, code instead
of words ;) i hope the attached patch hits the treshold. The #define looks
ugly but so does code repetition.

-- mingo

--- linux/arch/i386/kernel/io_apic.c.orig       Fri Feb  2 09:38:58 1996
+++ linux/arch/i386/kernel/io_apic.c    Tue Oct  6 00:10:15 1998
@@ -103,8 +103,10 @@
 
 /*
  * This is performance-critical, we want to do it O(1)
+ *
+ * the indexing order of this array favors 1:1 mappings
+ * between pins and IRQs.
  */
-static int irq_2_pin[NR_IRQS];
 
 static inline unsigned int io_apic_read(unsigned int reg)
 {
@@ -128,58 +130,69 @@
 }
 
 /*
- * We disable IO-APIC IRQs by setting their 'destination CPU mask' to
- * zero. Trick by Ramesh Nalluri.
+ * Rough estimation of how many shared IRQs there are, can
+ * be changed anytime.
  */
-static inline void disable_IO_APIC_irq(unsigned int irq)
-{
-       int pin = irq_2_pin[irq];
-       struct IO_APIC_route_entry entry;
+#define MAX_PLUS_SHARED_IRQS NR_IRQS
+#define PIN_MAP_SIZE (MAX_PLUS_SHARED_IRQS + NR_IRQS)
 
-       if (pin != -1) {
-               *(((int *)&entry) + 1) = io_apic_read(0x11 + pin * 2);
-               entry.dest.logical.logical_dest = 0x0;
-               io_apic_write(0x11 + 2 * pin, *(((int *)&entry) + 1));
-               io_apic_sync();
-       }
-}
+static struct irq_pin_list {
+       int pin, next;
+} irq_2_pin[PIN_MAP_SIZE];
 
-static inline void enable_IO_APIC_irq(unsigned int irq)
+/*
+ * The common case is 1:1 IRQ<->pin mappings. Sometimes there are
+ * shared ISA-space IRQs, so we have to support them. We are super
+ * fast in the common case, and fast for shared ISA-space IRQs.
+ */
+static void add_pin_to_irq(unsigned int irq, int pin)
 {
-       int pin = irq_2_pin[irq];
-       struct IO_APIC_route_entry entry;
+       static int first_free_entry = NR_IRQS;
+       struct irq_pin_list *entry = irq_2_pin + irq;
 
-       if (pin != -1) {
-               *(((int *)&entry) + 1) = io_apic_read(0x11 + pin * 2);
-               entry.dest.logical.logical_dest = 0xff;
-               io_apic_write(0x11 + 2 * pin, *(((int *)&entry) + 1));
-       }
-}
+       while (entry->next)
+               entry = irq_2_pin + entry->next;
 
-static inline void mask_IO_APIC_irq(unsigned int irq)
-{
-       int pin = irq_2_pin[irq];
-       struct IO_APIC_route_entry entry;
-
-       if (pin != -1) {
-               *(((int *)&entry) + 0) = io_apic_read(0x10 + pin * 2);
-               entry.mask = 1;
-               io_apic_write(0x10 + 2 * pin, *(((int *)&entry) + 0));
-               io_apic_sync();
-       }
+       if (entry->pin != -1) {
+               entry->next = first_free_entry;
+               entry = irq_2_pin + entry->next;
+               if (++first_free_entry >= PIN_MAP_SIZE)
+                       panic("io_apic.c: whoops");
+       }
+       entry->pin = pin;
+}
+
+#define DO_ACTION(name,R,ACTION)                                       \
+                                                                       \
+static void name##_IO_APIC_irq(unsigned int irq)                       \
+{                                                                      \
+       int pin;                                                        \
+       struct IO_APIC_route_entry reg;                                 \
+       struct irq_pin_list *entry = irq_2_pin + irq;                   \
+                                                                       \
+       for (;;) {                                                      \
+               pin = entry->pin;                                       \
+               if (pin == -1)                                          \
+                       break;                                          \
+               *(((int *)&reg) + R) = io_apic_read(0x10 + R + pin*2);  \
+               ACTION;                                                 \
+               io_apic_write(0x10 + R + 2*pin, *(((int *)&reg) + R));  \
+               if (!entry->next)                                       \
+                       break;                                          \
+               entry = irq_2_pin + entry->next;                        \
+       }                                                               \
+       io_apic_sync();                                                 \
 }
 
-static inline void unmask_IO_APIC_irq(unsigned int irq)
-{
-       int pin = irq_2_pin[irq];
-       struct IO_APIC_route_entry entry;
+/*
+ * We disable IO-APIC IRQs by setting their 'destination CPU mask' to
+ * zero. Trick by Ramesh Nalluri.
+ */
 
-       if (pin != -1) {
-               *(((int *)&entry) + 0) = io_apic_read(0x10 + pin * 2);
-               entry.mask = 0;
-               io_apic_write(0x10 + 2 * pin, *(((int *)&entry) + 0));
-       }
-}
+DO_ACTION( disable, 1, reg.dest.logical.logical_dest = 0x0)
+DO_ACTION( enable, 1, reg.dest.logical.logical_dest = 0xff)
+DO_ACTION( mask, 0, reg.mask = 1)
+DO_ACTION( unmask, 0, reg.mask = 0)
 
 static void __init clear_IO_APIC_pin(unsigned int pin)
 {
@@ -577,7 +590,7 @@
                }
 
                irq = pin_2_irq(idx,pin);
-               irq_2_pin[irq] = pin;
+               add_pin_to_irq(irq, pin);
 
                if (!IO_APIC_IRQ(irq))
                        continue;
@@ -703,8 +716,8 @@
        }
 
        printk("IRQ to pin mappings:\n");
-       for (i = 0; i < NR_IRQS; i++)
-               printk("%d->%d ", i, irq_2_pin[i]);
+       for (i = 0; i < PIN_MAP_SIZE; i++)
+               printk("%d->%d(%d) ", i, irq_2_pin[i].pin, irq_2_pin[i].next);
        printk("\n");
 
        printk(".................................... done.\n");
@@ -716,8 +729,10 @@
 {
        int i, pin;
 
-       for (i = 0; i < NR_IRQS; i++)
-               irq_2_pin[i] = -1;
+       for (i = 0; i < PIN_MAP_SIZE; i++) {
+               irq_2_pin[i].pin = -1;
+               irq_2_pin[i].next = 0;
+       }
        if (!pirqs_enabled)
                for (i = 0; i < MAX_PIRQS; i++)
                        pirq_entries[i] =- 1;

Reply via email to