kgdb patches for 5.8-rc3
The main change here is a fix for a number of unsafe interactions between kdb and the console system. The fixes are specific to kdb (pure kgdb debugging does not use the console system at all). On systems with an NMI then kdb, if it is enabled, must get messages to the user despite potentially running from some "difficult" calling contexts. These fixes avoid using the console system where we have been provided an alternative (safer) way to interact with the user and, if using the console system in unavoidable, use oops_in_progress for deadlock avoidance. These fixes also ensure kdb honours the console enable flag. Also included is a fix that wraps kgdb trap handling in an RCU read lock to avoids triggering diagnostic warnings. This is a wide lock scope but this is OK because kgdb is a stop-the-world debugger. When we stop the world we put all the CPUs into holding pens and this inhibits RCU update anyway. Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org> -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEELzVBU1D3lWq6cKzwfOMlXTn3iKEFAl72C5AACgkQfOMlXTn3 iKFfmA//SCJU7zJsrKTsr6+HJY+gIuwHm70aGCNIr3EjBgTZQHQYflG6msmMHTAX d4qnGSkfKzC8jYJrHPpX4eU3bnqYci6GnaT/N5p9YkTGHun+kYYTz3wLzZiWxKRg iE4QLEwjU/dGAYyRz0CKCTRNTLTG+R79HWLL2Wi5OQiNhYiPuFAgS/NSUjpnJIuf fmj8jSPP/7T/m0cEUWXbLwTfolEZLIa1heqtaJq4fAftPsAk5a5TZ0NugaxUPoo4 YS06eASIZoVcDQiehVy+gH05FyEjJGXnkFtTkAoRL/yOERKLy0WMzFZAAh6NT4St 16Hx3Nnw+7ds7Iq8jEIpM/XJo1d3haYvAQdzy6HakAOwp7vrD/CjF45wwju78woY Jq54Vjvaxjaw1vlJCVrAAjdj3bAHdufBeWrBGmYO8F1HSn9eNeLS7wWbq6lEhxNd ObXRUFwebzYpOT6DI2TdnDg/2+xAn2oXpzk4UK9I/Vbxew8R4lOPQm4vC0V3CTME cHXFGV3ncjXlVRKdMAmnYcN7pMY4NCdX5vGqC/djQRwKRV1Ve8jwUCFVKRAd4zio wHpCFziwSaz9giZJ5I831EKsvSj9DVoPPJFgoEXIzIWF3OS0qzP6UqO2HwJNbA+e W4laVRzdBcMuVVa+7XWYzdAhof0hNX0Ov78dyDMcX1MkOS02O7o= =ovnT -----END PGP SIGNATURE----- Merge tag 'kgdb-5.8-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/danielt/linux Pull kgdb fixes from Daniel Thompson: "The main change here is a fix for a number of unsafe interactions between kdb and the console system. The fixes are specific to kdb (pure kgdb debugging does not use the console system at all). On systems with an NMI then kdb, if it is enabled, must get messages to the user despite potentially running from some "difficult" calling contexts. These fixes avoid using the console system where we have been provided an alternative (safer) way to interact with the user and, if using the console system in unavoidable, use oops_in_progress for deadlock avoidance. These fixes also ensure kdb honours the console enable flag. Also included is a fix that wraps kgdb trap handling in an RCU read lock to avoids triggering diagnostic warnings. This is a wide lock scope but this is OK because kgdb is a stop-the-world debugger. When we stop the world we put all the CPUs into holding pens and this inhibits RCU update anyway" * tag 'kgdb-5.8-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/danielt/linux: kgdb: Avoid suspicious RCU usage warning kdb: Switch to use safer dbg_io_ops over console APIs kdb: Make kdb_printf() console handling more robust kdb: Check status of console prior to invoking handlers kdb: Re-factor kdb_printf() message write code
This commit is contained in:
commit
6116dea80d
@ -50,7 +50,7 @@ static int kgdb_nmi_console_setup(struct console *co, char *options)
|
||||
* I/O utilities that messages sent to the console will automatically
|
||||
* be displayed on the dbg_io.
|
||||
*/
|
||||
dbg_io_ops->is_console = true;
|
||||
dbg_io_ops->cons = co;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -45,7 +45,6 @@ static struct platform_device *kgdboc_pdev;
|
||||
|
||||
#if IS_BUILTIN(CONFIG_KGDB_SERIAL_CONSOLE)
|
||||
static struct kgdb_io kgdboc_earlycon_io_ops;
|
||||
static struct console *earlycon;
|
||||
static int (*earlycon_orig_exit)(struct console *con);
|
||||
#endif /* IS_BUILTIN(CONFIG_KGDB_SERIAL_CONSOLE) */
|
||||
|
||||
@ -145,7 +144,7 @@ static void kgdboc_unregister_kbd(void)
|
||||
#if IS_BUILTIN(CONFIG_KGDB_SERIAL_CONSOLE)
|
||||
static void cleanup_earlycon(void)
|
||||
{
|
||||
if (earlycon)
|
||||
if (kgdboc_earlycon_io_ops.cons)
|
||||
kgdb_unregister_io_module(&kgdboc_earlycon_io_ops);
|
||||
}
|
||||
#else /* !IS_BUILTIN(CONFIG_KGDB_SERIAL_CONSOLE) */
|
||||
@ -178,7 +177,7 @@ static int configure_kgdboc(void)
|
||||
goto noconfig;
|
||||
}
|
||||
|
||||
kgdboc_io_ops.is_console = 0;
|
||||
kgdboc_io_ops.cons = NULL;
|
||||
kgdb_tty_driver = NULL;
|
||||
|
||||
kgdboc_use_kms = 0;
|
||||
@ -198,7 +197,7 @@ static int configure_kgdboc(void)
|
||||
int idx;
|
||||
if (cons->device && cons->device(cons, &idx) == p &&
|
||||
idx == tty_line) {
|
||||
kgdboc_io_ops.is_console = 1;
|
||||
kgdboc_io_ops.cons = cons;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -433,7 +432,8 @@ static int kgdboc_earlycon_get_char(void)
|
||||
{
|
||||
char c;
|
||||
|
||||
if (!earlycon->read(earlycon, &c, 1))
|
||||
if (!kgdboc_earlycon_io_ops.cons->read(kgdboc_earlycon_io_ops.cons,
|
||||
&c, 1))
|
||||
return NO_POLL_CHAR;
|
||||
|
||||
return c;
|
||||
@ -441,7 +441,8 @@ static int kgdboc_earlycon_get_char(void)
|
||||
|
||||
static void kgdboc_earlycon_put_char(u8 chr)
|
||||
{
|
||||
earlycon->write(earlycon, &chr, 1);
|
||||
kgdboc_earlycon_io_ops.cons->write(kgdboc_earlycon_io_ops.cons, &chr,
|
||||
1);
|
||||
}
|
||||
|
||||
static void kgdboc_earlycon_pre_exp_handler(void)
|
||||
@ -461,7 +462,7 @@ static void kgdboc_earlycon_pre_exp_handler(void)
|
||||
* boot if we detect this case.
|
||||
*/
|
||||
for_each_console(con)
|
||||
if (con == earlycon)
|
||||
if (con == kgdboc_earlycon_io_ops.cons)
|
||||
return;
|
||||
|
||||
already_warned = true;
|
||||
@ -484,25 +485,25 @@ static int kgdboc_earlycon_deferred_exit(struct console *con)
|
||||
|
||||
static void kgdboc_earlycon_deinit(void)
|
||||
{
|
||||
if (!earlycon)
|
||||
if (!kgdboc_earlycon_io_ops.cons)
|
||||
return;
|
||||
|
||||
if (earlycon->exit == kgdboc_earlycon_deferred_exit)
|
||||
if (kgdboc_earlycon_io_ops.cons->exit == kgdboc_earlycon_deferred_exit)
|
||||
/*
|
||||
* kgdboc_earlycon is exiting but original boot console exit
|
||||
* was never called (AKA kgdboc_earlycon_deferred_exit()
|
||||
* didn't ever run). Undo our trap.
|
||||
*/
|
||||
earlycon->exit = earlycon_orig_exit;
|
||||
else if (earlycon->exit)
|
||||
kgdboc_earlycon_io_ops.cons->exit = earlycon_orig_exit;
|
||||
else if (kgdboc_earlycon_io_ops.cons->exit)
|
||||
/*
|
||||
* We skipped calling the exit() routine so we could try to
|
||||
* keep using the boot console even after it went away. We're
|
||||
* finally done so call the function now.
|
||||
*/
|
||||
earlycon->exit(earlycon);
|
||||
kgdboc_earlycon_io_ops.cons->exit(kgdboc_earlycon_io_ops.cons);
|
||||
|
||||
earlycon = NULL;
|
||||
kgdboc_earlycon_io_ops.cons = NULL;
|
||||
}
|
||||
|
||||
static struct kgdb_io kgdboc_earlycon_io_ops = {
|
||||
@ -511,7 +512,6 @@ static struct kgdb_io kgdboc_earlycon_io_ops = {
|
||||
.write_char = kgdboc_earlycon_put_char,
|
||||
.pre_exception = kgdboc_earlycon_pre_exp_handler,
|
||||
.deinit = kgdboc_earlycon_deinit,
|
||||
.is_console = true,
|
||||
};
|
||||
|
||||
#define MAX_CONSOLE_NAME_LEN (sizeof((struct console *) 0)->name)
|
||||
@ -557,10 +557,10 @@ static int __init kgdboc_earlycon_init(char *opt)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
earlycon = con;
|
||||
kgdboc_earlycon_io_ops.cons = con;
|
||||
pr_info("Going to register kgdb with earlycon '%s'\n", con->name);
|
||||
if (kgdb_register_io_module(&kgdboc_earlycon_io_ops) != 0) {
|
||||
earlycon = NULL;
|
||||
kgdboc_earlycon_io_ops.cons = NULL;
|
||||
pr_info("Failed to register kgdb with earlycon\n");
|
||||
} else {
|
||||
/* Trap exit so we can keep earlycon longer if needed. */
|
||||
|
@ -1058,7 +1058,8 @@ static int __init kgdbdbgp_parse_config(char *str)
|
||||
kgdbdbgp_wait_time = simple_strtoul(ptr, &ptr, 10);
|
||||
}
|
||||
kgdb_register_io_module(&kgdbdbgp_io_ops);
|
||||
kgdbdbgp_io_ops.is_console = early_dbgp_console.index != -1;
|
||||
if (early_dbgp_console.index != -1)
|
||||
kgdbdbgp_io_ops.cons = &early_dbgp_console;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -276,8 +276,7 @@ struct kgdb_arch {
|
||||
* the I/O driver.
|
||||
* @post_exception: Pointer to a function that will do any cleanup work
|
||||
* for the I/O driver.
|
||||
* @is_console: 1 if the end device is a console 0 if the I/O device is
|
||||
* not a console
|
||||
* @cons: valid if the I/O device is a console; else NULL.
|
||||
*/
|
||||
struct kgdb_io {
|
||||
const char *name;
|
||||
@ -288,7 +287,7 @@ struct kgdb_io {
|
||||
void (*deinit) (void);
|
||||
void (*pre_exception) (void);
|
||||
void (*post_exception) (void);
|
||||
int is_console;
|
||||
struct console *cons;
|
||||
};
|
||||
|
||||
extern const struct kgdb_arch arch_kgdb_ops;
|
||||
|
@ -587,6 +587,7 @@ static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs,
|
||||
arch_kgdb_ops.disable_hw_break(regs);
|
||||
|
||||
acquirelock:
|
||||
rcu_read_lock();
|
||||
/*
|
||||
* Interrupts will be restored by the 'trap return' code, except when
|
||||
* single stepping.
|
||||
@ -646,6 +647,7 @@ return_normal:
|
||||
atomic_dec(&slaves_in_kgdb);
|
||||
dbg_touch_watchdogs();
|
||||
local_irq_restore(flags);
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
cpu_relax();
|
||||
@ -664,6 +666,7 @@ return_normal:
|
||||
raw_spin_unlock(&dbg_master_lock);
|
||||
dbg_touch_watchdogs();
|
||||
local_irq_restore(flags);
|
||||
rcu_read_unlock();
|
||||
|
||||
goto acquirelock;
|
||||
}
|
||||
@ -787,6 +790,7 @@ kgdb_restore:
|
||||
raw_spin_unlock(&dbg_master_lock);
|
||||
dbg_touch_watchdogs();
|
||||
local_irq_restore(flags);
|
||||
rcu_read_unlock();
|
||||
|
||||
return kgdb_info[cpu].ret_state;
|
||||
}
|
||||
|
@ -542,6 +542,44 @@ static int kdb_search_string(char *searched, char *searchfor)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kdb_msg_write(const char *msg, int msg_len)
|
||||
{
|
||||
struct console *c;
|
||||
|
||||
if (msg_len == 0)
|
||||
return;
|
||||
|
||||
if (dbg_io_ops) {
|
||||
const char *cp = msg;
|
||||
int len = msg_len;
|
||||
|
||||
while (len--) {
|
||||
dbg_io_ops->write_char(*cp);
|
||||
cp++;
|
||||
}
|
||||
}
|
||||
|
||||
for_each_console(c) {
|
||||
if (!(c->flags & CON_ENABLED))
|
||||
continue;
|
||||
if (c == dbg_io_ops->cons)
|
||||
continue;
|
||||
/*
|
||||
* Set oops_in_progress to encourage the console drivers to
|
||||
* disregard their internal spin locks: in the current calling
|
||||
* context the risk of deadlock is a bigger problem than risks
|
||||
* due to re-entering the console driver. We operate directly on
|
||||
* oops_in_progress rather than using bust_spinlocks() because
|
||||
* the calls bust_spinlocks() makes on exit are not appropriate
|
||||
* for this calling context.
|
||||
*/
|
||||
++oops_in_progress;
|
||||
c->write(c, msg, msg_len);
|
||||
--oops_in_progress;
|
||||
touch_nmi_watchdog();
|
||||
}
|
||||
}
|
||||
|
||||
int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap)
|
||||
{
|
||||
int diag;
|
||||
@ -553,7 +591,6 @@ int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap)
|
||||
int this_cpu, old_cpu;
|
||||
char *cp, *cp2, *cphold = NULL, replaced_byte = ' ';
|
||||
char *moreprompt = "more> ";
|
||||
struct console *c;
|
||||
unsigned long uninitialized_var(flags);
|
||||
|
||||
/* Serialize kdb_printf if multiple cpus try to write at once.
|
||||
@ -687,22 +724,11 @@ kdb_printit:
|
||||
*/
|
||||
retlen = strlen(kdb_buffer);
|
||||
cp = (char *) printk_skip_headers(kdb_buffer);
|
||||
if (!dbg_kdb_mode && kgdb_connected) {
|
||||
if (!dbg_kdb_mode && kgdb_connected)
|
||||
gdbstub_msg_write(cp, retlen - (cp - kdb_buffer));
|
||||
} else {
|
||||
if (dbg_io_ops && !dbg_io_ops->is_console) {
|
||||
len = retlen - (cp - kdb_buffer);
|
||||
cp2 = cp;
|
||||
while (len--) {
|
||||
dbg_io_ops->write_char(*cp2);
|
||||
cp2++;
|
||||
}
|
||||
}
|
||||
for_each_console(c) {
|
||||
c->write(c, cp, retlen - (cp - kdb_buffer));
|
||||
touch_nmi_watchdog();
|
||||
}
|
||||
}
|
||||
else
|
||||
kdb_msg_write(cp, retlen - (cp - kdb_buffer));
|
||||
|
||||
if (logging) {
|
||||
saved_loglevel = console_loglevel;
|
||||
console_loglevel = CONSOLE_LOGLEVEL_SILENT;
|
||||
@ -751,19 +777,7 @@ kdb_printit:
|
||||
moreprompt = "more> ";
|
||||
|
||||
kdb_input_flush();
|
||||
|
||||
if (dbg_io_ops && !dbg_io_ops->is_console) {
|
||||
len = strlen(moreprompt);
|
||||
cp = moreprompt;
|
||||
while (len--) {
|
||||
dbg_io_ops->write_char(*cp);
|
||||
cp++;
|
||||
}
|
||||
}
|
||||
for_each_console(c) {
|
||||
c->write(c, moreprompt, strlen(moreprompt));
|
||||
touch_nmi_watchdog();
|
||||
}
|
||||
kdb_msg_write(moreprompt, strlen(moreprompt));
|
||||
|
||||
if (logging)
|
||||
printk("%s", moreprompt);
|
||||
|
Loading…
Reference in New Issue
Block a user