]> err.no Git - linux-2.6/blobdiff - arch/x86_64/kernel/io_apic.c
Merge branch 'audit.b32' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit...
[linux-2.6] / arch / x86_64 / kernel / io_apic.c
index bb16f3790f09ebc704959df4db4785fd320d2d40..91728d9d34724fdcd175d83fce1c5d08290fe0dd 100644 (file)
@@ -30,6 +30,8 @@
 #include <linux/mc146818rtc.h>
 #include <linux/acpi.h>
 #include <linux/sysdev.h>
+#include <linux/msi.h>
+#include <linux/htirq.h>
 #ifdef CONFIG_ACPI
 #include <acpi/acpi_bus.h>
 #endif
@@ -43,8 +45,9 @@
 #include <asm/dma.h>
 #include <asm/nmi.h>
 #include <asm/msidef.h>
+#include <asm/hypertransport.h>
 
-static int assign_irq_vector(int irq);
+static int assign_irq_vector(int irq, cpumask_t mask);
 
 #define __apicdebuginit  __init
 
@@ -135,11 +138,35 @@ static void ioapic_write_entry(int apic, int pin, struct IO_APIC_route_entry e)
 }
 
 #ifdef CONFIG_SMP
+static void __target_IO_APIC_irq(unsigned int irq, unsigned int dest, u8 vector)
+{
+       int apic, pin;
+       struct irq_pin_list *entry = irq_2_pin + irq;
+
+       BUG_ON(irq >= NR_IRQS);
+       for (;;) {
+               unsigned int reg;
+               apic = entry->apic;
+               pin = entry->pin;
+               if (pin == -1)
+                       break;
+               io_apic_write(apic, 0x11 + pin*2, dest);
+               reg = io_apic_read(apic, 0x10 + pin*2);
+               reg &= ~0x000000ff;
+               reg |= vector;
+               io_apic_modify(apic, reg);
+               if (!entry->next)
+                       break;
+               entry = irq_2_pin + entry->next;
+       }
+}
+
 static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask)
 {
        unsigned long flags;
        unsigned int dest;
        cpumask_t tmp;
+       int vector;
 
        cpus_and(tmp, mask, cpu_online_map);
        if (cpus_empty(tmp))
@@ -147,7 +174,13 @@ static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask)
 
        cpus_and(mask, tmp, CPU_MASK_ALL);
 
-       dest = cpu_mask_to_apicid(mask);
+       vector = assign_irq_vector(irq, mask);
+       if (vector < 0)
+               return;
+
+       cpus_clear(tmp);
+       cpu_set(vector >> 8, tmp);
+       dest = cpu_mask_to_apicid(tmp);
 
        /*
         * Only the high 8 bits are valid.
@@ -155,14 +188,12 @@ static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask)
        dest = SET_APIC_LOGICAL_ID(dest);
 
        spin_lock_irqsave(&ioapic_lock, flags);
-       __DO_ACTION(1, = dest, )
+       __target_IO_APIC_irq(irq, dest, vector & 0xff);
        set_native_irq_info(irq, mask);
        spin_unlock_irqrestore(&ioapic_lock, flags);
 }
 #endif
 
-static u8 gsi_2_irq[NR_IRQ_VECTORS] = { [0 ... NR_IRQ_VECTORS-1] = 0xFF };
-
 /*
  * 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
@@ -488,64 +519,6 @@ static inline int irq_trigger(int idx)
        return MPBIOS_trigger(idx);
 }
 
-static int next_irq = 16;
-
-/*
- * gsi_irq_sharing -- Name overload!  "irq" can be either a legacy IRQ
- * in the range 0-15, a linux IRQ in the range 0-223, or a GSI number
- * from ACPI, which can reach 800 in large boxen.
- *
- * Compact the sparse GSI space into a sequential IRQ series and reuse
- * vectors if possible.
- */
-int gsi_irq_sharing(int gsi)
-{
-       int i, tries, vector;
-
-       BUG_ON(gsi >= NR_IRQ_VECTORS);
-
-       if (platform_legacy_irq(gsi))
-               return gsi;
-
-       if (gsi_2_irq[gsi] != 0xFF)
-               return (int)gsi_2_irq[gsi];
-
-       tries = NR_IRQS;
-  try_again:
-       vector = assign_irq_vector(gsi);
-
-       /*
-        * Sharing vectors means sharing IRQs, so scan irq_vectors for previous
-        * use of vector and if found, return that IRQ.  However, we never want
-        * to share legacy IRQs, which usually have a different trigger mode
-        * than PCI.
-        */
-       for (i = 0; i < NR_IRQS; i++)
-               if (IO_APIC_VECTOR(i) == vector)
-                       break;
-       if (platform_legacy_irq(i)) {
-               if (--tries >= 0) {
-                       IO_APIC_VECTOR(i) = 0;
-                       goto try_again;
-               }
-               panic("gsi_irq_sharing: didn't find an IRQ using vector 0x%02X for GSI %d", vector, gsi);
-       }
-       if (i < NR_IRQS) {
-               gsi_2_irq[gsi] = i;
-               printk(KERN_INFO "GSI %d sharing vector 0x%02X and IRQ %d\n",
-                               gsi, vector, i);
-               return i;
-       }
-
-       i = next_irq++;
-       BUG_ON(i >= NR_IRQS);
-       gsi_2_irq[gsi] = i;
-       IO_APIC_VECTOR(i) = vector;
-       printk(KERN_INFO "GSI %d assigned vector 0x%02X and IRQ %d\n",
-                       gsi, vector, i);
-       return i;
-}
-
 static int pin_2_irq(int idx, int apic, int pin)
 {
        int irq, i;
@@ -567,7 +540,6 @@ static int pin_2_irq(int idx, int apic, int pin)
                while (i < apic)
                        irq += nr_ioapic_registers[i++];
                irq += pin;
-               irq = gsi_irq_sharing(irq);
        }
        BUG_ON(irq >= NR_IRQS);
        return irq;
