[PATCH 04/19] printk: Reintroduce consoles_suspended global state

Marcos Paulo de Souza
2025-12-27T12:17:25Z

This change partially reverts commit 9e70a5e109a4
("printk: Add per-console suspended state"). The intent of the original
commit was to move the management of the console suspended state to the
consoles themselves to be able to use SRCU instead of console lock.

But having a global state is still useful when checking if the global
suspend was triggered by power management. This way, instead of setting
the state of each individual console, the code would only set/read from the
global state.

Along with this change, two more fixes are necessary: change
console_{suspend,resume} to set/clear CON_SUSPEND instead of setting
CON_ENABLED and change show_cons_active to call __console_is_usable to
check console usefulness.

Link: https://lore.kernel.org/lkml/844j4lepak.fsf@jogness.linutronix.de/
Signed-off-by: Petr Mladek <pmladek@suse.com>
[mpdesouza@suse.com: Adapted code related to console_is_usable]
Signed-off-by: Marcos Paulo de Souza <mpdesouza@suse.com>
---
drivers/tty/tty_io.c | 6 +++---
include/linux/console.h | 53 +++++++++++++++++++++++++++++++++++++++++++++++--
kernel/printk/printk.c | 23 +++++++++++----------
3 files changed, 67 insertions(+), 15 deletions(-)

diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index e2d92cf70eb7..7d2bded75b75 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -3552,9 +3552,9 @@ static ssize_t show_cons_active(struct device *dev,
for_each_console(c) {
if (!c->device)
continue;
- if (!(c->flags & CON_NBCON) && !c->write)
- continue;
- if ((c->flags & CON_ENABLED) == 0)
+ if (!__console_is_usable(c, c->flags,
+ consoles_suspended,
+ NBCON_USE_ANY))
continue;
cs[i++] = c;
if (i >= ARRAY_SIZE(cs))
diff --git a/include/linux/console.h b/include/linux/console.h
index 648cf10e3f93..caf9b0951129 100644
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -509,6 +509,7 @@ extern void console_list_lock(void) __acquires(console_mutex);
extern void console_list_unlock(void) __releases(console_mutex);

extern struct hlist_head console_list;
+extern bool consoles_suspended;

/**
* console_srcu_read_flags - Locklessly read flags of a possibly registered
@@ -561,6 +562,47 @@ static inline void console_srcu_write_flags(struct console *con, short flags)
WRITE_ONCE(con->flags, flags);
}

+/**
+ * consoles_suspended_srcu_read - Locklessly read the global flag for
+ * suspending all consoles.
+ *
+ * The global "consoles_suspended" flag is synchronized using console_list_lock
+ * and console_srcu_read_lock. It is the same approach as CON_SUSPENDED flag.
+ * See console_srcu_read_flags() for more details.
+ *
+ * Context: Any context.
+ * Return: The current value of the global "consoles_suspended" flag.
+ */
+static inline bool consoles_suspended_srcu_read(void)
+{
+ WARN_ON_ONCE(!console_srcu_read_lock_is_held());
+
+ /*
+ * The READ_ONCE() matches the WRITE_ONCE() when "consoles_suspended"
+ * is modified with consoles_suspended_srcu_write().
+ */
+ return data_race(READ_ONCE(consoles_suspended));
+}
+
+/**
+ * consoles_suspended_srcu_write - Write the global flag for suspending
+ * all consoles.
+ * @suspend: new value to write
+ *
+ * The write must be done under the console_list_lock. The caller is responsible
+ * for calling synchronize_srcu() to make sure that all callers checking the
+ * usablility of registered consoles see the new state.
+ *
+ * Context: Any context.
+ */
+static inline void consoles_suspended_srcu_write(bool suspend)
+{
+ lockdep_assert_console_list_lock_held();
+
+ /* This matches the READ_ONCE() in consoles_suspended_srcu_read(). */
+ WRITE_ONCE(consoles_suspended, suspend);
+}
+
/**
* console_srcu_is_nbcon - Locklessly check whether the console is nbcon
* @con: struct console pointer of console to check
@@ -658,8 +700,12 @@ extern void nbcon_kdb_release(struct nbcon_write_context *wctxt);

/* Variant of console_is_usable() when the console_list_lock is held. */
static inline bool __console_is_usable(struct console *con, short flags,
- enum nbcon_write_cb nwc)
+ bool all_suspended,
+ enum nbcon_write_cb nwc)
{
+ if (all_suspended)
+ return false;
+
if (!(flags & CON_ENABLED))
return false;

@@ -711,7 +757,9 @@ static inline bool __console_is_usable(struct console *con, short flags,
static inline bool console_is_usable(struct console *con,
enum nbcon_write_cb nwc)
{
- return __console_is_usable(con, console_srcu_read_flags(con), nwc);
+ return __console_is_usable(con, console_srcu_read_flags(con),
+ consoles_suspended_srcu_read(),
+ nwc);
}

#else
@@ -727,6 +775,7 @@ static inline bool nbcon_kdb_try_acquire(struct console *con,
struct nbcon_write_context *wctxt) { return false; }
static inline void nbcon_kdb_release(struct nbcon_write_context *wctxt) { }
static inline bool __console_is_usable(struct console *con, short flags,
+ bool all_suspended,
enum nbcon_write_cb nwc) { return false; }
static inline bool console_is_usable(struct console *con,
enum nbcon_write_cb nwc) { return false; }
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index b03ffc23c27c..173c14e08afe 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -104,6 +104,13 @@ DEFINE_STATIC_SRCU(console_srcu);
*/
int __read_mostly suppress_printk;

+/*
+ * Global flag for calling down all consoles during suspend.
+ * There is also a per-console flag which is used when the related
+ * device HW gets disabled, see CON_SUSPEND.
+ */
+bool consoles_suspended;
+
#ifdef CONFIG_LOCKDEP
static struct lockdep_map console_lock_dep_map = {
.name = "console_lock"
@@ -2731,8 +2738,6 @@ MODULE_PARM_DESC(console_no_auto_verbose, "Disable console loglevel raise to hig
*/
void console_suspend_all(void)
{
- struct console *con;
-
if (console_suspend_enabled)
pr_info("Suspending console(s) (use no_console_suspend to debug)\n");

@@ -2749,8 +2754,7 @@ void console_suspend_all(void)
return;

console_list_lock();
- for_each_console(con)
- console_srcu_write_flags(con, con->flags | CON_SUSPENDED);
+ consoles_suspended_srcu_write(true);
console_list_unlock();

/*
@@ -2765,7 +2769,6 @@ void console_suspend_all(void)
void console_resume_all(void)
{
struct console_flush_type ft;
- struct console *con;

/*
* Allow queueing irq_work. After restoring console state, deferred
@@ -2776,8 +2779,7 @@ void console_resume_all(void)

if (console_suspend_enabled) {
console_list_lock();
- for_each_console(con)
- console_srcu_write_flags(con, con->flags & ~CON_SUSPENDED);
+ consoles_suspended_srcu_write(false);
console_list_unlock();

/*
@@ -3557,7 +3559,7 @@ void console_suspend(struct console *console)
{
__pr_flush(console, 1000, true);
console_list_lock();
- console_srcu_write_flags(console, console->flags & ~CON_ENABLED);
+ console_srcu_write_flags(console, console->flags | CON_SUSPENDED);
console_list_unlock();

/*
@@ -3576,7 +3578,7 @@ void console_resume(struct console *console)
bool is_nbcon;

console_list_lock();
- console_srcu_write_flags(console, console->flags | CON_ENABLED);
+ console_srcu_write_flags(console, console->flags & ~CON_SUSPENDED);
is_nbcon = console->flags & CON_NBCON;
console_list_unlock();

@@ -4200,7 +4202,8 @@ static int unregister_console_locked(struct console *console)

if (!console_is_registered_locked(console))
res = -ENODEV;
- else if (__console_is_usable(console, console->flags, NBCON_USE_ATOMIC))
+ else if (__console_is_usable(console, console->flags,
+ consoles_suspended, NBCON_USE_ATOMIC))
__pr_flush(console, 1000, true);

/* Disable it unconditionally */

--
2.52.0