From dd723f7d7b00a82f9a4e46da7fa412414103fe29 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Fri, 22 Jul 2011 17:58:40 +0200 Subject: [PATCH 068/352] 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. Change-Id: Ia418a0ccca4f61786b03e045a7867f235d512e22 Signed-off-by: Thomas Gleixner --- include/linux/printk.h | 2 ++ kernel/printk/printk.c | 20 --------------- kernel/printk/printk_safe.c | 60 +++++++++++++++++++++++++++++++++++++++++++++ kernel/watchdog_hld.c | 9 +++++++ 4 files changed, 71 insertions(+), 20 deletions(-) diff --git a/include/linux/printk.h b/include/linux/printk.h index 45993a72..c666419 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -127,9 +127,11 @@ struct va_format { #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 #ifdef CONFIG_PRINTK_NMI diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 4063140..1477488 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -2032,26 +2032,6 @@ static bool suppress_message_printing(int level) { return false; } #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/printk/printk_safe.c b/kernel/printk/printk_safe.c index 033e50a..3c50299 100644 --- a/kernel/printk/printk_safe.c +++ b/kernel/printk/printk_safe.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "internal.h" @@ -349,8 +350,67 @@ void __printk_safe_exit(void) this_cpu_dec(printk_context); } +#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 + __printf(1, 0) int vprintk_func(const char *fmt, va_list args) { + /* + * Fall back to early_printk if a debugging subsystem has + * killed printk output + */ + if (unlikely(forced_early_printk(fmt, args))) + return 1; + if (this_cpu_read(printk_context) & PRINTK_NMI_CONTEXT_MASK) return vprintk_nmi(fmt, args); diff --git a/kernel/watchdog_hld.c b/kernel/watchdog_hld.c index e5709b8..b3bdf40 100644 --- a/kernel/watchdog_hld.c +++ b/kernel/watchdog_hld.c @@ -21,6 +21,7 @@ static DEFINE_PER_CPU(bool, hard_watchdog_warn); static DEFINE_PER_CPU(bool, watchdog_nmi_touch); static DEFINE_PER_CPU(struct perf_event *, watchdog_ev); static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts_saved); +static DEFINE_RAW_SPINLOCK(watchdog_output_lock); #ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU static cpumask_t __read_mostly watchdog_cpus; @@ -240,6 +241,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(); @@ -257,6 +265,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) nmi_panic(regs, "Hard LOCKUP"); -- 2.7.4