summaryrefslogtreecommitdiff
path: root/drivers/tty/serial/kgdboc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/kgdboc.c')
-rw-r--r--drivers/tty/serial/kgdboc.c46
1 files changed, 41 insertions, 5 deletions
diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c
index 7aa37be3216a..a3ed9b34e2ab 100644
--- a/drivers/tty/serial/kgdboc.c
+++ b/drivers/tty/serial/kgdboc.c
@@ -189,9 +189,27 @@ static int configure_kgdboc(void)
if (kgdboc_register_kbd(&cptr))
goto do_register;
+ /*
+ * tty_find_polling_driver() can call uart_set_options()
+ * (via poll_init) to configure the uart. Take the console_list_lock
+ * in order to synchronize against register_console(), which can also
+ * configure the uart via uart_set_options(). This also allows safe
+ * traversal of the console list.
+ */
+ console_list_lock();
+
p = tty_find_polling_driver(cptr, &tty_line);
- if (!p)
+ if (!p) {
+ console_list_unlock();
goto noconfig;
+ }
+
+ /*
+ * 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();
for_each_console(cons) {
int idx;
@@ -202,6 +220,10 @@ static int configure_kgdboc(void)
}
}
+ console_unlock();
+
+ console_list_unlock();
+
kgdb_tty_driver = p;
kgdb_tty_line = tty_line;
@@ -449,6 +471,7 @@ static void kgdboc_earlycon_pre_exp_handler(void)
{
struct console *con;
static bool already_warned;
+ int cookie;
if (already_warned)
return;
@@ -461,9 +484,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 +556,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 +606,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;