]> git.karo-electronics.de Git - linux-beck.git/commitdiff
x86, kdump, ioapic: Reset remote-IRR in clear_IO_APIC
authorSuresh Siddha <suresh.b.siddha@intel.com>
Thu, 25 Aug 2011 19:01:11 +0000 (12:01 -0700)
committerIngo Molnar <mingo@elte.hu>
Wed, 21 Sep 2011 08:26:25 +0000 (10:26 +0200)
In the kdump scenario mentioned below, we can have a case where
the device using level triggered interrupt will not generate any
interrupts in the kdump kernel.

1. IO-APIC sends a level triggered interrupt to the CPU's local APIC.

2. Kernel crashed before the CPU services this interrupt, leaving
   the remote-IRR in the IO-APIC set.

3. kdump kernel boot sequence does clear_IO_APIC() as part of IO-APIC
   initialization. But this fails to reset remote-IRR bit of the
   IO-APIC RTE as the remote-IRR bit is read-only.

4. Device using that level triggered entry can't generate any
   more interrupts because of the remote-IRR bit.

In clear_IO_APIC_pin(), check if the remote-IRR bit is set and if
so do an explicit attempt to clear it (by doing EOI write on
modern io-apic's and changing trigger mode to edge/level on
older io-apic's). Also before doing the explicit EOI to the
io-apic, ensure that the trigger mode is indeed set to level.
This will enable the explicit EOI to the io-apic to reset the
remote-IRR bit.

Tested-by: Leonardo Chiquitto <lchiquitto@novell.com>
Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
Fixes: https://bugzilla.novell.com/show_bug.cgi?id=701686
Cc: Rafael Wysocki <rjw@novell.com>
Cc: Maciej W. Rozycki <macro@linux-mips.org>
Cc: Thomas Renninger <trenn@suse.de>
Cc: jbeulich@novell.com
Cc: yinghai@kernel.org
Link: http://lkml.kernel.org/r/20110825190657.157502602@sbsiddha-desk.sc.intel.com
Signed-off-by: Ingo Molnar <mingo@elte.hu>
arch/x86/kernel/apic/io_apic.c

index 620da6fed6b7075289b152b5e88dde83cecbf814..913d4bd2913add4df07f8691009be850487d1cfc 100644 (file)
@@ -593,10 +593,56 @@ static void clear_IO_APIC_pin(unsigned int apic, unsigned int pin)
        entry = ioapic_read_entry(apic, pin);
        if (entry.delivery_mode == dest_SMI)
                return;
+
+       /*
+        * Make sure the entry is masked and re-read the contents to check
+        * if it is a level triggered pin and if the remote-IRR is set.
+        */
+       if (!entry.mask) {
+               entry.mask = 1;
+               ioapic_write_entry(apic, pin, entry);
+               entry = ioapic_read_entry(apic, pin);
+       }
+
+       if (entry.irr) {
+               /*
+                * Make sure the trigger mode is set to level. Explicit EOI
+                * doesn't clear the remote-IRR if the trigger mode is not
+                * set to level.
+                */
+               if (!entry.trigger) {
+                       entry.trigger = IOAPIC_LEVEL;
+                       ioapic_write_entry(apic, pin, entry);
+               }
+
+               if (mpc_ioapic_ver(apic) >= 0x20) {
+                       unsigned long flags;
+
+                       raw_spin_lock_irqsave(&ioapic_lock, flags);
+                       io_apic_eoi(apic, entry.vector);
+                       raw_spin_unlock_irqrestore(&ioapic_lock, flags);
+               } else {
+                       /*
+                        * Mechanism by which we clear remote-IRR in this
+                        * case is by changing the trigger mode to edge and
+                        * back to level.
+                        */
+                       entry.trigger = IOAPIC_EDGE;
+                       ioapic_write_entry(apic, pin, entry);
+                       entry.trigger = IOAPIC_LEVEL;
+                       ioapic_write_entry(apic, pin, entry);
+               }
+       }
+
        /*
-        * Disable it in the IO-APIC irq-routing table:
+        * Clear the rest of the bits in the IO-APIC RTE except for the mask
+        * bit.
         */
        ioapic_mask_entry(apic, pin);
+       entry = ioapic_read_entry(apic, pin);
+       if (entry.irr)
+               printk(KERN_ERR "Unable to reset IRR for apic: %d, pin :%d\n",
+                      mpc_ioapic_id(apic), pin);
 }
 
 static void clear_IO_APIC (void)