Commit 58687acb authored by Don Zickus's avatar Don Zickus Committed by Frederic Weisbecker

lockup_detector: Combine nmi_watchdog and softlockup detector

The new nmi_watchdog (which uses the perf event subsystem) is very
similar in structure to the softlockup detector.  Using Ingo's
suggestion, I combined the two functionalities into one file:
kernel/watchdog.c.

Now both the nmi_watchdog (or hardlockup detector) and softlockup
detector sit on top of the perf event subsystem, which is run every
60 seconds or so to see if there are any lockups.

To detect hardlockups, cpus not responding to interrupts, I
implemented an hrtimer that runs 5 times for every perf event
overflow event.  If that stops counting on a cpu, then the cpu is
most likely in trouble.

To detect softlockups, tasks not yielding to the scheduler, I used the
previous kthread idea that now gets kicked every time the hrtimer fires.
If the kthread isn't being scheduled neither is anyone else and the
warning is printed to the console.

I tested this on x86_64 and both the softlockup and hardlockup paths
work.

V2:
- cleaned up the Kconfig and softlockup combination
- surrounded hardlockup cases with #ifdef CONFIG_PERF_EVENTS_NMI
- seperated out the softlockup case from perf event subsystem
- re-arranged the enabling/disabling nmi watchdog from proc space
- added cpumasks for hardlockup failure cases
- removed fallback to soft events if no PMU exists for hard events

V3:
- comment cleanups
- drop support for older softlockup code
- per_cpu cleanups
- completely remove software clock base hardlockup detector
- use per_cpu masking on hard/soft lockup detection
- #ifdef cleanups
- rename config option NMI_WATCHDOG to LOCKUP_DETECTOR
- documentation additions

V4:
- documentation fixes
- convert per_cpu to __get_cpu_var
- powerpc compile fixes

V5:
- split apart warn flags for hard and soft lockups

TODO:
- figure out how to make an arch-agnostic clock2cycles call
  (if possible) to feed into perf events as a sample period

