]> xenbits.xensource.com Git - people/vhanquez/xen-unstable.git/commitdiff
x86: adjust handling of interrupts coming in via legacy vectors
authorJan Beulich <jbeulich@suse.com>
Tue, 15 May 2012 07:18:33 +0000 (09:18 +0200)
committerJan Beulich <jbeulich@suse.com>
Tue, 15 May 2012 07:18:33 +0000 (09:18 +0200)
The debugging code added in c/s 24707:96987c324a4f was hit a (small)
number of times (one report being
http://lists.xen.org/archives/html/xen-devel/2012-05/msg00332.html),
apparently always with a vector within the legacy range. Obviously,
besides legacy vectors not normally expected to be in use on systems
with IO-APIC(s), they should never make it to the IRQ migration logic.

This wasn't being prevented so far: Since we don't have a one-to-one
mapping between vectors and IRQs - legacy IRQs may have two vectors
associated with them (one used in either 8259A, the other used in one
of the IO-APICs) -, vector-to-IRQ translations for legacy vectors (as
used in do_IRQ()) would yield a valid IRQ number despite the IRQ
really being handled via an IO-APIC.

This gets changed here - disable_8259A_irq() zaps the legacy vector-to-
IRQ mapping, and enable_8259A_irq(), should it ever be called for a
particular interrupts, restores it.

The spurious interrupt logic in do_IRQ() gets adjusted too: Interrupts
coming in via legacy vectors presumably didn't get reported through the
IO-APIC/LAPIC pair (as we never program these vectors into any RTE or
LVT). Call ack_APIC_irq() only when the LAPIC's ISR bit says an
interrupt is pending at the given vector. Plus, a new function (pointer)
bogus_8259A_irq() gets used to have the 8259A driver take care of the
bogus interrupt (as outside of automatic EOI mode it may need an EOI to
be issued for it to prevent other interrupts legitimately going through
the 8259As from getting masked out).

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Keir Fraser <keir@xen.org>
Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>
xen/arch/x86/apic.c
xen/arch/x86/i8259.c
xen/arch/x86/irq.c
xen/include/asm-x86/apic.h
xen/include/asm-x86/irq.h

index e05dd82bb24c79f60694e1bd610dc775eb445ce5..50430d2f398c0cf1421b74cdc1b8d5ab8f8271ba 100644 (file)
@@ -1317,15 +1317,12 @@ void smp_send_state_dump(unsigned int cpu)
  */
 void spurious_interrupt(struct cpu_user_regs *regs)
 {
-    unsigned long v;
-
     /*
      * Check if this is a vectored interrupt (most likely, as this is probably
      * a request to dump local CPU state). Vectored interrupts are ACKed;
      * spurious interrupts are not.
      */
-    v = apic_read(APIC_ISR + ((SPURIOUS_APIC_VECTOR & ~0x1f) >> 1));
-    if (v & (1 << (SPURIOUS_APIC_VECTOR & 0x1f))) {
+    if (apic_isr_read(SPURIOUS_APIC_VECTOR)) {
         ack_APIC_irq();
         if (this_cpu(state_dump_pending)) {
             this_cpu(state_dump_pending) = 0;
@@ -1491,6 +1488,5 @@ enum apic_mode current_local_apic_mode(void)
 
 void check_for_unexpected_msi(unsigned int vector)
 {
-    unsigned long v = apic_read(APIC_ISR + ((vector & ~0x1f) >> 1));
-    BUG_ON(v & (1 << (vector & 0x1f)));
+    BUG_ON(apic_isr_read(vector));
 }
index 2cbda3cb7636b9108664801c4b6d28b68606e6f7..91f0bc5d46043441466442180f1f66c2c5523612 100644 (file)
@@ -85,7 +85,15 @@ BUILD_16_IRQS(0xc) BUILD_16_IRQS(0xd) BUILD_16_IRQS(0xe) BUILD_16_IRQS(0xf)
 
 static DEFINE_SPINLOCK(i8259A_lock);
 
-static void mask_and_ack_8259A_irq(struct irq_desc *);
+static void _mask_and_ack_8259A_irq(unsigned int irq);
+
+void (*__read_mostly bogus_8259A_irq)(unsigned int irq) =
+    _mask_and_ack_8259A_irq;
+
+static void mask_and_ack_8259A_irq(struct irq_desc *desc)
+{
+    _mask_and_ack_8259A_irq(desc->irq);
+}
 
 static unsigned int startup_8259A_irq(struct irq_desc *desc)
 {
@@ -133,20 +141,26 @@ static unsigned int cached_irq_mask = 0xffff;
  */
 unsigned int __read_mostly io_apic_irqs;
 
-void disable_8259A_irq(struct irq_desc *desc)
+static void _disable_8259A_irq(unsigned int irq)
 {
-    unsigned int mask = 1 << desc->irq;
+    unsigned int mask = 1 << irq;
     unsigned long flags;
 
     spin_lock_irqsave(&i8259A_lock, flags);
     cached_irq_mask |= mask;
-    if (desc->irq & 8)
+    if (irq & 8)
         outb(cached_A1,0xA1);
     else
         outb(cached_21,0x21);
+    per_cpu(vector_irq, 0)[LEGACY_VECTOR(irq)] = -1;
     spin_unlock_irqrestore(&i8259A_lock, flags);
 }
 
+void disable_8259A_irq(struct irq_desc *desc)
+{
+    _disable_8259A_irq(desc->irq);
+}
+
 void enable_8259A_irq(struct irq_desc *desc)
 {
     unsigned int mask = ~(1 << desc->irq);
@@ -154,6 +168,7 @@ void enable_8259A_irq(struct irq_desc *desc)
 
     spin_lock_irqsave(&i8259A_lock, flags);
     cached_irq_mask &= mask;
+    per_cpu(vector_irq, 0)[LEGACY_VECTOR(desc->irq)] = desc->irq;
     if (desc->irq & 8)
         outb(cached_A1,0xA1);
     else
@@ -226,9 +241,9 @@ static inline int i8259A_irq_real(unsigned int irq)
  * first, _then_ send the EOI, and the order of EOI
  * to the two 8259s is important!
  */
-static void mask_and_ack_8259A_irq(struct irq_desc *desc)
+static void _mask_and_ack_8259A_irq(unsigned int irq)
 {
-    unsigned int irqmask = 1 << desc->irq;
+    unsigned int irqmask = 1 << irq;
     unsigned long flags;
 
     spin_lock_irqsave(&i8259A_lock, flags);
@@ -252,15 +267,15 @@ static void mask_and_ack_8259A_irq(struct irq_desc *desc)
     cached_irq_mask |= irqmask;
 
  handle_real_irq:
-    if (desc->irq & 8) {
+    if (irq & 8) {
         inb(0xA1);              /* DUMMY - (do we need this?) */
         outb(cached_A1,0xA1);
-        outb(0x60 + (desc->irq & 7), 0xA0);/* 'Specific EOI' to slave */
+        outb(0x60 + (irq & 7), 0xA0);/* 'Specific EOI' to slave */
         outb(0x62,0x20);        /* 'Specific EOI' to master-IRQ2 */
     } else {
         inb(0x21);              /* DUMMY - (do we need this?) */
         outb(cached_21,0x21);
-        outb(0x60 + desc->irq, 0x20);/* 'Specific EOI' to master */
+        outb(0x60 + irq, 0x20);/* 'Specific EOI' to master */
     }
     spin_unlock_irqrestore(&i8259A_lock, flags);
     return;
@@ -269,7 +284,7 @@ static void mask_and_ack_8259A_irq(struct irq_desc *desc)
     /*
      * this is the slow path - should happen rarely.
      */
-    if (i8259A_irq_real(desc->irq))
+    if (i8259A_irq_real(irq))
         /*
          * oops, the IRQ _is_ in service according to the
          * 8259A - not spurious, go handle it.
@@ -283,7 +298,7 @@ static void mask_and_ack_8259A_irq(struct irq_desc *desc)
          * lets ACK and report it. [once per IRQ]
          */
         if (!(spurious_irq_mask & irqmask)) {
-            printk("spurious 8259A interrupt: IRQ%d.\n", desc->irq);
+            printk("spurious 8259A interrupt: IRQ%d.\n", irq);
             spurious_irq_mask |= irqmask;
         }
         /*
@@ -352,13 +367,19 @@ void __devinit init_8259A(int auto_eoi)
                                is to be investigated) */
 
     if (auto_eoi)
+    {
         /*
          * in AEOI mode we just have to mask the interrupt
          * when acking.
          */
         i8259A_irq_type.ack = disable_8259A_irq;
+        bogus_8259A_irq = _disable_8259A_irq;
+    }
     else
+    {
         i8259A_irq_type.ack = mask_and_ack_8259A_irq;
+        bogus_8259A_irq = _mask_and_ack_8259A_irq;
+    }
 
     udelay(100);            /* wait for 8259A to initialize */
 
index 2fc676b716a17db45efbc704f492eb1da4a808d8..496c8048ae6349ec43b3bb5aa6575e89294f2efa 100644 (file)
@@ -811,9 +811,17 @@ void do_IRQ(struct cpu_user_regs *regs)
         if (direct_apic_vector[vector] != NULL) {
             (*direct_apic_vector[vector])(regs);
         } else {
-            ack_APIC_irq();
-            printk("%s: %d.%d No irq handler for vector (irq %d)\n",
-                   __func__, smp_processor_id(), vector, irq);
+            const char *kind = ", LAPIC";
+
+            if ( apic_isr_read(vector) )
+                ack_APIC_irq();
+            else
+                kind = "";
+            if ( vector >= FIRST_LEGACY_VECTOR &&
+                 vector <= LAST_LEGACY_VECTOR )
+                bogus_8259A_irq(vector - FIRST_LEGACY_VECTOR);
+            printk("CPU%u: No irq handler for vector %02x (IRQ %d%s)\n",
+                   smp_processor_id(), vector, irq, kind);
             TRACE_1D(TRC_HW_IRQ_UNMAPPED_VECTOR, vector);
         }
         goto out_no_unlock;
index 36c65b43dc44b2a327ac3df998ee230feb732df8..4e6bea962e2b8e2d09a02d0d363f85f975ac419b 100644 (file)
@@ -146,6 +146,12 @@ static __inline void apic_icr_write(u32 low, u32 dest)
     }
 }
 
+static __inline bool_t apic_isr_read(u8 vector)
+{
+    return (apic_read(APIC_ISR + ((vector & ~0x1f) >> 1)) >>
+            (vector & 0x1f)) & 1;
+}
+
 static __inline u32 get_apic_id(void) /* Get the physical APIC id */
 {
     u32 id = apic_read(APIC_ID);
index ffa3e8f2618f71e42f4aa5eb16207d02223f0e4f..ccc013e25c2b6386d14e2a874fa32c0b26d2ec31 100644 (file)
@@ -104,6 +104,7 @@ void mask_8259A(void);
 void unmask_8259A(void);
 void init_8259A(int aeoi);
 void make_8259A_irq(unsigned int irq);
+extern void (*bogus_8259A_irq)(unsigned int irq);
 int i8259A_suspend(void);
 int i8259A_resume(void);