diff options
Diffstat (limited to 'drivers/tty/serial/kgdboc.c')
| -rw-r--r-- | drivers/tty/serial/kgdboc.c | 36 |
1 files changed, 31 insertions, 5 deletions
diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index 7aa37be3216a..7ce7bb164005 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -171,6 +171,7 @@ static int configure_kgdboc(void) int err = -ENODEV; char *cptr = config; struct console *cons; + int cookie; if (!strlen(config) || isspace(config[0])) { err = 0; @@ -193,7 +194,15 @@ static int configure_kgdboc(void) if (!p) goto noconfig; - for_each_console(cons) { + /* + * Take console_lock to serialize device() callback with + * other console operations. For example, fg_console is + * modified under console_lock when switching vt. + */ + console_lock(); + + cookie = console_srcu_read_lock(); + for_each_console_srcu(cons) { int idx; if (cons->device && cons->device(cons, &idx) == p && idx == tty_line) { @@ -201,6 +210,9 @@ static int configure_kgdboc(void) break; } } + console_srcu_read_unlock(cookie); + + console_unlock(); kgdb_tty_driver = p; kgdb_tty_line = tty_line; @@ -449,6 +461,7 @@ static void kgdboc_earlycon_pre_exp_handler(void) { struct console *con; static bool already_warned; + int cookie; if (already_warned) return; @@ -461,9 +474,14 @@ static void kgdboc_earlycon_pre_exp_handler(void) * serial drivers might be OK with this, print a warning once per * boot if we detect this case. */ - for_each_console(con) + cookie = console_srcu_read_lock(); + for_each_console_srcu(con) { if (con == kgdboc_earlycon_io_ops.cons) - return; + break; + } + console_srcu_read_unlock(cookie); + if (con) + return; already_warned = true; pr_warn("kgdboc_earlycon is still using bootconsole\n"); @@ -528,7 +546,15 @@ static int __init kgdboc_earlycon_init(char *opt) * Look for a matching console, or if the name was left blank just * pick the first one we find. */ - console_lock(); + + /* + * Hold the console_list_lock to guarantee that no consoles are + * unregistered until the kgdboc_earlycon setup is complete. + * Trapping the exit() callback relies on exit() not being + * called until the trap is setup. This also allows safe + * traversal of the console list and race-free reading of @flags. + */ + console_list_lock(); for_each_console(con) { if (con->write && con->read && (con->flags & (CON_BOOT | CON_ENABLED)) && @@ -570,7 +596,7 @@ static int __init kgdboc_earlycon_init(char *opt) } unlock: - console_unlock(); + console_list_unlock(); /* Non-zero means malformed option so we always return zero */ return 0; |