Skip to content

Commit 26873e3

Browse files
jognesspmladek
authored andcommitted
printk: Avoid scheduling irq_work on suspend
Allowing irq_work to be scheduled while trying to suspend has shown to cause problems as some architectures interpret the pending interrupts as a reason to not suspend. This became a problem for printk() with the introduction of NBCON consoles. With every printk() call, NBCON console printing kthreads are woken by queueing irq_work. This means that irq_work continues to be queued due to printk() calls late in the suspend procedure. Avoid this problem by preventing printk() from queueing irq_work once console suspending has begun. This applies to triggering NBCON and legacy deferred printing as well as klogd waiters. Since triggering of NBCON threaded printing relies on irq_work, the pr_flush() within console_suspend_all() is used to perform the final flushing before suspending consoles and blocking irq_work queueing. NBCON consoles that are not suspended (due to the usage of the "no_console_suspend" boot argument) transition to atomic flushing. Introduce a new global variable @console_irqwork_blocked to flag when irq_work queueing is to be avoided. The flag is used by printk_get_console_flush_type() to avoid allowing deferred printing and switch NBCON consoles to atomic flushing. It is also used by vprintk_emit() to avoid klogd waking. Add WARN_ON_ONCE(console_irqwork_blocked) to the irq_work queuing functions to catch any code that attempts to queue printk irq_work during the suspending/resuming procedure. Cc: stable@vger.kernel.org # 6.13.x because no drivers in 6.12.x Fixes: 6b93bb4 ("printk: Add non-BKL (nbcon) console basic infrastructure") Closes: https://lore.kernel.org/lkml/DB9PR04MB8429E7DDF2D93C2695DE401D92C4A@DB9PR04MB8429.eurprd04.prod.outlook.com Signed-off-by: John Ogness <john.ogness@linutronix.de> Reviewed-by: Petr Mladek <pmladek@suse.com> Tested-by: Sherry Sun <sherry.sun@nxp.com> Link: https://patch.msgid.link/20251113160351.113031-3-john.ogness@linutronix.de Signed-off-by: Petr Mladek <pmladek@suse.com>
1 parent d01ff28 commit 26873e3

3 files changed

Lines changed: 55 additions & 18 deletions

File tree

kernel/printk/internal.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,8 @@ struct console_flush_type {
230230
bool legacy_offload;
231231
};
232232

