diff options
Diffstat (limited to 'arch/x86/kernel/nmi.c')
-rw-r--r-- | arch/x86/kernel/nmi.c | 61 |
1 files changed, 47 insertions, 14 deletions
diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c index 2c97f07f1c2c..45a09ccdc214 100644 --- a/arch/x86/kernel/nmi.c +++ b/arch/x86/kernel/nmi.c | |||
@@ -26,11 +26,10 @@ | |||
26 | #include <linux/kernel_stat.h> | 26 | #include <linux/kernel_stat.h> |
27 | #include <linux/kdebug.h> | 27 | #include <linux/kdebug.h> |
28 | #include <linux/smp.h> | 28 | #include <linux/smp.h> |
29 | #include <linux/nmi.h> | ||
29 | 30 | ||
30 | #include <asm/i8259.h> | 31 | #include <asm/i8259.h> |
31 | #include <asm/io_apic.h> | 32 | #include <asm/io_apic.h> |
32 | #include <asm/smp.h> | ||
33 | #include <asm/nmi.h> | ||
34 | #include <asm/proto.h> | 33 | #include <asm/proto.h> |
35 | #include <asm/timer.h> | 34 | #include <asm/timer.h> |
36 | 35 | ||
@@ -131,6 +130,11 @@ static void report_broken_nmi(int cpu, int *prev_nmi_count) | |||
131 | atomic_dec(&nmi_active); | 130 | atomic_dec(&nmi_active); |
132 | } | 131 | } |
133 | 132 | ||
133 | static void __acpi_nmi_disable(void *__unused) | ||
134 | { | ||
135 | apic_write(APIC_LVT0, APIC_DM_NMI | APIC_LVT_MASKED); | ||
136 | } | ||
137 | |||
134 | int __init check_nmi_watchdog(void) | 138 | int __init check_nmi_watchdog(void) |
135 | { | 139 | { |
136 | unsigned int *prev_nmi_count; | 140 | unsigned int *prev_nmi_count; |
@@ -179,8 +183,12 @@ int __init check_nmi_watchdog(void) | |||
179 | kfree(prev_nmi_count); | 183 | kfree(prev_nmi_count); |
180 | return 0; | 184 | return 0; |
181 | error: | 185 | error: |
182 | if (nmi_watchdog == NMI_IO_APIC && !timer_through_8259) | 186 | if (nmi_watchdog == NMI_IO_APIC) { |
183 | disable_8259A_irq(0); | 187 | if (!timer_through_8259) |
188 | disable_8259A_irq(0); | ||
189 | on_each_cpu(__acpi_nmi_disable, NULL, 1); | ||
190 | } | ||
191 | |||
184 | #ifdef CONFIG_X86_32 | 192 | #ifdef CONFIG_X86_32 |
185 | timer_ack = 0; | 193 | timer_ack = 0; |
186 | #endif | 194 | #endif |
@@ -199,12 +207,17 @@ static int __init setup_nmi_watchdog(char *str) | |||
199 | ++str; | 207 | ++str; |
200 | } | 208 | } |
201 | 209 | ||
202 | get_option(&str, &nmi); | 210 | if (!strncmp(str, "lapic", 5)) |
203 | 211 | nmi_watchdog = NMI_LOCAL_APIC; | |
204 | if (nmi >= NMI_INVALID) | 212 | else if (!strncmp(str, "ioapic", 6)) |
205 | return 0; | 213 | nmi_watchdog = NMI_IO_APIC; |
214 | else { | ||
215 | get_option(&str, &nmi); | ||
216 | if (nmi >= NMI_INVALID) | ||
217 | return 0; | ||
218 | nmi_watchdog = nmi; | ||
219 | } | ||
206 | 220 | ||
207 | nmi_watchdog = nmi; | ||
208 | return 1; | 221 | return 1; |
209 | } | 222 | } |
210 | __setup("nmi_watchdog=", setup_nmi_watchdog); | 223 | __setup("nmi_watchdog=", setup_nmi_watchdog); |
@@ -285,11 +298,6 @@ void acpi_nmi_enable(void) | |||
285 | on_each_cpu(__acpi_nmi_enable, NULL, 1); | 298 | on_each_cpu(__acpi_nmi_enable, NULL, 1); |
286 | } | 299 | } |
287 | 300 | ||
288 | static void __acpi_nmi_disable(void *__unused) | ||
289 | { | ||
290 | apic_write(APIC_LVT0, APIC_DM_NMI | APIC_LVT_MASKED); | ||
291 | } | ||
292 | |||
293 | /* | 301 | /* |
294 | * Disable timer based NMIs on all CPUs: | 302 | * Disable timer based NMIs on all CPUs: |
295 | */ | 303 | */ |
@@ -340,6 +348,8 @@ void stop_apic_nmi_watchdog(void *unused) | |||
340 | return; | 348 | return; |
341 | if (nmi_watchdog == NMI_LOCAL_APIC) | 349 | if (nmi_watchdog == NMI_LOCAL_APIC) |
342 | lapic_watchdog_stop(); | 350 | lapic_watchdog_stop(); |
351 | else | ||
352 | __acpi_nmi_disable(NULL); | ||
343 | __get_cpu_var(wd_enabled) = 0; | 353 | __get_cpu_var(wd_enabled) = 0; |
344 | atomic_dec(&nmi_active); | 354 | atomic_dec(&nmi_active); |
345 | } | 355 | } |
@@ -465,6 +475,24 @@ nmi_watchdog_tick(struct pt_regs *regs, unsigned reason) | |||
465 | 475 | ||
466 | #ifdef CONFIG_SYSCTL | 476 | #ifdef CONFIG_SYSCTL |
467 | 477 | ||
478 | static void enable_ioapic_nmi_watchdog_single(void *unused) | ||
479 | { | ||
480 | __get_cpu_var(wd_enabled) = 1; | ||
481 | atomic_inc(&nmi_active); | ||
482 | __acpi_nmi_enable(NULL); | ||
483 | } | ||
484 | |||
485 | static void enable_ioapic_nmi_watchdog(void) | ||
486 | { | ||
487 | on_each_cpu(enable_ioapic_nmi_watchdog_single, NULL, 1); | ||
488 | touch_nmi_watchdog(); | ||
489 | } | ||
490 | |||
491 | static void disable_ioapic_nmi_watchdog(void) | ||
492 | { | ||
493 | on_each_cpu(stop_apic_nmi_watchdog, NULL, 1); | ||
494 | } | ||
495 | |||
468 | static int __init setup_unknown_nmi_panic(char *str) | 496 | static int __init setup_unknown_nmi_panic(char *str) |
469 | { | 497 | { |
470 | unknown_nmi_panic = 1; | 498 | unknown_nmi_panic = 1; |
@@ -507,6 +535,11 @@ int proc_nmi_enabled(struct ctl_table *table, int write, struct file *file, | |||
507 | enable_lapic_nmi_watchdog(); | 535 | enable_lapic_nmi_watchdog(); |
508 | else | 536 | else |
509 | disable_lapic_nmi_watchdog(); | 537 | disable_lapic_nmi_watchdog(); |
538 | } else if (nmi_watchdog == NMI_IO_APIC) { | ||
539 | if (nmi_watchdog_enabled) | ||
540 | enable_ioapic_nmi_watchdog(); | ||
541 | else | ||
542 | disable_ioapic_nmi_watchdog(); | ||
510 | } else { | 543 | } else { |
511 | printk(KERN_WARNING | 544 | printk(KERN_WARNING |
512 | "NMI watchdog doesn't know what hardware to touch\n"); | 545 | "NMI watchdog doesn't know what hardware to touch\n"); |