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: Implement legacy printer kthread for PREEMPT_RT

The write() callback of legacy consoles usually makes use of
spinlocks. This is not permitted with PREEMPT_RT in atomic
contexts.

For PREEMPT_RT, create a new kthread to handle printing of all
the legacy consoles (and nbcon consoles if boot consoles are
registered). This allows legacy consoles to work on PREEMPT_RT
without requiring modification. (However they will not have
the reliability properties guaranteed by nbcon atomic
consoles.)

Use the existing printk_kthreads_check_locked() to start/stop
the legacy kthread as needed.

Introduce the macro force_legacy_kthread() to query if the
forced threading of legacy consoles is in effect. Although
currently only enabled for PREEMPT_RT, this acts as a simple
mechanism for the future to allow other preemption models to
easily take advantage of the non-interference property provided
by the legacy kthread.

When force_legacy_kthread() is true, the legacy kthread
fulfills the role of the console_flush_type @legacy_offload by
waking the legacy kthread instead of printing via the
console_lock in the irq_work. If the legacy kthread is not
yet available, no legacy printing takes place (unless in
panic).

If for some reason the legacy kthread fails to create, any
legacy consoles are unregistered. With force_legacy_kthread(),
the legacy kthread is a critical component for legacy consoles.

These changes only affect CONFIG_PREEMPT_RT.

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

authored by

John Ogness and committed by
Petr Mladek
5f53ca3f def84b44

