Skip to content

Commit d04d588

Browse files
committed
printk: Disable passing console lock owner completely during panic()
The commit d515070 ("printk: disable optimistic spin during panic") added checks to avoid becoming a console waiter if a panic is in progress. However, the transition to panic can occur while there is already a waiter. The current owner should not pass the lock to the waiter because it might get stopped or blocked anytime. Also the panic context might pass the console lock owner to an already stopped waiter by mistake. It might happen when console_flush_on_panic() ignores the current lock owner, for example: CPU0 CPU1 ---- ---- console_lock_spinning_enable() console_trylock_spinning() [CPU1 now console waiter] NMI: panic() panic_other_cpus_shutdown() [stopped as console waiter] console_flush_on_panic() console_lock_spinning_enable() [print 1 record] console_lock_spinning_disable_and_check() [handover to stopped CPU1] This results in panic() not flushing the panic messages. Fix these problems by disabling all spinning operations completely during panic(). Another advantage is that it prevents possible deadlocks caused by "console_owner_lock". The panic() context does not need to take it any longer. The lockless checks are safe because the functions become NOPs when they see the panic in progress. All operations manipulating the state are still synchronized by the lock even when non-panic CPUs would notice the panic synchronously. The current owner might stay spinning. But non-panic() CPUs would get stopped anyway and the panic context will never start spinning. Fixes: dbdda84 ("printk: Add console owner and waiter logic to load balance console writes") Signed-off-by: John Ogness <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Petr Mladek <[email protected]>
1 parent b1c4c67 commit d04d588

File tree

1 file changed

+29
-0
lines changed

1 file changed

+29
-0
lines changed

kernel/printk/printk.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1869,10 +1869,23 @@ static bool console_waiter;
18691869
*/
18701870
static void console_lock_spinning_enable(void)
18711871
{
1872+
/*
1873+
* Do not use spinning in panic(). The panic CPU wants to keep the lock.
1874+
* Non-panic CPUs abandon the flush anyway.
1875+
*
1876+
* Just keep the lockdep annotation. The panic-CPU should avoid
1877+
* taking console_owner_lock because it might cause a deadlock.
1878+
* This looks like the easiest way how to prevent false lockdep
1879+
* reports without handling races a lockless way.
1880+
*/
1881+
if (panic_in_progress())
1882+
goto lockdep;
1883+
18721884
raw_spin_lock(&console_owner_lock);
18731885
console_owner = current;
18741886
raw_spin_unlock(&console_owner_lock);
18751887

1888+
lockdep:
18761889
/* The waiter may spin on us after setting console_owner */
18771890
spin_acquire(&console_owner_dep_map, 0, 0, _THIS_IP_);
18781891
}
@@ -1897,6 +1910,22 @@ static int console_lock_spinning_disable_and_check(int cookie)
18971910
{
18981911
int waiter;
18991912

1913+
/*
1914+
* Ignore spinning waiters during panic() because they might get stopped
1915+
* or blocked at any time,
1916+
*
1917+
* It is safe because nobody is allowed to start spinning during panic
1918+
* in the first place. If there has been a waiter then non panic CPUs
1919+
* might stay spinning. They would get stopped anyway. The panic context
1920+
* will never start spinning and an interrupted spin on panic CPU will
1921+
* never continue.
1922+
*/
1923+
if (panic_in_progress()) {
1924+
/* Keep lockdep happy. */
1925+
spin_release(&console_owner_dep_map, _THIS_IP_);
1926+
return 0;
1927+
}
1928+
19001929
raw_spin_lock(&console_owner_lock);
19011930
waiter = READ_ONCE(console_waiter);
19021931
console_owner = NULL;

0 commit comments

Comments
 (0)