Skip to content

Commit 3ef5abd

Browse files
jognesspmladek
authored andcommitted
tty: serial: kgdboc: fix mutex locking order for configure_kgdboc()
Several mutexes are taken while setting up console serial ports. In particular, the tty_port->mutex and @console_mutex are taken: serial_pnp_probe serial8250_register_8250_port uart_add_one_port (locks tty_port->mutex) uart_configure_port register_console (locks @console_mutex) In order to synchronize kgdb's tty_find_polling_driver() with register_console(), commit 6193bc9 ("tty: serial: kgdboc: synchronize tty_find_polling_driver() and register_console()") takes the @console_mutex. However, this leads to the following call chain (with locking): platform_probe kgdboc_probe configure_kgdboc (locks @console_mutex) tty_find_polling_driver uart_poll_init (locks tty_port->mutex) uart_set_options This is clearly deadlock potential due to the reverse lock ordering. Since uart_set_options() requires holding @console_mutex in order to serialize early initialization of the serial-console lock, take the @console_mutex in uart_poll_init() instead of configure_kgdboc(). Since configure_kgdboc() was using @console_mutex for safe traversal of the console list, change it to use the SRCU iterator instead. Add comments to uart_set_options() kerneldoc mentioning that it requires holding @console_mutex (aka the console_list_lock). Fixes: 6193bc9 ("tty: serial: kgdboc: synchronize tty_find_polling_driver() and register_console()") Signed-off-by: John Ogness <[email protected]> Reviewed-by: Sergey Senozhatsky <[email protected]> Reviewed-by: Petr Mladek <[email protected]> [[email protected]: Export console_srcu_read_lock_is_held() to fix build kgdboc as a module.] Signed-off-by: Petr Mladek <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 5074ffb commit 3ef5abd

File tree

3 files changed

+11
-15
lines changed

3 files changed

+11
-15
lines changed

drivers/tty/serial/kgdboc.c

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ static int configure_kgdboc(void)
171171
int err = -ENODEV;
172172
char *cptr = config;
173173
struct console *cons;
174+
int cookie;
174175

175176
if (!strlen(config) || isspace(config[0])) {
176177
err = 0;
@@ -189,20 +190,9 @@ static int configure_kgdboc(void)
189190
if (kgdboc_register_kbd(&cptr))
190191
goto do_register;
191192

192-
/*
193-
* tty_find_polling_driver() can call uart_set_options()
194-
* (via poll_init) to configure the uart. Take the console_list_lock
195-
* in order to synchronize against register_console(), which can also
196-
* configure the uart via uart_set_options(). This also allows safe
197-
* traversal of the console list.
198-
*/
199-
console_list_lock();
200-
201193
p = tty_find_polling_driver(cptr, &tty_line);
202-
if (!p) {
203-
console_list_unlock();
194+
if (!p)
204195
goto noconfig;
205-
}
206196

207197
/*
208198
* Take console_lock to serialize device() callback with
@@ -211,19 +201,19 @@ static int configure_kgdboc(void)
211201
*/
212202
console_lock();
213203

214-
for_each_console(cons) {
204+
cookie = console_srcu_read_lock();
205+
for_each_console_srcu(cons) {
215206
int idx;
216207
if (cons->device && cons->device(cons, &idx) == p &&
217208
idx == tty_line) {
218209
kgdboc_io_ops.cons = cons;
219210
break;
220211
}
221212
}
213+
console_srcu_read_unlock(cookie);
222214

223215
console_unlock();
224216

225-
console_list_unlock();
226-
227217
kgdb_tty_driver = p;
228218
kgdb_tty_line = tty_line;
229219

drivers/tty/serial/serial_core.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2212,6 +2212,9 @@ EXPORT_SYMBOL_GPL(uart_parse_options);
22122212
* @parity: parity character - 'n' (none), 'o' (odd), 'e' (even)
22132213
* @bits: number of data bits
22142214
* @flow: flow control character - 'r' (rts)
2215+
*
2216+
* Locking: Caller must hold console_list_lock in order to serialize
2217+
* early initialization of the serial-console lock.
22152218
*/
22162219
int
22172220
uart_set_options(struct uart_port *port, struct console *co,
@@ -2619,7 +2622,9 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options)
26192622

26202623
if (!ret && options) {
26212624
uart_parse_options(options, &baud, &parity, &bits, &flow);
2625+
console_list_lock();
26222626
ret = uart_set_options(port, NULL, baud, parity, bits, flow);
2627+
console_list_unlock();
26232628
}
26242629
out:
26252630
mutex_unlock(&tport->mutex);

kernel/printk/printk.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ bool console_srcu_read_lock_is_held(void)
123123
{
124124
return srcu_read_lock_held(&console_srcu);
125125
}
126+
EXPORT_SYMBOL(console_srcu_read_lock_is_held);
126127
#endif
127128

128129
enum devkmsg_log_bits {

0 commit comments

Comments
 (0)