| From 48d672df39a6adea156af6c8d7083b620578cf32 Mon Sep 17 00:00:00 2001 |
| From: Ingo Molnar <mingo@elte.hu> |
| Date: Fri, 22 Jul 2011 17:58:40 +0200 |
| Subject: [PATCH] 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> |
| |
| diff --git a/include/linux/printk.h b/include/linux/printk.h |
| index f4da695fd615..cfd34c12c0a0 100644 |
| --- a/include/linux/printk.h |
| +++ b/include/linux/printk.h |
| @@ -117,9 +117,11 @@ do { \ |
| #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 60cdf6386763..80e0946b0e9c 100644 |
| --- a/kernel/printk/printk.c |
| +++ b/kernel/printk/printk.c |
| @@ -247,6 +247,58 @@ __packed __aligned(4) |
| */ |
| 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 */ |
| @@ -1622,6 +1674,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; |
| @@ -1894,26 +1953,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 9acb29f280ec..7884a6248e8b 100644 |
| --- a/kernel/watchdog.c |
| +++ b/kernel/watchdog.c |
| @@ -315,6 +315,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, |
| @@ -349,6 +351,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(); |
| @@ -366,6 +375,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.5.0 |
| |