[fweisbec: merged conflict patch]
Signed-off-by: default avatarDon Zickus <dzickus@redhat.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Eric Paris <eparis@redhat.com>
Cc: Randy Dunlap <randy.dunlap@oracle.com>
LKML-Reference: <1273266711-18706-2-git-send-email-dzickus@redhat.com>
Signed-off-by: default avatarFrederic Weisbecker <fweisbec@gmail.com>
parent a9aa1d02
...@@ -1777,6 +1777,8 @@ and is between 256 and 4096 characters. It is defined in the file ...@@ -1777,6 +1777,8 @@ and is between 256 and 4096 characters. It is defined in the file
nousb [USB] Disable the USB subsystem nousb [USB] Disable the USB subsystem
nowatchdog [KNL] Disable the lockup detector.
nowb [ARM] nowb [ARM]
nox2apic [X86-64,APIC] Do not enable x2APIC mode. nox2apic [X86-64,APIC] Do not enable x2APIC mode.
......
...@@ -17,7 +17,7 @@ int do_nmi_callback(struct pt_regs *regs, int cpu); ...@@ -17,7 +17,7 @@ int do_nmi_callback(struct pt_regs *regs, int cpu);
extern void die_nmi(char *str, struct pt_regs *regs, int do_panic); extern void die_nmi(char *str, struct pt_regs *regs, int do_panic);
extern int check_nmi_watchdog(void); extern int check_nmi_watchdog(void);
#if !defined(CONFIG_NMI_WATCHDOG) #if !defined(CONFIG_LOCKUP_DETECTOR)
extern int nmi_watchdog_enabled; extern int nmi_watchdog_enabled;
#endif #endif
extern int avail_to_resrv_perfctr_nmi_bit(unsigned int); extern int avail_to_resrv_perfctr_nmi_bit(unsigned int);
......
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
# #
obj-$(CONFIG_X86_LOCAL_APIC) += apic.o apic_noop.o probe_$(BITS).o ipi.o obj-$(CONFIG_X86_LOCAL_APIC) += apic.o apic_noop.o probe_$(BITS).o ipi.o
ifneq ($(CONFIG_NMI_WATCHDOG),y) ifneq ($(CONFIG_LOCKUP_DETECTOR),y)
obj-$(CONFIG_X86_LOCAL_APIC) += nmi.o obj-$(CONFIG_X86_LOCAL_APIC) += nmi.o
endif endif
obj-$(CONFIG_NMI_WATCHDOG) += hw_nmi.o obj-$(CONFIG_LOCKUP_DETECTOR) += hw_nmi.o
obj-$(CONFIG_X86_IO_APIC) += io_apic.o obj-$(CONFIG_X86_IO_APIC) += io_apic.o
obj-$(CONFIG_SMP) += ipi.o obj-$(CONFIG_SMP) += ipi.o
......
...@@ -89,7 +89,7 @@ int hw_nmi_is_cpu_stuck(struct pt_regs *regs) ...@@ -89,7 +89,7 @@ int hw_nmi_is_cpu_stuck(struct pt_regs *regs)
u64 hw_nmi_get_sample_period(void) u64 hw_nmi_get_sample_period(void)
{ {
return cpu_khz * 1000; return (u64)(cpu_khz) * 1000 * 60;
} }
#ifdef ARCH_HAS_NMI_WATCHDOG #ifdef ARCH_HAS_NMI_WATCHDOG
......
...@@ -406,7 +406,7 @@ static notrace __kprobes void default_do_nmi(struct pt_regs *regs) ...@@ -406,7 +406,7 @@ static notrace __kprobes void default_do_nmi(struct pt_regs *regs)
== NOTIFY_STOP) == NOTIFY_STOP)
return; return;
#ifndef CONFIG_NMI_WATCHDOG #ifndef CONFIG_LOCKUP_DETECTOR
/* /*
* Ok, so this is none of the documented NMI sources, * Ok, so this is none of the documented NMI sources,
* so it must be the NMI watchdog. * so it must be the NMI watchdog.
...@@ -414,7 +414,7 @@ static notrace __kprobes void default_do_nmi(struct pt_regs *regs) ...@@ -414,7 +414,7 @@ static notrace __kprobes void default_do_nmi(struct pt_regs *regs)
if (nmi_watchdog_tick(regs, reason)) if (nmi_watchdog_tick(regs, reason))
return; return;
if (!do_nmi_callback(regs, cpu)) if (!do_nmi_callback(regs, cpu))
#endif /* !CONFIG_NMI_WATCHDOG */ #endif /* !CONFIG_LOCKUP_DETECTOR */
unknown_nmi_error(reason, regs); unknown_nmi_error(reason, regs);
#else #else
unknown_nmi_error(reason, regs); unknown_nmi_error(reason, regs);
......
...@@ -20,7 +20,7 @@ extern void touch_nmi_watchdog(void); ...@@ -20,7 +20,7 @@ extern void touch_nmi_watchdog(void);
extern void acpi_nmi_disable(void); extern void acpi_nmi_disable(void);
extern void acpi_nmi_enable(void); extern void acpi_nmi_enable(void);
#else #else
#ifndef CONFIG_NMI_WATCHDOG #ifndef CONFIG_LOCKUP_DETECTOR
static inline void touch_nmi_watchdog(void) static inline void touch_nmi_watchdog(void)
{ {
touch_softlockup_watchdog(); touch_softlockup_watchdog();
...@@ -51,12 +51,12 @@ static inline bool trigger_all_cpu_backtrace(void) ...@@ -51,12 +51,12 @@ static inline bool trigger_all_cpu_backtrace(void)
} }
#endif #endif
#ifdef CONFIG_NMI_WATCHDOG #ifdef CONFIG_LOCKUP_DETECTOR
int hw_nmi_is_cpu_stuck(struct pt_regs *); int hw_nmi_is_cpu_stuck(struct pt_regs *);
u64 hw_nmi_get_sample_period(void); u64 hw_nmi_get_sample_period(void);
extern int nmi_watchdog_enabled; extern int watchdog_enabled;
struct ctl_table; struct ctl_table;
extern int proc_nmi_enabled(struct ctl_table *, int , extern int proc_dowatchdog_enabled(struct ctl_table *, int ,
void __user *, size_t *, loff_t *); void __user *, size_t *, loff_t *);
#endif #endif
......
...@@ -346,6 +346,12 @@ extern int proc_dohung_task_timeout_secs(struct ctl_table *table, int write, ...@@ -346,6 +346,12 @@ extern int proc_dohung_task_timeout_secs(struct ctl_table *table, int write,
size_t *lenp, loff_t *ppos); size_t *lenp, loff_t *ppos);
#endif #endif
#ifdef CONFIG_LOCKUP_DETECTOR
extern int proc_dowatchdog_thresh(struct ctl_table *table, int write,
void __user *buffer,
size_t *lenp, loff_t *ppos);
#endif
/* Attach to any functions which should be ignored in wchan output. */ /* Attach to any functions which should be ignored in wchan output. */
#define __sched __attribute__((__section__(".sched.text"))) #define __sched __attribute__((__section__(".sched.text")))
......
...@@ -944,8 +944,11 @@ config PERF_USE_VMALLOC ...@@ -944,8 +944,11 @@ config PERF_USE_VMALLOC
config PERF_EVENTS_NMI config PERF_EVENTS_NMI
bool bool
depends on PERF_EVENTS
help help
Arch has support for nmi_watchdog System hardware can generate an NMI using the perf event
subsystem. Also has support for calculating CPU cycle events
to determine how many clock cycles in a given period.
menu "Kernel Performance Events And Counters" menu "Kernel Performance Events And Counters"
......
...@@ -76,9 +76,8 @@ obj-$(CONFIG_GCOV_KERNEL) += gcov/ ...@@ -76,9 +76,8 @@ obj-$(CONFIG_GCOV_KERNEL) += gcov/
obj-$(CONFIG_AUDIT_TREE) += audit_tree.o obj-$(CONFIG_AUDIT_TREE) += audit_tree.o
obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_KGDB) += kgdb.o obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_DETECT_SOFTLOCKUP) += softlockup.o
obj-$(CONFIG_NMI_WATCHDOG) += nmi_watchdog.o
obj-$(CONFIG_DETECT_HUNG_TASK) += hung_task.o obj-$(CONFIG_DETECT_HUNG_TASK) += hung_task.o
obj-$(CONFIG_LOCKUP_DETECTOR) += watchdog.o
obj-$(CONFIG_GENERIC_HARDIRQS) += irq/ obj-$(CONFIG_GENERIC_HARDIRQS) += irq/
obj-$(CONFIG_SECCOMP) += seccomp.o obj-$(CONFIG_SECCOMP) += seccomp.o
obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o
......
...@@ -74,7 +74,7 @@ ...@@ -74,7 +74,7 @@
#include <scsi/sg.h> #include <scsi/sg.h>
#endif #endif
#ifdef CONFIG_NMI_WATCHDOG #ifdef CONFIG_LOCKUP_DETECTOR
#include <linux/nmi.h> #include <linux/nmi.h>
#endif #endif
...@@ -686,16 +686,25 @@ static struct ctl_table kern_table[] = { ...@@ -686,16 +686,25 @@ static struct ctl_table kern_table[] = {
.mode = 0444, .mode = 0444,
.proc_handler = proc_dointvec, .proc_handler = proc_dointvec,
}, },
#if defined(CONFIG_NMI_WATCHDOG) #if defined(CONFIG_LOCKUP_DETECTOR)
{ {
.procname = "nmi_watchdog", .procname = "watchdog",
.data = &nmi_watchdog_enabled, .data = &watchdog_enabled,
.maxlen = sizeof (int), .maxlen = sizeof (int),
.mode = 0644, .mode = 0644,
.proc_handler = proc_nmi_enabled, .proc_handler = proc_dowatchdog_enabled,
},
{
.procname = "watchdog_thresh",
.data = &softlockup_thresh,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dowatchdog_thresh,
.extra1 = &neg_one,
.extra2 = &sixty,
}, },
#endif #endif
#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86) && !defined(CONFIG_NMI_WATCHDOG) #if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86) && !defined(CONFIG_LOCKUP_DETECTOR)
{ {
.procname = "unknown_nmi_panic", .procname = "unknown_nmi_panic",
.data = &unknown_nmi_panic, .data = &unknown_nmi_panic,
......
This diff is collapsed.
...@@ -153,7 +153,7 @@ config DEBUG_SHIRQ ...@@ -153,7 +153,7 @@ config DEBUG_SHIRQ
points; some don't and need to be caught. points; some don't and need to be caught.
config DETECT_SOFTLOCKUP config DETECT_SOFTLOCKUP
bool "Detect Soft Lockups" bool
depends on DEBUG_KERNEL && !S390 depends on DEBUG_KERNEL && !S390
default y default y
help help
...@@ -171,17 +171,27 @@ config DETECT_SOFTLOCKUP ...@@ -171,17 +171,27 @@ config DETECT_SOFTLOCKUP
can be detected via the NMI-watchdog, on platforms that can be detected via the NMI-watchdog, on platforms that
support it.) support it.)
config NMI_WATCHDOG config LOCKUP_DETECTOR
bool "Detect Hard Lockups with an NMI Watchdog" bool "Detect Hard and Soft Lockups"
depends on DEBUG_KERNEL && PERF_EVENTS && PERF_EVENTS_NMI depends on DEBUG_KERNEL
default DETECT_SOFTLOCKUP
help help
Say Y here to enable the kernel to use the NMI as a watchdog Say Y here to enable the kernel to act as a watchdog to detect
to detect hard lockups. This is useful when a cpu hangs for no hard and soft lockups.
reason but can still respond to NMIs. A backtrace is displayed
for reviewing and reporting. Softlockups are bugs that cause the kernel to loop in kernel
mode for more than 60 seconds, without giving other tasks a
chance to run. The current stack trace is displayed upon
detection and the system will stay locked up.
Hardlockups are bugs that cause the CPU to loop in kernel mode
for more than 60 seconds, without letting other interrupts have a
chance to run. The current stack trace is displayed upon detection
and the system will stay locked up.
The overhead should be minimal, just an extra NMI every few The overhead should be minimal. A periodic hrtimer runs to
seconds. generate interrupts and kick the watchdog task every 10-12 seconds.
An NMI is generated every 60 seconds or so to check for hardlockups.
config BOOTPARAM_SOFTLOCKUP_PANIC config BOOTPARAM_SOFTLOCKUP_PANIC
bool "Panic (Reboot) On Soft Lockups" bool "Panic (Reboot) On Soft Lockups"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment