]> Pileus Git - ~andy/linux/blobdiff - arch/x86/kernel/apic/io_apic.c
Merge branch 'linus' into x86/apic
[~andy/linux] / arch / x86 / kernel / apic / io_apic.c
index 1bb5c6cee3ebb140e30e421c05615bf94b001936..84990002240745cf852b09a49ebd0202d09248cd 100644 (file)
@@ -518,120 +518,6 @@ static void ioapic_mask_entry(int apic, int pin)
        spin_unlock_irqrestore(&ioapic_lock, flags);
 }
 
-#ifdef CONFIG_SMP
-static void send_cleanup_vector(struct irq_cfg *cfg)
-{
-       cpumask_var_t cleanup_mask;
-
-       if (unlikely(!alloc_cpumask_var(&cleanup_mask, GFP_ATOMIC))) {
-               unsigned int i;
-               cfg->move_cleanup_count = 0;
-               for_each_cpu_and(i, cfg->old_domain, cpu_online_mask)
-                       cfg->move_cleanup_count++;
-               for_each_cpu_and(i, cfg->old_domain, cpu_online_mask)
-                       apic->send_IPI_mask(cpumask_of(i), IRQ_MOVE_CLEANUP_VECTOR);
-       } else {
-               cpumask_and(cleanup_mask, cfg->old_domain, cpu_online_mask);
-               cfg->move_cleanup_count = cpumask_weight(cleanup_mask);
-               apic->send_IPI_mask(cleanup_mask, IRQ_MOVE_CLEANUP_VECTOR);
-               free_cpumask_var(cleanup_mask);
-       }
-       cfg->move_in_progress = 0;
-}
-
-static void __target_IO_APIC_irq(unsigned int irq, unsigned int dest, struct irq_cfg *cfg)
-{
-       int apic, pin;
-       struct irq_pin_list *entry;
-       u8 vector = cfg->vector;
-
-       entry = cfg->irq_2_pin;
-       for (;;) {
-               unsigned int reg;
-
-               if (!entry)
-                       break;
-
-               apic = entry->apic;
-               pin = entry->pin;
-               /*
-                * With interrupt-remapping, destination information comes
-                * from interrupt-remapping table entry.
-                */
-               if (!irq_remapped(irq))
-                       io_apic_write(apic, 0x11 + pin*2, dest);
-               reg = io_apic_read(apic, 0x10 + pin*2);
-               reg &= ~IO_APIC_REDIR_VECTOR_MASK;
-               reg |= vector;
-               io_apic_modify(apic, 0x10 + pin*2, reg);
-               if (!entry->next)
-                       break;
-               entry = entry->next;
-       }
-}
-
-static int
-assign_irq_vector(int irq, struct irq_cfg *cfg, const struct cpumask *mask);
-
-/*
- * Either sets desc->affinity to a valid value, and returns
- * ->cpu_mask_to_apicid of that, or returns BAD_APICID and
- * leaves desc->affinity untouched.
- */
-static unsigned int
-set_desc_affinity(struct irq_desc *desc, const struct cpumask *mask)
-{
-       struct irq_cfg *cfg;
-       unsigned int irq;
-
-       if (!cpumask_intersects(mask, cpu_online_mask))
-               return BAD_APICID;
-
-       irq = desc->irq;
-       cfg = desc->chip_data;
-       if (assign_irq_vector(irq, cfg, mask))
-               return BAD_APICID;
-
-       /* check that before desc->addinity get updated */
-       set_extra_move_desc(desc, mask);
-
-       cpumask_copy(desc->affinity, mask);
-
-       return apic->cpu_mask_to_apicid_and(desc->affinity, cfg->domain);
-}
-
-static void
-set_ioapic_affinity_irq_desc(struct irq_desc *desc, const struct cpumask *mask)
-{
-       struct irq_cfg *cfg;
-       unsigned long flags;
-       unsigned int dest;
-       unsigned int irq;
-
-       irq = desc->irq;
-       cfg = desc->chip_data;
-
-       spin_lock_irqsave(&ioapic_lock, flags);
-       dest = set_desc_affinity(desc, mask);
-       if (dest != BAD_APICID) {
-               /* Only the high 8 bits are valid. */
-               dest = SET_APIC_LOGICAL_ID(dest);
-               __target_IO_APIC_irq(irq, dest, cfg);
-       }
-       spin_unlock_irqrestore(&ioapic_lock, flags);
-}
-
-static void
-set_ioapic_affinity_irq(unsigned int irq, const struct cpumask *mask)
-{
-       struct irq_desc *desc;
-
-       desc = irq_to_desc(irq);
-
-       set_ioapic_affinity_irq_desc(desc, mask);
-}
-#endif /* CONFIG_SMP */
-
 /*
  * 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
@@ -851,63 +737,74 @@ __setup("pirq=", ioapic_pirq_setup);
 #endif /* CONFIG_X86_32 */
 
 #ifdef CONFIG_INTR_REMAP
-/* I/O APIC RTE contents at the OS boot up */
-static struct IO_APIC_route_entry *early_ioapic_entries[MAX_IO_APICS];
+struct IO_APIC_route_entry **alloc_ioapic_entries(void)
+{
+       int apic;
+       struct IO_APIC_route_entry **ioapic_entries;
+
+       ioapic_entries = kzalloc(sizeof(*ioapic_entries) * nr_ioapics,
+                               GFP_ATOMIC);
+       if (!ioapic_entries)
+               return 0;
+
+       for (apic = 0; apic < nr_ioapics; apic++) {
+               ioapic_entries[apic] =
+                       kzalloc(sizeof(struct IO_APIC_route_entry) *
+                               nr_ioapic_registers[apic], GFP_ATOMIC);
+               if (!ioapic_entries[apic])
+                       goto nomem;
+       }
+
+       return ioapic_entries;
+
+nomem:
+       while (--apic >= 0)
+               kfree(ioapic_entries[apic]);
+       kfree(ioapic_entries);
+
+       return 0;
+}
 
 /*
  * Saves all the IO-APIC RTE's
  */
-int save_IO_APIC_setup(void)
+int save_IO_APIC_setup(struct IO_APIC_route_entry **ioapic_entries)
 {
-       union IO_APIC_reg_01 reg_01;
-       unsigned long flags;
        int apic, pin;
 
-       /*
-        * The number of IO-APIC IRQ registers (== #pins):
-        */
-       for (apic = 0; apic < nr_ioapics; apic++) {
-               spin_lock_irqsave(&ioapic_lock, flags);
-               reg_01.raw = io_apic_read(apic, 1);
-               spin_unlock_irqrestore(&ioapic_lock, flags);
-               nr_ioapic_registers[apic] = reg_01.bits.entries+1;
-       }
+       if (!ioapic_entries)
+               return -ENOMEM;
 
        for (apic = 0; apic < nr_ioapics; apic++) {
-               early_ioapic_entries[apic] =
-                       kzalloc(sizeof(struct IO_APIC_route_entry) *
-                               nr_ioapic_registers[apic], GFP_KERNEL);
-               if (!early_ioapic_entries[apic])
-                       goto nomem;
-       }
+               if (!ioapic_entries[apic])
+                       return -ENOMEM;
 
-       for (apic = 0; apic < nr_ioapics; apic++)
                for (pin = 0; pin < nr_ioapic_registers[apic]; pin++)
-                       early_ioapic_entries[apic][pin] =
+                       ioapic_entries[apic][pin] =
                                ioapic_read_entry(apic, pin);
+       }
 
        return 0;
-
-nomem:
-       while (apic >= 0)
-               kfree(early_ioapic_entries[apic--]);
-       memset(early_ioapic_entries, 0,
-               ARRAY_SIZE(early_ioapic_entries));
-
-       return -ENOMEM;
 }
 
-void mask_IO_APIC_setup(void)
+/*
+ * Mask all IO APIC entries.
+ */
+void mask_IO_APIC_setup(struct IO_APIC_route_entry **ioapic_entries)
 {
        int apic, pin;
 
+       if (!ioapic_entries)
+               return;
+
        for (apic = 0; apic < nr_ioapics; apic++) {
-               if (!early_ioapic_entries[apic])
+               if (!ioapic_entries[apic])
                        break;
+
                for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) {
                        struct IO_APIC_route_entry entry;
 
-                       entry = early_ioapic_entries[apic][pin];
+                       entry = ioapic_entries[apic][pin];
                        if (!entry.mask) {
                                entry.mask = 1;
                                ioapic_write_entry(apic, pin, entry);
@@ -916,22 +813,30 @@ void mask_IO_APIC_setup(void)
        }
 }
 
-void restore_IO_APIC_setup(void)
+/*
+ * Restore IO APIC entries which was saved in ioapic_entries.
+ */
+int restore_IO_APIC_setup(struct IO_APIC_route_entry **ioapic_entries)
 {
        int apic, pin;
 
+       if (!ioapic_entries)
+               return -ENOMEM;
+
        for (apic = 0; apic < nr_ioapics; apic++) {
-               if (!early_ioapic_entries[apic])
-                       break;
+               if (!ioapic_entries[apic])
+                       return -ENOMEM;
+
                for (pin = 0; pin < nr_ioapic_registers[apic]; pin++)
                        ioapic_write_entry(apic, pin,
-                                          early_ioapic_entries[apic][pin]);
-               kfree(early_ioapic_entries[apic]);
-               early_ioapic_entries[apic] = NULL;
+                                       ioapic_entries[apic][pin]);
        }
+       return 0;
 }
 
