* [PATCH printk 0/3] Introduce sync mode
@ 2026-07-10 14:45 John Ogness
2026-07-10 14:45 ` [PATCH printk 1/3] printk: Introduce console " John Ogness
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: John Ogness @ 2026-07-10 14:45 UTC (permalink / raw)
To: Petr Mladek
Cc: Sergey Senozhatsky, Steven Rostedt, Andrew Murray, Chris Down,
linux-kernel, Greg Kroah-Hartman, linux-fsdevel, Jonathan Corbet,
Shuah Khan, linux-doc
Hi,
As proposed in an LKML thread [0], here is a series to introduce a
new console feature to use synchronous printing. The feature is
activated using the keyword "sync" in the console= command line
argument. For example:
console=ttyS0,115200,sync
Sync mode is only available for nbcon consoles that provide a
safe write_atomic() callback. Specifying it for other consoles
will have no effect other than a log entry that sync mode is not
supported.
Patch 3/3 shamelessly copied an implementation from Chris Down
to parse and update the console options. (Chris's version of
find_and_remove_console_option() is here [1]). I slightly extended
the function to support valueless-options.
At some point we may want to add a sysfs interface to toggle sync
mode.
John Ogness
[0] https://lore.kernel.org/lkml/87v7aruub1.fsf@jogness.linutronix.de
[1] https://lore.kernel.org/lkml/77aa59337507e067f3a4ad7e15375893612bcfa3.1763492585.git.chris@chrisdown.name
John Ogness (3):
printk: Introduce console sync mode
proc: Add console sync support for /proc/consoles
printk: Support setting console sync mode via console=
Documentation/admin-guide/serial-console.rst | 8 ++
fs/proc/consoles.c | 1 +
include/linux/console.h | 2 +
kernel/printk/console_cmdline.h | 1 +
kernel/printk/internal.h | 2 +
kernel/printk/nbcon.c | 42 ++++++--
kernel/printk/printk.c | 104 +++++++++++++++++++
7 files changed, 154 insertions(+), 6 deletions(-)
base-commit: 080d60fffa8e0d285871cde8395438006a9b5b0c
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH printk 1/3] printk: Introduce console sync mode
2026-07-10 14:45 [PATCH printk 0/3] Introduce sync mode John Ogness
@ 2026-07-10 14:45 ` John Ogness
2026-07-14 14:23 ` Petr Mladek
2026-07-10 14:45 ` [PATCH printk 2/3] proc: Add console sync support for /proc/consoles John Ogness
` (2 subsequent siblings)
3 siblings, 1 reply; 7+ messages in thread
From: John Ogness @ 2026-07-10 14:45 UTC (permalink / raw)
To: Petr Mladek
Cc: Sergey Senozhatsky, Steven Rostedt, Andrew Murray, Chris Down,
linux-kernel, Greg Kroah-Hartman
Sometimes it is desirable that console printing occurs synchronously
in the context of the printk() caller. Introduce a "sync mode" for
nbcon consoles that provide safe atomic_write() implementations. A
new console flag CON_SYNC shows if a console is operating in this
mode.
When in sync mode, a console will flush directly within vprintk_emit()
using the same mechanism as emergency printing. If the console
hardware is currently owned by another context, the flushing will
occur when that context releases ownership (just as is the case with
non-panic emergency printing).
If CON_SYNC is set for a legacy console driver or a nbcon console
driver that does not provide a safe atomic_write(), the flag is
cleared upon console registration and a message is logged that the
console does not support sync mode.
Signed-off-by: John Ogness <john.ogness@linutronix.de>
---
include/linux/console.h | 2 ++
kernel/printk/internal.h | 2 ++
kernel/printk/nbcon.c | 42 ++++++++++++++++++++++++++++++++++------
kernel/printk/printk.c | 25 ++++++++++++++++++++++++
4 files changed, 65 insertions(+), 6 deletions(-)
diff --git a/include/linux/console.h b/include/linux/console.h
index d624200cfc170..0f69e52bc3b6b 100644
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -180,6 +180,7 @@ static inline void con_debug_leave(void) { }
* constraints.
* @CON_NBCON_ATOMIC_UNSAFE: The write_atomic() callback is not safe and is
* therefore only used by nbcon_atomic_flush_unsafe().
+ * @CON_SYNC: Print using write_atomic() from the printk() calling context.
*/
enum cons_flags {
CON_PRINTBUFFER = BIT(0),
@@ -192,6 +193,7 @@ enum cons_flags {
CON_SUSPENDED = BIT(7),
CON_NBCON = BIT(8),
CON_NBCON_ATOMIC_UNSAFE = BIT(9),
+ CON_SYNC = BIT(10),
};
/**
diff --git a/kernel/printk/internal.h b/kernel/printk/internal.h
index 85fbf1801cbe0..1c1e0e97e3a04 100644
--- a/kernel/printk/internal.h
+++ b/kernel/printk/internal.h
@@ -105,6 +105,7 @@ 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_sync_flush_pending(void);
bool nbcon_legacy_emit_next_record(struct console *con, bool *handover,
int cookie, bool use_atomic);
bool nbcon_kthread_create(struct console *con);
@@ -157,6 +158,7 @@ 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_sync_flush_pending(void) { }
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) { }
diff --git a/kernel/printk/nbcon.c b/kernel/printk/nbcon.c
index 4b03b019cd5ee..77abb98042648 100644
--- a/kernel/printk/nbcon.c
+++ b/kernel/printk/nbcon.c
@@ -1200,7 +1200,10 @@ static bool nbcon_kthread_should_wakeup(struct console *con, struct nbcon_contex
cookie = console_srcu_read_lock();
flags = console_srcu_read_flags(con);
- if (console_is_usable(con, flags, false)) {
+ if (unlikely(flags & CON_SYNC)) {
+ /* Sync consoles never print from the printer thread. */
+ ;
+ } else if (console_is_usable(con, flags, false)) {
/* Bring the sequence in @ctxt up to date */
ctxt->seq = nbcon_seq_read(con);
@@ -1653,8 +1656,9 @@ 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
* @stop_seq: Flush up until this record
+ * @sync_only: Only flush sync consoles (CON_SYNC)
*/
-static void __nbcon_atomic_flush_pending(u64 stop_seq)
+static void __nbcon_atomic_flush_pending(u64 stop_seq, bool sync_only)
{
struct console *con;
int cookie;
@@ -1666,6 +1670,9 @@ static void __nbcon_atomic_flush_pending(u64 stop_seq)
if (!(flags & CON_NBCON))
continue;
+ if (sync_only && !(flags & CON_SYNC))
+ continue;
+
if (!console_is_usable(con, flags, true))
continue;
@@ -1688,7 +1695,22 @@ static void __nbcon_atomic_flush_pending(u64 stop_seq)
*/
void nbcon_atomic_flush_pending(void)
{
- __nbcon_atomic_flush_pending(prb_next_reserve_seq(prb));
+ __nbcon_atomic_flush_pending(prb_next_reserve_seq(prb), false);
+}
+
+/**
+ * nbcon_sync_flush_pending - Flush all nbcon sync consoles (CON_SYNC)
+ * using their write_atomic() callback
+ *
+ * Flush the backlog of all sync consoles up through the currently newest
+ * record. This is the same as nbcon_atomic_flush_pending() except only
+ * for sync (CON_SYNC) consoles.
+ *
+ * See nbcon_atomic_flush_pending() for more details.
+ */
+void nbcon_sync_flush_pending(void)
+{
+ __nbcon_atomic_flush_pending(prb_next_reserve_seq(prb), true);
}
/**
@@ -1701,7 +1723,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(prb_next_reserve_seq(prb), false);
panic_nbcon_allow_unsafe_takeover = false;
}
@@ -1905,6 +1927,7 @@ void nbcon_device_release(struct console *con)
{
struct nbcon_context *ctxt = &ACCESS_PRIVATE(con, nbcon_device_ctxt);
struct console_flush_type ft;
+ short flags;
int cookie;
if (!nbcon_context_exit_unsafe(ctxt))
@@ -1919,8 +1942,15 @@ void nbcon_device_release(struct console *con)
* usable throughout flushing.
*/
cookie = console_srcu_read_lock();
- printk_get_console_flush_type(&ft);
- if (console_is_usable(con, console_srcu_read_flags(con), true) &&
+ flags = console_srcu_read_flags(con);
+ if (unlikely(flags & CON_SYNC)) {
+ /* Sync consoles will always perform nbcon_atomic flushing. */
+ memset(&ft, 0, sizeof(ft));
+ ft.nbcon_atomic = true;
+ } else {
+ printk_get_console_flush_type(&ft);
+ }
+ if (console_is_usable(con, flags, true) &&
!ft.nbcon_offload &&
prb_read_valid(prb, nbcon_seq_read(con), NULL)) {
/*
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 2fe9a963c823a..e82e864a4d672 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -453,6 +453,13 @@ bool have_legacy_console;
*/
bool have_nbcon_console;
+/*
+ * Specifies if a console is running in sync mode. If any consoles are running
+ * in sync mode, printk() will immediately flush such consoles using their
+ * write_atomic() callback.
+ */
+bool have_sync_console;
+
/*
* Specifies if a boot console is registered. If boot consoles are present,
* nbcon consoles cannot print simultaneously and must be synchronized by
@@ -2456,6 +2463,8 @@ asmlinkage int vprintk_emit(int facility, int level,
if (ft.nbcon_atomic)
nbcon_atomic_flush_pending();
+ else if (have_sync_console)
+ nbcon_sync_flush_pending();
if (ft.nbcon_offload)
nbcon_kthreads_wake();
@@ -4144,6 +4153,16 @@ void register_console(struct console *newcon)
newcon->dropped = 0;
init_seq = get_init_console_seq(newcon, bootcon_registered);
+ if (newcon->flags & CON_SYNC) {
+ if (!(newcon->flags & CON_NBCON) || (newcon->flags & CON_NBCON_ATOMIC_UNSAFE)) {
+ newcon->flags &= ~CON_SYNC;
+ con_printk(KERN_INFO, newcon, "sync mode unsupported\n");
+ }
+ }
+
+ if (newcon->flags & CON_SYNC)
+ have_sync_console = true;
+
if (newcon->flags & CON_NBCON) {
have_nbcon_console = true;
nbcon_seq_force(newcon, init_seq);
@@ -4231,6 +4250,7 @@ static int unregister_console_locked(struct console *console)
bool found_legacy_con = false;
bool found_nbcon_con = false;
bool found_boot_con = false;
+ bool found_sync_con = false;
unsigned long flags;
struct console *c;
int res;
@@ -4299,6 +4319,9 @@ static int unregister_console_locked(struct console *console)
found_nbcon_con = true;
else
found_legacy_con = true;
+
+ if (c->flags & CON_SYNC)
+ found_sync_con = true;
}
if (!found_boot_con)
have_boot_console = found_boot_con;
@@ -4306,6 +4329,8 @@ static int unregister_console_locked(struct console *console)
have_legacy_console = found_legacy_con;
if (!found_nbcon_con)
have_nbcon_console = found_nbcon_con;
+ if (!found_sync_con)
+ have_sync_console = found_sync_con;
/* @have_nbcon_console must be updated before calling nbcon_free(). */
if (console->flags & CON_NBCON)
--
2.47.3
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH printk 2/3] proc: Add console sync support for /proc/consoles
2026-07-10 14:45 [PATCH printk 0/3] Introduce sync mode John Ogness
2026-07-10 14:45 ` [PATCH printk 1/3] printk: Introduce console " John Ogness
@ 2026-07-10 14:45 ` John Ogness
2026-07-14 15:03 ` Petr Mladek
2026-07-10 14:45 ` [PATCH printk 3/3] printk: Support setting console sync mode via console= John Ogness
2026-07-10 21:05 ` [PATCH printk 0/3] Introduce sync mode John Ogness
3 siblings, 1 reply; 7+ messages in thread
From: John Ogness @ 2026-07-10 14:45 UTC (permalink / raw)
To: Petr Mladek
Cc: Sergey Senozhatsky, Steven Rostedt, Andrew Murray, Chris Down,
linux-kernel, linux-fsdevel
Update /proc/consoles output to show 's' if a console is
running in sync mode.
Signed-off-by: John Ogness <john.ogness@linutronix.de>
---
fs/proc/consoles.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/fs/proc/consoles.c b/fs/proc/consoles.c
index b7cab1ad990da..f9af07d161412 100644
--- a/fs/proc/consoles.c
+++ b/fs/proc/consoles.c
@@ -25,6 +25,7 @@ static int show_console_dev(struct seq_file *m, void *v)
{ CON_PRINTBUFFER, 'p' },
{ CON_BRL, 'b' },
{ CON_ANYTIME, 'a' },
+ { CON_SYNC, 's' },
};
char flags[ARRAY_SIZE(con_flags) + 1];
struct console *con = v;
--
2.47.3
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH printk 3/3] printk: Support setting console sync mode via console=
2026-07-10 14:45 [PATCH printk 0/3] Introduce sync mode John Ogness
2026-07-10 14:45 ` [PATCH printk 1/3] printk: Introduce console " John Ogness
2026-07-10 14:45 ` [PATCH printk 2/3] proc: Add console sync support for /proc/consoles John Ogness
@ 2026-07-10 14:45 ` John Ogness
2026-07-10 21:05 ` [PATCH printk 0/3] Introduce sync mode John Ogness
3 siblings, 0 replies; 7+ messages in thread
From: John Ogness @ 2026-07-10 14:45 UTC (permalink / raw)
To: Petr Mladek
Cc: Sergey Senozhatsky, Steven Rostedt, Andrew Murray, Chris Down,
linux-kernel, Jonathan Corbet, Shuah Khan, linux-doc
Extend the console= kernel command line argument to support specifying
sync mode at boot time. This is achieved by introducing a new "sync"
option that can be passed within a console= cmdline argument.
For example, assuming the first serial device should be a console using
sync mode:
console=ttyS0,115200,sync
Signed-off-by: John Ogness <john.ogness@linutronix.de>
Co-authored-by: Chris Down <chris@chrisdown.name>
---
Documentation/admin-guide/serial-console.rst | 8 ++
kernel/printk/console_cmdline.h | 1 +
kernel/printk/printk.c | 79 ++++++++++++++++++++
3 files changed, 88 insertions(+)
diff --git a/Documentation/admin-guide/serial-console.rst b/Documentation/admin-guide/serial-console.rst
index 1609e7479249f..37cbd9581c53a 100644
--- a/Documentation/admin-guide/serial-console.rst
+++ b/Documentation/admin-guide/serial-console.rst
@@ -32,6 +32,14 @@ The format of this option is::
and F is flow control ('r' for RTS). Default is
9600n8. The maximum baudrate is 115200.
+ One can also specify extra options (comma-separated)
+ to modify console printing behavior. The "sync" extra
+ option will cause console printing on that console to
+ be synchronous (printed in the context of the printk()
+ caller). This could cause adverse timing side-effects
+ for the system, but can be useful in certain debugging
+ scenarios.
+
You can specify multiple console= options on the kernel command line.
The behavior is well defined when each device type is mentioned only once.
diff --git a/kernel/printk/console_cmdline.h b/kernel/printk/console_cmdline.h
index 0ab573b6d4dc2..ce4709f7a1b0c 100644
--- a/kernel/printk/console_cmdline.h
+++ b/kernel/printk/console_cmdline.h
@@ -8,6 +8,7 @@ struct console_cmdline
int index; /* Minor dev. to use */
char devname[32]; /* DEVNAME:0.0 style device name */
bool user_specified; /* Specified by command line vs. platform */
+ bool sync; /* Print in sync mode */
char *options; /* Options for the driver */
#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
char *brl_options; /* Options for braille driver */
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index e82e864a4d672..e53ac5554e5eb 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2567,6 +2567,81 @@ static void set_user_specified(struct console_cmdline *c, bool user_specified)
console_set_on_cmdline = 1;
}
+/**
+ * find_and_remove_console_option - Find and remove a named option from console options string
+ * @options: The console options string (will be modified in-place)
+ * @key: The option name to find (e.g., "loglevel")
+ * @val_buf: Buffer to store the option value (if present)
+ * @val_buf_size: Size of @val_buf
+ *
+ * This function searches for a named option in a comma-separated options string
+ * (e.g., "9600n8,loglevel:3,sync"). If found, it extracts the value
+ * (the part after ':') and removes the entire option from the string.
+ *
+ * If an option does not support values, @val_buf should be NULL, in which
+ * case no value part is extracted and any user provided ':' is considered part
+ * of the option key.
+ *
+ * The function modifies @options in-place by:
+ * 1. Temporarily null-terminating option names and values during parsing
+ * 2. Restoring separators if the option isn't found
+ * 3. Removing the found option by shifting the remaining string
+ *
+ * Return: true if the option was found and removed, false otherwise
+ */
+static bool find_and_remove_console_option(char *options, const char *key,
+ char *val_buf, size_t val_buf_size)
+{
+ bool found = false, first = true;
+ char *option, *next = options;
+
+ while ((option = strsep(&next, ","))) {
+ char *value = NULL;
+
+ if (val_buf) {
+ value = strchr(option, ':');
+ if (value)
+ *(value++) = '\0';
+ }
+
+ if (strcmp(option, key) == 0) {
+ found = true;
+ if (value) {
+ if (strlen(value) >= val_buf_size) {
+ pr_warn("Cannot copy console option value for %s:%s: not enough space (%zu)\n",
+ option, value, val_buf_size);
+ found = false;
+ } else {
+ strscpy(val_buf, value, val_buf_size);
+ }
+ } else if (val_buf) {
+ *val_buf = '\0';
+ }
+ }
+
+ if (found)
+ break;
+
+ if (next)
+ *(next - 1) = ',';
+ if (value)
+ *(value - 1) = ':';
+
+ first = false;
+ }
+
+ if (found) {
+ if (next)
+ memmove(option, next, strlen(next) + 1);
+ else if (first)
+ *option = '\0';
+ else
+ *--option = '\0';
+ }
+
+ return found;
+}
+
static int __add_preferred_console(const char *name, const short idx,
const char *devname, char *options,
char *brl_options, bool user_specified)
@@ -2609,6 +2684,7 @@ static int __add_preferred_console(const char *name, const short idx,
strscpy(c->name, name);
if (devname)
strscpy(c->devname, devname);
+ c->sync = find_and_remove_console_option(options, "sync", NULL, 0);
c->options = options;
set_user_specified(c, user_specified);
braille_set_options(c, brl_options);
@@ -3932,6 +4008,9 @@ static int try_enable_preferred_console(struct console *newcon,
if (_braille_register_console(newcon, c))
return 0;
+ if (c->sync)
+ newcon->flags |= CON_SYNC;
+
err = console_call_setup(newcon, c->options);
if (err)
return err;
--
2.47.3
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH printk 0/3] Introduce sync mode
2026-07-10 14:45 [PATCH printk 0/3] Introduce sync mode John Ogness
` (2 preceding siblings ...)
2026-07-10 14:45 ` [PATCH printk 3/3] printk: Support setting console sync mode via console= John Ogness
@ 2026-07-10 21:05 ` John Ogness
3 siblings, 0 replies; 7+ messages in thread
From: John Ogness @ 2026-07-10 21:05 UTC (permalink / raw)
To: Petr Mladek
Cc: Sergey Senozhatsky, Steven Rostedt, Andrew Murray, Chris Down,
linux-kernel, Greg Kroah-Hartman, linux-fsdevel, Jonathan Corbet,
Shuah Khan, linux-doc
On 2026-07-10, John Ogness <john.ogness@linutronix.de> wrote:
> As proposed in an LKML thread [0], here is a series to introduce a
> new console feature to use synchronous printing.
So the Sashiko review [0] found all kinds of issues and every one of
them are legitimate. In particular, I totally forgot about:
- the console_lock synchronization when boot consoles exist
- the various sites that _rely_ on kthreads printing when
printk_get_console_flush_type() reports ft.nbcon_offload
I will consider each of the points mentioned by Sashiko and include my
proposed solution in my response. I will need a few days to go through
all of it.
John
[0] https://sashiko.dev/#/patchset/20260710144609.194487-1-john.ogness%40linutronix.de
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH printk 1/3] printk: Introduce console sync mode
2026-07-10 14:45 ` [PATCH printk 1/3] printk: Introduce console " John Ogness
@ 2026-07-14 14:23 ` Petr Mladek
0 siblings, 0 replies; 7+ messages in thread
From: Petr Mladek @ 2026-07-14 14:23 UTC (permalink / raw)
To: John Ogness
Cc: Sergey Senozhatsky, Steven Rostedt, Andrew Murray, Chris Down,
linux-kernel, Greg Kroah-Hartman
On Fri 2026-07-10 16:51:51, John Ogness wrote:
> Sometimes it is desirable that console printing occurs synchronously
> in the context of the printk() caller. Introduce a "sync mode" for
> nbcon consoles that provide safe atomic_write() implementations. A
> new console flag CON_SYNC shows if a console is operating in this
> mode.
>
> When in sync mode, a console will flush directly within vprintk_emit()
> using the same mechanism as emergency printing. If the console
> hardware is currently owned by another context, the flushing will
> occur when that context releases ownership (just as is the case with
> non-panic emergency printing).
>
> If CON_SYNC is set for a legacy console driver or a nbcon console
> driver that does not provide a safe atomic_write(), the flag is
> cleared upon console registration and a message is logged that the
> console does not support sync mode.
>
> --- a/include/linux/console.h
> +++ b/include/linux/console.h
> @@ -180,6 +180,7 @@ static inline void con_debug_leave(void) { }
> * constraints.
> * @CON_NBCON_ATOMIC_UNSAFE: The write_atomic() callback is not safe and is
> * therefore only used by nbcon_atomic_flush_unsafe().
> + * @CON_SYNC: Print using write_atomic() from the printk() calling context.
> */
> enum cons_flags {
> CON_PRINTBUFFER = BIT(0),
> @@ -192,6 +193,7 @@ enum cons_flags {
> CON_SUSPENDED = BIT(7),
> CON_NBCON = BIT(8),
> CON_NBCON_ATOMIC_UNSAFE = BIT(9),
> + CON_SYNC = BIT(10),
My first feeling was that this was an overhead. But it might make
sense to keep only one console synchronous, ...
> diff --git a/kernel/printk/nbcon.c b/kernel/printk/nbcon.c
> index 4b03b019cd5ee..77abb98042648 100644
> --- a/kernel/printk/nbcon.c
> +++ b/kernel/printk/nbcon.c
> @@ -1919,8 +1942,15 @@ void nbcon_device_release(struct console *con)
> * usable throughout flushing.
> */
> cookie = console_srcu_read_lock();
> - printk_get_console_flush_type(&ft);
> - if (console_is_usable(con, console_srcu_read_flags(con), true) &&
> + flags = console_srcu_read_flags(con);
> + if (unlikely(flags & CON_SYNC)) {
> + /* Sync consoles will always perform nbcon_atomic flushing. */
> + memset(&ft, 0, sizeof(ft));
> + ft.nbcon_atomic = true;
They have to use the legacy loop when boot consoles are still
registered, see below.
> + } else {
> + printk_get_console_flush_type(&ft);
> + }
> + if (console_is_usable(con, flags, true) &&
> !ft.nbcon_offload &&
> prb_read_valid(prb, nbcon_seq_read(con), NULL)) {
> /*
> --- a/kernel/printk/printk.c
> +++ b/kernel/printk/printk.c
> @@ -2456,6 +2463,8 @@ asmlinkage int vprintk_emit(int facility, int level,
>
> if (ft.nbcon_atomic)
> nbcon_atomic_flush_pending();
> + else if (have_sync_console)
> + nbcon_sync_flush_pending();
Again. we should not do this when boot consoles are still
registered.
Also I think that we need to flush it in many more situations.
In general, it should be needed everywhere
printk_get_console_flush_type() is called for nbcon consoles
in a context with NBCON_PRIO_NORMAL, see below.
BTW: I wonder if this would work when more CPUs are adding
messages in parallel. I guess that they would fight for
the printing context. But I guess that they will
eventually finish the printing.
It is a bit different from the best effort approach used
by the legacy loop. Synchronous nbcon console will cause
that printk() might serialize CPUs...
It might be acceptable because this mode should not be
normally used. But we should at least document it.
> if (ft.nbcon_offload)
> nbcon_kthreads_wake();
I think that we should integrate this into
printk_get_console_flush_type(). We could solve the problem
with per-console flags the same way as for other flags,
by global variables:
+ have_nbcon_sync_console
+ have_nbcon_async_console
I wondered how it might look like. And I wanted to try AI.
I came with the following patch on top of this patchset.
The first version was generated by Claude, including the commit
message. Later, I modified the code a lot, and the commit message
a bit, by hand.
It is relatively complex. But I think that it is the prize for
the feature. And I think that the modification of
printk_get_console_flush_type() better fits the existing
design and helps to make sure that we handle it well
in all situations.
Feel free to integrate it into the first patch. Or maybe,
we could add the two have_nbcon* variables in a separate
patch, first, ...
Here is the patch:
From 4b1fb1806c69465dd71321f564fcdec700c13d88 Mon Sep 17 00:00:00 2001
From: Petr Mladek <pmladek@suse.com>
Date: Tue, 14 Jul 2026 13:53:01 +0200
Subject: [RFC] printk: Centralize nbcon sync console flush decision in
console_flush_type
The ad-hoc have_sync_console check in vprintk_emit() bypassed
printk_get_console_flush_type() to call nbcon_sync_flush_pending()
directly. This meant the sync-flush decision was scattered rather than
centralized, and was missing from all other call sites such as
console_cpu_notify(), __pr_flush(), printk_trigger_flush(), and
console_try_replay_all().
Introduce a new nbcon_atomic_sync field in struct console_flush_type to
represent the case where sync nbcon consoles (CON_SYNC) should be flushed
atomically in the caller's context. To support this, add a companion global
flag have_nbcon_async_console that tracks whether any non-sync nbcon
console is registered, maintained symmetrically with have_sync_console
in register_console() and unregister_console_locked().
Also rename the flag have_sync_console to have_nbcon_sync_console
to make it more symmetrical with have_nbcon_async_console.
Update printk_get_console_flush_type() NBCON_PRIO_NORMAL to set
nbcon_atomic_sync and nbcon_offload independently based on which console
types are present:
- kthreads running: nbcon_offload if non-sync nbcon consoles exist,
nbcon_atomic_sync if sync consoles exist (both can be set)
- kthreads not running: nbcon_atomic if non-sync consoles exist
(covers all nbcon including sync), else nbcon_atomic_sync
nbcon_atomic_sync is never set when nbcon_atomic is set because nbcon_atomic
already covers all nbcon consoles.
Update all printk_get_console_flush_type() call sites to handle
nbcon_atomic_sync by calling nbcon_sync_flush_pending(). Update the legacy
loop and legacy kthread skip conditions to correctly exclude only the console
type that is being handled atomically or offloaded, rather than all nbcon
consoles unconditionally.
Fix the loop condition in nbcon_atomic_flush_pending_con() to also loop
when nbcon_atomic_sync is set, because the printer thread never handles
sync consoles even when nbcon_offload is true.
Assisted-by: claude-sonnet-4.6
Signed-off-by: Petr Mladek <pmladek@suse.com>
---
kernel/printk/internal.h | 22 ++++++++++++---
kernel/printk/nbcon.c | 24 ++++++++--------
kernel/printk/printk.c | 60 ++++++++++++++++++++++++++++------------
3 files changed, 71 insertions(+), 35 deletions(-)
diff --git a/kernel/printk/internal.h b/kernel/printk/internal.h
index 1c1e0e97e3a0..3acea42b8720 100644
--- a/kernel/printk/internal.h
+++ b/kernel/printk/internal.h
@@ -169,19 +169,26 @@ static inline void nbcon_kthreads_wake(void) { }
extern bool have_boot_console;
extern bool have_nbcon_console;
extern bool have_legacy_console;
+extern bool have_nbcon_sync_console;
+extern bool have_nbcon_async_console;
extern bool legacy_allow_panic_sync;
/**
* struct console_flush_type - Define available console flush methods
* @nbcon_atomic: Flush directly using nbcon_atomic() callback
+ * @nbcon_atomic_sync: Flush sync consoles (CON_SYNC) using nbcon_atomic() callback
* @nbcon_offload: Offload flush to printer thread
* @legacy_direct: Call the legacy loop in this context
* @legacy_offload: Offload the legacy loop into IRQ or legacy thread
*
* Note that the legacy loop also flushes the nbcon consoles.
+ *
+ * @nbcon_atomic_sync is never set when @nbcon_atomic is set because
+ * @nbcon_atomic already covers all nbcon consoles including sync ones.
*/
struct console_flush_type {
bool nbcon_atomic;
+ bool nbcon_atomic_sync;
bool nbcon_offload;
bool legacy_direct;
bool legacy_offload;
@@ -200,10 +207,17 @@ static inline void printk_get_console_flush_type(struct console_flush_type *ft)
switch (nbcon_get_default_prio()) {
case NBCON_PRIO_NORMAL:
if (have_nbcon_console && !have_boot_console) {
- if (printk_kthreads_running && !console_irqwork_blocked)
- ft->nbcon_offload = true;
- else
- ft->nbcon_atomic = true;
+ if (printk_kthreads_running && !console_irqwork_blocked) {
+ if (have_nbcon_async_console)
+ ft->nbcon_offload = true;
+ if (have_nbcon_sync_console)
+ ft->nbcon_atomic_sync = true;
+ } else {
+ if (have_nbcon_async_console)
+ ft->nbcon_atomic = true;
+ else
+ ft->nbcon_atomic_sync = true;
+ }
}
/* Legacy consoles are flushed directly when possible. */
diff --git a/kernel/printk/nbcon.c b/kernel/printk/nbcon.c
index 77abb9804264..093683ee7262 100644
--- a/kernel/printk/nbcon.c
+++ b/kernel/printk/nbcon.c
@@ -1623,6 +1623,7 @@ static int __nbcon_atomic_flush_pending_con(struct console *con, u64 stop_seq)
static void nbcon_atomic_flush_pending_con(struct console *con, u64 stop_seq)
{
struct console_flush_type ft;
+ short flags;
int err;
again:
@@ -1642,10 +1643,11 @@ static void nbcon_atomic_flush_pending_con(struct console *con, u64 stop_seq)
/*
* If flushing was successful but more records are available, this
* context must flush those remaining records if the printer thread
- * is not available do it.
+ * is not available to do it or the console is explicitely synchronous.
*/
printk_get_console_flush_type(&ft);
- if (!ft.nbcon_offload &&
+ flags = console_srcu_read_flags(con);
+ if ((!ft.nbcon_offload || flags & CON_SYNC) &&
prb_read_valid(prb, nbcon_seq_read(con), NULL)) {
stop_seq = prb_next_reserve_seq(prb);
goto again;
@@ -1937,21 +1939,17 @@ void nbcon_device_release(struct console *con)
/*
* This context must flush any new records added while the console
- * was locked if the printer thread is not available to do it. The
- * console_srcu_read_lock must be taken to ensure the console is
- * usable throughout flushing.
+ * was locked if the printer thread is not available to do it or
+ * when the console is explicitely synchonous.
+ *
+ * The console_srcu_read_lock must be taken to ensure the console
+ * is usable throughout flushing.
*/
cookie = console_srcu_read_lock();
flags = console_srcu_read_flags(con);
- if (unlikely(flags & CON_SYNC)) {
- /* Sync consoles will always perform nbcon_atomic flushing. */
- memset(&ft, 0, sizeof(ft));
- ft.nbcon_atomic = true;
- } else {
- printk_get_console_flush_type(&ft);
- }
+ printk_get_console_flush_type(&ft);
if (console_is_usable(con, flags, true) &&
- !ft.nbcon_offload &&
+ (!ft.nbcon_offload || flags & CON_SYNC) &&
prb_read_valid(prb, nbcon_seq_read(con), NULL)) {
/*
* If nbcon_atomic flushing is not available, fallback to
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index e53ac5554e5e..96da8ac04856 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -454,11 +454,18 @@ bool have_legacy_console;
bool have_nbcon_console;
/*
- * Specifies if a console is running in sync mode. If any consoles are running
- * in sync mode, printk() will immediately flush such consoles using their
- * write_atomic() callback.
+ * Specifies if an nbcon console is running in sync mode. If any consoles are
+ * running in sync mode, printk() will immediately flush such consoles using
+ * their write_atomic() callback.
*/
-bool have_sync_console;
+bool have_nbcon_sync_console;
+
+/*
+ * Specifies if an nbcon console without sync mode is registered. It there are
+ * any such consoles then printk() has to wake up the related kthreads when
+ * possible.
+ */
+bool have_nbcon_async_console;
/*
* Specifies if a boot console is registered. If boot consoles are present,
@@ -2463,7 +2470,7 @@ asmlinkage int vprintk_emit(int facility, int level,
if (ft.nbcon_atomic)
nbcon_atomic_flush_pending();
- else if (have_sync_console)
+ if (ft.nbcon_atomic_sync)
nbcon_sync_flush_pending();
if (ft.nbcon_offload)
@@ -2954,6 +2961,8 @@ static int console_cpu_notify(unsigned int cpu)
printk_get_console_flush_type(&ft);
if (ft.nbcon_atomic)
nbcon_atomic_flush_pending();
+ if (ft.nbcon_atomic_sync)
+ nbcon_sync_flush_pending();
if (ft.legacy_direct) {
if (console_trylock())
console_unlock();
@@ -3339,7 +3348,8 @@ static bool console_flush_one_record(bool do_cond_resched, u64 *next_seq, bool *
* nbcon consoles when the nbcon consoles cannot print via
* their atomic or threaded flushing.
*/
- if ((flags & CON_NBCON) && (ft.nbcon_atomic || ft.nbcon_offload))
+ if ((flags & CON_NBCON) &&
+ (ft.nbcon_atomic || ft.nbcon_atomic_sync || ft.nbcon_offload))
continue;
if (!console_is_usable(con, flags, !do_cond_resched))
@@ -3753,7 +3763,8 @@ static bool legacy_kthread_should_wakeup(void)
* consoles when the nbcon consoles cannot print via their
* atomic or threaded flushing.
*/
- if ((flags & CON_NBCON) && (ft.nbcon_atomic || ft.nbcon_offload))
+ if ((flags & CON_NBCON) &&
+ (ft.nbcon_atomic || ft.nbcon_atomic_sync || ft.nbcon_offload))
continue;
if (!console_is_usable(con, flags, false))
@@ -4239,11 +4250,13 @@ void register_console(struct console *newcon)
}
}
- if (newcon->flags & CON_SYNC)
- have_sync_console = true;
-
if (newcon->flags & CON_NBCON) {
have_nbcon_console = true;
+ if (newcon->flags & CON_SYNC)
+ have_nbcon_sync_console = true;
+ else
+ have_nbcon_async_console = true;
+
nbcon_seq_force(newcon, init_seq);
} else {
have_legacy_console = true;
@@ -4329,7 +4342,8 @@ static int unregister_console_locked(struct console *console)
bool found_legacy_con = false;
bool found_nbcon_con = false;
bool found_boot_con = false;
- bool found_sync_con = false;
+ bool found_nbcon_sync_con = false;
+ bool found_nbcon_async_con = false;
unsigned long flags;
struct console *c;
int res;
@@ -4394,13 +4408,15 @@ static int unregister_console_locked(struct console *console)
if (c->flags & CON_BOOT)
found_boot_con = true;
- if (c->flags & CON_NBCON)
+ if (c->flags & CON_NBCON) {
found_nbcon_con = true;
- else
+ if (c->flags & CON_SYNC)
+ found_nbcon_sync_con = true;
+ else
+ found_nbcon_async_con = true;
+ } else {
found_legacy_con = true;
-
- if (c->flags & CON_SYNC)
- found_sync_con = true;
+ }
}
if (!found_boot_con)
have_boot_console = found_boot_con;
@@ -4408,8 +4424,10 @@ static int unregister_console_locked(struct console *console)
have_legacy_console = found_legacy_con;
if (!found_nbcon_con)
have_nbcon_console = found_nbcon_con;
- if (!found_sync_con)
- have_sync_console = found_sync_con;
+ if (!found_nbcon_sync_con)
+ have_nbcon_sync_console = found_nbcon_sync_con;
+ if (!found_nbcon_async_con)
+ have_nbcon_async_console = found_nbcon_async_con;
/* @have_nbcon_console must be updated before calling nbcon_free(). */
if (console->flags & CON_NBCON)
@@ -4595,6 +4613,8 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
printk_get_console_flush_type(&ft);
if (ft.nbcon_atomic)
nbcon_atomic_flush_pending();
+ if (ft.nbcon_atomic_sync)
+ nbcon_sync_flush_pending();
if (ft.legacy_direct) {
console_lock();
console_unlock();
@@ -4803,6 +4823,8 @@ void printk_trigger_flush(void)
printk_get_console_flush_type(&ft);
if (ft.nbcon_atomic)
nbcon_atomic_flush_pending();
+ if (ft.nbcon_atomic_sync)
+ nbcon_sync_flush_pending();
if (ft.nbcon_offload)
nbcon_kthreads_wake();
if (ft.legacy_direct) {
@@ -5155,6 +5177,8 @@ void console_try_replay_all(void)
__console_rewind_all();
if (ft.nbcon_atomic)
nbcon_atomic_flush_pending();
+ if (ft.nbcon_atomic_sync)
+ nbcon_sync_flush_pending();
if (ft.nbcon_offload)
nbcon_kthreads_wake();
if (ft.legacy_offload)
--
2.55.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH printk 2/3] proc: Add console sync support for /proc/consoles
2026-07-10 14:45 ` [PATCH printk 2/3] proc: Add console sync support for /proc/consoles John Ogness
@ 2026-07-14 15:03 ` Petr Mladek
0 siblings, 0 replies; 7+ messages in thread
From: Petr Mladek @ 2026-07-14 15:03 UTC (permalink / raw)
To: John Ogness
Cc: Sergey Senozhatsky, Steven Rostedt, Andrew Murray, Chris Down,
linux-kernel, linux-fsdevel
On Fri 2026-07-10 16:51:52, John Ogness wrote:
> Update /proc/consoles output to show 's' if a console is
> running in sync mode.
>
> Signed-off-by: John Ogness <john.ogness@linutronix.de>
Looks good to me:
Reviewed-by: Petr Mladek <pmladek@suse.com>
Best Regards,
Petr
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-07-14 15:03 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-10 14:45 [PATCH printk 0/3] Introduce sync mode John Ogness
2026-07-10 14:45 ` [PATCH printk 1/3] printk: Introduce console " John Ogness
2026-07-14 14:23 ` Petr Mladek
2026-07-10 14:45 ` [PATCH printk 2/3] proc: Add console sync support for /proc/consoles John Ogness
2026-07-14 15:03 ` Petr Mladek
2026-07-10 14:45 ` [PATCH printk 3/3] printk: Support setting console sync mode via console= John Ogness
2026-07-10 21:05 ` [PATCH printk 0/3] Introduce sync mode John Ogness
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.