Introduce policies for unsafe flushing
Subsystems (11)
John Ogness
maintainercover Introduce policies for unsafe flushing
Until now unsafe flushing is only performed at the very end of panic(), before entering the infinite loop. For other situations (such as panic-kexec or panic-reboot or emergency-reboot) no unsafe flushing is performed. The reasoning for this is because an unsafe flush could lead to fireworks, which could prevent the kexec or reboot from occurring.
However, some users may be willing to take on such risks in order to see panic output. Indeed, for the netconsole (which does not support safe atomic printing) it is a regression that such unsafe printing does not occur. [0]
This series adds unsafe flushing policy flags per-console to allow individual consoles to perform unsafe flushing for such scenarios. The unsafe flushing is hard-code-enabled for the netconsole in patch 6/6. The justification is that this is not less safe than the legacy netconsole.
Later these policy flags could be easily exposed to userspace via /sys/class/consoles/, once the sysfs per-console interface is available.
John Ogness
[0] https://lore.kernel.org/lkml/[email protected]
John Ogness (6):
printk: Introduce and specify flush reasons
console: Introduce unsafe flush policy flags
printk: Evaluate flush policy in nbcon_allow_unsafe_takeover()
printk: Remove nbcon_atomic_flush_unsafe()
printk: Add panic+kexec flush point
netconsole: Always perform unsafe flushing on panic/kdb/restart
arch/powerpc/kernel/watchdog.c | 2 +- drivers/net/netconsole.c | 4 ++ include/linux/console.h | 37 ++++++++--- include/linux/printk.h | 27 ++++++-- kernel/crash_core.c | 1 + kernel/panic.c | 2 +- kernel/printk/internal.h | 4 +- kernel/printk/nbcon.c | 117 ++++++++++++++++++++++----------- kernel/printk/printk.c | 16 ++--- kernel/reboot.c | 2 +- lib/nmi_backtrace.c | 2 +- 11 files changed, 146 insertions(+), 68 deletions(-)
base-commit: d01f86a7da8161aac8c6304b377cf58fb75861ca
Signature · 1 line
2.47.3
John Ogness
maintainerPATCH 1/6 printk: Introduce and specify flush reasons
Prepare for per-console atomic flushing policies by introducing enum pr_flush_reason to classify various reasons for performing a printk backlog flush using printk_trigger_flush().
Propagate the reason down to __nbcon_atomic_flush_pending_con(), where the actually atomic flushing is executed.
Update all printk_trigger_flush() call sites as well as an explicit lower-level atomic flush calls to specify the reason.
Note that PR_FLUSH_RUNTIME is used for cases where the reason for the flush is not clearly defined.
No functional change.
--- arch/powerpc/kernel/watchdog.c | 2 +- include/linux/printk.h | 22 ++++++++++++++++++++-- kernel/printk/internal.h | 4 ++-- kernel/printk/nbcon.c | 28 ++++++++++++++++++---------- kernel/printk/printk.c | 16 ++++++++-------- kernel/reboot.c | 2 +- lib/nmi_backtrace.c | 2 +- 7 files changed, 51 insertions(+), 25 deletions(-)
arch/powerpc/kernel/watchdog.c +1 -1
@@ -365,7 +365,7 @@ static void watchdog_timer_interrupt(int cpu)* * See wd_lockup_ipi. */ -printk_trigger_flush (); +printk_trigger_flush (PR_FLUSH_RUNTIME ); } }
include/linux/printk.h +20 -2
@@ -145,6 +145,24 @@ void early_printk(const char *s, ...) { }struct dev_printk_info ; +/** +* enum pr_flush_reason - Reason for a flush +* @PR_FLUSH_RUNTIME: A flush while the kernel is running +* @PR_FLUSH_RESTART: The kernel is ending, but another instance should +* follow (reboot, kexec) +* @PR_FLUSH_HANG: The kernel is ending and will enter an infinite loop +* @PR_FLUSH_KDB: Printing while KDB is active +* +* Flush reasons are used to determine if unsafe atomic flushing should be +* performed, based on individual console policies. +*/ +enum pr_flush_reason { +PR_FLUSH_RUNTIME , +PR_FLUSH_RESTART , +PR_FLUSH_HANG , +PR_FLUSH_KDB , +}; +#ifdef CONFIG_PRINTK asmlinkage __printf(4, 0) int vprintk_emit(int facility, int level,
@@ -202,7 +220,7 @@ void dump_stack_print_info(const char *log_lvl);void show_regs_print_info (const char *log_lvl); 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_trigger_flush (enum pr_flush_reason reason);void console_try_replay_all (void );void printk_legacy_allow_panic_sync (void );extern bool nbcon_device_try_acquire (struct console *con);
@@ -298,7 +316,7 @@ static inline void dump_stack_lvl(const char *log_lvl)static inline void dump_stack (void ) { } -static inline void printk_trigger_flush (void ) +static inline void printk_trigger_flush (enum pr_flush_reason reason) { }static inline void console_try_replay_all (void )
kernel/printk/internal.h +2 -2
@@ -104,7 +104,7 @@ void nbcon_seq_force(struct console *con, u64 seq);bool nbcon_alloc (struct console *con);void nbcon_free (struct console *con);enum nbcon_prio nbcon_get_default_prio (void ); -void nbcon_atomic_flush_pending (void ); +void nbcon_atomic_flush_pending (enum pr_flush_reason reason);bool nbcon_legacy_emit_next_record (struct console *con,bool *handover,int cookie,bool use_atomic);bool nbcon_kthread_create (struct console *con);
@@ -156,7 +156,7 @@ static inline void nbcon_seq_force(struct console *con, u64 seq) { }static inline bool nbcon_alloc (struct console *con) {return false; }static inline void nbcon_free (struct console *con) { }static inline enum nbcon_prio nbcon_get_default_prio (void ) {return NBCON_PRIO_NONE ; } -static inline void nbcon_atomic_flush_pending (void ) { } +static inline void nbcon_atomic_flush_pending (enum pr_flush_reason reason) { }static inline bool nbcon_legacy_emit_next_record (struct console *con,bool *handover,int cookie,bool use_atomic) {return false; }static inline void nbcon_kthread_wake (struct console *con) { }
kernel/printk/nbcon.c +18 -10
@@ -1542,6 +1542,7 @@ bool nbcon_legacy_emit_next_record(struct console *con, bool *handover,* __nbcon_atomic_flush_pending_con - Flush specified nbcon console using its * write_atomic() callback * @con: The nbcon console to flush +* @reason: The reason for the flush * @stop_seq: Flush up until this record * * Return: 0 if @con was flushed up to @stop_seq Otherwise, error code on
@@ -1561,7 +1562,8 @@ bool nbcon_legacy_emit_next_record(struct console *con, bool *handover,* returned, it cannot be expected that the unfinalized record will become * available. */ -static int __nbcon_atomic_flush_pending_con (struct console *con,u64 stop_seq) +static int __nbcon_atomic_flush_pending_con (struct console *con,enum pr_flush_reason reason, +u64 stop_seq) {struct nbcon_write_context wctxt = { };struct nbcon_context *ctxt = &ACCESS_PRIVATE (&wctxt, ctxt);
@@ -1610,6 +1612,7 @@ static int __nbcon_atomic_flush_pending_con(struct console *con, u64 stop_seq)* nbcon_atomic_flush_pending_con - Flush specified nbcon console using its * write_atomic() callback * @con: The nbcon console to flush +* @reason: The reason for the flush * @stop_seq: Flush up until this record * * This will stop flushing before @stop_seq if another context has ownership.
@@ -1617,13 +1620,14 @@ static int __nbcon_atomic_flush_pending_con(struct console *con, u64 stop_seq)* are added while this context was flushing and there is no other context * to handle the printing, this context must also flush those records. */ -static void nbcon_atomic_flush_pending_con (struct console *con,u64 stop_seq) +static void nbcon_atomic_flush_pending_con (struct console *con,enum pr_flush_reason reason, +u64 stop_seq) {struct console_flush_type ft;int err; again: - err =__nbcon_atomic_flush_pending_con (con, stop_seq); + err =__nbcon_atomic_flush_pending_con (con, reason, stop_seq);/* * If there was a new owner (-EPERM, -EAGAIN), that context is
@@ -1652,9 +1656,10 @@ static void nbcon_atomic_flush_pending_con(struct console *con, u64 stop_seq)/** * __nbcon_atomic_flush_pending - Flush all nbcon consoles using their * write_atomic() callback +* @reason: The reason for the flush * @stop_seq: Flush up until this record */ -static void __nbcon_atomic_flush_pending (u64 stop_seq) +static void __nbcon_atomic_flush_pending (enum pr_flush_reason reason,u64 stop_seq) {struct console *con;int cookie;
@@ -1672,7 +1677,7 @@ static void __nbcon_atomic_flush_pending(u64 stop_seq)if (nbcon_seq_read (con) >= stop_seq)continue ; -nbcon_atomic_flush_pending_con (con, stop_seq); +nbcon_atomic_flush_pending_con (con, reason, stop_seq); }console_srcu_read_unlock (cookie); }
@@ -1681,14 +1686,16 @@ static void __nbcon_atomic_flush_pending(u64 stop_seq)* nbcon_atomic_flush_pending - Flush all nbcon consoles using their * write_atomic() callback * +* @reason: The reason for the flush +* * Flush the backlog up through the currently newest record. Any new * records added while flushing will not be flushed if there is another * context available to handle the flushing. This is to avoid one CPU * printing unbounded because other CPUs continue to add records. */ -void nbcon_atomic_flush_pending (void ) +void nbcon_atomic_flush_pending (enum pr_flush_reason reason) { -__nbcon_atomic_flush_pending (prb_next_reserve_seq (prb)); +__nbcon_atomic_flush_pending (reason,prb_next_reserve_seq (prb)); }/**
@@ -1701,7 +1708,7 @@ void nbcon_atomic_flush_pending(void)void nbcon_atomic_flush_unsafe (void ) { panic_nbcon_allow_unsafe_takeover = true; -__nbcon_atomic_flush_pending (prb_next_reserve_seq (prb)); +__nbcon_atomic_flush_pending (PR_FLUSH_HANG ,prb_next_reserve_seq (prb)); panic_nbcon_allow_unsafe_takeover = false; }
@@ -1931,7 +1938,8 @@ void nbcon_device_release(struct console *con)* using the legacy loop. */ if (ft.nbcon_atomic) { -__nbcon_atomic_flush_pending_con (con,prb_next_reserve_seq (prb)); +__nbcon_atomic_flush_pending_con (con,PR_FLUSH_RUNTIME , +prb_next_reserve_seq (prb)); }else if (ft.legacy_direct) {if (console_trylock ())console_unlock ();
@@ -2001,7 +2009,7 @@ void nbcon_kdb_release(struct nbcon_write_context *wctxt)* The console was locked only when the write_atomic() callback * was usable. */ -__nbcon_atomic_flush_pending_con (ctxt->console,prb_next_reserve_seq (prb)); +__nbcon_atomic_flush_pending_con (ctxt->console,PR_FLUSH_KDB ,prb_next_reserve_seq (prb)); }/**
kernel/printk/printk.c +8 -8
@@ -2455,7 +2455,7 @@ asmlinkage int vprintk_emit(int facility, int level, printed_len =vprintk_store (facility, level, dev_info, fmt, args);if (ft.nbcon_atomic) -nbcon_atomic_flush_pending (); +nbcon_atomic_flush_pending (PR_FLUSH_RUNTIME );if (ft.nbcon_offload)nbcon_kthreads_wake ();
@@ -2870,7 +2870,7 @@ static int console_cpu_notify(unsigned int cpu)if (!cpuhp_tasks_frozen) {printk_get_console_flush_type (&ft);if (ft.nbcon_atomic) -nbcon_atomic_flush_pending (); +nbcon_atomic_flush_pending (PR_FLUSH_RUNTIME );if (ft.legacy_direct) {if (console_trylock ())console_unlock ();
@@ -3561,7 +3561,7 @@ void console_flush_on_panic(enum con_flush_mode mode)printk_get_console_flush_type (&ft);if (ft.nbcon_atomic) -nbcon_atomic_flush_pending (); +nbcon_atomic_flush_pending (PR_FLUSH_RUNTIME );/* Flush legacy consoles once allowed, even when dangerous. */ if (legacy_allow_panic_sync)
@@ -3775,7 +3775,7 @@ static void printk_kthreads_shutdown(void *data)* The threads may have been stopped while printing a * backlog. Flush any records left over. */ -nbcon_atomic_flush_pending (); +nbcon_atomic_flush_pending (PR_FLUSH_RUNTIME ); }console_list_unlock (); }
@@ -4537,7 +4537,7 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre/* Flush the consoles so that records up to @seq are printed. */ printk_get_console_flush_type (&ft);if (ft.nbcon_atomic) -nbcon_atomic_flush_pending (); +nbcon_atomic_flush_pending (PR_FLUSH_RUNTIME );if (ft.legacy_direct) {console_lock ();console_unlock ();
@@ -4732,13 +4732,13 @@ void defer_console_output(void)* This is best effort. Depending on the system state, console states, * and caller context, no actual flushing may result from this call. */ -void printk_trigger_flush (void ) +void printk_trigger_flush (enum pr_flush_reason reason) {struct console_flush_type ft;printk_get_console_flush_type (&ft);if (ft.nbcon_atomic) -nbcon_atomic_flush_pending (); +nbcon_atomic_flush_pending (reason);if (ft.nbcon_offload)nbcon_kthreads_wake ();if (ft.legacy_direct) {
@@ -5090,7 +5090,7 @@ void console_try_replay_all(void)if (console_trylock ()) {__console_rewind_all ();if (ft.nbcon_atomic) -nbcon_atomic_flush_pending (); +nbcon_atomic_flush_pending (PR_FLUSH_RUNTIME );if (ft.nbcon_offload)nbcon_kthreads_wake ();if (ft.legacy_offload)
kernel/reboot.c +1 -1
@@ -101,7 +101,7 @@ void emergency_restart(void)* the emergency restart. Move them to the atomic mode. */ nbcon_cpu_emergency_enter (); -printk_trigger_flush (); +printk_trigger_flush (PR_FLUSH_RESTART );machine_emergency_restart (); }
lib/nmi_backtrace.c +1 -1
@@ -91,7 +91,7 @@ void nmi_trigger_cpumask_backtrace(const cpumask_t *mask,* Force flush any remote buffers that might be stuck in IRQ context * and therefore could not run their irq_work. */ -printk_trigger_flush (); +printk_trigger_flush (PR_FLUSH_RUNTIME );clear_bit_unlock (0 , &backtrace_flag);put_cpu ();
Signature · 1 line
2.47.3
kernel test robot
build botRe: [PATCH printk 1/6] printk: Introduce and specify flush reasons
kernel test robot noticed the following build warnings:
Hi John,
kernel test robot noticed the following build warnings:
[auto build test WARNING on d01f86a7da8161aac8c6304b377cf58fb75861ca]
url: https://github.com/intel-lab-lkp/linux/commits/John-Ogness/printk-Introduce-and-specify-flush-reasons/20261109-133718 base: d01f86a7da8161aac8c6304b377cf58fb75861ca patch link: https://lore.kernel.org/r/20261109123127.489676-2-john.ogness%40linutronix.de patch subject: [PATCH printk 1/6] printk: Introduce and specify flush reasons config: openrisc-defconfig (https://download.01.org/0day-ci/archive/20261110/[email protected]/config) compiler: or1k-linux-gcc (GCC) 16.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261110/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All warnings (new ones prefixed by >>):
>> Warning: kernel/printk/printk.c:4735 function parameter 'reason' not described in 'printk_trigger_flush' >> Warning: kernel/printk/printk.c:4735 function parameter 'reason' not described in 'printk_trigger_flush'
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
[email protected]
AI reviewRe: [PATCH printk 1/6] printk: Introduce and specify flush reasons
In reply to John OgnessThank you for your contribution! Sashiko AI review found 1 potential issue to consider: - [Low] printk: missing @reason kernel-doc description for printk_trigger_flush() --
Thank you for your contribution! Sashiko AI review found 1 potential issue to consider:
- [Low] printk: missing @reason kernel-doc description for printk_trigger_flush()
--
commit 2671602433b21536db216fd234b44b9f5ac6b234
Author: John Ogness <[email protected]>
printk: Introduce and specify flush reasons
This commit introduces enum pr_flush_reason and propagates it from printk_trigger_flush() and atomic flush callers down to __nbcon_atomic_flush_pending_con().
Quoted 4 lines
kernel/printk/printk.c +0
-0
[ ... ]
kernel/printk/printk.c void defer_console_output(void) · line 4735
Quoted 6 lines
kernel/printk/printk.c +1
-1@@ -4732,13 +4732,13 @@ void defer_console_output(void)* This is best effort. Depending on the system state, console states, * and caller context, no actual flushing may result from this call. */ -void printk_trigger_flush (void ) +void printk_trigger_flush (enum pr_flush_reason reason)
[Severity: Low]
Should the kernel-doc comment above printk_trigger_flush() also document @reason to avoid a W=1 kernel-doc warning?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
John Ogness
maintainerPATCH 2/6 console: Introduce unsafe flush policy flags
In reply to John OgnessPrepare for per-console atomic flushing policies by introducing a new nbcon-specific console field @unsafe_flush_flags to store the unsafe flush policies for a console. The policies are represented by bits, specified by enum nbcon_unsafe_flush_flags.
Currently it is expected that any @unsafe_flush_flags bits are set prior to console registration and do not change while registered.
No functional change.
--- include/linux/console.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
include/linux/console.h +26 -0
@@ -194,6 +194,29 @@ enum cons_flags { CON_NBCON_ATOMIC_UNSAFE = BIT(9), }; +/** +* enum nbcon_unsafe_flush_flags - Unsafe atomic flush policy flags +* @NBCON_PANIC_FLUSH: In panic, unsafe flush before kexec or reboot. +* @NBCON_PANIC_PRINTK: In panic, unsafe flush on each printk(). +* @NBCON_NORMAL_NOTHREAD: During normal operation, when printing +* kthreads are unreliable or have been shutdown, +* unsafe flush on each printk() or on explicit +* flushing. +* @NBCON_KDB: Unsafe flush kdb output. +* +* NBCON unsafe atomic flushing always occurs at the end of panic() before +* the kernel enters an infinite loop. Users may choose to perform unsafe +* flushing in additional scenarios where the current kernel is ending in +* order to increase the chances of seeing console messages, even if it is +* not safe to do so. +*/ +enum nbcon_unsafe_flush_flags { +NBCON_PANIC_FLUSH =BIT (0 ), +NBCON_PANIC_PRINTK =BIT (1 ), +NBCON_NORMAL_NOTHREAD =BIT (2 ), +NBCON_KDB =BIT (3 ), +}; +/** * struct nbcon_state - console state for nbcon consoles * @atom: Compound of the state fields for atomic operations
@@ -326,6 +349,7 @@ struct nbcon_write_context {* @data: Driver private data * @node: hlist node for the console list * +* @unsafe_flush_flags: Unsafe atomic flush policy. See enum nbcon_unsafe_flush_flags * @nbcon_state: State for nbcon consoles * @nbcon_seq: Sequence number of the next record for nbcon to print * @nbcon_device_ctxt: Context available for non-printing operations
@@ -461,6 +485,8 @@ struct console {*/ void (*device_unlock)(struct console *con,unsigned long flags); +int unsafe_flush_flags; +atomic_t __private nbcon_state;atomic_long_t __private nbcon_seq;struct nbcon_context __private nbcon_device_ctxt;
Signature · 1 line
2.47.3
Re: [PATCH printk 2/6] console: Introduce unsafe flush policy flags
Quoted 9 lines · John Ogness
Prepare for per-console atomic flushing policies by introducing a new nbcon-specific console field @unsafe_flush_flags to store the unsafe flush policies for a console. The policies are represented by bits, specified by enum
nbcon_unsafe_flush_flags.Currently it is expected that any @unsafe_flush_flags bits are set prior to console registration and do not change while registered.
No functional change.
...
include/linux/console.h struct console { · line 488
Quoted 1 line
include/linux/console.h +1
-0+int unsafe_flush_flags;
Why are flags signed?
Signature · 2 lines
With Best Regards, Andy Shevchenko
John Ogness
maintainerRe: [PATCH printk 2/6] console: Introduce unsafe flush policy flags
Quoted 9 lines · Andy Shevchenko
Quoted 8 lines · John Ogness
Prepare for per-console atomic flushing policies by introducing a new nbcon-specific console field @unsafe_flush_flags to store the unsafe flush policies for a console. The policies are represented by bits, specified by enum
nbcon_unsafe_flush_flags.Currently it is expected that any @unsafe_flush_flags bits are set prior to console registration and do not change while registered.
Quoted 7 lines
Quoted 1 line
No functional change.
...
include/linux/console.h struct console { · line 488
Quoted 1 line
include/linux/console.h +1
-0+int unsafe_flush_flags;Why are flags signed?
It is not relevant and all other flags in struct console are signed, so I figured it would blend in. I will change it to unsigned for v2.
Thanks for taking a look.
John
John Ogness
maintainerPATCH 3/6 printk: Evaluate flush policy in nbcon_allow_unsafe_takeover()
In reply to John OgnessCurrently nbcon_allow_unsafe_takeover() is a hack, relying on the temporary setting of a global variable @panic_nbcon_allow_unsafe_takeover as a gate to determine if unsafe flushing is allowed. This hack works because unsafe flushing is only performed at the end of panic(). However, this hack is _not_ sufficient if unsafe flushing is to be allowed in other contexts.
Allow unsafe flushing in other contexts by modifying nbcon_allow_unsafe_takeover() to take a console a flush reason as input and, based on the unsafe flush policies of that console, determine if unsafe flushing is allowed. This also allows the global variable @panic_nbcon_allow_unsafe_takeover to be removed.
Remove the hard-coded policy for CON_NBCON_ATOMIC_UNSAFE from console_is_usable() and implement that policy logic within nbcon_allow_unsafe_takeover().
Note that this changes the flushing flow for CON_NBCON_ATOMIC_UNSAFE consoles. Rather than console_is_usable() reporting that the console is not usable, __nbcon_atomic_flush_pending_con() will report that it is unable to acquire ownership.
--- include/linux/console.h | 11 ++---- kernel/printk/nbcon.c | 79 ++++++++++++++++++++++++++++++++--------- 2 files changed, 64 insertions(+), 26 deletions(-)
include/linux/console.h +2 -9
@@ -179,7 +179,8 @@ static inline void con_debug_leave(void) { }* @CON_NBCON: Console can operate outside of the legacy style console_lock * constraints. * @CON_NBCON_ATOMIC_UNSAFE: The write_atomic() callback is not safe and is -* therefore only used by nbcon_atomic_flush_unsafe(). +* therefore only called when allowed by +* nbcon_allow_unsafe_takeover(). */ enum cons_flags {CON_PRINTBUFFER =BIT (0 ),
@@ -637,7 +638,6 @@ extern void nbcon_write_context_set_buf(struct nbcon_write_context *wctxt,extern bool nbcon_enter_unsafe (struct nbcon_write_context *wctxt);extern bool nbcon_exit_unsafe (struct nbcon_write_context *wctxt);extern void nbcon_reacquire_nobuf (struct nbcon_write_context *wctxt); -extern bool nbcon_allow_unsafe_takeover (void );extern bool nbcon_kdb_try_acquire (struct console *con,struct nbcon_write_context *wctxt);extern void nbcon_kdb_release (struct nbcon_write_context *wctxt);
@@ -670,13 +670,6 @@ static inline bool console_is_usable(struct console *con, short flags, bool use_/* The write_atomic() callback is optional. */ if (!con->write_atomic)return false; - -/* -* An unsafe write_atomic() callback is only usable -* when unsafe takeovers are allowed. -*/ -if ((flags &CON_NBCON_ATOMIC_UNSAFE ) && !nbcon_allow_unsafe_takeover ()) -return false; }/*
kernel/printk/nbcon.c +62 -17
110 lines not on this page · raw message
Signature · 1 line
2.47.3
Breno Leitao
maintainerRe: [PATCH printk 3/6] printk: Evaluate flush policy in nbcon_allow_unsafe_takeover()
kernel/printk/nbcon.c static int __nbcon_atomic_flush_pending_con(struct console *con, enum pr_flush_r · line 1611
Quoted 6 lines · John Ogness
kernel/printk/nbcon.c +1
-1@@ -1572,7 +1608,12 @@ static int __nbcon_atomic_flush_pending_con(struct console *con, enum pr_flush_r ctxt->console = con; ctxt->spinwait_max_us =2000 ; ctxt->prio =nbcon_get_default_prio (); - ctxt->allow_unsafe_takeover =nbcon_allow_unsafe_takeover (); + ctxt->allow_unsafe_takeover =nbcon_allow_unsafe_takeover (con, reason);
Thanks for the series, I've tried it here, and I got:
sysrq: Resetting
------------[ cut here ]------------
WARNING: kernel/printk/nbcon.c:561 at nbcon_context_try_acquire+0x72c/0x790, CPU#0: sh/212
Modules linked in: nbcon_hold(OE+)
CPU: 0 UID: 0 PID: 212 Comm: sh Tainted: G OE 7.4.0-rc1-next-20261105upstream-00006-g4170adac69fb #1 PREEMPT(full)
Hardware name: linux,dummy-virt (DT)
pc : nbcon_context_try_acquire+0x72c/0x790
lr : nbcon_context_try_acquire+0x508/0x790
...
Call trace:
nbcon_context_try_acquire+0x72c/0x790 (P)
__nbcon_atomic_flush_pending_con+0x1c0/0x358
nbcon_atomic_flush_pending+0x224/0x390
printk_trigger_flush+0x50/0x1c8
emergency_restart+0x54/0x98
sysrq_handle_reboot+0x5c/0x88
__handle_sysrq+0x298/0x3c0
write_sysrq_trigger+0x178/0x1d0
proc_reg_write+0x148/0x270
vfs_write+0x1d4/0x7c8
ksys_write+0xbc/0x168
__arm64_sys_write+0x88/0xa8
invoke_syscall+0x74/0x188
do_el0_svc+0x108/0x190
el0_svc+0x60/0x1d8
el0t_64_sync_handler+0x68/0xe0
el0t_64_sync+0x198/0x1a0That is coming from:
550 static int nbcon_context_try_acquire_hostile(struct nbcon_context *ctxt,
551 struct nbcon_state *cur)
552 {
553 unsigned int cpu = smp_processor_id();
554 struct console *con = ctxt->console;
555 struct nbcon_state new;
556
557 if (!ctxt->allow_unsafe_takeover)
558 return -EPERM;
559
560 /* Ensure caller is allowed to perform unsafe hostile takeovers. */
561 if (WARN_ON_ONCE(ctxt->prio != NBCON_PRIO_PANIC))
562 return -EPERM;Thanks,
--breno
John Ogness
maintainerRe: [PATCH printk 3/6] printk: Evaluate flush policy in nbcon_allow_unsafe_takeover()
Quoted 9 lines · Breno Leitao
kernel/printk/nbcon.c static int __nbcon_atomic_flush_pending_con(struct console *con, enum pr_flush_r · line 1611
Quoted 6 lines
kernel/printk/nbcon.c +1
-1@@ -1572,7 +1608,12 @@ static int __nbcon_atomic_flush_pending_con(struct console *con, enum pr_flush_r ctxt->console = con; ctxt->spinwait_max_us =2000 ; ctxt->prio =nbcon_get_default_prio (); - ctxt->allow_unsafe_takeover =nbcon_allow_unsafe_takeover (); + ctxt->allow_unsafe_takeover =nbcon_allow_unsafe_takeover (con, reason);Thanks for the series, I've tried it here, and I got:
Thanks for testing.
Quoted 30 lines
sysrq: Resetting
------------[ cut here ]------------ WARNING: kernel/printk/nbcon.c:561 at nbcon_context_try_acquire+0x72c/0x790, CPU#0: sh/212 Modules linked in: nbcon_hold(OE+) CPU: 0 UID: 0 PID: 212 Comm: sh Tainted: G OE 7.4.0-rc1-next-20261105upstream-00006-g4170adac69fb #1 PREEMPT(full) Hardware name: linux,dummy-virt (DT) pc : nbcon_context_try_acquire+0x72c/0x790 lr : nbcon_context_try_acquire+0x508/0x790 ... Call trace: nbcon_context_try_acquire+0x72c/0x790 (P) __nbcon_atomic_flush_pending_con+0x1c0/0x358 nbcon_atomic_flush_pending+0x224/0x390 printk_trigger_flush+0x50/0x1c8 emergency_restart+0x54/0x98 sysrq_handle_reboot+0x5c/0x88 __handle_sysrq+0x298/0x3c0 write_sysrq_trigger+0x178/0x1d0 proc_reg_write+0x148/0x270 vfs_write+0x1d4/0x7c8 ksys_write+0xbc/0x168 __arm64_sys_write+0x88/0xa8 invoke_syscall+0x74/0x188 do_el0_svc+0x108/0x190 el0_svc+0x60/0x1d8 el0t_64_sync_handler+0x68/0xe0 el0t_64_sync+0x198/0x1a0That is coming from:
Quoted 13 lines
550 static int nbcon_context_try_acquire_hostile(struct nbcon_context *ctxt,
551 struct nbcon_state *cur)
552 {
553 unsigned int cpu = smp_processor_id();
554 struct console *con = ctxt->console;
555 struct nbcon_state new;
556
557 if (!ctxt->allow_unsafe_takeover)
558 return -EPERM;
559
560 /* Ensure caller is allowed to perform unsafe hostile takeovers. */
561 if (WARN_ON_ONCE(ctxt->prio != NBCON_PRIO_PANIC))
562 return -EPERM;Wow, you managed to get a hostile takeover. Out of curiousity, do you know how dangerous this really is? Can this basically just result in garbage on the network? Or is there dangers of running into the forest with NULL or garbage pointers? And is the danger acceptable?
@pmladek: I expect we just need to remove the WARN_ON_ONCE() since this series introduces unsafe takeovers when not in panic. Do you agree?
John
Breno Leitao
maintainerRe: [PATCH printk 3/6] printk: Evaluate flush policy in nbcon_allow_unsafe_takeover()
Quoted 17 lines · John Ogness
Quoted 15 lines · Breno Leitao
That is coming from:
550 static int nbcon_context_try_acquire_hostile(struct nbcon_context *ctxt, 551 struct nbcon_state *cur) 552 { 553 unsigned int cpu = smp_processor_id(); 554 struct console *con = ctxt->console; 555 struct nbcon_state new; 556 557 if (!ctxt->allow_unsafe_takeover) 558 return -EPERM; 559 560 /* Ensure caller is allowed to perform unsafe hostile takeovers. */ 561 if (WARN_ON_ONCE(ctxt->prio != NBCON_PRIO_PANIC)) 562 return -EPERM;
Quoted 4 lines
Wow, you managed to get a hostile takeover. Out of curiousity, do you know how dangerous this really is? Can this basically just result in garbage on the network? Or is there dangers of running into the forest with NULL or garbage pointers? And is the danger acceptable?
Sure, I was playing with different scenarios, and this was done with a test module that forces the takeover:
1) Acquire netcon_ext with nbcon_device_try_acquire(), so the console is owned at NORMAL priority and in an unsafe state
2) mdelay() for a few seconds without releasing it (simulating a possible netpoll busy TX queue -- or some other networkdelay)
3) echo b > /proc/sysrq-trigger from another CPU
--breno
[email protected]
AI reviewRe: [PATCH printk 3/6] printk: Evaluate flush policy in nbcon_allow_unsafe_takeover()
In reply to John OgnessThank you for your contribution! Sashiko AI review found 2 potential issues to consider: - [High] printk: nbcon: unguarded unsafe write_atomic() in nbcon_legacy_emit_next_record() - [High] printk…
Thank you for your contribution! Sashiko AI review found 2 potential issues to consider:
- [High] printk: nbcon: unguarded unsafe write_atomic() in nbcon_legacy_emit_next_record()
- [High] printk: nbcon: non-panic allow_unsafe_takeover hits WARN in try_acquire_hostile()
--
commit 9f014302f348a33d58c0b585b64d7f094ae56e2b
Author: John Ogness <[email protected]>
printk: Evaluate flush policy in nbcon_allow_unsafe_takeover()
This commit replaces the panic_nbcon_allow_unsafe_takeover global variable by updating nbcon_allow_unsafe_takeover() to evaluate per-console unsafe flush policy flags for a given flush reason, and moves the CON_NBCON_ATOMIC_UNSAFE check out of console_is_usable() into the nbcon acquire and flush paths.
Quoted 4 lines
include/linux/console.h +0
-0
[ ... ]
Quoted 4 lines
include/linux/console.h +0
-0@@ -670,13 +670,6 @@ static inline bool console_is_usable(struct console *con, short flags, bool use_/* The write_atomic() callback is optional. */ if (!con->write_atomic)return false;
include/linux/console.h static inline bool console_is_usable(struct console *con, short flags, bool use_ · line 675
Quoted 10 lines
include/linux/console.h +0
-7- -/* -* An unsafe write_atomic() callback is only usable -* when unsafe takeovers are allowed. -*/ -if ((flags &CON_NBCON_ATOMIC_UNSAFE ) && !nbcon_allow_unsafe_takeover ()) -return false; }/*
[Severity: High]
Does removing the CON_NBCON_ATOMIC_UNSAFE check from console_is_usable() allow console_flush_one_record() to invoke an unsafe write_atomic() callback during normal runtime?
While __nbcon_atomic_flush_pending_con() and nbcon_kdb_try_acquire() now check CON_NBCON_ATOMIC_UNSAFE, the legacy flushing loop still relies on console_is_usable() when printing to nbcon consoles (for example, while a boot console is registered):
kernel/printk/printk.c:console_flush_one_record() {
...
if (!console_is_usable(con, flags, !do_cond_resched))
continue;
any_usable = true;
if (flags & CON_NBCON) {
progress = nbcon_legacy_emit_next_record(con, handover, cookie,
!do_cond_resched);
...
}When do_cond_resched is false (such as in vprintk_emit() -> console_trylock_spinning() -> console_unlock()), console_is_usable() now returns true for a CON_NBCON_ATOMIC_UNSAFE console and nbcon_legacy_emit_next_record() -> nbcon_emit_one() calls con->write_atomic() without con->device_lock() or a check against nbcon_allow_unsafe_takeover(). For a driver like netconsole, where write_atomic() relies on device_lock() acquiring target_list_lock, this causes unsynchronized traversal of target_list or a deadlock if printk() is called while target_list_lock is already held.
Quoted 91 lines
kernel/printk/nbcon.c +54
-13@@ -1453,24 +1453,60 @@ enum nbcon_prio nbcon_get_default_prio(void)return NBCON_PRIO_NORMAL ; } -/* -* Track if it is allowed to perform unsafe hostile takeovers of console -* ownership. When true, console drivers might perform unsafe actions while -* printing. It is externally available via nbcon_allow_unsafe_takeover(). -*/ -static bool panic_nbcon_allow_unsafe_takeover; -/** * nbcon_allow_unsafe_takeover - Check if unsafe console takeovers are allowed +* for a specified console and reason * -* Return: True, when it is permitted to perform unsafe console printing +* @con: The console to check +* @reason: The reason for the unsafe takeover * -* This is also used by console_is_usable() to determine if it is allowed to -* call write_atomic() callbacks flagged as unsafe (CON_NBCON_ATOMIC_UNSAFE). +* Return: True, when @con is allowed to perform unsafe console printing */ -bool nbcon_allow_unsafe_takeover (void ) +static bool nbcon_allow_unsafe_takeover (struct console *con,enum pr_flush_reason reason) { -return panic_on_this_cpu () && panic_nbcon_allow_unsafe_takeover; +int unsafe_flags = con->unsafe_flush_flags; +bool in_panic =panic_on_this_cpu (); +bool allow = false; + +switch (reason) { +case PR_FLUSH_RUNTIME : +if (in_panic) { +if (unsafe_flags &NBCON_PANIC_PRINTK ) + allow = true; + + }else { +/* Only when the kthreads have been shutdown. */ +if ((unsafe_flags &NBCON_NORMAL_NOTHREAD ) && + !con->kthread && + system_state >SYSTEM_RUNNING ) { + allow = true; + } + } +break ; +case PR_FLUSH_RESTART : +/* kexec, reboot */ + +if (in_panic) { +if (unsafe_flags &NBCON_PANIC_FLUSH ) + allow = true; + + }else { +/* Printing kthreads not reliable. */ +if (unsafe_flags &NBCON_NORMAL_NOTHREAD ) + allow = true; + } +break ; +case PR_FLUSH_HANG : +/* Always unsafe flush before hang. */ + allow = true; +break ; +case PR_FLUSH_KDB : +if (unsafe_flags &NBCON_KDB ) + allow = true; +break ; + } + +return allow; }/** @@ -1572,7 +1608,12 @@ static int __nbcon_atomic_flush_pending_con(struct console *con, enum pr_flush_r ctxt->console = con; ctxt->spinwait_max_us =2000 ; ctxt->prio =nbcon_get_default_prio (); - ctxt->allow_unsafe_takeover =nbcon_allow_unsafe_takeover (); + ctxt->allow_unsafe_takeover =nbcon_allow_unsafe_takeover (con, reason); + +if ((console_srcu_read_flags (con) &CON_NBCON_ATOMIC_UNSAFE ) && + !ctxt->allow_unsafe_takeover) { +return -EPERM ; + }while (nbcon_seq_read (con) < stop_seq) {/*
[ ... ]
kernel/printk/nbcon.c bool nbcon_kdb_try_acquire(struct console *con, · line 2020
Quoted 9 lines
kernel/printk/nbcon.c +3
-2@@ -1976,8 +2015,14 @@ bool nbcon_kdb_try_acquire(struct console *con,struct nbcon_context *ctxt = &ACCESS_PRIVATE (wctxt, ctxt);memset (ctxt,0 ,sizeof (*ctxt)); - ctxt->console = con; - ctxt->prio =NBCON_PRIO_EMERGENCY ; + ctxt->console = con; + ctxt->prio =NBCON_PRIO_EMERGENCY ; + ctxt->allow_unsafe_takeover =nbcon_allow_unsafe_takeover (con,PR_FLUSH_KDB );
[Severity: High]
When nbcon_allow_unsafe_takeover() returns true outside panic (or in nbcon_kdb_try_acquire(), which always sets ctxt->prio to NBCON_PRIO_EMERGENCY), won't a hostile takeover attempt trigger the WARN_ON_ONCE() in nbcon_context_try_acquire_hostile() and fail with -EPERM?
kernel/printk/nbcon.c:nbcon_context_try_acquire_hostile() {
...
if (!ctxt->allow_unsafe_takeover)
return -EPERM;
/* Ensure caller is allowed to perform unsafe hostile takeovers. */
if (WARN_ON_ONCE(ctxt->prio != NBCON_PRIO_PANIC))
return -EPERM;
...
}If a console with NBCON_KDB or NBCON_NORMAL_NOTHREAD set in con->unsafe_flush_flags is held in an unsafe section when KDB or emergency_restart() tries to acquire it, nbcon_context_try_acquire() falls through to nbcon_context_try_acquire_hostile(), hits the warning, and refuses the takeover. In addition, when ctxt->prio is NBCON_PRIO_NORMAL (such as PR_FLUSH_RUNTIME after printk kthreads are shut down), nbcon_context_try_acquire_direct() rejects a console already owned at NBCON_PRIO_NORMAL (ctxt->prio <= cur->prio) with -EPERM before reaching nbcon_context_try_acquire_hostile().
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
John Ogness
maintainerRe: [PATCH printk 3/6] printk: Evaluate flush policy in nbcon_allow_unsafe_takeover()
Quoted 17 lines · On 2026-11-10, [email protected] wrote:
Quoted 17 lines
include/linux/console.h +0
-7@@ -670,13 +670,6 @@ static inline bool console_is_usable(struct console *con, short flags, bool use_/* The write_atomic() callback is optional. */ if (!con->write_atomic)return false; - -/* -* An unsafe write_atomic() callback is only usable -* when unsafe takeovers are allowed. -*/ -if ((flags &CON_NBCON_ATOMIC_UNSAFE ) && !nbcon_allow_unsafe_takeover ()) -return false; }
Quoted 6 lines
include/linux/console.h static inline bool console_is_usable(struct console *con, short flags, bool use_ · line 675
Quoted 1 line
include/linux/console.h +0
-0/* [Severity: High]
Does removing theCON_NBCON_ATOMIC_UNSAFEcheck fromconsole_is_usable()allowconsole_flush_one_record()to invoke an unsafewrite_atomic()callback during normal runtime?
Indeed. nbcon_legacy_emit_next_record() needs to set and check @allow_unsafe_takeover in the atomic case. Something like this:
@@ -1547,6 +1548,12 @@ bool nbcon_legacy_emit_next_record(struct console *con, bool *handover,
ctxt->prio = nbcon_get_default_prio();
if (use_atomic) {
+ ctxt->allow_unsafe_takeover = nbcon_allow_unsafe_takeover(con, printk_legacy_reason);
+ if ((console_srcu_read_flags(con) & CON_NBCON_ATOMIC_UNSAFE) &&
+ !ctxt->allow_unsafe_takeover) {
+ return false;
+ }
+
/*
* In an atomic or unknown context, use the same procedure as
* in console_emit_next_record(). It allows to handover.
Of course, the question becomes, where does @printk_legacy_reason come from? For legacy consoles the whole call chain is horrible and hidden under console_unlock(). So passing in a reason is not feasible. My suggestion is to introduce a horrible hack: a global variable @printk_legacy_reason, initialized to PR_FLUSH_RUNTIME and modified in printk_trigger_flush(). Something like this:
@@ -4743,8 +4749,11 @@ void printk_trigger_flush(enum pr_flush_reason reason)
if (ft.nbcon_offload)
nbcon_kthreads_wake();
if (ft.legacy_direct) {
- if (console_trylock())
+ if (console_trylock()) {
+ printk_legacy_reason = reason;
console_unlock();
+ printk_legacy_reason = PR_FLUSH_RUNTIME;
+ }
}
if (ft.legacy_offload)
defer_console_output();
My justification is that legacy is on its way out, so we should not spend too much effort trying to beautify it. Also, since @printk_legacy_reason is modified and used under the console_lock, this should be good enough.
Thoughts?
John
John Ogness
maintainerPATCH 4/6 printk: Remove nbcon_atomic_flush_unsafe()
In reply to John OgnessThere is no need for the special function nbcon_atomic_flush_unsafe(), which is only called at the end of panic(). Its functionality is replaced with printk_trigger_flush(PR_FLUSH_HANG).
--- include/linux/printk.h | 5 ----- kernel/panic.c | 2 +- kernel/printk/nbcon.c | 12 ------------ 3 files changed, 1 insertion(+), 18 deletions(-)
include/linux/printk.h +0 -5
@@ -225,7 +225,6 @@ void console_try_replay_all(void);void printk_legacy_allow_panic_sync (void );extern bool nbcon_device_try_acquire (struct console *con);extern void nbcon_device_release (struct console *con); -void nbcon_atomic_flush_unsafe (void );bool pr_flush (int timeout_ms,bool reset_on_progress);#else static inline __printf(1, 0)
@@ -336,10 +335,6 @@ static inline void nbcon_device_release(struct console *con) { } -static inline void nbcon_atomic_flush_unsafe (void ) -{ -} -static inline bool pr_flush (int timeout_ms,bool reset_on_progress) {return true;
kernel/panic.c +1 -1
@@ -765,7 +765,7 @@ void vpanic(const char *fmt, va_list args)* Explicitly flush the kernel log buffer one last time. */ console_flush_on_panic (CONSOLE_FLUSH_PENDING ); -nbcon_atomic_flush_unsafe (); +printk_trigger_flush (PR_FLUSH_HANG );local_irq_enable ();for (i =0 ; ; i +=PANIC_TIMER_STEP ) {
kernel/printk/nbcon.c +0 -12
@@ -1739,18 +1739,6 @@ void nbcon_atomic_flush_pending(enum pr_flush_reason reason)__nbcon_atomic_flush_pending (reason,prb_next_reserve_seq (prb)); } -/** -* nbcon_atomic_flush_unsafe - Flush all nbcon consoles using their -* write_atomic() callback and allowing unsafe hostile takeovers -* -* Flush the backlog up through the currently newest record. Unsafe hostile -* takeovers will be performed, if necessary. -*/ -void nbcon_atomic_flush_unsafe (void ) -{ -__nbcon_atomic_flush_pending (PR_FLUSH_HANG ,prb_next_reserve_seq (prb)); -} -/** * nbcon_cpu_emergency_enter - Enter an emergency section where printk() * messages for that CPU are flushed directly
Signature · 1 line
2.47.3
[email protected]
AI reviewRe: [PATCH printk 4/6] printk: Remove nbcon_atomic_flush_unsafe()
Thank you for your contribution! Sashiko AI review found 1 potential issue to consider: - [Medium] printk: vpanic() skips unsafe nbcon flush when boot console is active --
Thank you for your contribution! Sashiko AI review found 1 potential issue to consider:
- [Medium] printk: vpanic() skips unsafe nbcon flush when boot console is active
--
commit 6f37b0702a64fde38f7e7f4187ca677f41bb3307d
Author: John Ogness <[email protected]>
printk: Remove nbcon_atomic_flush_unsafe()
This commit removes nbcon_atomic_flush_unsafe() and replaces its call at the end of vpanic() with printk_trigger_flush(PR_FLUSH_HANG).
kernel/panic.c void vpanic(const char *fmt, va_list args) · line 771
Quoted 13 lines
kernel/panic.c +1
-1@@ -765,7 +765,7 @@ void vpanic(const char *fmt, va_list args)* Explicitly flush the kernel log buffer one last time. */ console_flush_on_panic (CONSOLE_FLUSH_PENDING ); -nbcon_atomic_flush_unsafe (); +printk_trigger_flush (PR_FLUSH_HANG );local_irq_enable ();for (i =0 ; ; i +=PANIC_TIMER_STEP ) {
[Severity: Medium]
Does replacing nbcon_atomic_flush_unsafe() with printk_trigger_flush(PR_FLUSH_HANG) skip the final unsafe nbcon flush when a boot console is still registered?
Previously, nbcon_atomic_flush_unsafe() unconditionally called __nbcon_atomic_flush_pending(PR_FLUSH_HANG, ...) for all nbcon consoles. printk_trigger_flush() only calls nbcon_atomic_flush_pending(reason) when ft.nbcon_atomic is set by printk_get_console_flush_type():
kernel/printk/internal.h:printk_get_console_flush_type() {
...
case NBCON_PRIO_PANIC:
if (have_nbcon_console && !have_boot_console)
ft->nbcon_atomic = true;
...
}When have_boot_console is true (such as before printk_late_init() or with keep_bootcon), ft.nbcon_atomic is false. The fallback ft.legacy_direct path in printk_trigger_flush() does not forward the flush reason to nbcon_legacy_emit_next_record(), leaving ctxt->allow_unsafe_takeover false (and does not run at all if console_trylock() fails or legacy printing is deferred), so an nbcon console held in an unsafe state will no longer be taken over and flushed at the end of panic.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4
John Ogness
maintainerRe: [PATCH printk 4/6] printk: Remove nbcon_atomic_flush_unsafe()
Quoted 11 lines · On 2026-11-10, [email protected] wrote:
Quoted 11 lines
kernel/panic.c +1
-1@@ -765,7 +765,7 @@ void vpanic(const char *fmt, va_list args)* Explicitly flush the kernel log buffer one last time. */ console_flush_on_panic (CONSOLE_FLUSH_PENDING ); -nbcon_atomic_flush_unsafe (); +printk_trigger_flush (PR_FLUSH_HANG );
Quoted 7 lines
kernel/panic.c void vpanic(const char *fmt, va_list args) · line 771
Quoted 2 lines
kernel/panic.c +0
-0local_irq_enable ();for (i =0 ; ; i +=PANIC_TIMER_STEP ) {[Severity: Medium]
Does replacingnbcon_atomic_flush_unsafe()withprintk_trigger_flush(PR_FLUSH_HANG)skip the final unsafe nbcon flush when a boot console is still registered?
Indeed. My suggestion for Sashiko's response to patch 3/6 will not necessarily cover this case because printk_trigger_flush() will still respect the console_lock when in "legacy mode". I think in addition to the above change, console_flush_on_panic() should also be updated to take a @reason argument and then do something like this (assuming we are ok with the @printk_legacy_reason global variable):
@@ -3564,8 +3570,11 @@ void console_flush_on_panic(enum con_flush_mode mode)
nbcon_atomic_flush_pending(PR_FLUSH_RUNTIME);
/* Flush legacy consoles once allowed, even when dangerous. */
- if (legacy_allow_panic_sync)
+ if (legacy_allow_panic_sync) {
+ printk_legacy_reason = reason;
console_flush_all(false, &next_seq, &handover);
+ printk_legacy_reason = PR_FLUSH_RUNTIME;
+ }
}
/*
I am not happy about this, but see it is a workaround until we can free the nbcon consoles from these legacy chains.
Thoughts?
John
John Ogness
maintainerPATCH 5/6 printk: Add panic+kexec flush point
In reply to John OgnessDuring panic, nbcon consoles transition to atomic printing. However, there are consoles that do not support safe atomic printing (CON_NBCON_ATOMIC_UNSAFE). Such consoles will only flush during panic if their unsafe flush policy allows them to do so.
The unsafe flush policy NBCON_PANIC_FLUSH only allows unsafe flushing when particular flushes are explicitly invoked. Call printk_trigger_flush(PR_FLUSH_RESTART) before performing the kexec in order to give such consoles a chance to perform an unsafe flush.
--- kernel/crash_core.c | 1 + 1 file changed, 1 insertion(+)
kernel/crash_core.c +1 -0
@@ -140,6 +140,7 @@ void __noclone __crash_kexec(struct pt_regs *regs)crash_setup_regs (&fixed_regs, regs);crash_save_vmcoreinfo (); +printk_trigger_flush (PR_FLUSH_RESTART );machine_crash_shutdown (&fixed_regs);crash_cma_clear_pending_dma ();machine_kexec (kexec_crash_image);
Signature · 1 line
2.47.3
[email protected]
AI reviewRe: [PATCH printk 5/6] printk: Add panic+kexec flush point
Sashiko has reviewed this patch and found no issues.
Quoted 11 lines
During panic, nbcon consoles transition to atomic printing. However, there are consoles that do not support safe atomic printing (
CON_NBCON_ATOMIC_UNSAFE). Such consoles will only flush during panic if their unsafe flush policy allows them to do so.The unsafe flush policy
NBCON_PANIC_FLUSHonly allows unsafe flushing when particular flushes are explicitly invoked. Callprintk_trigger_flush(PR_FLUSH_RESTART)before performing the kexec in order to give such consoles a chance to perform an unsafe flush.Signed-off-by John Ogness <[email protected]>
Sashiko has reviewed this patch and found no issues.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=5
John Ogness
maintainerPATCH 6/6 netconsole: Always perform unsafe flushing on panic/kdb/restart
In reply to John OgnessAs a legacy console the netconsole would perform unsafe flushing during panic/kdb/restart. With commit 7eab73b18630 ("netconsole:
convert to NBCON console infrastructure") the netconsole is now an NBCON console, however it uses the CON_NBCON_ATOMIC_UNSAFE flag and this prevents unsafe flushing except for at the very end of panic().
Enable unsafe flushing policies to allow the netconsole to perform unsafe flushing during all panic/kdb/restart variations, just as it did when it was a legacy console.
--- drivers/net/netconsole.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/net/netconsole.c +4 -0
@@ -2695,6 +2695,8 @@ static void free_param_target(struct netconsole_target *nt)static struct console netconsole_ext = { .name ="netcon_ext" , .flags =CON_ENABLED |CON_EXTENDED |CON_NBCON |CON_NBCON_ATOMIC_UNSAFE , + .unsafe_flush_flags =NBCON_PANIC_FLUSH |NBCON_PANIC_PRINTK |NBCON_KDB | +NBCON_NORMAL_NOTHREAD , .write_thread = netconsole_write_ext, .write_atomic = netconsole_write_ext, .device_lock = netconsole_device_lock,
@@ -2704,6 +2706,8 @@ static struct console netconsole_ext = {static struct console netconsole = { .name ="netcon" , .flags =CON_ENABLED |CON_NBCON |CON_NBCON_ATOMIC_UNSAFE , + .unsafe_flush_flags =NBCON_PANIC_FLUSH |NBCON_PANIC_PRINTK |NBCON_KDB | +NBCON_NORMAL_NOTHREAD , .write_thread = netconsole_write_basic, .write_atomic = netconsole_write_basic, .device_lock = netconsole_device_lock,
Signature · 1 line
2.47.3
[email protected]
AI reviewRe: [PATCH printk 6/6] netconsole: Always perform unsafe flushing on panic/kdb/restart
Thank you for your contribution! Sashiko AI review found 1 potential issue to consider: - [Medium] netconsole: non-panic unsafe_flush_flags trigger WARN in hostile takeover It also noticed 1 related…
Thank you for your contribution! Sashiko AI review found 1 potential issue to consider:
- [Medium] netconsole: non-panic unsafe_flush_flags trigger WARN in hostile takeover
It also noticed 1 related pre-existing issue this patch does not introduce:
- [Critical] netconsole: unsafe lockless list traversal in netconsole_write() [1]
--
commit 1e2f8b2c899dd40b8577d9ff311ebf00557f27b8
Author: John Ogness <[email protected]>
netconsole: Always perform unsafe flushing on panic/kdb/restart
This commit sets unsafe_flush_flags on the netconsole and netconsole_ext consoles to allow unsafe NBCON flushing during panic, kdb, and restart.
Quoted 14 lines
drivers/net/netconsole.c +2
-0@@ -2695,6 +2695,8 @@ static void free_param_target(struct netconsole_target *nt)static struct console netconsole_ext = { .name ="netcon_ext" , .flags =CON_ENABLED |CON_EXTENDED |CON_NBCON |CON_NBCON_ATOMIC_UNSAFE , + .unsafe_flush_flags =NBCON_PANIC_FLUSH |NBCON_PANIC_PRINTK |NBCON_KDB | +NBCON_NORMAL_NOTHREAD , .write_thread = netconsole_write_ext, .write_atomic = netconsole_write_ext, .device_lock = netconsole_device_lock,
drivers/net/netconsole.c static struct console netconsole_ext = { · line 2710
Quoted 5 lines
drivers/net/netconsole.c +2
-0@@ -2704,6 +2706,8 @@ static struct console netconsole_ext = {static struct console netconsole = { .name ="netcon" , .flags =CON_ENABLED |CON_NBCON |CON_NBCON_ATOMIC_UNSAFE , + .unsafe_flush_flags =NBCON_PANIC_FLUSH |NBCON_PANIC_PRINTK |NBCON_KDB | +NBCON_NORMAL_NOTHREAD ,
[Severity: Medium]
Can enabling NBCON_KDB and NBCON_NORMAL_NOTHREAD trigger the WARN_ON_ONCE() in nbcon_context_try_acquire_hostile() and fail to flush?
__nbcon_atomic_flush_pending_con() and nbcon_kdb_try_acquire() set ctxt->allow_unsafe_takeover from nbcon_allow_unsafe_takeover(con, reason), which returns true for PR_FLUSH_KDB, PR_FLUSH_RESTART, and post-kthread PR_FLUSH_RUNTIME even when ctxt->prio is NBCON_PRIO_EMERGENCY or NBCON_PRIO_NORMAL:
kernel/printk/nbcon.c:nbcon_kdb_try_acquire() {
...
ctxt->prio = NBCON_PRIO_EMERGENCY;
ctxt->allow_unsafe_takeover = nbcon_allow_unsafe_takeover(con, PR_FLUSH_KDB);
}kernel/printk/nbcon.c:nbcon_context_try_acquire_hostile() {
...
if (!ctxt->allow_unsafe_takeover)
return -EPERM;
/* Ensure caller is allowed to perform unsafe hostile takeovers. */
if (WARN_ON_ONCE(ctxt->prio != NBCON_PRIO_PANIC))
return -EPERM;
}If KDB or emergency_restart() interrupts the netconsole kthread while it is inside the nbcon_enter_unsafe() section of netconsole_write(), direct and handover acquisition fail with -EBUSY and nbcon_context_try_acquire() falls through to nbcon_context_try_acquire_hostile(), hitting the warning and returning -EPERM without flushing.
drivers/net/netconsole.c static struct console netconsole_ext = { · line 2712
Quoted 2 lines
drivers/net/netconsole.c +0
-0.write_thread = netconsole_write_basic, .write_atomic = netconsole_write_basic,
[Severity: Critical]
This problem wasn't introduced by this patch, but can netconsole_write() race with target list updates or device teardown [1] when called from write_atomic without target_list_lock?
While write_thread holds target_list_lock via netconsole_device_lock(), write_atomic invokes netconsole_write() locklessly. netconsole_write() traverses target_list with non-RCU list_for_each_entry() and dereferences nt->np.dev in netif_running(nt->np.dev) and netcons_skb_pop():
drivers/net/netconsole.c:netconsole_write() {
...
list_for_each_entry(nt, &target_list, list) {
if (nt->extended != extended || nt->state != STATE_ENABLED ||
!netif_running(nt->np.dev))
continue;
...
}A concurrent target addition, removal, or device release on another CPU can corrupt the list traversal or cause a NULL pointer dereference or use-after-free on nt->np.dev:
CPU0 CPU1drop_netconsole_target() netconsole_write()
spin_lock_irqsave(&target_list_lock)
list_del(&nt->list)
list_for_each_entry(nt, &target_list, ...)
<-- follows torn pointer / LIST_POISONnetcons_release_dev()
nt->np.dev = NULL
netif_running(nt->np.dev)
<-- NULL pointer dereference
[1] https://sashiko.dev/#/bug/linux-5d4ba9e4-a230-401f-bfcf-b5d9ed4aa3b5
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=6