181 lines
4.7 KiB
Diff
181 lines
4.7 KiB
Diff
|
From 1c6f7f178af39e484583a091c34a5a0b35f99c46 Mon Sep 17 00:00:00 2001
|
||
|
From: Ingo Molnar <mingo@elte.hu>
|
||
|
Date: Fri, 22 Jul 2011 17:58:40 +0200
|
||
|
Subject: [PATCH 067/365] 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: Id189c263b6a1572e9967ee5a213f10ba2e634784
|
||
|
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||
|
---
|
||
|
include/linux/printk.h | 2 ++
|
||
|
kernel/printk/printk.c | 79 +++++++++++++++++++++++++++++++-----------
|
||
|
kernel/watchdog_hld.c | 9 +++++
|
||
|
3 files changed, 69 insertions(+), 21 deletions(-)
|
||
|
|
||
|
diff --git a/include/linux/printk.h b/include/linux/printk.h
|
||
|
index 45993a72ee43..c666419e8e6a 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 673aadadf6c4..5c62cfde8cf2 100644
|
||
|
--- a/kernel/printk/printk.c
|
||
|
+++ b/kernel/printk/printk.c
|
||
|
@@ -374,7 +374,6 @@ __packed __aligned(4)
|
||
|
* console_unlock() or anything else that might wake up a process.
|
||
|
*/
|
||
|
DEFINE_RAW_SPINLOCK(logbuf_lock);
|
||
|
-
|
||
|
/*
|
||
|
* Helper macros to lock/unlock logbuf_lock and switch between
|
||
|
* printk-safe/unsafe modes.
|
||
|
@@ -402,6 +401,57 @@ DEFINE_RAW_SPINLOCK(logbuf_lock);
|
||
|
raw_spin_unlock(&logbuf_lock); \
|
||
|
printk_safe_exit_irqrestore(flags); \
|
||
|
} while (0)
|
||
|
+#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);
|
||
|
@@ -1770,6 +1820,13 @@ asmlinkage int vprintk_emit(int facility, int level,
|
||
|
int printed_len = 0;
|
||
|
bool in_sched = false;
|
||
|
|
||
|
+ /*
|
||
|
+ * 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;
|
||
|
@@ -1952,26 +2009,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/watchdog_hld.c b/kernel/watchdog_hld.c
|
||
|
index e5709b8603ac..9566409a1d50 100644
|
||
|
--- a/kernel/watchdog_hld.c
|
||
|
+++ b/kernel/watchdog_hld.c
|
||
|
@@ -25,6 +25,7 @@ static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts_saved);
|
||
|
#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU
|
||
|
static cpumask_t __read_mostly watchdog_cpus;
|
||
|
#endif
|
||
|
+static DEFINE_RAW_SPINLOCK(watchdog_output_lock);
|
||
|
|
||
|
/* boot commands */
|
||
|
/*
|
||
|
@@ -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.28.0
|
||
|
|