-void reinit_intr_remapped_IO_APIC(int intr_remapping)
+void reinit_intr_remapped_IO_APIC(int intr_remapping,
+       struct IO_APIC_route_entry **ioapic_entries)
+
 {
        /*
         * for now plain restore of previous settings.
@@ -940,7 +845,17 @@ void reinit_intr_remapped_IO_APIC(int intr_remapping)
         * table entries. for now, do a plain restore, and wait for
         * the setup_IO_APIC_irqs() to do proper initialization.
         */
-       restore_IO_APIC_setup();
+       restore_IO_APIC_setup(ioapic_entries);
+}
+
+void free_ioapic_entries(struct IO_APIC_route_entry **ioapic_entries)
+{
+       int apic;
+
+       for (apic = 0; apic < nr_ioapics; apic++)
+               kfree(ioapic_entries[apic]);
+
+       kfree(ioapic_entries);
 }
 #endif
 
@@ -2331,6 +2246,115 @@ static int ioapic_retrigger_irq(unsigned int irq)
  */
 
 #ifdef CONFIG_SMP
+static void send_cleanup_vector(struct irq_cfg *cfg)
+{
+       cpumask_var_t cleanup_mask;
+
+       if (unlikely(!alloc_cpumask_var(&cleanup_mask, GFP_ATOMIC))) {
+               unsigned int i;
+               cfg->move_cleanup_count = 0;
+               for_each_cpu_and(i, cfg->old_domain, cpu_online_mask)
+                       cfg->move_cleanup_count++;
+               for_each_cpu_and(i, cfg->old_domain, cpu_online_mask)
+                       apic->send_IPI_mask(cpumask_of(i), IRQ_MOVE_CLEANUP_VECTOR);
+       } else {
+               cpumask_and(cleanup_mask, cfg->old_domain, cpu_online_mask);
+               cfg->move_cleanup_count = cpumask_weight(cleanup_mask);
+               apic->send_IPI_mask(cleanup_mask, IRQ_MOVE_CLEANUP_VECTOR);
+               free_cpumask_var(cleanup_mask);
+       }
+       cfg->move_in_progress = 0;
+}
+
+static void
+__target_IO_APIC_irq(unsigned int irq, unsigned int dest, struct irq_cfg *cfg)
+{
+       int apic, pin;
+       struct irq_pin_list *entry;
+       u8 vector = cfg->vector;
+
+       entry = cfg->irq_2_pin;
+       for (;;) {
+               unsigned int reg;
+
+               if (!entry)
+                       break;
+
+               apic = entry->apic;
+               pin = entry->pin;
+               /*
+                * With interrupt-remapping, destination information comes
+                * from interrupt-remapping table entry.
+                */
+               if (!irq_remapped(irq))
+                       io_apic_write(apic, 0x11 + pin*2, dest);
+               reg = io_apic_read(apic, 0x10 + pin*2);
+               reg &= ~IO_APIC_REDIR_VECTOR_MASK;
+               reg |= vector;
+               io_apic_modify(apic, 0x10 + pin*2, reg);
+               if (!entry->next)
+                       break;
+               entry = entry->next;
+       }
+}
+
+/*
+ * Either sets desc->affinity to a valid value, and returns
+ * ->cpu_mask_to_apicid of that, or returns BAD_APICID and
+ * leaves desc->affinity untouched.
+ */
+static unsigned int
+set_desc_affinity(struct irq_desc *desc, const struct cpumask *mask)
+{
+       struct irq_cfg *cfg;
+       unsigned int irq;
+
+       if (!cpumask_intersects(mask, cpu_online_mask))
+               return BAD_APICID;
+
+       irq = desc->irq;
+       cfg = desc->chip_data;
+       if (assign_irq_vector(irq, cfg, mask))
+               return BAD_APICID;
+
+       /* check that before desc->addinity get updated */
+       set_extra_move_desc(desc, mask);
+
+       cpumask_copy(desc->affinity, mask);
+
+       return apic->cpu_mask_to_apicid_and(desc->affinity, cfg->domain);
+}
+
+static void
+set_ioapic_affinity_irq_desc(struct irq_desc *desc, const struct cpumask *mask)
+{
+       struct irq_cfg *cfg;
+       unsigned long flags;
+       unsigned int dest;
+       unsigned int irq;
+
+       irq = desc->irq;
+       cfg = desc->chip_data;
+
+       spin_lock_irqsave(&ioapic_lock, flags);
+       dest = set_desc_affinity(desc, mask);
+       if (dest != BAD_APICID) {
+               /* Only the high 8 bits are valid. */
+               dest = SET_APIC_LOGICAL_ID(dest);
+               __target_IO_APIC_irq(irq, dest, cfg);
+       }
+       spin_unlock_irqrestore(&ioapic_lock, flags);
+}
+
+static void
+set_ioapic_affinity_irq(unsigned int irq, const struct cpumask *mask)
+{
+       struct irq_desc *desc;
+
+       desc = irq_to_desc(irq);
+
+       set_ioapic_affinity_irq_desc(desc, mask);
+}
 
 #ifdef CONFIG_INTR_REMAP
 
