From: John Ogness <john.ogness@linutronix.de> Date: Fri, 30 Dec 2022 15:49:42 +0106 Subject: [PATCH 02/24] printk: Add NMI check to console_flush_on_panic() and console_unblank() 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 <john.ogness@linutronix.de> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- 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/kernel/printk/printk.c:3068 @ 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 @ linux-6.3.0-rt11/kernel/printk/printk.c:3107 @ 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) { @ linux-6.3.0-rt11/kernel/printk/printk.c:3133 @ void console_flush_on_panic(enum con_flu } console_srcu_read_unlock(cookie); } - console_unlock(); + if (!in_nmi()) + console_unlock(); } /*