@@ -93,6 +93,12 @@ EXPORT_SYMBOL_GPL(console_drivers);
93
93
*/
94
94
int __read_mostly suppress_printk ;
95
95
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
+
96
102
#ifdef CONFIG_LOCKDEP
97
103
static struct lockdep_map console_lock_dep_map = {
98
104
.name = "console_lock"
@@ -258,6 +264,11 @@ static void __up_console_sem(unsigned long ip)
258
264
}
259
265
#define up_console_sem () __up_console_sem(_RET_IP_)
260
266
267
+ static bool panic_in_progress (void )
268
+ {
269
+ return unlikely (atomic_read (& panic_cpu ) != PANIC_CPU_INVALID );
270
+ }
271
+
261
272
/*
262
273
* This is used for debugging the mess that is the VT code by
263
274
* keeping track if we have the console semaphore held. It's
@@ -1844,6 +1855,16 @@ static int console_trylock_spinning(void)
1844
1855
if (console_trylock ())
1845
1856
return 1 ;
1846
1857
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
+
1847
1868
printk_safe_enter_irqsave (flags );
1848
1869
1849
1870
raw_spin_lock (& console_owner_lock );
@@ -2219,6 +2240,10 @@ asmlinkage int vprintk_emit(int facility, int level,
2219
2240
if (unlikely (suppress_printk ))
2220
2241
return 0 ;
2221
2242
2243
+ if (unlikely (suppress_panic_printk ) &&
2244
+ atomic_read (& panic_cpu ) != raw_smp_processor_id ())
2245
+ return 0 ;
2246
+
2222
2247
if (level == LOGLEVEL_SCHED ) {
2223
2248
level = LOGLEVEL_DEFAULT ;
2224
2249
in_sched = true;
@@ -2586,6 +2611,25 @@ static int have_callable_console(void)
2586
2611
return 0 ;
2587
2612
}
2588
2613
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
+
2589
2633
/*
2590
2634
* Can we actually use the console at this time on this cpu?
2591
2635
*
@@ -2616,6 +2660,7 @@ void console_unlock(void)
2616
2660
{
2617
2661
static char ext_text [CONSOLE_EXT_LOG_MAX ];
2618
2662
static char text [CONSOLE_LOG_MAX ];
2663
+ static int panic_console_dropped ;
2619
2664
unsigned long flags ;
2620
2665
bool do_cond_resched , retry ;
2621
2666
struct printk_info info ;
@@ -2670,6 +2715,10 @@ void console_unlock(void)
2670
2715
if (console_seq != r .info -> seq ) {
2671
2716
console_dropped += r .info -> seq - console_seq ;
2672
2717
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
+ }
2673
2722
}
2674
2723
2675
2724
if (suppress_message_printing (r .info -> level )) {
@@ -2729,6 +2778,10 @@ void console_unlock(void)
2729
2778
if (handover )
2730
2779
return ;
2731
2780
2781
+ /* Allow panic_cpu to take over the consoles safely */
2782
+ if (abandon_console_lock_in_panic ())
2783
+ break ;
2784
+
2732
2785
if (do_cond_resched )
2733
2786
cond_resched ();
2734
2787
}
@@ -2746,7 +2799,7 @@ void console_unlock(void)
2746
2799
* flush, no worries.
2747
2800
*/
2748
2801
retry = prb_read_valid (prb , next_seq , NULL );
2749
- if (retry && console_trylock ())
2802
+ if (retry && ! abandon_console_lock_in_panic () && console_trylock ())
2750
2803
goto again ;
2751
2804
}
2752
2805
EXPORT_SYMBOL (console_unlock );
0 commit comments