--- linux/drivers/char/Config.in.orig Sat Jan 27 20:16:03 2001 +++ linux/drivers/char/Config.in Sat Jan 27 20:16:40 2001 @@ -114,6 +114,12 @@ fi fi tristate ' WDT PCI Watchdog timer' CONFIG_WDTPCI + if [ "$CONFIG_SMP" = "y" ]; then + bool ' SMP-IOAPIC NMI Software Watchdog' CONFIG_NMI_WATCHDOG + if [ "$CONFIG_NMI_WATCHDOG" = "y" ]; then + int ' watchdog source IRQ' CONFIG_NMI_WATCHDOG_IRQ 0 + fi + fi endmenu fi --- linux/arch/i386/kernel/smp.c.orig Sat Jan 27 20:16:03 2001 +++ linux/arch/i386/kernel/smp.c Sat Jan 27 20:16:14 2001 @@ -2076,8 +2076,17 @@ * [ if a single-CPU system runs an SMP kernel then we call the local * interrupt as well. Thus we cannot inline the local irq ... ] */ +#ifdef CONFIG_NMI_WATCHDOG +atomic_t apic_timer_irqs [NR_CPUS] = { ATOMIC_INIT(0), }; +#endif void smp_apic_timer_interrupt(struct pt_regs * regs) { +#ifdef CONFIG_NMI_WATCHDOG + /* + * the only thing that can lock an NMI is an unACK-ed APIC ... + */ + atomic_inc(apic_timer_irqs+smp_processor_id()); +#endif /* * NOTE! We'd better ACK the irq immediately, * because timer handling can be slow, and we @@ -2127,6 +2136,8 @@ */ asmlinkage void smp_stop_cpu_interrupt(void) { + for (;;) __cli(); + ack_APIC_irq(); stop_this_cpu(); } --- linux/arch/i386/kernel/traps.c.orig Sat Jan 27 20:16:03 2001 +++ linux/arch/i386/kernel/traps.c Sat Jan 27 20:16:14 2001 @@ -2,6 +2,8 @@ * linux/arch/i386/traps.c * * Copyright (C) 1991, 1992 Linus Torvalds + * + * 1998, Ingo Molnar, added NMI-Watchdog driver */ /* @@ -20,6 +22,7 @@ #include #include #include +#include #ifdef CONFIG_MCA #include @@ -56,10 +59,17 @@ */ struct desc_struct idt_table[256] __attribute__((__section__(".data.idt"))) = { {0, 0}, }; +extern int console_loglevel; + +static inline void console_silent(void) +{ + console_loglevel = 0; +} + static inline void console_verbose(void) { - extern int console_loglevel; - console_loglevel = 15; + if (console_loglevel) + console_loglevel = 15; } #define DO_ERROR(trapnr, signr, str, name, tsk) \ @@ -292,12 +302,15 @@ } } +#ifndef CONFIG_NMI_WATCHDOG static void mem_parity_error(unsigned char reason, struct pt_regs * regs) { printk("Uhhuh. NMI received. Dazed and confused, but trying to continue\n"); printk("You probably have a hardware problem with your RAM chips\n"); -} +} +#endif +#ifndef CONFIG_NMI_WATCHDOG static void io_check_error(unsigned char reason, struct pt_regs * regs) { unsigned long i; @@ -313,7 +326,9 @@ reason &= ~8; outb(reason, 0x61); } +#endif +#ifndef CONFIG_NMI_WATCHDOG static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs) { #ifdef CONFIG_MCA @@ -328,11 +343,14 @@ printk("Dazed and confused, but trying to continue\n"); printk("Do you have a strange power saving mode enabled?\n"); } +#endif +extern atomic_t nmi_counter; + +#ifndef CONFIG_NMI_WATCHDOG asmlinkage void do_nmi(struct pt_regs * regs, long error_code) { unsigned char reason = inb(0x61); - extern atomic_t nmi_counter; atomic_inc(&nmi_counter); if (reason & 0x80) @@ -342,6 +360,69 @@ if (!(reason & 0xc0)) unknown_nmi_error(reason, regs); } +#else + +/* + * FIXME: we assume here that the NMI came from the IO-APIC. It's a quite safe + * assumption in most cases, but if anyone knows a way to distinguish between + * NMI reasons, please speak up ... [i doubt that the IO-APIC does IO port 0x61 + * correctly] + */ + +extern atomic_t apic_timer_irqs [NR_CPUS]; +extern spinlock_t console_lock; +static spinlock_t nmi_print_lock = SPIN_LOCK_UNLOCKED; + +asmlinkage void do_nmi(struct pt_regs * regs, long error_code) +{ + /* + * the best way to detect wether a CPU has a 'hard lockup' problem + * is to check it's local APIC timer IRQ counts. If they are not + * changing then that CPU has some problem. + * + * as these watchdog NMI IRQs are broadcasted to every CPU, here + * we only have to check the current processor. + * + * since NMIs dont listen to _any_ locks, we have to be extremely + * careful not to rely on unsafe variables. The printk might lock + * up though, so we have to break up console_lock first ... + * [when there will be more tty-related locks, break them up + * here too!] + */ + + static atomic_t last_irq_sums [NR_CPUS] = { ATOMIC_INIT(0), }; + static atomic_t alert_counter [NR_CPUS] = { ATOMIC_INIT(0), }; + + /* + * Since current-> is always on the stack, and we always switch + * the stack NMI-atomically, it's safe to use smp_processor_id(). + */ + int sum, cpu = smp_processor_id(); + + atomic_inc(&nmi_counter); + sum = atomic_read(apic_timer_irqs+cpu); + + if (atomic_read(last_irq_sums+cpu) == sum) { + /* + * Ayiee, looks like this CPU is stuck ... + * wait a few IRQs (5 seconds) before doing the oops ... + */ + atomic_inc(alert_counter+cpu); + if (atomic_read(alert_counter+cpu) == 5*HZ) { + spin_lock(&nmi_print_lock); + printk("NMI Watchdog detected LOCKUP on CPU%d, registers:\n", cpu); + show_registers(regs); + printk("console shuts up ...\n"); + console_silent(); + spin_unlock(&nmi_print_lock); + do_exit(SIGSEGV); + } + } else { + atomic_set(last_irq_sums+cpu,sum); + atomic_set(alert_counter+cpu,0); + } +} +#endif /* * Careful - we must not do a lock-kernel until we have checked that the --- linux/arch/i386/kernel/io_apic.c.orig Mon Sep 4 19:39:16 2000 +++ linux/arch/i386/kernel/io_apic.c Sat Jan 27 20:16:14 2001 @@ -22,6 +22,17 @@ #define IO_APIC_BASE(idx) ((volatile int *)__fix_to_virt(FIX_IO_APIC_BASE_0 + idx)) /* + * We want to avoid #ifdef CONFIG_'s in the main code whenever possible: + */ +#ifdef CONFIG_NMI_WATCHDOG + int nmi_pin = -1; + const int nmi_irq = CONFIG_NMI_WATCHDOG_IRQ; +#else + int nmi_pin = 0; + const int nmi_irq = -1; +#endif + +/* * The structure of the IO-APIC: */ @@ -638,6 +649,16 @@ if (!apic && !IO_APIC_IRQ(irq)) continue; + if (irq == nmi_irq) { + entry.delivery_mode = 4; /* broadcast NMI */ + make_8259A_irq(irq); + /* + * Remember which register has the NMI IRQ entry, + * so we can turn it off in case there is some + * screwup + */ + nmi_pin = pin; + } entry.vector = assign_irq_vector(irq); @@ -1237,6 +1258,11 @@ { int pin1, pin2; + if (nmi_pin == -1) + printk(".. NMI watchdog has invalid source IRQ.\n"); + else if (nmi_irq != -1) + printk("NMI Watchdog activated on source IRQ %d\n", nmi_irq); + pin1 = find_timer_pin(mp_INT); pin2 = find_timer_pin(mp_ExtINT); enable_IO_APIC_irq(0); @@ -1274,6 +1300,8 @@ } } printk(" works.\n"); + if ((nmi_pin != -1) && (nmi_irq == 0)) + printk("NMI Watchdog disabled (source IRQ was 0)!\n"); } } --- linux/arch/i386/kernel/entry.S.orig Sat Jan 27 20:16:03 2001 +++ linux/arch/i386/kernel/entry.S Sat Jan 27 20:16:14 2001 @@ -306,9 +306,14 @@ jmp error_code ENTRY(nmi) + pushl %eax + SAVE_ALL + movl %esp,%edx pushl $0 - pushl $ SYMBOL_NAME(do_nmi) - jmp error_code + pushl %edx + call SYMBOL_NAME(do_nmi) + addl $8,%esp + RESTORE_ALL ENTRY(int3) pushl $0 --- linux/arch/i386/defconfig.orig Mon Dec 11 01:49:41 2000 +++ linux/arch/i386/defconfig Sat Jan 27 20:16:14 2001 @@ -321,6 +321,18 @@ # CONFIG_JOYSTICK is not set # CONFIG_QIC02_TAPE is not set # CONFIG_WATCHDOG is not set +CONFIG_WATCHDOG=y + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG_NOWAYOUT is not set +# CONFIG_WDT is not set +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_PCWATCHDOG is not set +# CONFIG_ACQUIRE_WDT is not set +CONFIG_NMI_WATCHDOG=y +CONFIG_NMI_WATCHDOG_IRQ=0 # CONFIG_NVRAM is not set # CONFIG_RTC is not set --- linux/Documentation/Configure.help.orig Sat Jan 27 20:16:03 2001 +++ linux/Documentation/Configure.help Sat Jan 27 20:16:15 2001 @@ -10534,6 +10534,21 @@ with major 203 and minors 0 to 31 for /dev/cpu/0/cpuid to /dev/cpu/31/cpuid. +SMP-NMI Software Watchdog +CONFIG_NMI_WATCHDOG + This software watchdog only works on Intel-SMP compliant boxes. The + driver tweaks the IRQ-delivery mechanizm to route a periodic NMI + interrupt to all CPUs. If one of the CPUs is detected as 'locked up' + in the nmi-handler, then an oops is generated. This oops can then be + used to debug the lockup. + +SMP-NMI-Bug Workaround, Alternative Watchdog IRQ Source +CONFIG_NMI_WATCHDOG_IRQ + Some boards are buggy and cannot generate a periodic NMI interrupt. Use + non-zero IRQ as an NMI source in this case, eg. IRQ=1 (keyboard IRQ). + This has the drawback that in the case of a lockup you'll have to + generate some keyboard IRQs to see the oops. (type on the keyboard) + Enhanced Real Time Clock Support CONFIG_RTC If you say Y here and create a character special file /dev/rtc with