From 35542c5ebced864776d90d83d1e255016fd4c084 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 21 May 2008 22:10:22 +0100 Subject: [PATCH] x86: I/O APIC: clean up the 8259A on a NMI watchdog failure There is no point in keeping the 8259A enabled if the I/O APIC NMI watchdog has failed and the 8259A is not used to pass through regular timer interrupts. This fixes problems with some systems where some logic gets confused. Signed-off-by: Maciej W. Rozycki Signed-off-by: Ingo Molnar --- arch/x86/kernel/io_apic_32.c | 2 ++ arch/x86/kernel/io_apic_64.c | 2 ++ arch/x86/kernel/nmi_32.c | 4 ++++ arch/x86/kernel/nmi_64.c | 11 +++++++++-- include/asm-x86/io_apic.h | 4 ++++ 5 files changed, 21 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/io_apic_32.c b/arch/x86/kernel/io_apic_32.c index 41218ac75d10..7c7d88ebb966 100644 --- a/arch/x86/kernel/io_apic_32.c +++ b/arch/x86/kernel/io_apic_32.c @@ -58,6 +58,7 @@ static struct { int pin, apic; } ioapic_i8259 = { -1, -1 }; static DEFINE_SPINLOCK(ioapic_lock); static DEFINE_SPINLOCK(vector_lock); +int timer_through_8259 __initdata; /* * Is the SiS APIC rmw bug present ? @@ -2194,6 +2195,7 @@ static inline void __init check_timer(void) enable_8259A_irq(0); if (timer_irq_works()) { printk("works.\n"); + timer_through_8259 = 1; if (pin1 != -1) replace_pin_at_irq(0, apic1, pin1, apic2, pin2); else diff --git a/arch/x86/kernel/io_apic_64.c b/arch/x86/kernel/io_apic_64.c index aa45a85c4d11..2a60bfb7446f 100644 --- a/arch/x86/kernel/io_apic_64.c +++ b/arch/x86/kernel/io_apic_64.c @@ -90,6 +90,7 @@ static int no_timer_check; static int disable_timer_pin_1 __initdata; +int timer_through_8259 __initdata; /* Where if anywhere is the i8259 connect in external int mode */ static struct { int pin, apic; } ioapic_i8259 = { -1, -1 }; @@ -1700,6 +1701,7 @@ static inline void __init check_timer(void) enable_8259A_irq(0); if (timer_irq_works()) { apic_printk(APIC_VERBOSE," works.\n"); + timer_through_8259 = 1; nmi_watchdog_default(); if (nmi_watchdog == NMI_IO_APIC) { disable_8259A_irq(0); diff --git a/arch/x86/kernel/nmi_32.c b/arch/x86/kernel/nmi_32.c index d322901c38e6..6580dae46277 100644 --- a/arch/x86/kernel/nmi_32.c +++ b/arch/x86/kernel/nmi_32.c @@ -24,6 +24,8 @@ #include #include +#include +#include #include #include #include @@ -131,6 +133,8 @@ int __init check_nmi_watchdog(void) kfree(prev_nmi_count); return 0; error: + if (nmi_watchdog == NMI_IO_APIC && !timer_through_8259) + disable_8259A_irq(0); timer_ack = 0; return -1; diff --git a/arch/x86/kernel/nmi_64.c b/arch/x86/kernel/nmi_64.c index 5a29ded994fa..0060e44e8989 100644 --- a/arch/x86/kernel/nmi_64.c +++ b/arch/x86/kernel/nmi_64.c @@ -21,6 +21,8 @@ #include #include +#include +#include #include #include #include @@ -90,7 +92,7 @@ int __init check_nmi_watchdog(void) prev_nmi_count = kmalloc(NR_CPUS * sizeof(int), GFP_KERNEL); if (!prev_nmi_count) - return -1; + goto error; printk(KERN_INFO "Testing NMI watchdog ... "); @@ -121,7 +123,7 @@ int __init check_nmi_watchdog(void) if (!atomic_read(&nmi_active)) { kfree(prev_nmi_count); atomic_set(&nmi_active, -1); - return -1; + goto error; } printk("OK.\n"); @@ -132,6 +134,11 @@ int __init check_nmi_watchdog(void) kfree(prev_nmi_count); return 0; +error: + if (nmi_watchdog == NMI_IO_APIC && !timer_through_8259) + disable_8259A_irq(0); + + return -1; } static int __init setup_nmi_watchdog(char *str) diff --git a/include/asm-x86/io_apic.h b/include/asm-x86/io_apic.h index d593e14f0341..a6732566ad05 100644 --- a/include/asm-x86/io_apic.h +++ b/include/asm-x86/io_apic.h @@ -137,6 +137,9 @@ extern int sis_apic_bug; /* 1 if "noapic" boot option passed */ extern int skip_ioapic_setup; +/* 1 if the timer IRQ uses the '8259A Virtual Wire' mode */ +extern int timer_through_8259; + static inline void disable_ioapic_setup(void) { skip_ioapic_setup = 1; @@ -162,6 +165,7 @@ extern void ioapic_init_mappings(void); #else /* !CONFIG_X86_IO_APIC */ #define io_apic_assign_pci_irqs 0 +static const int timer_through_8259 = 0; #endif #endif