printk: Add per-console suspended state

Currently the global @console_suspended is used to determine if
consoles are in a suspended state. Its primary purpose is to allow
usage of the console_lock when suspended without causing console
printing. It is synchronized by the console_lock.

Rather than relying on the console_lock to determine suspended
state, make it an official per-console state that is set within
console->flags. This allows the state to be queried via SRCU.

Remove @console_suspended. Console printing will still be avoided
when suspended because console_is_usable() returns false when
the new suspended flag is set for that console.

Signed-off-by: John Ogness <john.ogness@linutronix.de>
Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
Reviewed-by: Petr Mladek <pmladek@suse.com>
Signed-off-by: Petr Mladek <pmladek@suse.com>
Link: https://lore.kernel.org/r/20230717194607.145135-7-john.ogness@linutronix.de
This commit is contained in:
John Ogness 2023-07-17 21:52:06 +02:06 committed by Petr Mladek
parent 696ffaf50e
commit 9e70a5e109
2 changed files with 47 additions and 30 deletions

View File

@ -154,6 +154,8 @@ static inline int con_debug_leave(void)
* receiving the printk spam for obvious reasons. * receiving the printk spam for obvious reasons.
* @CON_EXTENDED: The console supports the extended output format of * @CON_EXTENDED: The console supports the extended output format of
* /dev/kmesg which requires a larger output buffer. * /dev/kmesg which requires a larger output buffer.
* @CON_SUSPENDED: Indicates if a console is suspended. If true, the
* printing callbacks must not be called.
*/ */
enum cons_flags { enum cons_flags {
CON_PRINTBUFFER = BIT(0), CON_PRINTBUFFER = BIT(0),
@ -163,6 +165,7 @@ enum cons_flags {
CON_ANYTIME = BIT(4), CON_ANYTIME = BIT(4),
CON_BRL = BIT(5), CON_BRL = BIT(5),
CON_EXTENDED = BIT(6), CON_EXTENDED = BIT(6),
CON_SUSPENDED = BIT(7),
}; };
/** /**

View File

@ -86,7 +86,7 @@ EXPORT_SYMBOL(oops_in_progress);
static DEFINE_MUTEX(console_mutex); static DEFINE_MUTEX(console_mutex);
/* /*
* console_sem protects updates to console->seq and console_suspended, * console_sem protects updates to console->seq
* and also provides serialization for console printing. * and also provides serialization for console printing.
*/ */
static DEFINE_SEMAPHORE(console_sem); static DEFINE_SEMAPHORE(console_sem);
@ -359,7 +359,7 @@ static bool panic_in_progress(void)
* paths in the console code where we end up in places I want * paths in the console code where we end up in places I want
* locked without the console semaphore held). * locked without the console semaphore held).
*/ */
static int console_locked, console_suspended; static int console_locked;
/* /*
* Array of consoles built from command line options (console=) * Array of consoles built from command line options (console=)
@ -2549,22 +2549,46 @@ MODULE_PARM_DESC(console_no_auto_verbose, "Disable console loglevel raise to hig
*/ */
void suspend_console(void) void suspend_console(void)
{ {
struct console *con;
if (!console_suspend_enabled) if (!console_suspend_enabled)
return; return;
pr_info("Suspending console(s) (use no_console_suspend to debug)\n"); pr_info("Suspending console(s) (use no_console_suspend to debug)\n");
pr_flush(1000, true); pr_flush(1000, true);
console_lock();
console_suspended = 1; console_list_lock();
up_console_sem(); for_each_console(con)
console_srcu_write_flags(con, con->flags | CON_SUSPENDED);
console_list_unlock();
/*
* Ensure that all SRCU list walks have completed. All printing
* contexts must be able to see that they are suspended so that it
* is guaranteed that all printing has stopped when this function
* completes.
*/
synchronize_srcu(&console_srcu);
} }
void resume_console(void) void resume_console(void)
{ {
struct console *con;
if (!console_suspend_enabled) if (!console_suspend_enabled)
return; return;
down_console_sem();
console_suspended = 0; console_list_lock();
console_unlock(); for_each_console(con)
console_srcu_write_flags(con, con->flags & ~CON_SUSPENDED);
console_list_unlock();
/*
* Ensure that all SRCU list walks have completed. All printing
* contexts must be able to see they are no longer suspended so
* that they are guaranteed to wake up and resume printing.
*/
synchronize_srcu(&console_srcu);
pr_flush(1000, true); pr_flush(1000, true);
} }
@ -2623,8 +2647,6 @@ void console_lock(void)
msleep(1000); msleep(1000);
down_console_sem(); down_console_sem();
if (console_suspended)
return;
console_locked = 1; console_locked = 1;
console_may_schedule = 1; console_may_schedule = 1;
} }
@ -2645,10 +2667,6 @@ int console_trylock(void)
return 0; return 0;
if (down_trylock_console_sem()) if (down_trylock_console_sem())
return 0; return 0;
if (console_suspended) {
up_console_sem();
return 0;
}
console_locked = 1; console_locked = 1;
console_may_schedule = 0; console_may_schedule = 0;
return 1; return 1;
@ -2674,6 +2692,9 @@ static inline bool console_is_usable(struct console *con)
if (!(flags & CON_ENABLED)) if (!(flags & CON_ENABLED))
return false; return false;
if ((flags & CON_SUSPENDED))
return false;
if (!con->write) if (!con->write)
return false; return false;
@ -2992,11 +3013,6 @@ void console_unlock(void)
bool flushed; bool flushed;
u64 next_seq; u64 next_seq;
if (console_suspended) {
up_console_sem();
return;
}
/* /*
* Console drivers are called with interrupts disabled, so * Console drivers are called with interrupts disabled, so
* @console_may_schedule should be cleared before; however, we may * @console_may_schedule should be cleared before; however, we may
@ -3726,8 +3742,7 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
/* /*
* Hold the console_lock to guarantee safe access to * Hold the console_lock to guarantee safe access to
* console->seq and to prevent changes to @console_suspended * console->seq.
* until all consoles have been processed.
*/ */
console_lock(); console_lock();
@ -3735,6 +3750,11 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
for_each_console_srcu(c) { for_each_console_srcu(c) {
if (con && con != c) if (con && con != c)
continue; continue;
/*
* If consoles are not usable, it cannot be expected
* that they make forward progress, so only increment
* @diff for usable consoles.
*/
if (!console_is_usable(c)) if (!console_is_usable(c))
continue; continue;
printk_seq = c->seq; printk_seq = c->seq;
@ -3743,18 +3763,12 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
} }
console_srcu_read_unlock(cookie); console_srcu_read_unlock(cookie);
/* if (diff != last_diff && reset_on_progress)
* If consoles are suspended, it cannot be expected that they
* make forward progress, so timeout immediately. @diff is
* still used to return a valid flush status.
*/
if (console_suspended)
remaining = 0;
else if (diff != last_diff && reset_on_progress)
remaining = timeout_ms; remaining = timeout_ms;
console_unlock(); console_unlock();
/* Note: @diff is 0 if there are no usable consoles. */
if (diff == 0 || remaining == 0) if (diff == 0 || remaining == 0)
break; break;
@ -3788,7 +3802,7 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
* printer has been seen to make some forward progress. * printer has been seen to make some forward progress.
* *
* Context: Process context. May sleep while acquiring console lock. * Context: Process context. May sleep while acquiring console lock.
* Return: true if all enabled printers are caught up. * Return: true if all usable printers are caught up.
*/ */
static bool pr_flush(int timeout_ms, bool reset_on_progress) static bool pr_flush(int timeout_ms, bool reset_on_progress)
{ {