Skip to content

Commit b87f023

Browse files
committed
printk: Wait for the global console lock when the system is going down
There are reports that the console kthreads block the global console lock when the system is going down, for example, reboot, panic. First part of the solution was to block kthreads in these problematic system states so they stopped handling newly added messages. Second part of the solution is to wait when for the kthreads when they are actively printing. It solves the problem when a message was printed before the system entered the problematic state and the kthreads managed to step in. A busy waiting has to be used because panic() can be called in any context and in an unknown state of the scheduler. There must be a timeout because the kthread might get stuck or sleeping and never release the lock. The timeout 10s is an arbitrary value inspired by the softlockup timeout. Link: https://lore.kernel.org/r/20220610205038.GA3050413@paulmck-ThinkPad-P17-Gen-1 Link: https://lore.kernel.org/r/CAMdYzYpF4FNTBPZsEFeWRuEwSies36QM_As8osPWZSr2q-viEA@mail.gmail.com Signed-off-by: Petr Mladek <[email protected]> Tested-by: Paul E. McKenney <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent c323028 commit b87f023

File tree

6 files changed

+47
-0
lines changed

6 files changed

+47
-0
lines changed

include/linux/printk.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ extern void printk_prefer_direct_enter(void);
174174
extern void printk_prefer_direct_exit(void);
175175

176176
extern bool pr_flush(int timeout_ms, bool reset_on_progress);
177+
extern void try_block_console_kthreads(int timeout_ms);
177178

178179
/*
179180
* Please don't use printk_ratelimit(), because it shares ratelimiting state
@@ -238,6 +239,10 @@ static inline bool pr_flush(int timeout_ms, bool reset_on_progress)
238239
return true;
239240
}
240241

242+
static inline void try_block_console_kthreads(int timeout_ms)
243+
{
244+
}
245+
241246
static inline int printk_ratelimit(void)
242247
{
243248
return 0;

kernel/panic.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,13 +273,15 @@ void panic(const char *fmt, ...)
273273
* unfortunately means it may not be hardened to work in a
274274
* panic situation.
275275
*/
276+
try_block_console_kthreads(10000);
276277
smp_send_stop();
277278
} else {
278279
/*
279280
* If we want to do crash dump after notifier calls and
280281
* kmsg_dump, we will need architecture dependent extra
281282
* works in addition to stopping other CPUs.
282283
*/
284+
try_block_console_kthreads(10000);
283285
crash_smp_send_stop();
284286
}
285287

kernel/printk/internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ enum printk_info_flags {
2020
LOG_CONT = 8, /* text is a fragment of a continuation line */
2121
};
2222

23+
extern bool block_console_kthreads;
24+
2325
__printf(4, 0)
2426
int vprintk_store(int facility, int level,
2527
const struct dev_printk_info *dev_info,

kernel/printk/printk.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,9 @@ static atomic_t console_kthreads_active = ATOMIC_INIT(0);
250250
#define console_kthread_printing_exit() \
251251
atomic_dec(&console_kthreads_active)
252252

253+
/* Block console kthreads to avoid processing new messages. */
254+
bool block_console_kthreads;
255+
253256
/*
254257
* Helper macros to handle lockdep when locking/unlocking console_sem. We use
255258
* macros instead of functions so that _RET_IP_ contains useful information.
@@ -3730,6 +3733,7 @@ static bool printer_should_wake(struct console *con, u64 seq)
37303733

37313734
if (con->blocked ||
37323735
console_kthreads_atomically_blocked() ||
3736+
block_console_kthreads ||
37333737
system_state > SYSTEM_RUNNING ||
37343738
oops_in_progress) {
37353739
return false;

kernel/printk/printk_safe.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
#include <linux/smp.h>
99
#include <linux/cpumask.h>
1010
#include <linux/printk.h>
11+
#include <linux/console.h>
1112
#include <linux/kprobes.h>
13+
#include <linux/delay.h>
1214

1315
#include "internal.h"
1416

@@ -50,3 +52,33 @@ asmlinkage int vprintk(const char *fmt, va_list args)
5052
return vprintk_default(fmt, args);
5153
}
5254
EXPORT_SYMBOL(vprintk);
55+
56+
/**
57+
* try_block_console_kthreads() - Try to block console kthreads and
58+
* make the global console_lock() avaialble
59+
*
60+
* @timeout_ms: The maximum time (in ms) to wait.
61+
*
62+
* Prevent console kthreads from starting processing new messages. Wait
63+
* until the global console_lock() become available.
64+
*
65+
* Context: Can be called in any context.
66+
*/
67+
void try_block_console_kthreads(int timeout_ms)
68+
{
69+
block_console_kthreads = true;
70+
71+
/* Do not wait when the console lock could not be safely taken. */
72+
if (this_cpu_read(printk_context) || in_nmi())
73+
return;
74+
75+
while (timeout_ms > 0) {
76+
if (console_trylock()) {
77+
console_unlock();
78+
return;
79+
}
80+
81+
udelay(1000);
82+
timeout_ms -= 1;
83+
}
84+
}

kernel/reboot.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ void kernel_restart_prepare(char *cmd)
7474
{
7575
blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);
7676
system_state = SYSTEM_RESTART;
77+
try_block_console_kthreads(10000);
7778
usermodehelper_disable();
7879
device_shutdown();
7980
}
@@ -262,6 +263,7 @@ static void kernel_shutdown_prepare(enum system_states state)
262263
blocking_notifier_call_chain(&reboot_notifier_list,
263264
(state == SYSTEM_HALT) ? SYS_HALT : SYS_POWER_OFF, NULL);
264265
system_state = state;
266+
try_block_console_kthreads(10000);
265267
usermodehelper_disable();
266268
device_shutdown();
267269
}

0 commit comments

Comments
 (0)