@@ -2495,7 +2519,6 @@ static void irq_complete_move(struct irq_desc **descp)
 static inline void irq_complete_move(struct irq_desc **descp) {}
 #endif
 
-#ifdef CONFIG_INTR_REMAP
 static void __eoi_ioapic_irq(unsigned int irq, struct irq_cfg *cfg)
 {
        int apic, pin;
@@ -2529,6 +2552,7 @@ eoi_ioapic_irq(struct irq_desc *desc)
        spin_unlock_irqrestore(&ioapic_lock, flags);
 }
 
+#ifdef CONFIG_X86_X2APIC
 static void ack_x2apic_level(unsigned int irq)
 {
        struct irq_desc *desc = irq_to_desc(irq);
@@ -2540,7 +2564,6 @@ static void ack_x2apic_edge(unsigned int irq)
 {
        ack_x2APIC_irq();
 }
-
 #endif
 
 static void ack_apic_edge(unsigned int irq)
@@ -2606,6 +2629,9 @@ static void ack_apic_level(unsigned int irq)
         */
        ack_APIC_irq();
 
+       if (irq_remapped(irq))
+               eoi_ioapic_irq(desc);
+
        /* Now we can move and renable the irq */
        if (unlikely(do_unmask_irq)) {
                /* Only migrate the irq if the ack has been received.
@@ -2651,6 +2677,26 @@ static void ack_apic_level(unsigned int irq)
 #endif
 }
 
+#ifdef CONFIG_INTR_REMAP
+static void ir_ack_apic_edge(unsigned int irq)
+{
+#ifdef CONFIG_X86_X2APIC
+       if (x2apic_enabled())
+               return ack_x2apic_edge(irq);
+#endif
+       return ack_apic_edge(irq);
+}
+
+static void ir_ack_apic_level(unsigned int irq)
+{
+#ifdef CONFIG_X86_X2APIC
+       if (x2apic_enabled())
+               return ack_x2apic_level(irq);
+#endif
+       return ack_apic_level(irq);
+}
+#endif /* CONFIG_INTR_REMAP */
+
 static struct irq_chip ioapic_chip __read_mostly = {
        .name           = "IO-APIC",
        .startup        = startup_ioapic_irq,
@@ -2670,8 +2716,8 @@ static struct irq_chip ir_ioapic_chip __read_mostly = {
        .mask           = mask_IO_APIC_irq,
        .unmask         = unmask_IO_APIC_irq,
 #ifdef CONFIG_INTR_REMAP
-       .ack            = ack_x2apic_edge,
-       .eoi            = ack_x2apic_level,
+       .ack            = ir_ack_apic_edge,
+       .eoi            = ir_ack_apic_level,
 #ifdef CONFIG_SMP
        .set_affinity   = set_ir_ioapic_affinity_irq,
 #endif
@@ -3397,7 +3443,7 @@ static struct irq_chip msi_ir_chip = {
        .unmask         = unmask_msi_irq,
        .mask           = mask_msi_irq,
 #ifdef CONFIG_INTR_REMAP
-       .ack            = ack_x2apic_edge,
+       .ack            = ir_ack_apic_edge,
 #ifdef CONFIG_SMP
        .set_affinity   = ir_set_msi_irq_affinity,
 #endif