diff options
author | Ingo Molnar <mingo@elte.hu> | 2011-07-22 17:58:40 +0200 |
---|---|---|
committer | Sebastian Andrzej Siewior <bigeasy@linutronix.de> | 2016-02-13 00:34:44 +0100 |
commit | 58c1a4bc94cadc200aec8260a3904a289501fa8d (patch) | |
tree | 2131cdac1abaa98a80bc4802e289f19ccc7de463 | |
parent | 219d98de6dc900ed9b4ce3fe597210280adb3997 (diff) | |
download | rt-linux-58c1a4bc94cadc200aec8260a3904a289501fa8d.tar.gz |
printk: Add a printk kill switch
Add a prinkt-kill-switch. This is used from (NMI) watchdog to ensure that
it does not dead-lock with the early printk code.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | include/linux/printk.h | 2 | ||||
-rw-r--r-- | kernel/printk/printk.c | 79 | ||||
-rw-r--r-- | kernel/watchdog.c | 10 |
3 files changed, 71 insertions, 20 deletions
diff --git a/include/linux/printk.h b/include/linux/printk.h index 9729565c25ff19..9cdca696b71857 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -117,9 +117,11 @@ int no_printk(const char *fmt, ...) #ifdef CONFIG_EARLY_PRINTK extern asmlinkage __printf(1, 2) void early_printk(const char *fmt, ...); +extern void printk_kill(void); #else static inline __printf(1, 2) __cold void early_printk(const char *s, ...) { } +static inline void printk_kill(void) { } #endif typedef __printf(1, 0) int (*printk_func_t)(const char *fmt, va_list args); diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 2ce8826f1053d8..77c5b4814160c3 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -241,6 +241,58 @@ struct printk_log { */ static DEFINE_RAW_SPINLOCK(logbuf_lock); +#ifdef CONFIG_EARLY_PRINTK +struct console *early_console; + +static void early_vprintk(const char *fmt, va_list ap) +{ + if (early_console) { + char buf[512]; + int n = vscnprintf(buf, sizeof(buf), fmt, ap); + + early_console->write(early_console, buf, n); + } +} + +asmlinkage void early_printk(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + early_vprintk(fmt, ap); + va_end(ap); +} + +/* + * This is independent of any log levels - a global + * kill switch that turns off all of printk. + * + * Used by the NMI watchdog if early-printk is enabled. + */ +static bool __read_mostly printk_killswitch; + +void printk_kill(void) +{ + printk_killswitch = true; +} + +#ifdef CONFIG_PRINTK +static int forced_early_printk(const char *fmt, va_list ap) +{ + if (!printk_killswitch) + return 0; + early_vprintk(fmt, ap); + return 1; +} +#endif + +#else +static inline int forced_early_printk(const char *fmt, va_list ap) +{ + return 0; +} +#endif + #ifdef CONFIG_PRINTK DECLARE_WAIT_QUEUE_HEAD(log_wait); /* the next printk record to read by syslog(READ) or /proc/kmsg */ @@ -1672,6 +1724,13 @@ asmlinkage int vprintk_emit(int facility, int level, /* cpu currently holding logbuf_lock in this function */ static unsigned int logbuf_cpu = UINT_MAX; + /* + * Fall back to early_printk if a debugging subsystem has + * killed printk output + */ + if (unlikely(forced_early_printk(fmt, args))) + return 1; + if (level == LOGLEVEL_SCHED) { level = LOGLEVEL_DEFAULT; in_sched = true; @@ -1961,26 +2020,6 @@ DEFINE_PER_CPU(printk_func_t, printk_func); #endif /* CONFIG_PRINTK */ -#ifdef CONFIG_EARLY_PRINTK -struct console *early_console; - -asmlinkage __visible void early_printk(const char *fmt, ...) -{ - va_list ap; - char buf[512]; - int n; - - if (!early_console) - return; - - va_start(ap, fmt); - n = vscnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - - early_console->write(early_console, buf, n); -} -#endif - static int __add_preferred_console(char *name, int idx, char *options, char *brl_options) { diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 18f34cf75f741e..68f928743ac5c9 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -299,6 +299,8 @@ static int is_softlockup(unsigned long touch_ts) #ifdef CONFIG_HARDLOCKUP_DETECTOR +static DEFINE_RAW_SPINLOCK(watchdog_output_lock); + static struct perf_event_attr wd_hw_attr = { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES, @@ -333,6 +335,13 @@ static void watchdog_overflow_callback(struct perf_event *event, /* only print hardlockups once */ if (__this_cpu_read(hard_watchdog_warn) == true) return; + /* + * If early-printk is enabled then make sure we do not + * lock up in printk() and kill console logging: + */ + printk_kill(); + + raw_spin_lock(&watchdog_output_lock); pr_emerg("Watchdog detected hard LOCKUP on cpu %d", this_cpu); print_modules(); @@ -350,6 +359,7 @@ static void watchdog_overflow_callback(struct perf_event *event, !test_and_set_bit(0, &hardlockup_allcpu_dumped)) trigger_allbutself_cpu_backtrace(); + raw_spin_unlock(&watchdog_output_lock); if (hardlockup_panic) panic("Hard LOCKUP"); |