Subject: [PATCH 02/24] printk: Add NMI check to console_flush_on_panic() and console_unblank()
From: John Ogness <[email-masked]>
Date: Fri, 30 Dec 2022 15:49:42 +0106

The printk path is NMI safe because it only adds content to the
buffer and then triggers the delayed output via irq_work. If the
console is flushed or unblanked on panic (from NMI context) then it
can deadlock in down_trylock_console_sem() because the semaphore is
not NMI safe.

Avoid taking the console_lock when flushing in panic and the current
context is NMI. Since the consoles are encouraged to ignore their
locks and also will stop printing if not the panic CPU, this change
does not really make things any more dangerous. But it does avoid
a possible deadlock in NMI context.

Skip unblanking in panic if the current context is NMI.

Signed-off-by: John Ogness <[email-masked]>
Signed-off-by: Sebastian Andrzej Siewior <[email-masked]>
---
 kernel/printk/printk.c |   13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

Index: linux-6.3.0-rt11/kernel/printk/printk.c
===================================================================
--- linux-6.3.0-rt11.orig/kernel/printk/printk.c
+++ linux-6.3.0-rt11/kernel/printk/printk.c
@@ -3065,6 +3065,10 @@ void console_unblank(void)
 	 * In that case, attempt a trylock as best-effort.
 	 */
 	if (oops_in_progress) {
+		/* Semaphores are not NMI-safe. */
+		if (in_nmi())
+			return;
+
 		if (down_trylock_console_sem() != 0)
 			return;
 	} else
@@ -3100,8 +3104,12 @@ void console_flush_on_panic(enum con_flu
 	 * that messages are flushed out.  As this can be called from any
 	 * context and we don't want to get preempted while flushing,
 	 * ensure may_schedule is cleared.
+	 *
+	 * Since semaphores are not NMI-safe, the console lock must be
+	 * ignored if the panic is in NMI context.
 	 */
-	console_trylock();
+	if (!in_nmi())
+		console_trylock();
 	console_may_schedule = 0;
 
 	if (mode == CONSOLE_REPLAY_ALL) {
@@ -3122,7 +3130,8 @@ void console_flush_on_panic(enum con_flu
 		}
 		console_srcu_read_unlock(cookie);
 	}
-	console_unlock();
+	if (!in_nmi())
+		console_unlock();
 }
 
 /*