233+
extern bool console_irqwork_blocked;
234+
233235
/*
234236
* Identify which console flushing methods should be used in the context of
235237
* the caller.
@@ -241,7 +243,7 @@ static inline void printk_get_console_flush_type(struct console_flush_type *ft)
241243
switch (nbcon_get_default_prio()) {
242244
case NBCON_PRIO_NORMAL:
243245
if (have_nbcon_console && !have_boot_console) {
244-
if (printk_kthreads_running)
246+
if (printk_kthreads_running && !console_irqwork_blocked)
245247
ft->nbcon_offload = true;
246248
else
247249
ft->nbcon_atomic = true;
@@ -251,7 +253,7 @@ static inline void printk_get_console_flush_type(struct console_flush_type *ft)
251253
if (have_legacy_console || have_boot_console) {
252254
if (!is_printk_legacy_deferred())
253255
ft->legacy_direct = true;
254-
else
256+
else if (!console_irqwork_blocked)
255257
ft->legacy_offload = true;
256258
}
257259
break;
@@ -264,7 +266,7 @@ static inline void printk_get_console_flush_type(struct console_flush_type *ft)
264266
if (have_legacy_console || have_boot_console) {
265267
if (!is_printk_legacy_deferred())
266268
ft->legacy_direct = true;
267-
else
269+
else if (!console_irqwork_blocked)
268270
ft->legacy_offload = true;
269271
}
270272
break;

kernel/printk/nbcon.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,13 @@ void nbcon_kthreads_wake(void)
12761276
if (!printk_kthreads_running)
12771277
return;
12781278

1279+
/*
1280+
* It is not allowed to call this function when console irq_work
1281+
* is blocked.
1282+
*/
1283+
if (WARN_ON_ONCE(console_irqwork_blocked))
1284+
return;
1285+
12791286
cookie = console_srcu_read_lock();
12801287
for_each_console_srcu(con) {
12811288
if (!(console_srcu_read_flags(con) & CON_NBCON))

kernel/printk/printk.c

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,9 @@ bool have_boot_console;
462462
/* See printk_legacy_allow_panic_sync() for details. */
463463
bool legacy_allow_panic_sync;
464464

465+
/* Avoid using irq_work when suspending. */
466+
bool console_irqwork_blocked;
467+
465468
#ifdef CONFIG_PRINTK
466469
DECLARE_WAIT_QUEUE_HEAD(log_wait);
467470
static DECLARE_WAIT_QUEUE_HEAD(legacy_wait);
@@ -2426,7 +2429,7 @@ asmlinkage int vprintk_emit(int facility, int level,
24262429

24272430
if (ft.legacy_offload)
24282431
defer_console_output();
2429-
else
2432+
else if (!console_irqwork_blocked)
24302433
wake_up_klogd();
24312434

24322435
return printed_len;
@@ -2730,10 +2733,20 @@ void console_suspend_all(void)
27302733
{
27312734
struct console *con;
27322735

2736+
if (console_suspend_enabled)
2737+
pr_info("Suspending console(s) (use no_console_suspend to debug)\n");
2738+
2739+
/*
2740+
* Flush any console backlog and then avoid queueing irq_work until
2741+
* console_resume_all(). Until then deferred printing is no longer
2742+
* triggered, NBCON consoles transition to atomic flushing, and
2743+
* any klogd waiters are not triggered.
2744+
*/
2745+
pr_flush(1000, true);
2746+
console_irqwork_blocked = true;
2747+
27332748
if (!console_suspend_enabled)
27342749
return;
2735-
pr_info("Suspending console(s) (use no_console_suspend to debug)\n");
2736-
pr_flush(1000, true);
27372750

27382751
console_list_lock();
27392752
for_each_console(con)
@@ -2754,26 +2767,34 @@ void console_resume_all(void)
27542767
struct console_flush_type ft;
27552768
struct console *con;
27562769

2757-
if (!console_suspend_enabled)
2758-
return;
2759-
2760-
console_list_lock();
2761-
for_each_console(con)
2762-
console_srcu_write_flags(con, con->flags & ~CON_SUSPENDED);
2763-
console_list_unlock();
2764-
27652770
/*
2766-
* Ensure that all SRCU list walks have completed. All printing
2767-
* contexts must be able to see they are no longer suspended so
2768-
* that they are guaranteed to wake up and resume printing.
2771+
* Allow queueing irq_work. After restoring console state, deferred
2772+
* printing and any klogd waiters need to be triggered in case there
2773+
* is now a console backlog.
27692774
*/
2770-
synchronize_srcu(&console_srcu);
2775+
console_irqwork_blocked = false;
2776+
2777+
if (console_suspend_enabled) {
2778+
console_list_lock();
2779+
for_each_console(con)
2780+
console_srcu_write_flags(con, con->flags & ~CON_SUSPENDED);
2781+
console_list_unlock();
2782+
2783+
/*
2784+
* Ensure that all SRCU list walks have completed. All printing
2785+
* contexts must be able to see they are no longer suspended so
2786+
* that they are guaranteed to wake up and resume printing.
2787+
*/
2788+
synchronize_srcu(&console_srcu);
2789+
}
27712790

27722791
printk_get_console_flush_type(&ft);
27732792
if (ft.nbcon_offload)
27742793
nbcon_kthreads_wake();
27752794
if (ft.legacy_offload)
27762795
defer_console_output();
2796+
else
2797+
wake_up_klogd();
27772798

27782799
pr_flush(1000, true);
27792800
}
@@ -4511,6 +4532,13 @@ static void __wake_up_klogd(int val)
45114532
if (!printk_percpu_data_ready())
45124533
return;
45134534

4535+
/*
4536+
* It is not allowed to call this function when console irq_work
4537+
* is blocked.
4538+
*/
4539+
if (WARN_ON_ONCE(console_irqwork_blocked))
4540+
return;
4541+
45144542
preempt_disable();
45154543
/*
45164544
* Guarantee any new records can be seen by tasks preparing to wait

0 commit comments

Comments
 (0)