x86, ioapic: Get rid of needless check and simplify ioapic_setup_resources()
[safe/jmp/linux-2.6] / arch / x86 / kernel / apic / io_apic.c
index f8aa546..d836b4d 100644 (file)
@@ -66,6 +66,8 @@
 #include <asm/apic.h>
 
 #define __apicdebuginit(type) static type __init
+#define for_each_irq_pin(entry, head) \
+       for (entry = head; entry; entry = entry->next)
 
 /*
  *      Is the SiS APIC rmw bug present ?
@@ -116,15 +118,6 @@ static int __init parse_noapic(char *str)
 }
 early_param("noapic", parse_noapic);
 
-struct irq_pin_list;
-
-/*
- * 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.
- */
-
 struct irq_pin_list {
        int apic, pin;
        struct irq_pin_list *next;
@@ -139,6 +132,11 @@ static struct irq_pin_list *get_one_free_irq_2_pin(int node)
        return pin;
 }
 
+/*
+ * This is performance-critical, we want to do it O(1)
+ *
+ * Most irqs are mapped 1:1 with pins.
+ */
 struct irq_cfg {
        struct irq_pin_list *irq_2_pin;
        cpumask_var_t domain;
@@ -414,13 +412,10 @@ static bool io_apic_level_ack_pending(struct irq_cfg *cfg)
        unsigned long flags;
 
        spin_lock_irqsave(&ioapic_lock, flags);
-       entry = cfg->irq_2_pin;
-       for (;;) {
+       for_each_irq_pin(entry, cfg->irq_2_pin) {
                unsigned int reg;
                int pin;
 
-               if (!entry)
-                       break;
                pin = entry->pin;
                reg = io_apic_read(entry->apic, 0x10 + pin*2);
                /* Is the remote IRR bit set? */
@@ -428,9 +423,6 @@ static bool io_apic_level_ack_pending(struct irq_cfg *cfg)
                        spin_unlock_irqrestore(&ioapic_lock, flags);
                        return true;
                }
-               if (!entry->next)
-                       break;
-               entry = entry->next;
        }
        spin_unlock_irqrestore(&ioapic_lock, flags);
 
@@ -498,62 +490,58 @@ static void ioapic_mask_entry(int apic, int pin)
  * 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_node(struct irq_cfg *cfg, int node, int apic, int pin)
+static int
+add_pin_to_irq_node_nopanic(struct irq_cfg *cfg, int node, int apic, int pin)
 {
-       struct irq_pin_list *entry;
+       struct irq_pin_list **last, *entry;
 
-       entry = cfg->irq_2_pin;
-       if (!entry) {
-               entry = get_one_free_irq_2_pin(node);
-               if (!entry) {
-                       printk(KERN_ERR "can not alloc irq_2_pin to add %d - %d\n",
-                                       apic, pin);
-                       return;
-               }
-               cfg->irq_2_pin = entry;
-               entry->apic = apic;
-               entry->pin = pin;
-               return;
-       }
-
-       while (entry->next) {
-               /* not again, please */
+       /* don't allow duplicates */
+       last = &cfg->irq_2_pin;
+       for_each_irq_pin(entry, cfg->irq_2_pin) {
                if (entry->apic == apic && entry->pin == pin)
-                       return;
-
-               entry = entry->next;
+                       return 0;
+               last = &entry->next;
        }
 
-       entry->next = get_one_free_irq_2_pin(node);
-       entry = entry->next;
+       entry = get_one_free_irq_2_pin(node);
+       if (!entry) {
+               printk(KERN_ERR "can not alloc irq_pin_list (%d,%d,%d)\n",
+                               node, apic, pin);
+               return -ENOMEM;
+       }
        entry->apic = apic;
        entry->pin = pin;
+
+       *last = entry;
+       return 0;
+}
+
+static void add_pin_to_irq_node(struct irq_cfg *cfg, int node, int apic, int pin)
+{
+       if (add_pin_to_irq_node_nopanic(cfg, node, apic, pin))
+               panic("IO-APIC: failed to add irq-pin. Can not proceed\n");
 }
 
 /*
  * Reroute an IRQ to a different pin.
  */
 static void __init replace_pin_at_irq_node(struct irq_cfg *cfg, int node,
-                                     int oldapic, int oldpin,
-                                     int newapic, int newpin)
+                                          int oldapic, int oldpin,
+                                          int newapic, int newpin)
 {
-       struct irq_pin_list *entry = cfg->irq_2_pin;
-       int replaced = 0;
+       struct irq_pin_list *entry;
 
-       while (entry) {
+       for_each_irq_pin(entry, cfg->irq_2_pin) {
                if (entry->apic == oldapic && entry->pin == oldpin) {
                        entry->apic = newapic;
                        entry->pin = newpin;
-                       replaced = 1;
                        /* every one is different, right? */
-                       break;
+                       return;
                }
-               entry = entry->next;
        }
 
-       /* why? call replace before add? */
-       if (!replaced)
-               add_pin_to_irq_node(cfg, node, newapic, newpin);
+       /* old apic/pin didn't exist, so just add new ones */
+       add_pin_to_irq_node(cfg, node, newapic, newpin);
 }
 
 static void io_apic_modify_irq(struct irq_cfg *cfg,
@@ -563,7 +551,7 @@ static void io_apic_modify_irq(struct irq_cfg *cfg,
        int pin;
        struct irq_pin_list *entry;
 
-       for (entry = cfg->irq_2_pin; entry != NULL; entry = entry->next) {
+       for_each_irq_pin(entry, cfg->irq_2_pin) {
                unsigned int reg;
                pin = entry->pin;
                reg = io_apic_read(entry->apic, 0x10 + pin * 2);
@@ -596,7 +584,6 @@ static void __mask_IO_APIC_irq(struct irq_cfg *cfg)
        io_apic_modify_irq(cfg, ~0, IO_APIC_REDIR_MASKED, &io_apic_sync);
 }
 
-#ifdef CONFIG_X86_32
 static void __mask_and_edge_IO_APIC_irq(struct irq_cfg *cfg)
 {
        io_apic_modify_irq(cfg, ~IO_APIC_REDIR_LEVEL_TRIGGER,
@@ -608,7 +595,6 @@ static void __unmask_and_level_IO_APIC_irq(struct irq_cfg *cfg)
        io_apic_modify_irq(cfg, ~IO_APIC_REDIR_MASKED,
                        IO_APIC_REDIR_LEVEL_TRIGGER, NULL);
 }
-#endif /* CONFIG_X86_32 */
 
 static void mask_IO_APIC_irq_desc(struct irq_desc *desc)
 {
@@ -1697,12 +1683,8 @@ __apicdebuginit(void) print_IO_APIC(void)
                if (!entry)
                        continue;
                printk(KERN_DEBUG "IRQ%d ", irq);
-               for (;;) {
+               for_each_irq_pin(entry, cfg->irq_2_pin)
                        printk("-> %d:%d", entry->apic, entry->pin);
-                       if (!entry->next)
-                               break;
-                       entry = entry->next;
-               }
                printk("\n");
        }
 
@@ -2206,7 +2188,6 @@ static unsigned int startup_ioapic_irq(unsigned int irq)
        return was_pending;
 }
 
-#ifdef CONFIG_X86_64
 static int ioapic_retrigger_irq(unsigned int irq)
 {
 
@@ -2219,14 +2200,6 @@ static int ioapic_retrigger_irq(unsigned int irq)
 
        return 1;
 }
-#else
-static int ioapic_retrigger_irq(unsigned int irq)
-{
-       apic->send_IPI_self(irq_cfg(irq)->vector);
-
-       return 1;
-}
-#endif
 
 /*
  * Level and edge triggered IO-APIC interrupts need different handling,
@@ -2264,13 +2237,9 @@ static void __target_IO_APIC_irq(unsigned int irq, unsigned int dest, struct irq
        struct irq_pin_list *entry;
        u8 vector = cfg->vector;
 
-       entry = cfg->irq_2_pin;
-       for (;;) {
+       for_each_irq_pin(entry, cfg->irq_2_pin) {
                unsigned int reg;
 
-               if (!entry)
-                       break;
-
                apic = entry->apic;
                pin = entry->pin;
                /*
@@ -2283,9 +2252,6 @@ static void __target_IO_APIC_irq(unsigned int irq, unsigned int dest, struct irq
                reg &= ~IO_APIC_REDIR_VECTOR_MASK;
                reg |= vector;
                io_apic_modify(apic, 0x10 + pin*2, reg);
-               if (!entry->next)
-                       break;
-               entry = entry->next;
        }
 }
 
@@ -2510,11 +2476,8 @@ atomic_t irq_mis_count;
 static void ack_apic_level(unsigned int irq)
 {
        struct irq_desc *desc = irq_to_desc(irq);
-
-#ifdef CONFIG_X86_32
        unsigned long v;
        int i;
-#endif
        struct irq_cfg *cfg;
        int do_unmask_irq = 0;
 
@@ -2527,31 +2490,28 @@ static void ack_apic_level(unsigned int irq)
        }
 #endif
 
-#ifdef CONFIG_X86_32
        /*
-       * It appears there is an erratum which affects at least version 0x11
-       * of I/O APIC (that's the 82093AA and cores integrated into various
-       * chipsets).  Under certain conditions a level-triggered interrupt is
-       * erroneously delivered as edge-triggered one but the respective IRR
-       * bit gets set nevertheless.  As a result the I/O unit expects an EOI
-       * message but it will never arrive and further interrupts are blocked
-       * from the source.  The exact reason is so far unknown, but the
-       * phenomenon was observed when two consecutive interrupt requests
-       * from a given source get delivered to the same CPU and the source is
-       * temporarily disabled in between.
-       *
-       * A workaround is to simulate an EOI message manually.  We achieve it
-       * by setting the trigger mode to edge and then to level when the edge
-       * trigger mode gets detected in the TMR of a local APIC for a
-       * level-triggered interrupt.  We mask the source for the time of the
-       * operation to prevent an edge-triggered interrupt escaping meanwhile.
-       * The idea is from Manfred Spraul.  --macro
-       */
+        * It appears there is an erratum which affects at least version 0x11
+        * of I/O APIC (that's the 82093AA and cores integrated into various
+        * chipsets).  Under certain conditions a level-triggered interrupt is
+        * erroneously delivered as edge-triggered one but the respective IRR
+        * bit gets set nevertheless.  As a result the I/O unit expects an EOI
+        * message but it will never arrive and further interrupts are blocked
+        * from the source.  The exact reason is so far unknown, but the
+        * phenomenon was observed when two consecutive interrupt requests
+        * from a given source get delivered to the same CPU and the source is
+        * temporarily disabled in between.
+        *
+        * A workaround is to simulate an EOI message manually.  We achieve it
+        * by setting the trigger mode to edge and then to level when the edge
+        * trigger mode gets detected in the TMR of a local APIC for a
+        * level-triggered interrupt.  We mask the source for the time of the
+        * operation to prevent an edge-triggered interrupt escaping meanwhile.
+        * The idea is from Manfred Spraul.  --macro
+        */
        cfg = desc->chip_data;
        i = cfg->vector;
-
        v = apic_read(APIC_TMR + ((i & ~0x1f) >> 1));
-#endif
 
        /*
         * We must acknowledge the irq before we move it or the acknowledge will
@@ -2593,7 +2553,7 @@ static void ack_apic_level(unsigned int irq)
                unmask_IO_APIC_irq_desc(desc);
        }
 
-#ifdef CONFIG_X86_32
+       /* Tail end of version 0x11 I/O APIC bug workaround */
        if (!(v & (1 << (i & 0x1f)))) {
                atomic_inc(&irq_mis_count);
                spin_lock(&ioapic_lock);
@@ -2601,26 +2561,15 @@ static void ack_apic_level(unsigned int irq)
                __unmask_and_level_IO_APIC_irq(cfg);
                spin_unlock(&ioapic_lock);
        }
-#endif
 }
 
 #ifdef CONFIG_INTR_REMAP
 static void __eoi_ioapic_irq(unsigned int irq, struct irq_cfg *cfg)
 {
-       int apic, pin;
        struct irq_pin_list *entry;
 
-       entry = cfg->irq_2_pin;
-       for (;;) {
-
-               if (!entry)
-                       break;
-
-               apic = entry->apic;
-               pin = entry->pin;
-               io_apic_eoi(apic, pin);
-               entry = entry->next;
-       }
+       for_each_irq_pin(entry, cfg->irq_2_pin)
+               io_apic_eoi(entry->apic, entry->pin);
 }
 
 static void
@@ -3236,8 +3185,7 @@ void destroy_irq(unsigned int irq)
        cfg = desc->chip_data;
        dynamic_irq_cleanup(irq);
        /* connect back irq_cfg */
-       if (desc)
-               desc->chip_data = cfg;
+       desc->chip_data = cfg;
 
        free_irte(irq);
        spin_lock_irqsave(&vector_lock, flags);
@@ -3904,7 +3852,11 @@ static int __io_apic_set_pci_routing(struct device *dev, int irq,
         */
        if (irq >= NR_IRQS_LEGACY) {
                cfg = desc->chip_data;
-               add_pin_to_irq_node(cfg, node, ioapic, pin);
+               if (add_pin_to_irq_node_nopanic(cfg, node, ioapic, pin)) {
+                       printk(KERN_INFO "can not add pin %d for irq %d\n",
+                               pin, irq);
+                       return 0;
+               }
        }
 
        setup_IO_APIC_irq(ioapic, pin, irq, desc, trigger, polarity);
@@ -4101,7 +4053,7 @@ void __init setup_ioapic_dest(void)
 
 static struct resource *ioapic_resources;
 
-static struct resource * __init ioapic_setup_resources(void)
+static struct resource * __init ioapic_setup_resources(int nr_ioapics)
 {
        unsigned long n;
        struct resource *res;
@@ -4117,15 +4069,13 @@ static struct resource * __init ioapic_setup_resources(void)
        mem = alloc_bootmem(n);
        res = (void *)mem;
 
-       if (mem != NULL) {
-               mem += sizeof(struct resource) * nr_ioapics;
+       mem += sizeof(struct resource) * nr_ioapics;
 
-               for (i = 0; i < nr_ioapics; i++) {
-                       res[i].name = mem;
-                       res[i].flags = IORESOURCE_MEM | IORESOURCE_BUSY;
-                       sprintf(mem,  "IOAPIC %u", i);
-                       mem += IOAPIC_RESOURCE_NAME_SIZE;
-               }
+       for (i = 0; i < nr_ioapics; i++) {
+               res[i].name = mem;
+               res[i].flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+               sprintf(mem,  "IOAPIC %u", i);
+               mem += IOAPIC_RESOURCE_NAME_SIZE;
        }
 
        ioapic_resources = res;
@@ -4139,7 +4089,7 @@ void __init ioapic_init_mappings(void)
        struct resource *ioapic_res;
        int i;
 
-       ioapic_res = ioapic_setup_resources();
+       ioapic_res = ioapic_setup_resources(nr_ioapics);
        for (i = 0; i < nr_ioapics; i++) {
                if (smp_found_config) {
                        ioapic_phys = mp_ioapics[i].apicaddr;
@@ -4168,11 +4118,9 @@ fake_ioapic_page:
                            __fix_to_virt(idx), ioapic_phys);
                idx++;
 
-               if (ioapic_res != NULL) {
-                       ioapic_res->start = ioapic_phys;
-                       ioapic_res->end = ioapic_phys + (4 * 1024) - 1;
-                       ioapic_res++;
-               }
+               ioapic_res->start = ioapic_phys;
+               ioapic_res->end = ioapic_phys + (4 * 1024) - 1;
+               ioapic_res++;
        }
 }