Skip to content

Commit 1ccd85f

Browse files
committed
Merge branch 'for-5.18-panic-deadlocks' into for-linus
2 parents 0834c6f + ce06e86 commit 1ccd85f

File tree

1 file changed

+54
-1
lines changed

1 file changed

+54
-1
lines changed

kernel/printk/printk.c

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ EXPORT_SYMBOL_GPL(console_drivers);
9393
*/
9494
int __read_mostly suppress_printk;
9595

96+
/*
97+
* During panic, heavy printk by other CPUs can delay the
98+
* panic and risk deadlock on console resources.
99+
*/
100+
static int __read_mostly suppress_panic_printk;
101+
96102
#ifdef CONFIG_LOCKDEP
97103
static struct lockdep_map console_lock_dep_map = {
98104
.name = "console_lock"
@@ -258,6 +264,11 @@ static void __up_console_sem(unsigned long ip)
258264
}
259265
#define up_console_sem() __up_console_sem(_RET_IP_)
260266

267+
static bool panic_in_progress(void)
268+
{
269+
return unlikely(atomic_read(&panic_cpu) != PANIC_CPU_INVALID);
270+
}
271+
261272
/*
262273
* This is used for debugging the mess that is the VT code by
263274
* keeping track if we have the console semaphore held. It's
@@ -1844,6 +1855,16 @@ static int console_trylock_spinning(void)
18441855
if (console_trylock())
18451856
return 1;
18461857

1858+
/*
1859+
* It's unsafe to spin once a panic has begun. If we are the
1860+
* panic CPU, we may have already halted the owner of the
1861+
* console_sem. If we are not the panic CPU, then we should
1862+
* avoid taking console_sem, so the panic CPU has a better
1863+
* chance of cleanly acquiring it later.
1864+
*/
1865+
if (panic_in_progress())
1866+
return 0;
1867+
18471868
printk_safe_enter_irqsave(flags);
18481869

18491870
raw_spin_lock(&console_owner_lock);
@@ -2219,6 +2240,10 @@ asmlinkage int vprintk_emit(int facility, int level,
22192240
if (unlikely(suppress_printk))
22202241
return 0;
22212242

2243+
if (unlikely(suppress_panic_printk) &&
2244+
atomic_read(&panic_cpu) != raw_smp_processor_id())
2245+
return 0;
2246+
22222247
if (level == LOGLEVEL_SCHED) {
22232248
level = LOGLEVEL_DEFAULT;
22242249
in_sched = true;
@@ -2586,6 +2611,25 @@ static int have_callable_console(void)
25862611
return 0;
25872612
}
25882613

2614+
/*
2615+
* Return true when this CPU should unlock console_sem without pushing all
2616+
* messages to the console. This reduces the chance that the console is
2617+
* locked when the panic CPU tries to use it.
2618+
*/
2619+
static bool abandon_console_lock_in_panic(void)
2620+
{
2621+
if (!panic_in_progress())
2622+
return false;
2623+
2624+
/*
2625+
* We can use raw_smp_processor_id() here because it is impossible for
2626+
* the task to be migrated to the panic_cpu, or away from it. If
2627+
* panic_cpu has already been set, and we're not currently executing on
2628+
* that CPU, then we never will be.
2629+
*/
2630+
return atomic_read(&panic_cpu) != raw_smp_processor_id();
2631+
}
2632+
25892633
/*
25902634
* Can we actually use the console at this time on this cpu?
25912635
*
@@ -2616,6 +2660,7 @@ void console_unlock(void)
26162660
{
26172661
static char ext_text[CONSOLE_EXT_LOG_MAX];
26182662
static char text[CONSOLE_LOG_MAX];
2663+
static int panic_console_dropped;
26192664
unsigned long flags;
26202665
bool do_cond_resched, retry;
26212666
struct printk_info info;
@@ -2670,6 +2715,10 @@ void console_unlock(void)
26702715
if (console_seq != r.info->seq) {
26712716
console_dropped += r.info->seq - console_seq;
26722717
console_seq = r.info->seq;
2718+
if (panic_in_progress() && panic_console_dropped++ > 10) {
2719+
suppress_panic_printk = 1;
2720+
pr_warn_once("Too many dropped messages. Suppress messages on non-panic CPUs to prevent livelock.\n");
2721+
}
26732722
}
26742723

26752724
if (suppress_message_printing(r.info->level)) {
@@ -2729,6 +2778,10 @@ void console_unlock(void)
27292778
if (handover)
27302779
return;
27312780

2781+
/* Allow panic_cpu to take over the consoles safely */
2782+
if (abandon_console_lock_in_panic())
2783+
break;
2784+
27322785
if (do_cond_resched)
27332786
cond_resched();
27342787
}
@@ -2746,7 +2799,7 @@ void console_unlock(void)
27462799
* flush, no worries.
27472800
*/
27482801
retry = prb_read_valid(prb, next_seq, NULL);
2749-
if (retry && console_trylock())
2802+
if (retry && !abandon_console_lock_in_panic() && console_trylock())
27502803
goto again;
27512804
}
27522805
EXPORT_SYMBOL(console_unlock);

0 commit comments

Comments
 (0)