From d292dbc316359c613bbadf765f01ac44798d4c16 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Thu, 31 Dec 2020 22:03:56 +0000 Subject: [PATCH] kernel/watchdog: flush all printk nmi buffers when hardlockup detected In NMI context printk() could save messages into per-cpu buffers and schedule flush by irq_work when IRQ are unblocked. This means message about hardlockup appears in kernel log only when/if lockup is gone. Comment in irq_work_queue_on() states that remote IPI aren't NMI safe thus printk() cannot schedule flush work to another cpu. This patch adds simple atomic counter of detected hardlockups and flushes all per-cpu printk buffers in context softlockup watchdog at any other cpu when it sees changes of this counter. Link: http://lkml.kernel.org/r/158132813726.1980.17382047082627699898.stgit@buzz Signed-off-by: Konstantin Khlebnikov Cc: Petr Mladek Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Sergey Senozhatsky Cc: Dmitry Monakhov Signed-off-by: Andrew Morton --- include/linux/nmi.h | 1 + kernel/watchdog.c | 21 +++++++++++++++++++++ kernel/watchdog_hld.c | 1 + 3 files changed, 23 insertions(+) diff --git a/include/linux/nmi.h b/include/linux/nmi.h index 750c7f395ca9..0c2a1d915453 100644 --- a/include/linux/nmi.h +++ b/include/linux/nmi.h @@ -84,6 +84,7 @@ static inline void reset_hung_task_detector(void) { } #if defined(CONFIG_HARDLOCKUP_DETECTOR) extern void hardlockup_detector_disable(void); extern unsigned int hardlockup_panic; +extern atomic_t hardlockup_detected; #else static inline void hardlockup_detector_disable(void) {} #endif diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 71109065bd8e..d8baf69aab4a 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -85,6 +85,25 @@ static int __init hardlockup_panic_setup(char *str) } __setup("nmi_watchdog=", hardlockup_panic_setup); +atomic_t hardlockup_detected = ATOMIC_INIT(0); + +static inline void flush_hardlockup_messages(void) +{ + static atomic_t flushed = ATOMIC_INIT(0); + + /* flush messages from hard lockup detector */ + if (atomic_read(&hardlockup_detected) != atomic_read(&flushed)) { + atomic_set(&flushed, atomic_read(&hardlockup_detected)); + printk_safe_flush(); + } +} + +#else /* CONFIG_HARDLOCKUP_DETECTOR */ + +static inline void flush_hardlockup_messages(void) +{ +} + #endif /* CONFIG_HARDLOCKUP_DETECTOR */ /* @@ -351,6 +370,8 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) /* kick the hardlockup detector */ watchdog_interrupt_count(); + flush_hardlockup_messages(); + /* kick the softlockup detector */ if (completion_done(this_cpu_ptr(&softlockup_completion))) { reinit_completion(this_cpu_ptr(&softlockup_completion)); diff --git a/kernel/watchdog_hld.c b/kernel/watchdog_hld.c index 247bf0b1582c..a546bc54f6ff 100644 --- a/kernel/watchdog_hld.c +++ b/kernel/watchdog_hld.c @@ -154,6 +154,7 @@ static void watchdog_overflow_callback(struct perf_event *event, if (hardlockup_panic) nmi_panic(regs, "Hard LOCKUP"); + atomic_inc(&hardlockup_detected); __this_cpu_write(hard_watchdog_warn, true); return; -- 2.50.1