kdb: Add support for external NMI handler to call KGDB/KDB
This patch adds a kgdb_nmicallin() interface that can be used by external NMI handlers to call the KGDB/KDB handler. The primary need for this is for those types of NMI interrupts where all the CPUs have already received the NMI signal. Therefore no send_IPI(NMI) is required, and in fact it will cause a 2nd unhandled NMI to occur. This generates the "Dazed and Confuzed" messages. Since all the CPUs are getting the NMI at roughly the same time, it's not guaranteed that the first CPU that hits the NMI handler will manage to enter KGDB and set the dbg_master_lock before the slaves start entering. The new argument "send_ready" was added for KGDB to signal the NMI handler to release the slave CPUs for entry into KGDB. Signed-off-by: Mike Travis <travis@sgi.com> Acked-by: Jason Wessel <jason.wessel@windriver.com> Reviewed-by: Dimitri Sivanich <sivanich@sgi.com> Reviewed-by: Hedi Berriche <hedi@sgi.com> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Paul Mackerras <paulus@samba.org> Cc: Arnaldo Carvalho de Melo <acme@ghostprotocols.net> Link: http://lkml.kernel.org/r/20131002151417.928886849@asylum.americas.sgi.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
parent
8a1f4653f2
commit
8daaa5f826
|
@ -109,6 +109,7 @@ typedef enum {
|
|||
KDB_REASON_RECURSE, /* Recursive entry to kdb;
|
||||
* regs probably valid */
|
||||
KDB_REASON_SSTEP, /* Single Step trap. - regs valid */
|
||||
KDB_REASON_SYSTEM_NMI, /* In NMI due to SYSTEM cmd; regs valid */
|
||||
} kdb_reason_t;
|
||||
|
||||
extern int kdb_trap_printk;
|
||||
|
|
|
@ -310,6 +310,7 @@ extern int
|
|||
kgdb_handle_exception(int ex_vector, int signo, int err_code,
|
||||
struct pt_regs *regs);
|
||||
extern int kgdb_nmicallback(int cpu, void *regs);
|
||||
extern int kgdb_nmicallin(int cpu, int trapnr, void *regs, atomic_t *snd_rdy);
|
||||
extern void gdbstub_exit(int status);
|
||||
|
||||
extern int kgdb_single_step;
|
||||
|
|
|
@ -575,8 +575,12 @@ static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs,
|
|||
raw_spin_lock(&dbg_slave_lock);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/* If send_ready set, slaves are already waiting */
|
||||
if (ks->send_ready)
|
||||
atomic_set(ks->send_ready, 1);
|
||||
|
||||
/* Signal the other CPUs to enter kgdb_wait() */
|
||||
if ((!kgdb_single_step) && kgdb_do_roundup)
|
||||
else if ((!kgdb_single_step) && kgdb_do_roundup)
|
||||
kgdb_roundup_cpus(flags);
|
||||
#endif
|
||||
|
||||
|
@ -678,11 +682,11 @@ kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs)
|
|||
if (arch_kgdb_ops.enable_nmi)
|
||||
arch_kgdb_ops.enable_nmi(0);
|
||||
|
||||
memset(ks, 0, sizeof(struct kgdb_state));
|
||||
ks->cpu = raw_smp_processor_id();
|
||||
ks->ex_vector = evector;
|
||||
ks->signo = signo;
|
||||
ks->err_code = ecode;
|
||||
ks->kgdb_usethreadid = 0;
|
||||
ks->linux_regs = regs;
|
||||
|
||||
if (kgdb_reenter_check(ks))
|
||||
|
@ -732,6 +736,30 @@ int kgdb_nmicallback(int cpu, void *regs)
|
|||
return 1;
|
||||
}
|
||||
|
||||
int kgdb_nmicallin(int cpu, int trapnr, void *regs, atomic_t *send_ready)
|
||||
{
|
||||
#ifdef CONFIG_SMP
|
||||
if (!kgdb_io_ready(0) || !send_ready)
|
||||
return 1;
|
||||
|
||||
if (kgdb_info[cpu].enter_kgdb == 0) {
|
||||
struct kgdb_state kgdb_var;
|
||||
struct kgdb_state *ks = &kgdb_var;
|
||||
|
||||
memset(ks, 0, sizeof(struct kgdb_state));
|
||||
ks->cpu = cpu;
|
||||
ks->ex_vector = trapnr;
|
||||
ks->signo = SIGTRAP;
|
||||
ks->err_code = KGDB_KDB_REASON_SYSTEM_NMI;
|
||||
ks->linux_regs = regs;
|
||||
ks->send_ready = send_ready;
|
||||
kgdb_cpu_enter(ks, regs, DCPU_WANT_MASTER);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void kgdb_console_write(struct console *co, const char *s,
|
||||
unsigned count)
|
||||
{
|
||||
|
|
|
@ -26,6 +26,7 @@ struct kgdb_state {
|
|||
unsigned long threadid;
|
||||
long kgdb_usethreadid;
|
||||
struct pt_regs *linux_regs;
|
||||
atomic_t *send_ready;
|
||||
};
|
||||
|
||||
/* Exception state values */
|
||||
|
@ -74,11 +75,13 @@ extern int kdb_stub(struct kgdb_state *ks);
|
|||
extern int kdb_parse(const char *cmdstr);
|
||||
extern int kdb_common_init_state(struct kgdb_state *ks);
|
||||
extern int kdb_common_deinit_state(void);
|
||||
#define KGDB_KDB_REASON_SYSTEM_NMI KDB_REASON_SYSTEM_NMI
|
||||
#else /* ! CONFIG_KGDB_KDB */
|
||||
static inline int kdb_stub(struct kgdb_state *ks)
|
||||
{
|
||||
return DBG_PASS_EVENT;
|
||||
}
|
||||
#define KGDB_KDB_REASON_SYSTEM_NMI 0
|
||||
#endif /* CONFIG_KGDB_KDB */
|
||||
|
||||
#endif /* _DEBUG_CORE_H_ */
|
||||
|
|
|
@ -69,7 +69,10 @@ int kdb_stub(struct kgdb_state *ks)
|
|||
if (atomic_read(&kgdb_setting_breakpoint))
|
||||
reason = KDB_REASON_KEYBOARD;
|
||||
|
||||
if (in_nmi())
|
||||
if (ks->err_code == KDB_REASON_SYSTEM_NMI && ks->signo == SIGTRAP)
|
||||
reason = KDB_REASON_SYSTEM_NMI;
|
||||
|
||||
else if (in_nmi())
|
||||
reason = KDB_REASON_NMI;
|
||||
|
||||
for (i = 0, bp = kdb_breakpoints; i < KDB_MAXBPT; i++, bp++) {
|
||||
|
|
|
@ -1200,6 +1200,9 @@ static int kdb_local(kdb_reason_t reason, int error, struct pt_regs *regs,
|
|||
instruction_pointer(regs));
|
||||
kdb_dumpregs(regs);
|
||||
break;
|
||||
case KDB_REASON_SYSTEM_NMI:
|
||||
kdb_printf("due to System NonMaskable Interrupt\n");
|
||||
break;
|
||||
case KDB_REASON_NMI:
|
||||
kdb_printf("due to NonMaskable Interrupt @ "
|
||||
kdb_machreg_fmt "\n",
|
||||
|
|
Loading…
Reference in New Issue