Skip to content

Commit 7b23a66

Browse files
jognesspmladek
authored andcommitted
printk: Reduce console_unblank() usage in unsafe scenarios
A semaphore is not NMI-safe, even when using down_trylock(). Both down_trylock() and up() are using internal spinlocks and up() might even call wake_up_process(). In the panic() code path it gets even worse because the internal spinlocks of the semaphore may have been taken by a CPU that has been stopped. To reduce the risk of deadlocks caused by the console semaphore in the panic path, make the following changes: - First check if any consoles have implemented the unblank() callback. If not, then there is no reason to take the console semaphore anyway. (This check is also useful for the non-panic path since the locking/unlocking of the console lock can be quite expensive due to console printing.) - If the panic path is in NMI context, bail out without attempting to take the console semaphore or calling any unblank() callbacks. Bailing out is acceptable because console_unblank() would already bail out if the console semaphore is contended. The alternative of ignoring the console semaphore and calling the unblank() callbacks anyway is a bad idea because these callbacks are also not NMI-safe. If consoles with unblank() callbacks exist and console_unblank() is called from a non-NMI panic context, it will still attempt a down_trylock(). This could still result in a deadlock if one of the stopped CPUs is holding the semaphore internal spinlock. But this is a risk that the kernel has been (and continues to be) willing to take. Signed-off-by: John Ogness <[email protected]> Reviewed-by: Sergey Senozhatsky <[email protected]> Reviewed-by: Petr Mladek <[email protected]> Signed-off-by: Petr Mladek <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 6d3e0d8 commit 7b23a66

File tree

1 file changed

+28
-0
lines changed

1 file changed

+28
-0
lines changed

kernel/printk/printk.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3043,9 +3043,27 @@ EXPORT_SYMBOL(console_conditional_schedule);
30433043

30443044
void console_unblank(void)
30453045
{
3046+
bool found_unblank = false;
30463047
struct console *c;
30473048
int cookie;
30483049

3050+
/*
3051+
* First check if there are any consoles implementing the unblank()
3052+
* callback. If not, there is no reason to continue and take the
3053+
* console lock, which in particular can be dangerous if
3054+
* @oops_in_progress is set.
3055+
*/
3056+
cookie = console_srcu_read_lock();
3057+
for_each_console_srcu(c) {
3058+
if ((console_srcu_read_flags(c) & CON_ENABLED) && c->unblank) {
3059+
found_unblank = true;
3060+
break;
3061+
}
3062+
}
3063+
console_srcu_read_unlock(cookie);
3064+
if (!found_unblank)
3065+
return;
3066+
30493067
/*
30503068
* Stop console printing because the unblank() callback may
30513069
* assume the console is not within its write() callback.
@@ -3054,6 +3072,16 @@ void console_unblank(void)
30543072
* In that case, attempt a trylock as best-effort.
30553073
*/
30563074
if (oops_in_progress) {
3075+
/* Semaphores are not NMI-safe. */
3076+
if (in_nmi())
3077+
return;
3078+
3079+
/*
3080+
* Attempting to trylock the console lock can deadlock
3081+
* if another CPU was stopped while modifying the
3082+
* semaphore. "Hope and pray" that this is not the
3083+
* current situation.
3084+
*/
30573085
if (down_trylock_console_sem() != 0)
30583086
return;
30593087
} else

0 commit comments

Comments
 (0)