+159 -18
+15 -1
kernel/printk/internal.h
··· 21 21 (con->flags & CON_BOOT) ? "boot" : "", \ 22 22 con->name, con->index, ##__VA_ARGS__) 23 23 24 + /* 25 + * Identify if legacy printing is forced in a dedicated kthread. If 26 + * true, all printing via console lock occurs within a dedicated 27 + * legacy printer thread. The only exception is on panic, after the 28 + * nbcon consoles have had their chance to print the panic messages 29 + * first. 30 + */ 31 + #ifdef CONFIG_PREEMPT_RT 32 + # define force_legacy_kthread() (true) 33 + #else 34 + # define force_legacy_kthread() (false) 35 + #endif 36 + 24 37 #ifdef CONFIG_PRINTK 25 38 26 39 #ifdef CONFIG_PRINTK_CALLER ··· 186 173 #define printk_safe_exit_irqrestore(flags) local_irq_restore(flags) 187 174 188 175 static inline bool printk_percpu_data_ready(void) { return false; } 176 + static inline void defer_console_output(void) { } 189 177 static inline bool is_printk_legacy_deferred(void) { return false; } 190 178 static inline u64 nbcon_seq_read(struct console *con) { return 0; } 191 179 static inline void nbcon_seq_force(struct console *con, u64 seq) { } ··· 214 200 * @nbcon_atomic: Flush directly using nbcon_atomic() callback 215 201 * @nbcon_offload: Offload flush to printer thread 216 202 * @legacy_direct: Call the legacy loop in this context 217 - * @legacy_offload: Offload the legacy loop into IRQ 203 + * @legacy_offload: Offload the legacy loop into IRQ or legacy thread 218 204 * 219 205 * Note that the legacy loop also flushes the nbcon consoles. 220 206 */
+141 -16
kernel/printk/printk.c
··· 491 491 492 492 #ifdef CONFIG_PRINTK 493 493 DECLARE_WAIT_QUEUE_HEAD(log_wait); 494 + static DECLARE_WAIT_QUEUE_HEAD(legacy_wait); 494 495 /* All 3 protected by @syslog_lock. */ 495 496 /* the next printk record to read by syslog(READ) or /proc/kmsg */ 496 497 static u64 syslog_seq; ··· 2757 2756 printk_get_console_flush_type(&ft); 2758 2757 if (ft.nbcon_offload) 2759 2758 nbcon_kthreads_wake(); 2759 + if (ft.legacy_offload) 2760 + defer_console_output(); 2760 2761 2761 2762 pr_flush(1000, true); 2762 2763 } ··· 3169 3166 return false; 3170 3167 } 3171 3168 3172 - /** 3173 - * console_unlock - unblock the console subsystem from printing 3174 - * 3175 - * Releases the console_lock which the caller holds to block printing of 3176 - * the console subsystem. 3177 - * 3178 - * While the console_lock was held, console output may have been buffered 3179 - * by printk(). If this is the case, console_unlock(); emits 3180 - * the output prior to releasing the lock. 3181 - * 3182 - * console_unlock(); may be called from any context. 3183 - */ 3184 - void console_unlock(void) 3169 + static void __console_flush_and_unlock(void) 3185 3170 { 3186 3171 bool do_cond_resched; 3187 3172 bool handover; ··· 3212 3221 * fails, another context is already handling the printing. 3213 3222 */ 3214 3223 } while (prb_read_valid(prb, next_seq, NULL) && console_trylock()); 3224 + } 3225 + 3226 + /** 3227 + * console_unlock - unblock the legacy console subsystem from printing 3228 + * 3229 + * Releases the console_lock which the caller holds to block printing of 3230 + * the legacy console subsystem. 3231 + * 3232 + * While the console_lock was held, console output may have been buffered 3233 + * by printk(). If this is the case, console_unlock() emits the output on 3234 + * legacy consoles prior to releasing the lock. 3235 + * 3236 + * console_unlock(); may be called from any context. 3237 + */ 3238 + void console_unlock(void) 3239 + { 3240 + struct console_flush_type ft; 3241 + 3242 + printk_get_console_flush_type(&ft); 3243 + if (ft.legacy_direct) 3244 + __console_flush_and_unlock(); 3245 + else 3246 + __console_unlock(); 3215 3247 } 3216 3248 EXPORT_SYMBOL(console_unlock); 3217 3249 ··· 3463 3449 printk_get_console_flush_type(&ft); 3464 3450 if (is_nbcon && ft.nbcon_offload) 3465 3451 nbcon_kthread_wake(console); 3452 + else if (ft.legacy_offload) 3453 + defer_console_output(); 3466 3454 3467 3455 __pr_flush(console, 1000, true); 3468 3456 } ··· 3475 3459 3476 3460 /* True when system boot is far enough to create printer threads. */ 3477 3461 static bool printk_kthreads_ready __ro_after_init; 3462 + 3463 + static struct task_struct *printk_legacy_kthread; 3464 + 3465 + static bool legacy_kthread_should_wakeup(void) 3466 + { 3467 + struct console_flush_type ft; 3468 + struct console *con; 3469 + bool ret = false; 3470 + int cookie; 3471 + 3472 + if (kthread_should_stop()) 3473 + return true; 3474 + 3475 + printk_get_console_flush_type(&ft); 3476 + 3477 + cookie = console_srcu_read_lock(); 3478 + for_each_console_srcu(con) { 3479 + short flags = console_srcu_read_flags(con); 3480 + u64 printk_seq; 3481 + 3482 + /* 3483 + * The legacy printer thread is only responsible for nbcon 3484 + * consoles when the nbcon consoles cannot print via their 3485 + * atomic or threaded flushing. 3486 + */ 3487 + if ((flags & CON_NBCON) && (ft.nbcon_atomic || ft.nbcon_offload)) 3488 + continue; 3489 + 3490 + if (!console_is_usable(con, flags, false)) 3491 + continue; 3492 + 3493 + if (flags & CON_NBCON) { 3494 + printk_seq = nbcon_seq_read(con); 3495 + } else { 3496 + /* 3497 + * It is safe to read @seq because only this 3498 + * thread context updates @seq. 3499 + */ 3500 + printk_seq = con->seq; 3501 + } 3502 + 3503 + if (prb_read_valid(prb, printk_seq, NULL)) { 3504 + ret = true; 3505 + break; 3506 + } 3507 + } 3508 + console_srcu_read_unlock(cookie); 3509 + 3510 + return ret; 3511 + } 3512 + 3513 + static int legacy_kthread_func(void *unused) 3514 + { 3515 + for (;;) { 3516 + wait_event_interruptible(legacy_wait, legacy_kthread_should_wakeup()); 3517 + 3518 + if (kthread_should_stop()) 3519 + break; 3520 + 3521 + console_lock(); 3522 + __console_flush_and_unlock(); 3523 + } 3524 + 3525 + return 0; 3526 + } 3527 + 3528 + static bool legacy_kthread_create(void) 3529 + { 3530 + struct task_struct *kt; 3531 + 3532 + lockdep_assert_console_list_lock_held(); 3533 + 3534 + kt = kthread_run(legacy_kthread_func, NULL, "pr/legacy"); 3535 + if (WARN_ON(IS_ERR(kt))) { 3536 + pr_err("failed to start legacy printing thread\n"); 3537 + return false; 3538 + } 3539 + 3540 + printk_legacy_kthread = kt; 3541 + 3542 + return true; 3543 + } 3478 3544 3479 3545 /** 3480 3546 * printk_kthreads_shutdown - shutdown all threaded printers ··· 3606 3508 3607 3509 if (!printk_kthreads_ready) 3608 3510 return; 3511 + 3512 + if (have_legacy_console || have_boot_console) { 3513 + if (!printk_legacy_kthread && 3514 + force_legacy_kthread() && 3515 + !legacy_kthread_create()) { 3516 + /* 3517 + * All legacy consoles must be unregistered. If there 3518 + * are any nbcon consoles, they will set up their own 3519 + * kthread. 3520 + */ 3521 + hlist_for_each_entry_safe(con, tmp, &console_list, node) { 3522 + if (con->flags & CON_NBCON) 3523 + continue; 3524 + 3525 + unregister_console_locked(con); 3526 + } 3527 + } 3528 + } else if (printk_legacy_kthread) { 3529 + kthread_stop(printk_legacy_kthread); 3530 + printk_legacy_kthread = NULL; 3531 + } 3609 3532 3610 3533 /* 3611 3534 * Printer threads cannot be started as long as any boot console is ··· 4404 4285 int pending = this_cpu_xchg(printk_pending, 0); 4405 4286 4406 4287 if (pending & PRINTK_PENDING_OUTPUT) { 4407 - /* If trylock fails, someone else is doing the printing */ 4408 - if (console_trylock()) 4409 - console_unlock(); 4288 + if (force_legacy_kthread()) { 4289 + if (printk_legacy_kthread) 4290 + wake_up_interruptible(&legacy_wait); 4291 + } else { 4292 + if (console_trylock()) 4293 + console_unlock(); 4294 + } 4410 4295 } 4411 4296 4412 4297 if (pending & PRINTK_PENDING_WAKEUP) ··· 4825 4702 nbcon_atomic_flush_pending(); 4826 4703 if (ft.nbcon_offload) 4827 4704 nbcon_kthreads_wake(); 4705 + if (ft.legacy_offload) 4706 + defer_console_output(); 4828 4707 /* Consoles are flushed as part of console_unlock(). */ 4829 4708 console_unlock(); 4830 4709 }
+3 -1
kernel/printk/printk_safe.c
··· 44 44 * The per-CPU variable @printk_context can be read safely in any 45 45 * context. CPU migration is always disabled when set. 46 46 */ 47 - return (this_cpu_read(printk_context) || in_nmi()); 47 + return (force_legacy_kthread() || 48 + this_cpu_read(printk_context) || 49 + in_nmi()); 48 50 } 49 51 50 52 asmlinkage int vprintk(const char *fmt, va_list args)