From: John Ogness Date: Wed, 22 Nov 2023 11:56:58 +0000 Subject: [PATCH 24/46] printk: Coordinate direct printing in panic Origin: https://www.kernel.org/pub/linux/kernel/projects/rt/6.9/older/patches-6.9-rt5.tar.xz Perform printing by nbcon consoles on the panic CPU from the printk() caller context in order to get panic messages printed as soon as possible. If legacy and nbcon consoles are registered, the legacy consoles will no longer perform direct printing on the panic CPU until after the backtrace has been stored. This will give the safe nbcon consoles a chance to print the panic messages before allowing the unsafe legacy consoles to print. If no nbcon consoles are registered, there is no change in behavior (i.e. legacy consoles will always attempt to print from the printk() caller context). Signed-off-by: John Ogness Reviewed-by: Petr Mladek Signed-off-by: Sebastian Andrzej Siewior --- include/linux/printk.h | 5 +++ kernel/panic.c | 2 + kernel/printk/printk.c | 62 ++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 61 insertions(+), 8 deletions(-) --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -195,6 +195,7 @@ void show_regs_print_info(const char *lo extern asmlinkage void dump_stack_lvl(const char *log_lvl) __cold; extern asmlinkage void dump_stack(void) __cold; void printk_trigger_flush(void); +void printk_legacy_allow_panic_sync(void); extern bool nbcon_driver_try_acquire(struct console *con); extern void nbcon_driver_release(struct console *con); void nbcon_atomic_flush_unsafe(void); @@ -278,6 +279,10 @@ static inline void printk_trigger_flush( { } +static inline void printk_legacy_allow_panic_sync(void) +{ +} + static inline bool nbcon_driver_try_acquire(struct console *con) { return false; --- a/kernel/panic.c +++ b/kernel/panic.c @@ -368,6 +368,8 @@ void panic(const char *fmt, ...) panic_other_cpus_shutdown(_crash_kexec_post_notifiers); + printk_legacy_allow_panic_sync(); + /* * Run any panic handlers, including those that might need to * add information to the kmsg dump output. --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -471,7 +471,9 @@ static DEFINE_MUTEX(syslog_lock); static bool have_legacy_console; /* - * Specifies if an nbcon console is registered. + * Specifies if an nbcon console is registered. If nbcon consoles are present, + * synchronous printing of legacy consoles will not occur during panic until + * the backtrace has been stored to the ringbuffer. */ static bool have_nbcon_console; @@ -2330,12 +2332,29 @@ int vprintk_store(int facility, int leve return ret; } +static bool legacy_allow_panic_sync; + +/* + * This acts as a one-way switch to allow legacy consoles to print from + * the printk() caller context on a panic CPU. It also attempts to flush + * the legacy consoles in this context. + */ +void printk_legacy_allow_panic_sync(void) +{ + legacy_allow_panic_sync = true; + + if (printing_via_unlock && !in_nmi()) { + if (console_trylock()) + console_unlock(); + } +} + asmlinkage int vprintk_emit(int facility, int level, const struct dev_printk_info *dev_info, const char *fmt, va_list args) { + bool do_trylock_unlock = printing_via_unlock; int printed_len; - bool in_sched = false; /* Suppress unimportant messages after panic happens */ if (unlikely(suppress_printk)) @@ -2351,15 +2370,42 @@ asmlinkage int vprintk_emit(int facility if (level == LOGLEVEL_SCHED) { level = LOGLEVEL_DEFAULT; - in_sched = true; + /* If called from the scheduler, we can not call up(). */ + do_trylock_unlock = false; } printk_delay(level); printed_len = vprintk_store(facility, level, dev_info, fmt, args); - /* If called from the scheduler, we can not call up(). */ - if (!in_sched && printing_via_unlock) { + if (have_nbcon_console && !have_boot_console) { + bool is_panic_context = this_cpu_in_panic(); + + /* + * In panic, the legacy consoles are not allowed to print from + * the printk calling context unless explicitly allowed. This + * gives the safe nbcon consoles a chance to print out all the + * panic messages first. This restriction only applies if + * there are nbcon consoles registered. + */ + if (is_panic_context) + do_trylock_unlock &= legacy_allow_panic_sync; + + /* + * There are situations where nbcon atomic printing should + * happen in the printk() caller context: + * + * - When this CPU is in panic. + * + * Note that if boot consoles are registered, the console + * lock/unlock dance must be relied upon instead because nbcon + * consoles cannot print simultaneously with boot consoles. + */ + if (is_panic_context) + nbcon_atomic_flush_pending(); + } + + if (do_trylock_unlock) { /* * The caller may be holding system-critical or * timing-sensitive locks. Disable preemption during @@ -2379,10 +2425,10 @@ asmlinkage int vprintk_emit(int facility preempt_enable(); } - if (in_sched) - defer_console_output(); - else + if (do_trylock_unlock) wake_up_klogd(); + else + defer_console_output(); return printed_len; }