kdb: Add support for external NMI handler to call KGDB/KDB
authorMike Travis <travis@sgi.com>
Wed, 2 Oct 2013 15:14:18 +0000 (10:14 -0500)
committerIngo Molnar <mingo@kernel.org>
Thu, 3 Oct 2013 16:47:54 +0000 (18:47 +0200)
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>
include/linux/kdb.h
include/linux/kgdb.h
kernel/debug/debug_core.c
kernel/debug/debug_core.h
kernel/debug/kdb/kdb_debugger.c
kernel/debug/kdb/kdb_main.c

index 7f6fe6e015bc8ade40f04ee8e3e4ba750ec45202..290db1269c4c7970ab016165c62d93eec53059b4 100644 (file)
@@ -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;
index c6e091bf39a52e73a0964036ef3e1888e7be61d9..dfb4f2ffdaa2e5eccddb6755f08305ff7a449891 100644 (file)
@@ -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;
index 0506d447aed270867214c9da188b4ac8a22ebecf..7d2f35e5df2f91c1ad0ec7a37741582929c22fec 100644 (file)
@@ -575,8 +575,12 @@ return_normal:
                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)
 {
index 2235967e78b0c8c6c181295269b2d7cc61e40232..572aa4f5677cb7c6034a72e040a6c0b9bd4fcb34 100644 (file)
@@ -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_ */
index 328d18ef31e4b4f02e7cde7aff71504ad8a0fb5e..8859ca34dcfe0a58dbd530b8668d8d88760a3eb4 100644 (file)
@@ -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++) {
index 00eb8f7fbf41c49e1deb4b09e6858858064ae9e9..0b097c8a1e50e0b0a0a299f6c1df240f15468b3f 100644 (file)
@@ -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",
This page took 0.04295 seconds and 5 git commands to generate.