@@ -591,44 +563,77 @@ static inline int IO_APIC_irq_trigger(int irq)
 }
 
 /* irq_vectors is indexed by the sum of all RTEs in all I/O APICs. */
-u8 irq_vector[NR_IRQ_VECTORS] __read_mostly = { FIRST_DEVICE_VECTOR , 0 };
+unsigned int irq_vector[NR_IRQ_VECTORS] __read_mostly = { FIRST_EXTERNAL_VECTOR, 0 };
 
-static int __assign_irq_vector(int irq)
+static int __assign_irq_vector(int irq, cpumask_t mask)
 {
-       static int current_vector = FIRST_DEVICE_VECTOR, offset = 0;
-       int vector;
+       /*
+        * NOTE! The local APIC isn't very good at handling
+        * multiple interrupts at the same interrupt level.
+        * As the interrupt level is determined by taking the
+        * vector number and shifting that right by 4, we
+        * want to spread these out a bit so that they don't
+        * all fall in the same interrupt level.
+        *
+        * Also, we've got to be careful not to trash gate
+        * 0x80, because int 0x80 is hm, kind of importantish. ;)
+        */
+       static struct {
+               int vector;
+               int offset;
+       } pos[NR_CPUS] = { [ 0 ... NR_CPUS - 1] = {FIRST_DEVICE_VECTOR, 0} };
+       int old_vector = -1;
+       int cpu;
 
        BUG_ON((unsigned)irq >= NR_IRQ_VECTORS);
 
-       if (IO_APIC_VECTOR(irq) > 0) {
-               return IO_APIC_VECTOR(irq);
+       if (IO_APIC_VECTOR(irq) > 0)
+               old_vector = IO_APIC_VECTOR(irq);
+       if ((old_vector > 0) && cpu_isset(old_vector >> 8, mask)) {
+               return old_vector;
        }
+
+       for_each_cpu_mask(cpu, mask) {
+               int vector, offset;
+               vector = pos[cpu].vector;
+               offset = pos[cpu].offset;
 next:
-       current_vector += 8;
-       if (current_vector == IA32_SYSCALL_VECTOR)
-               goto next;
-
-       if (current_vector >= FIRST_SYSTEM_VECTOR) {
-               /* If we run out of vectors on large boxen, must share them. */
-               offset = (offset + 1) % 8;
-               current_vector = FIRST_DEVICE_VECTOR + offset;
+               vector += 8;
+               if (vector >= FIRST_SYSTEM_VECTOR) {
+                       /* If we run out of vectors on large boxen, must share them. */
+                       offset = (offset + 1) % 8;
+                       vector = FIRST_DEVICE_VECTOR + offset;
+               }
+               if (unlikely(pos[cpu].vector == vector))
+                       continue;
+               if (vector == IA32_SYSCALL_VECTOR)
+                       goto next;
+               if (per_cpu(vector_irq, cpu)[vector] != -1)
+                       goto next;
+               /* Found one! */
+               pos[cpu].vector = vector;
+               pos[cpu].offset = offset;
+               if (old_vector >= 0) {
+                       int old_cpu = old_vector >> 8;
+                       old_vector &= 0xff;
+                       per_cpu(vector_irq, old_cpu)[old_vector] = -1;
+               }
+               per_cpu(vector_irq, cpu)[vector] = irq;
+               vector |= cpu << 8;
+               IO_APIC_VECTOR(irq) = vector;
+               return vector;
        }
-
-       vector = current_vector;
-       IO_APIC_VECTOR(irq) = vector;
-
-       return vector;
+       return -ENOSPC;
 }
 
-static int assign_irq_vector(int irq)
+static int assign_irq_vector(int irq, cpumask_t mask)
 {
        int vector;
        unsigned long flags;
 
        spin_lock_irqsave(&vector_lock, flags);
-       vector = __assign_irq_vector(irq);
+       vector = __assign_irq_vector(irq, mask);
        spin_unlock_irqrestore(&vector_lock, flags);
-
        return vector;
 }
 
@@ -649,7 +654,6 @@ static void ioapic_register_intr(int irq, int vector, unsigned long trigger)
        else
                set_irq_chip_and_handler(irq, &ioapic_chip,
                                         handle_edge_irq);
-       set_intr_gate(vector, interrupt[irq]);
 }
 
 static void __init setup_IO_APIC_irqs(void)
@@ -699,8 +703,15 @@ static void __init setup_IO_APIC_irqs(void)
                        continue;
 
                if (IO_APIC_IRQ(irq)) {
-                       vector = assign_irq_vector(irq);
-                       entry.vector = vector;
+                       cpumask_t mask;
+                       vector = assign_irq_vector(irq, TARGET_CPUS);
+                       if (vector < 0)
+                               continue;
+
+                       cpus_clear(mask);
+                       cpu_set(vector >> 8, mask);
+                       entry.dest.logical.logical_dest = cpu_mask_to_apicid(mask);
+                       entry.vector = vector & 0xff;
 
                        ioapic_register_intr(irq, vector, IOAPIC_AUTO);
                        if (!apic && (irq < 16))
@@ -1197,7 +1208,14 @@ static unsigned int startup_ioapic_irq(unsigned int irq)
 
 static int ioapic_retrigger_irq(unsigned int irq)
 {
-       send_IPI_self(IO_APIC_VECTOR(irq));
+       cpumask_t mask;
+       unsigned vector;
+
+       vector = irq_vector[irq];
+       cpus_clear(mask);
+       cpu_set(vector >> 8, mask);
+
+       send_IPI_mask(mask, vector & 0xff);
 
        return 1;
 }
@@ -1400,8 +1418,6 @@ static inline void unlock_ExtINT_logic(void)
        spin_unlock_irqrestore(&ioapic_lock, flags);
 }
 
-int timer_uses_ioapic_pin_0;
-
 /*
  * This code may look a bit paranoid, but it's supposed to cooperate with
  * a wide range of boards and BIOS bugs.  Fortunately only the timer IRQ
@@ -1419,8 +1435,7 @@ static inline void check_timer(void)
         * get/set the timer IRQ vector:
         */
        disable_8259A_irq(0);
-       vector = assign_irq_vector(0);
-       set_intr_gate(vector, interrupt[0]);
+       vector = assign_irq_vector(0, TARGET_CPUS);
 
        /*
         * Subtle, code in do_timer_interrupt() expects an AEOI
@@ -1439,9 +1454,6 @@ static inline void check_timer(void)
        pin2  = ioapic_i8259.pin;
        apic2 = ioapic_i8259.apic;
 
-       if (pin1 == 0)
-               timer_uses_ioapic_pin_0 = 1;
-
        apic_printk(APIC_VERBOSE,KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n",
                vector, apic1, pin1, apic2, pin2);
 
@@ -1663,7 +1675,7 @@ int create_irq(void)
                        continue;
                if (irq_vector[new] != 0)
                        continue;
-               vector = __assign_irq_vector(new);
+               vector = __assign_irq_vector(new, TARGET_CPUS);
                if (likely(vector > 0))
                        irq = new;
                break;
@@ -1671,7 +1683,6 @@ int create_irq(void)
        spin_unlock_irqrestore(&vector_lock, flags);
 
        if (irq >= 0) {
-               set_intr_gate(vector, interrupt[irq]);
                dynamic_irq_init(irq);
        }
        return irq;
@@ -1692,20 +1703,17 @@ void destroy_irq(unsigned int irq)
  * MSI mesage composition
  */
 #ifdef CONFIG_PCI_MSI
-static int msi_msg_setup(struct pci_dev *pdev, unsigned int irq, struct msi_msg *msg)
+static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_msg *msg)
 {
-       /* For now always this code always uses physical delivery
-        * mode.
-        */
        int vector;
        unsigned dest;
 
-       vector = assign_irq_vector(irq);
+       vector = assign_irq_vector(irq, TARGET_CPUS);
        if (vector >= 0) {
                cpumask_t tmp;
 
                cpus_clear(tmp);
-               cpu_set(first_cpu(cpu_online_map), tmp);
+               cpu_set(vector >> 8, tmp);
                dest = cpu_mask_to_apicid(tmp);
 
                msg->address_hi = MSI_ADDR_BASE_HI;
@@ -1730,35 +1738,171 @@ static int msi_msg_setup(struct pci_dev *pdev, unsigned int irq, struct msi_msg
        return vector;
 }
 
-static void msi_msg_teardown(unsigned int irq)
+#ifdef CONFIG_SMP
+static void set_msi_irq_affinity(unsigned int irq, cpumask_t mask)
+{
+       struct msi_msg msg;
+       unsigned int dest;
+       cpumask_t tmp;
+       int vector;
+
+       cpus_and(tmp, mask, cpu_online_map);
+       if (cpus_empty(tmp))
+               tmp = TARGET_CPUS;
+
+       cpus_and(mask, tmp, CPU_MASK_ALL);
+
+       vector = assign_irq_vector(irq, mask);
+       if (vector < 0)
+               return;
+
+       cpus_clear(tmp);
+       cpu_set(vector >> 8, tmp);
+       dest = cpu_mask_to_apicid(tmp);
+
+       read_msi_msg(irq, &msg);
+
+       msg.data &= ~MSI_DATA_VECTOR_MASK;
+       msg.data |= MSI_DATA_VECTOR(vector);
+       msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK;
+       msg.address_lo |= MSI_ADDR_DEST_ID(dest);
+
+       write_msi_msg(irq, &msg);
+       set_native_irq_info(irq, mask);
+}
+#endif /* CONFIG_SMP */
+
+/*
+ * IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices,
+ * which implement the MSI or MSI-X Capability Structure.
+ */
+static struct irq_chip msi_chip = {
+       .name           = "PCI-MSI",
+       .unmask         = unmask_msi_irq,
+       .mask           = mask_msi_irq,
+       .ack            = ack_apic_edge,
+#ifdef CONFIG_SMP
+       .set_affinity   = set_msi_irq_affinity,
+#endif
+       .retrigger      = ioapic_retrigger_irq,
+};
+
+int arch_setup_msi_irq(unsigned int irq, struct pci_dev *dev)
+{
+       struct msi_msg msg;
+       int ret;
+       ret = msi_compose_msg(dev, irq, &msg);
+       if (ret < 0)
+               return ret;
+
+       write_msi_msg(irq, &msg);
+
+       set_irq_chip_and_handler(irq, &msi_chip, handle_edge_irq);
+
+       return 0;
+}
+
+void arch_teardown_msi_irq(unsigned int irq)
 {
        return;
 }
 
-static void msi_msg_set_affinity(unsigned int irq, cpumask_t mask, struct msi_msg *msg)
+#endif /* CONFIG_PCI_MSI */
+
+/*
+ * Hypertransport interrupt support
+ */
+#ifdef CONFIG_HT_IRQ
+
+#ifdef CONFIG_SMP
+
+static void target_ht_irq(unsigned int irq, unsigned int dest, u8 vector)
+{
+       u32 low, high;
+       low  = read_ht_irq_low(irq);
+       high = read_ht_irq_high(irq);
+
+       low  &= ~(HT_IRQ_LOW_VECTOR_MASK | HT_IRQ_LOW_DEST_ID_MASK);
+       high &= ~(HT_IRQ_HIGH_DEST_ID_MASK);
+
+       low  |= HT_IRQ_LOW_VECTOR(vector) | HT_IRQ_LOW_DEST_ID(dest);
+       high |= HT_IRQ_HIGH_DEST_ID(dest);
+
+       write_ht_irq_low(irq, low);
+       write_ht_irq_high(irq, high);
+}
+
+static void set_ht_irq_affinity(unsigned int irq, cpumask_t mask)
 {
+       unsigned int dest;
+       cpumask_t tmp;
        int vector;
-       unsigned dest;
 
-       vector = assign_irq_vector(irq);
-       if (vector > 0) {
-               dest = cpu_mask_to_apicid(mask);
+       cpus_and(tmp, mask, cpu_online_map);
+       if (cpus_empty(tmp))
+               tmp = TARGET_CPUS;
 
-               msg->data &= ~MSI_DATA_VECTOR_MASK;
-               msg->data |= MSI_DATA_VECTOR(vector);
-               msg->address_lo &= ~MSI_ADDR_DEST_ID_MASK;
-               msg->address_lo |= MSI_ADDR_DEST_ID(dest);
-       }
+       cpus_and(mask, tmp, CPU_MASK_ALL);
+
+       vector = assign_irq_vector(irq, mask);
+       if (vector < 0)
+               return;
+
+       cpus_clear(tmp);
+       cpu_set(vector >> 8, tmp);
+       dest = cpu_mask_to_apicid(tmp);
+
+       target_ht_irq(irq, dest, vector & 0xff);
+       set_native_irq_info(irq, mask);
 }
+#endif
 
-struct msi_ops arch_msi_ops = {
-       .needs_64bit_address = 0,
-       .setup = msi_msg_setup,
-       .teardown = msi_msg_teardown,
-       .target = msi_msg_set_affinity,
+static struct hw_interrupt_type ht_irq_chip = {
+       .name           = "PCI-HT",
+       .mask           = mask_ht_irq,
+       .unmask         = unmask_ht_irq,
+       .ack            = ack_apic_edge,
+#ifdef CONFIG_SMP
+       .set_affinity   = set_ht_irq_affinity,
+#endif
+       .retrigger      = ioapic_retrigger_irq,
 };
 
-#endif
+int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev)
+{
+       int vector;
+
+       vector = assign_irq_vector(irq, TARGET_CPUS);
+       if (vector >= 0) {
+               u32 low, high;
+               unsigned dest;
+               cpumask_t tmp;
+
+               cpus_clear(tmp);
+               cpu_set(vector >> 8, tmp);
+               dest = cpu_mask_to_apicid(tmp);
+
+               high =  HT_IRQ_HIGH_DEST_ID(dest);
+
+               low =   HT_IRQ_LOW_BASE |
+                       HT_IRQ_LOW_DEST_ID(dest) |
+                       HT_IRQ_LOW_VECTOR(vector) |
+                       ((INT_DEST_MODE == 0) ?
+                               HT_IRQ_LOW_DM_PHYSICAL :
+                               HT_IRQ_LOW_DM_LOGICAL) |
+                       HT_IRQ_LOW_RQEOI_EDGE |
+                       ((INT_DELIVERY_MODE != dest_LowestPrio) ?
+                               HT_IRQ_LOW_MT_FIXED :
+                               HT_IRQ_LOW_MT_ARBITRATED);
+
+               write_ht_irq_low(irq, low);
+               write_ht_irq_high(irq, high);
+
+               set_irq_chip_and_handler(irq, &ht_irq_chip, handle_edge_irq);
+       }
+       return vector;
+}
+#endif /* CONFIG_HT_IRQ */
 
 /* --------------------------------------------------------------------------
                           ACPI-based IOAPIC Configuration
@@ -1785,6 +1929,8 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int triggering, int p
 {
        struct IO_APIC_route_entry entry;
        unsigned long flags;
+       int vector;
+       cpumask_t mask;
 
        if (!IO_APIC_IRQ(irq)) {
                apic_printk(APIC_QUIET,KERN_ERR "IOAPIC[%d]: Invalid reference to IRQ 0\n",
@@ -1792,6 +1938,20 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int triggering, int p
                return -EINVAL;
        }
 
+       /*
+        * IRQs < 16 are already in the irq_2_pin[] map
+        */
+       if (irq >= 16)
+               add_pin_to_irq(irq, ioapic, pin);
+
+
+       vector = assign_irq_vector(irq, TARGET_CPUS);
+       if (vector < 0)
+               return vector;
+
+       cpus_clear(mask);
+       cpu_set(vector >> 8, mask);
+
        /*
         * Generate a PCI IRQ routing entry and program the IOAPIC accordingly.
         * Note that we mask (disable) IRQs now -- these get enabled when the
@@ -1802,19 +1962,11 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int triggering, int p
 
        entry.delivery_mode = INT_DELIVERY_MODE;
        entry.dest_mode = INT_DEST_MODE;
-       entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS);
+       entry.dest.logical.logical_dest = cpu_mask_to_apicid(mask);
        entry.trigger = triggering;
        entry.polarity = polarity;
        entry.mask = 1;                                  /* Disabled (masked) */
-
-       irq = gsi_irq_sharing(irq);
-       /*
-        * IRQs < 16 are already in the irq_2_pin[] map
-        */
-       if (irq >= 16)
-               add_pin_to_irq(irq, ioapic, pin);
-
-       entry.vector = assign_irq_vector(irq);
+       entry.vector = vector & 0xff;
 
        apic_printk(APIC_VERBOSE,KERN_DEBUG "IOAPIC[%d]: Set PCI routing entry (%d-%d -> 0x%x -> "
                "IRQ %d Mode:%i Active:%i)\n", ioapic,