aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel
diff options
context:
space:
mode:
authorSuresh Siddha <suresh.b.siddha@intel.com>2011-08-25 15:01:11 -0400
committerIngo Molnar <mingo@elte.hu>2011-09-21 04:26:25 -0400
commit1e75b31d638d5242ca8e9771dfdcbd28a5f041df (patch)
treeaa89a8dc78eae9f20c12aed485ca04ad1dc4301e /arch/x86/kernel
parentd3f138106b4b40640dc667f0222fd9f137387b32 (diff)
x86, kdump, ioapic: Reset remote-IRR in clear_IO_APIC
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>
Diffstat (limited to 'arch/x86/kernel')
-rw-r--r--arch/x86/kernel/apic/io_apic.c48
1 files changed, 47 insertions, 1 deletions
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index 620da6fed6b7..913d4bd2913a 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -593,10 +593,56 @@ static void clear_IO_APIC_pin(unsigned int apic, unsigned int pin)
593 entry = ioapic_read_entry(apic, pin); 593 entry = ioapic_read_entry(apic, pin);
594 if (entry.delivery_mode == dest_SMI) 594 if (entry.delivery_mode == dest_SMI)
595 return; 595 return;
596
597 /*
598 * Make sure the entry is masked and re-read the contents to check
599 * if it is a level triggered pin and if the remote-IRR is set.
600 */
601 if (!entry.mask) {
602 entry.mask = 1;
603 ioapic_write_entry(apic, pin, entry);
604 entry = ioapic_read_entry(apic, pin);
605 }
606
607 if (entry.irr) {
608 /*
609 * Make sure the trigger mode is set to level. Explicit EOI
610 * doesn't clear the remote-IRR if the trigger mode is not
611 * set to level.
612 */
613 if (!entry.trigger) {
614 entry.trigger = IOAPIC_LEVEL;
615 ioapic_write_entry(apic, pin, entry);
616 }
617
618 if (mpc_ioapic_ver(apic) >= 0x20) {
619 unsigned long flags;
620
621 raw_spin_lock_irqsave(&ioapic_lock, flags);
622 io_apic_eoi(apic, entry.vector);
623 raw_spin_unlock_irqrestore(&ioapic_lock, flags);
624 } else {
625 /*
626 * Mechanism by which we clear remote-IRR in this
627 * case is by changing the trigger mode to edge and
628 * back to level.
629 */
630 entry.trigger = IOAPIC_EDGE;
631 ioapic_write_entry(apic, pin, entry);
632 entry.trigger = IOAPIC_LEVEL;
633 ioapic_write_entry(apic, pin, entry);
634 }
635 }
636
596 /* 637 /*
597 * Disable it in the IO-APIC irq-routing table: 638 * Clear the rest of the bits in the IO-APIC RTE except for the mask
639 * bit.
598 */ 640 */
599 ioapic_mask_entry(apic, pin); 641 ioapic_mask_entry(apic, pin);
642 entry = ioapic_read_entry(apic, pin);
643 if (entry.irr)
644 printk(KERN_ERR "Unable to reset IRR for apic: %d, pin :%d\n",
645 mpc_ioapic_id(apic), pin);
600} 646}
601 647
602static void clear_IO_APIC (void) 648static void clear_IO_APIC (void)