Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kdb: Switch to use safer dbg_io_ops over console APIs

In kgdb context, calling console handlers aren't safe due to locks used
in those handlers which could in turn lead to a deadlock. Although, using
oops_in_progress increases the chance to bypass locks in most console
handlers but it might not be sufficient enough in case a console uses
more locks (VT/TTY is good example).

Currently when a driver provides both polling I/O and a console then kdb
will output using the console. We can increase robustness by using the
currently active polling I/O driver (which should be lockless) instead
of the corresponding console. For several common cases (e.g. an
embedded system with a single serial port that is used both for console
output and debugger I/O) this will result in no console handler being
used.

In order to achieve this we need to reverse the order of preference to
use dbg_io_ops (uses polling I/O mode) over console APIs. So we just
store "struct console" that represents debugger I/O in dbg_io_ops and
while emitting kdb messages, skip console that matches dbg_io_ops
console in order to avoid duplicate messages. After this change,
"is_console" param becomes redundant and hence removed.

Suggested-by: Daniel Thompson <daniel.thompson@linaro.org>
Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
Link: https://lore.kernel.org/r/1591264879-25920-5-git-send-email-sumit.garg@linaro.org
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Reviewed-by: Petr Mladek <pmladek@suse.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>

authored by

Sumit Garg and committed by
Daniel Thompson
5946d1f5 2a78b85b

