Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

printk: nbcon: Rely on kthreads for normal operation

Once the kthread is running and available
(i.e. @printk_kthreads_running is set), the kthread becomes
responsible for flushing any pending messages which are added
in NBCON_PRIO_NORMAL context. Namely the legacy
console_flush_all() and device_release() no longer flush the
console. And nbcon_atomic_flush_pending() used by
nbcon_cpu_emergency_exit() no longer flushes messages added
after the emergency messages.

The console context is safe when used by the kthread only when
one of the following conditions are true:

1. Other caller acquires the console context with
NBCON_PRIO_NORMAL with preemption disabled. It will
release the context before rescheduling.

2. Other caller acquires the console context with
NBCON_PRIO_NORMAL under the device_lock.

3. The kthread is the only context which acquires the console
with NBCON_PRIO_NORMAL.

This is satisfied for all atomic printing call sites:

nbcon_legacy_emit_next_record() (#1)

nbcon_atomic_flush_pending_con() (#1)

nbcon_device_release() (#2)

It is even double guaranteed when @printk_kthreads_running
is set because then _only_ the kthread will print for
NBCON_PRIO_NORMAL. (#3)

Signed-off-by: John Ogness <john.ogness@linutronix.de>
Reviewed-by: Petr Mladek <pmladek@suse.com>
Link: https://lore.kernel.org/r/20240904120536.115780-10-john.ogness@linutronix.de
Signed-off-by: Petr Mladek <pmladek@suse.com>

authored by

John Ogness and committed by
Petr Mladek
13189fa7 5c586baa

+84 -7
+26
kernel/printk/internal.h
··· 113 113 /* The write_atomic() callback is optional. */ 114 114 if (use_atomic && !con->write_atomic) 115 115 return false; 116 + 117 + /* 118 + * For the !use_atomic case, @printk_kthreads_running is not 119 + * checked because the write_thread() callback is also used 120 + * via the legacy loop when the printer threads are not 121 + * available. 122 + */ 116 123 } else { 117 124 if (!con->write) 118 125 return false; ··· 183 176 static inline bool nbcon_legacy_emit_next_record(struct console *con, bool *handover, 184 177 int cookie, bool use_atomic) { return false; } 185 178 static inline void nbcon_kthread_wake(struct console *con) { } 179 + static inline void nbcon_kthreads_wake(void) { } 186 180 187 181 static inline bool console_is_usable(struct console *con, short flags, 188 182 bool use_atomic) { return false; } ··· 198 190 /** 199 191 * struct console_flush_type - Define available console flush methods 200 192 * @nbcon_atomic: Flush directly using nbcon_atomic() callback 193 + * @nbcon_offload: Offload flush to printer thread 201 194 * @legacy_direct: Call the legacy loop in this context 202 195 * @legacy_offload: Offload the legacy loop into IRQ 203 196 * ··· 206 197 */ 207 198 struct console_flush_type { 208 199 bool nbcon_atomic; 200 + bool nbcon_offload; 209 201 bool legacy_direct; 210 202 bool legacy_offload; 211 203 }; ··· 221 211 222 212 switch (nbcon_get_default_prio()) { 223 213 case NBCON_PRIO_NORMAL: 214 + if (have_nbcon_console && !have_boot_console) { 215 + if (printk_kthreads_running) 216 + ft->nbcon_offload = true; 217 + else 218 + ft->nbcon_atomic = true; 219 + } 220 + 221 + /* Legacy consoles are flushed directly when possible. */ 222 + if (have_legacy_console || have_boot_console) { 223 + if (!is_printk_legacy_deferred()) 224 + ft->legacy_direct = true; 225 + else 226 + ft->legacy_offload = true; 227 + } 228 + break; 229 + 224 230 case NBCON_PRIO_EMERGENCY: 225 231 if (have_nbcon_console && !have_boot_console) 226 232 ft->nbcon_atomic = true;
+11 -6
kernel/printk/nbcon.c
··· 1494 1494 static void nbcon_atomic_flush_pending_con(struct console *con, u64 stop_seq, 1495 1495 bool allow_unsafe_takeover) 1496 1496 { 1497 + struct console_flush_type ft; 1497 1498 unsigned long flags; 1498 1499 int err; 1499 1500 ··· 1524 1523 1525 1524 /* 1526 1525 * If flushing was successful but more records are available, this 1527 - * context must flush those remaining records because there is no 1528 - * other context that will do it. 1526 + * context must flush those remaining records if the printer thread 1527 + * is not available do it. 1529 1528 */ 1530 - if (prb_read_valid(prb, nbcon_seq_read(con), NULL)) { 1529 + printk_get_console_flush_type(&ft); 1530 + if (!ft.nbcon_offload && 1531 + prb_read_valid(prb, nbcon_seq_read(con), NULL)) { 1531 1532 stop_seq = prb_next_reserve_seq(prb); 1532 1533 goto again; 1533 1534 } ··· 1757 1754 1758 1755 /* 1759 1756 * This context must flush any new records added while the console 1760 - * was locked. The console_srcu_read_lock must be taken to ensure 1761 - * the console is usable throughout flushing. 1757 + * was locked if the printer thread is not available to do it. The 1758 + * console_srcu_read_lock must be taken to ensure the console is 1759 + * usable throughout flushing. 1762 1760 */ 1763 1761 cookie = console_srcu_read_lock(); 1762 + printk_get_console_flush_type(&ft); 1764 1763 if (console_is_usable(con, console_srcu_read_flags(con), true) && 1764 + !ft.nbcon_offload && 1765 1765 prb_read_valid(prb, nbcon_seq_read(con), NULL)) { 1766 1766 /* 1767 1767 * If nbcon_atomic flushing is not available, fallback to 1768 1768 * using the legacy loop. 1769 1769 */ 1770 - printk_get_console_flush_type(&ft); 1771 1770 if (ft.nbcon_atomic) { 1772 1771 __nbcon_atomic_flush_pending_con(con, prb_next_reserve_seq(prb), false); 1773 1772 } else if (ft.legacy_direct) {
+47 -1
kernel/printk/printk.c
··· 2384 2384 if (ft.nbcon_atomic) 2385 2385 nbcon_atomic_flush_pending(); 2386 2386 2387 + if (ft.nbcon_offload) 2388 + nbcon_kthreads_wake(); 2389 + 2387 2390 if (ft.legacy_direct) { 2388 2391 /* 2389 2392 * The caller may be holding system-critical or ··· 2735 2732 2736 2733 void resume_console(void) 2737 2734 { 2735 + struct console_flush_type ft; 2738 2736 struct console *con; 2739 2737 2740 2738 if (!console_suspend_enabled) ··· 2752 2748 * that they are guaranteed to wake up and resume printing. 2753 2749 */ 2754 2750 synchronize_srcu(&console_srcu); 2751 + 2752 + printk_get_console_flush_type(&ft); 2753 + if (ft.nbcon_offload) 2754 + nbcon_kthreads_wake(); 2755 2755 2756 2756 pr_flush(1000, true); 2757 2757 } ··· 3068 3060 */ 3069 3061 static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handover) 3070 3062 { 3063 + struct console_flush_type ft; 3071 3064 bool any_usable = false; 3072 3065 struct console *con; 3073 3066 bool any_progress; ··· 3080 3071 do { 3081 3072 any_progress = false; 3082 3073 3074 + printk_get_console_flush_type(&ft); 3075 + 3083 3076 cookie = console_srcu_read_lock(); 3084 3077 for_each_console_srcu(con) { 3085 3078 short flags = console_srcu_read_flags(con); 3086 3079 u64 printk_seq; 3087 3080 bool progress; 3081 + 3082 + /* 3083 + * console_flush_all() is only responsible for nbcon 3084 + * consoles when the nbcon consoles cannot print via 3085 + * their atomic or threaded flushing. 3086 + */ 3087 + if ((flags & CON_NBCON) && (ft.nbcon_atomic || ft.nbcon_offload)) 3088 + continue; 3088 3089 3089 3090 if (!console_is_usable(con, flags, !do_cond_resched)) 3090 3091 continue; ··· 3406 3387 3407 3388 void console_start(struct console *console) 3408 3389 { 3390 + struct console_flush_type ft; 3391 + bool is_nbcon; 3392 + 3409 3393 console_list_lock(); 3410 3394 console_srcu_write_flags(console, console->flags | CON_ENABLED); 3395 + is_nbcon = console->flags & CON_NBCON; 3411 3396 console_list_unlock(); 3397 + 3398 + /* 3399 + * Ensure that all SRCU list walks have completed. The related 3400 + * printing context must be able to see it is enabled so that 3401 + * it is guaranteed to wake up and resume printing. 3402 + */ 3403 + synchronize_srcu(&console_srcu); 3404 + 3405 + printk_get_console_flush_type(&ft); 3406 + if (is_nbcon && ft.nbcon_offload) 3407 + nbcon_kthread_wake(console); 3408 + 3412 3409 __pr_flush(console, 1000, true); 3413 3410 } 3414 3411 EXPORT_SYMBOL(console_start); ··· 4150 4115 4151 4116 /* Flush the consoles so that records up to @seq are printed. */ 4152 4117 printk_get_console_flush_type(&ft); 4118 + if (ft.nbcon_atomic) 4119 + nbcon_atomic_flush_pending(); 4153 4120 if (ft.legacy_direct) { 4154 4121 console_lock(); 4155 4122 console_unlock(); ··· 4189 4152 * that they make forward progress, so only increment 4190 4153 * @diff for usable consoles. 4191 4154 */ 4192 - if (!console_is_usable(c, flags, true)) 4155 + if (!console_is_usable(c, flags, true) && 4156 + !console_is_usable(c, flags, false)) { 4193 4157 continue; 4158 + } 4194 4159 4195 4160 if (flags & CON_NBCON) { 4196 4161 printk_seq = nbcon_seq_read(c); ··· 4668 4629 */ 4669 4630 void console_try_replay_all(void) 4670 4631 { 4632 + struct console_flush_type ft; 4633 + 4634 + printk_get_console_flush_type(&ft); 4671 4635 if (console_trylock()) { 4672 4636 __console_rewind_all(); 4637 + if (ft.nbcon_atomic) 4638 + nbcon_atomic_flush_pending(); 4639 + if (ft.nbcon_offload) 4640 + nbcon_kthreads_wake(); 4673 4641 /* Consoles are flushed as part of console_unlock(). */ 4674 4642 console_unlock(); 4675 4643 }