+24 -22
+1 -1
drivers/tty/serial/kgdb_nmi.c
··· 50 50 * I/O utilities that messages sent to the console will automatically 51 51 * be displayed on the dbg_io. 52 52 */ 53 - dbg_io_ops->is_console = true; 53 + dbg_io_ops->cons = co; 54 54 55 55 return 0; 56 56 }
+16 -16
drivers/tty/serial/kgdboc.c
··· 45 45 46 46 #if IS_BUILTIN(CONFIG_KGDB_SERIAL_CONSOLE) 47 47 static struct kgdb_io kgdboc_earlycon_io_ops; 48 - static struct console *earlycon; 49 48 static int (*earlycon_orig_exit)(struct console *con); 50 49 #endif /* IS_BUILTIN(CONFIG_KGDB_SERIAL_CONSOLE) */ 51 50 ··· 144 145 #if IS_BUILTIN(CONFIG_KGDB_SERIAL_CONSOLE) 145 146 static void cleanup_earlycon(void) 146 147 { 147 - if (earlycon) 148 + if (kgdboc_earlycon_io_ops.cons) 148 149 kgdb_unregister_io_module(&kgdboc_earlycon_io_ops); 149 150 } 150 151 #else /* !IS_BUILTIN(CONFIG_KGDB_SERIAL_CONSOLE) */ ··· 177 178 goto noconfig; 178 179 } 179 180 180 - kgdboc_io_ops.is_console = 0; 181 + kgdboc_io_ops.cons = NULL; 181 182 kgdb_tty_driver = NULL; 182 183 183 184 kgdboc_use_kms = 0; ··· 197 198 int idx; 198 199 if (cons->device && cons->device(cons, &idx) == p && 199 200 idx == tty_line) { 200 - kgdboc_io_ops.is_console = 1; 201 + kgdboc_io_ops.cons = cons; 201 202 break; 202 203 } 203 204 } ··· 432 433 { 433 434 char c; 434 435 435 - if (!earlycon->read(earlycon, &c, 1)) 436 + if (!kgdboc_earlycon_io_ops.cons->read(kgdboc_earlycon_io_ops.cons, 437 + &c, 1)) 436 438 return NO_POLL_CHAR; 437 439 438 440 return c; ··· 441 441 442 442 static void kgdboc_earlycon_put_char(u8 chr) 443 443 { 444 - earlycon->write(earlycon, &chr, 1); 444 + kgdboc_earlycon_io_ops.cons->write(kgdboc_earlycon_io_ops.cons, &chr, 445 + 1); 445 446 } 446 447 447 448 static void kgdboc_earlycon_pre_exp_handler(void) ··· 462 461 * boot if we detect this case. 463 462 */ 464 463 for_each_console(con) 465 - if (con == earlycon) 464 + if (con == kgdboc_earlycon_io_ops.cons) 466 465 return; 467 466 468 467 already_warned = true; ··· 485 484 486 485 static void kgdboc_earlycon_deinit(void) 487 486 { 488 - if (!earlycon) 487 + if (!kgdboc_earlycon_io_ops.cons) 489 488 return; 490 489 491 - if (earlycon->exit == kgdboc_earlycon_deferred_exit) 490 + if (kgdboc_earlycon_io_ops.cons->exit == kgdboc_earlycon_deferred_exit) 492 491 /* 493 492 * kgdboc_earlycon is exiting but original boot console exit 494 493 * was never called (AKA kgdboc_earlycon_deferred_exit() 495 494 * didn't ever run). Undo our trap. 496 495 */ 497 - earlycon->exit = earlycon_orig_exit; 498 - else if (earlycon->exit) 496 + kgdboc_earlycon_io_ops.cons->exit = earlycon_orig_exit; 497 + else if (kgdboc_earlycon_io_ops.cons->exit) 499 498 /* 500 499 * We skipped calling the exit() routine so we could try to 501 500 * keep using the boot console even after it went away. We're 502 501 * finally done so call the function now. 503 502 */ 504 - earlycon->exit(earlycon); 503 + kgdboc_earlycon_io_ops.cons->exit(kgdboc_earlycon_io_ops.cons); 505 504 506 - earlycon = NULL; 505 + kgdboc_earlycon_io_ops.cons = NULL; 507 506 } 508 507 509 508 static struct kgdb_io kgdboc_earlycon_io_ops = { ··· 512 511 .write_char = kgdboc_earlycon_put_char, 513 512 .pre_exception = kgdboc_earlycon_pre_exp_handler, 514 513 .deinit = kgdboc_earlycon_deinit, 515 - .is_console = true, 516 514 }; 517 515 518 516 #define MAX_CONSOLE_NAME_LEN (sizeof((struct console *) 0)->name) ··· 557 557 goto unlock; 558 558 } 559 559 560 - earlycon = con; 560 + kgdboc_earlycon_io_ops.cons = con; 561 561 pr_info("Going to register kgdb with earlycon '%s'\n", con->name); 562 562 if (kgdb_register_io_module(&kgdboc_earlycon_io_ops) != 0) { 563 - earlycon = NULL; 563 + kgdboc_earlycon_io_ops.cons = NULL; 564 564 pr_info("Failed to register kgdb with earlycon\n"); 565 565 } else { 566 566 /* Trap exit so we can keep earlycon longer if needed. */
+2 -1
drivers/usb/early/ehci-dbgp.c
··· 1058 1058 kgdbdbgp_wait_time = simple_strtoul(ptr, &ptr, 10); 1059 1059 } 1060 1060 kgdb_register_io_module(&kgdbdbgp_io_ops); 1061 - kgdbdbgp_io_ops.is_console = early_dbgp_console.index != -1; 1061 + if (early_dbgp_console.index != -1) 1062 + kgdbdbgp_io_ops.cons = &early_dbgp_console; 1062 1063 1063 1064 return 0; 1064 1065 }
+2 -3
include/linux/kgdb.h
··· 276 276 * the I/O driver. 277 277 * @post_exception: Pointer to a function that will do any cleanup work 278 278 * for the I/O driver. 279 - * @is_console: 1 if the end device is a console 0 if the I/O device is 280 - * not a console 279 + * @cons: valid if the I/O device is a console; else NULL. 281 280 */ 282 281 struct kgdb_io { 283 282 const char *name; ··· 287 288 void (*deinit) (void); 288 289 void (*pre_exception) (void); 289 290 void (*post_exception) (void); 290 - int is_console; 291 + struct console *cons; 291 292 }; 292 293 293 294 extern const struct kgdb_arch arch_kgdb_ops;
+3 -1
kernel/debug/kdb/kdb_io.c
··· 549 549 if (msg_len == 0) 550 550 return; 551 551 552 - if (dbg_io_ops && !dbg_io_ops->is_console) { 552 + if (dbg_io_ops) { 553 553 const char *cp = msg; 554 554 int len = msg_len; 555 555 ··· 561 561 562 562 for_each_console(c) { 563 563 if (!(c->flags & CON_ENABLED)) 564 + continue; 565 + if (c == dbg_io_ops->cons) 564 566 continue; 565 567 /* 566 568 * Set oops_in_progress to encourage the console drivers to